@nicia-ai/typegraph 0.46.2 → 0.48.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 +58 -49
- package/dist/backend/index.d.cts +37 -55
- package/dist/backend/index.d.ts +37 -55
- package/dist/backend/index.js +11 -10
- package/dist/backend/postgres/index.cjs +27 -26
- package/dist/backend/postgres/index.d.cts +21 -7
- package/dist/backend/postgres/index.d.ts +21 -7
- 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 +20 -7
- package/dist/backend/sqlite/index.d.ts +20 -7
- 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-TCT66A7N.cjs → chunk-26YKDNUD.cjs} +10 -10
- package/dist/{chunk-TCT66A7N.cjs.map → chunk-26YKDNUD.cjs.map} +1 -1
- package/dist/{chunk-E4OSGBCM.cjs → chunk-2HIYYMWY.cjs} +139 -136
- package/dist/chunk-2HIYYMWY.cjs.map +1 -0
- package/dist/chunk-2QGYQBS2.cjs +161 -0
- package/dist/chunk-2QGYQBS2.cjs.map +1 -0
- package/dist/{chunk-GWXXHOLU.js → chunk-2SYZXD54.js} +21 -18
- package/dist/chunk-2SYZXD54.js.map +1 -0
- package/dist/{chunk-KA4L2NZ3.js → chunk-2TYSSBU5.js} +14 -14
- package/dist/{chunk-KA4L2NZ3.js.map → chunk-2TYSSBU5.js.map} +1 -1
- package/dist/{chunk-QOJ2L2AD.js → chunk-3DMMGT6S.js} +3 -3
- package/dist/{chunk-QOJ2L2AD.js.map → chunk-3DMMGT6S.js.map} +1 -1
- package/dist/{chunk-F36KZDFX.cjs → chunk-3FPR224C.cjs} +91 -91
- package/dist/{chunk-F36KZDFX.cjs.map → chunk-3FPR224C.cjs.map} +1 -1
- package/dist/{chunk-32UETY6S.cjs → chunk-3KOY4FH3.cjs} +207 -28
- package/dist/chunk-3KOY4FH3.cjs.map +1 -0
- package/dist/{chunk-QU7QLJA4.cjs → chunk-3U5ABFDW.cjs} +26 -26
- package/dist/{chunk-QU7QLJA4.cjs.map → chunk-3U5ABFDW.cjs.map} +1 -1
- package/dist/{chunk-RFDEYLIK.js → chunk-4WMMBDS5.js} +7 -7
- package/dist/chunk-4WMMBDS5.js.map +1 -0
- package/dist/{chunk-R7EWHKID.js → chunk-5K2BKEBD.js} +19 -145
- package/dist/chunk-5K2BKEBD.js.map +1 -0
- package/dist/chunk-5PRKMB24.cjs +124 -0
- package/dist/chunk-5PRKMB24.cjs.map +1 -0
- package/dist/chunk-6LN7FIVU.js +107 -0
- package/dist/chunk-6LN7FIVU.js.map +1 -0
- package/dist/{chunk-D6GCNNMK.cjs → chunk-6NGW27EO.cjs} +10 -10
- package/dist/{chunk-D6GCNNMK.cjs.map → chunk-6NGW27EO.cjs.map} +1 -1
- package/dist/{chunk-WHS2Y43J.js → chunk-6SWPJEYN.js} +5 -5
- package/dist/{chunk-WHS2Y43J.js.map → chunk-6SWPJEYN.js.map} +1 -1
- package/dist/{chunk-7TKNK32B.js → chunk-7SQSZN2J.js} +5 -5
- package/dist/{chunk-7TKNK32B.js.map → chunk-7SQSZN2J.js.map} +1 -1
- package/dist/{chunk-UTVQPZPN.cjs → chunk-7XUYFFR4.cjs} +20 -20
- package/dist/chunk-7XUYFFR4.cjs.map +1 -0
- package/dist/{chunk-KXSMF6PL.js → chunk-ACGSAMD3.js} +39 -34
- package/dist/chunk-ACGSAMD3.js.map +1 -0
- package/dist/{chunk-PYKF7UIL.cjs → chunk-AOSSQDVO.cjs} +13 -13
- package/dist/{chunk-PYKF7UIL.cjs.map → chunk-AOSSQDVO.cjs.map} +1 -1
- package/dist/{chunk-AT3MLMJV.js → chunk-AX6JGVTZ.js} +91 -20
- package/dist/chunk-AX6JGVTZ.js.map +1 -0
- package/dist/{chunk-WA64ZSCA.cjs → chunk-BFUOME67.cjs} +11 -11
- package/dist/{chunk-WA64ZSCA.cjs.map → chunk-BFUOME67.cjs.map} +1 -1
- package/dist/{chunk-YQ4JONWE.js → chunk-BRRP76WO.js} +401 -131
- package/dist/chunk-BRRP76WO.js.map +1 -0
- package/dist/{chunk-FCIZNUW5.js → chunk-CHAOFCHV.js} +3 -3
- package/dist/{chunk-FCIZNUW5.js.map → chunk-CHAOFCHV.js.map} +1 -1
- package/dist/chunk-CICO5GXD.cjs +12 -0
- package/dist/{chunk-73MWKDFK.cjs.map → chunk-CICO5GXD.cjs.map} +1 -1
- package/dist/{chunk-S3G5VLSW.js → chunk-CT3ZKTOF.js} +7 -6
- package/dist/chunk-CT3ZKTOF.js.map +1 -0
- package/dist/{chunk-SXWLLQ3D.js → chunk-D5R42W5D.js} +4 -4
- package/dist/{chunk-SXWLLQ3D.js.map → chunk-D5R42W5D.js.map} +1 -1
- package/dist/{chunk-BZZI7B43.cjs → chunk-DO7SAMEK.cjs} +47 -47
- package/dist/{chunk-BZZI7B43.cjs.map → chunk-DO7SAMEK.cjs.map} +1 -1
- package/dist/chunk-DZAICA5J.js +147 -0
- package/dist/chunk-DZAICA5J.js.map +1 -0
- package/dist/{chunk-2ZWM2RFH.cjs → chunk-EDN4Y4ZG.cjs} +9 -9
- package/dist/{chunk-2ZWM2RFH.cjs.map → chunk-EDN4Y4ZG.cjs.map} +1 -1
- package/dist/{chunk-A4R3H7RU.cjs → chunk-EZEEWCFJ.cjs} +264 -361
- package/dist/chunk-EZEEWCFJ.cjs.map +1 -0
- package/dist/{chunk-BEB4OFZF.cjs → chunk-FA2AFWLP.cjs} +51 -59
- package/dist/chunk-FA2AFWLP.cjs.map +1 -0
- package/dist/{chunk-ZBVZ7HXD.cjs → chunk-FQPEMEWX.cjs} +12 -12
- package/dist/{chunk-ZBVZ7HXD.cjs.map → chunk-FQPEMEWX.cjs.map} +1 -1
- package/dist/{chunk-LNIWCQR4.js → chunk-GLHXM7C4.js} +3 -3
- package/dist/{chunk-LNIWCQR4.js.map → chunk-GLHXM7C4.js.map} +1 -1
- package/dist/{chunk-6BD6ZBKT.cjs → chunk-GYEG3ZLZ.cjs} +10 -10
- package/dist/{chunk-6BD6ZBKT.cjs.map → chunk-GYEG3ZLZ.cjs.map} +1 -1
- package/dist/{chunk-A7NMUYEL.cjs → chunk-HDL2ARHX.cjs} +27 -27
- package/dist/{chunk-A7NMUYEL.cjs.map → chunk-HDL2ARHX.cjs.map} +1 -1
- package/dist/{chunk-5ZJOB5YS.js → chunk-HEV35TGT.js} +4 -4
- package/dist/{chunk-5ZJOB5YS.js.map → chunk-HEV35TGT.js.map} +1 -1
- package/dist/{chunk-LHYSY6SE.js → chunk-I4GAZH5Z.js} +186 -9
- package/dist/chunk-I4GAZH5Z.js.map +1 -0
- package/dist/{chunk-BQM5P2NO.cjs → chunk-JB6T4GLL.cjs} +260 -223
- package/dist/chunk-JB6T4GLL.cjs.map +1 -0
- package/dist/{chunk-QPOQX2DI.cjs → chunk-JFRT7S3B.cjs} +1026 -369
- package/dist/chunk-JFRT7S3B.cjs.map +1 -0
- package/dist/{chunk-JCNTKRAK.js → chunk-JONXFAPO.js} +5 -5
- package/dist/{chunk-JCNTKRAK.js.map → chunk-JONXFAPO.js.map} +1 -1
- package/dist/{chunk-WEJHA7AD.js → chunk-KF6K7TGH.js} +109 -75
- package/dist/chunk-KF6K7TGH.js.map +1 -0
- package/dist/{chunk-EDJDDY6H.js → chunk-KKBDXCLQ.js} +4 -4
- package/dist/{chunk-EDJDDY6H.js.map → chunk-KKBDXCLQ.js.map} +1 -1
- package/dist/{chunk-ZHOAZD7Q.cjs → chunk-L3LQNR34.cjs} +161 -161
- package/dist/{chunk-ZHOAZD7Q.cjs.map → chunk-L3LQNR34.cjs.map} +1 -1
- package/dist/{chunk-AF3TWL5C.cjs → chunk-LRQKXRXO.cjs} +4 -4
- package/dist/{chunk-AF3TWL5C.cjs.map → chunk-LRQKXRXO.cjs.map} +1 -1
- package/dist/chunk-MKJ4KXL5.cjs +12 -0
- package/dist/{chunk-6LAXM4J2.cjs.map → chunk-MKJ4KXL5.cjs.map} +1 -1
- package/dist/{chunk-KCH7WQA5.cjs → chunk-MRDBBHIZ.cjs} +48 -2
- package/dist/chunk-MRDBBHIZ.cjs.map +1 -0
- package/dist/{chunk-FXNHZFZV.js → chunk-MYKHJI2J.js} +3 -3
- package/dist/{chunk-FXNHZFZV.js.map → chunk-MYKHJI2J.js.map} +1 -1
- package/dist/{chunk-Z43KWLZF.js → chunk-NACVVDWB.js} +213 -12
- package/dist/chunk-NACVVDWB.js.map +1 -0
- package/dist/{chunk-72WUT4GG.js → chunk-NVY5CJFP.js} +4 -4
- package/dist/{chunk-72WUT4GG.js.map → chunk-NVY5CJFP.js.map} +1 -1
- package/dist/{chunk-WYFB7BN3.js → chunk-OD4SSY2U.js} +166 -48
- package/dist/chunk-OD4SSY2U.js.map +1 -0
- package/dist/{chunk-ZIH42CRX.cjs → chunk-OI2GQ25X.cjs} +7 -7
- package/dist/{chunk-ZIH42CRX.cjs.map → chunk-OI2GQ25X.cjs.map} +1 -1
- package/dist/{chunk-AU3IJNNP.js → chunk-OMZ37BM2.js} +5 -5
- package/dist/{chunk-AU3IJNNP.js.map → chunk-OMZ37BM2.js.map} +1 -1
- package/dist/{chunk-C4DYN2SD.cjs → chunk-OORXGFL5.cjs} +48 -48
- package/dist/{chunk-C4DYN2SD.cjs.map → chunk-OORXGFL5.cjs.map} +1 -1
- package/dist/{chunk-HNYLMPQQ.cjs → chunk-OTUPEF2W.cjs} +149 -78
- package/dist/chunk-OTUPEF2W.cjs.map +1 -0
- package/dist/{chunk-BRN3RGD2.js → chunk-OVI7IXYX.js} +3 -3
- package/dist/{chunk-BRN3RGD2.js.map → chunk-OVI7IXYX.js.map} +1 -1
- package/dist/{chunk-WMKEGJG4.js → chunk-P6YLZTRE.js} +3 -3
- package/dist/{chunk-WMKEGJG4.js.map → chunk-P6YLZTRE.js.map} +1 -1
- package/dist/{chunk-C2ZP5YS6.cjs → chunk-PFBRJBUL.cjs} +278 -160
- package/dist/chunk-PFBRJBUL.cjs.map +1 -0
- package/dist/{chunk-EJSYWAEV.js → chunk-QTD6HX4O.js} +5 -5
- package/dist/{chunk-EJSYWAEV.js.map → chunk-QTD6HX4O.js.map} +1 -1
- package/dist/{chunk-JL5UEBWT.cjs → chunk-ROR5ZR6A.cjs} +45 -45
- package/dist/{chunk-JL5UEBWT.cjs.map → chunk-ROR5ZR6A.cjs.map} +1 -1
- package/dist/{chunk-EDHV4CBU.js → chunk-S5TERQVW.js} +37 -134
- package/dist/chunk-S5TERQVW.js.map +1 -0
- package/dist/{chunk-FSLTU57P.js → chunk-SDBBMD4N.js} +3 -3
- package/dist/{chunk-FSLTU57P.js.map → chunk-SDBBMD4N.js.map} +1 -1
- package/dist/{chunk-36LIJXWC.js → chunk-SHFBB23Q.js} +4 -4
- package/dist/{chunk-36LIJXWC.js.map → chunk-SHFBB23Q.js.map} +1 -1
- package/dist/chunk-SIT26LAZ.cjs +614 -0
- package/dist/chunk-SIT26LAZ.cjs.map +1 -0
- package/dist/{chunk-C6KE2HSQ.cjs → chunk-SR725PWF.cjs} +1662 -1392
- package/dist/chunk-SR725PWF.cjs.map +1 -0
- package/dist/{chunk-S7WIRFNS.cjs → chunk-TKZXMWQF.cjs} +13 -12
- package/dist/chunk-TKZXMWQF.cjs.map +1 -0
- package/dist/{chunk-5PDSCELM.cjs → chunk-TQHGE7UC.cjs} +29 -164
- package/dist/chunk-TQHGE7UC.cjs.map +1 -0
- package/dist/{chunk-XBHRKLZU.cjs → chunk-TUNPBZY3.cjs} +4 -4
- package/dist/{chunk-XBHRKLZU.cjs.map → chunk-TUNPBZY3.cjs.map} +1 -1
- package/dist/{chunk-KOAI3KIV.cjs → chunk-TZEGUG6D.cjs} +4 -4
- package/dist/{chunk-KOAI3KIV.cjs.map → chunk-TZEGUG6D.cjs.map} +1 -1
- package/dist/chunk-UGHG7PGX.js +592 -0
- package/dist/chunk-UGHG7PGX.js.map +1 -0
- package/dist/{chunk-N3QTGPC7.cjs → chunk-UMGZDVOA.cjs} +303 -101
- package/dist/chunk-UMGZDVOA.cjs.map +1 -0
- package/dist/{chunk-RHDOAQCT.cjs → chunk-WCCATHMB.cjs} +140 -135
- package/dist/chunk-WCCATHMB.cjs.map +1 -0
- package/dist/{chunk-DYPRZ53S.cjs → chunk-WODMN3Q2.cjs} +27 -27
- package/dist/{chunk-DYPRZ53S.cjs.map → chunk-WODMN3Q2.cjs.map} +1 -1
- package/dist/{chunk-OZJCH7ZS.js → chunk-XVWOYKIK.js} +3 -3
- package/dist/{chunk-OZJCH7ZS.js.map → chunk-XVWOYKIK.js.map} +1 -1
- package/dist/{chunk-IHRDX7K6.js → chunk-YIURNGXI.js} +46 -56
- package/dist/chunk-YIURNGXI.js.map +1 -0
- package/dist/{chunk-GWUWRZXW.js → chunk-YVF2EM7B.js} +3 -3
- package/dist/{chunk-GWUWRZXW.js.map → chunk-YVF2EM7B.js.map} +1 -1
- package/dist/{chunk-EAMVXR5B.js → chunk-ZDKTPBGV.js} +4 -4
- package/dist/{chunk-EAMVXR5B.js.map → chunk-ZDKTPBGV.js.map} +1 -1
- package/dist/{chunk-A2LJG5GM.js → chunk-ZFMCDJGB.js} +803 -148
- package/dist/chunk-ZFMCDJGB.js.map +1 -0
- package/dist/{chunk-62ZXBMSD.js → chunk-ZLONH2UD.js} +46 -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-BL1NZLMY.d.cts +78 -0
- package/dist/ddl-D_oW-EVO.d.ts +78 -0
- 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 +3418 -706
- package/dist/graph-merge/index.cjs.map +1 -1
- package/dist/graph-merge/index.d.cts +550 -58
- package/dist/graph-merge/index.d.ts +550 -58
- package/dist/graph-merge/index.js +3191 -497
- package/dist/graph-merge/index.js.map +1 -1
- package/dist/{index-Deyu8IXL.d.ts → index-COs_z6nV.d.cts} +47 -3
- package/dist/{index-DrRS51Ld.d.cts → index-D7d1u2gr.d.ts} +47 -3
- package/dist/index.cjs +233 -216
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -12
- package/dist/index.d.ts +12 -12
- 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-ptr3JLRP.d.cts → manager-CYaff3n3.d.cts} +2 -2
- package/dist/{manager-DX983gJ0.d.ts → manager-D9eRT_ns.d.ts} +2 -2
- package/dist/{materialize-indexes-CpXoggWU.d.ts → materialize-indexes-DJ3ykH59.d.ts} +1 -1
- package/dist/{materialize-indexes-BXz77YqO.d.cts → materialize-indexes-DTtAnvfY.d.cts} +1 -1
- package/dist/{postgres-DwgEXSPI.d.cts → postgres-CcL4bAHS.d.ts} +1 -1
- package/dist/{postgres-QdOMHTBz.d.ts → postgres-DMj39MEu.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 +46 -46
- 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 +13 -13
- package/dist/provenance/index.js.map +1 -1
- package/dist/repair-validity-windows-BFNZvd_H.d.cts +166 -0
- package/dist/repair-validity-windows-DlUSqwCg.d.ts +166 -0
- 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-BOUuA83f.d.ts → searchable-DHJMh_nj.d.cts} +1 -1
- package/dist/{searchable-BrUh0pdc.d.cts → searchable-bpE1bBfc.d.ts} +1 -1
- package/dist/{sqlite-BfYcV61E.d.cts → sqlite-D7Oz0SQh.d.ts} +1 -1
- package/dist/{sqlite-DK40XVR1.d.ts → sqlite-YppHrNFb.d.cts} +1 -1
- package/dist/{store-FTqBpuJD.d.cts → store-JUVvtmR4.d.cts} +228 -92
- package/dist/{store-Bo1psfVZ.d.ts → store-UzrGvZOo.d.ts} +228 -92
- package/dist/{temporal-DPtn0HOu.d.ts → temporal-5GqpYCk2.d.cts} +1 -1
- package/dist/{temporal-CXewwLTB.d.cts → temporal-CbnLzTmM.d.ts} +1 -1
- package/dist/{types-Du78EvpN.d.ts → types-BqFZTQmV.d.cts} +60 -9
- package/dist/{types-Du78EvpN.d.cts → types-BqFZTQmV.d.ts} +60 -9
- package/dist/{types-CeT7xM4M.d.ts → types-Bx74kaTt.d.ts} +1 -1
- package/dist/{types-XR1nHERK.d.cts → types-CXAhpW-c.d.cts} +1 -1
- package/dist/{where-CJsp-YUf.d.cts → where-DI_Fqz-X.d.cts} +1 -1
- package/dist/{where-CMbFiWwp.d.ts → where-O-k2wgYm.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/chunk-32UETY6S.cjs.map +0 -1
- package/dist/chunk-5PDSCELM.cjs.map +0 -1
- package/dist/chunk-62ZXBMSD.js.map +0 -1
- package/dist/chunk-6LAXM4J2.cjs +0 -12
- package/dist/chunk-73MWKDFK.cjs +0 -12
- package/dist/chunk-A2LJG5GM.js.map +0 -1
- package/dist/chunk-A4R3H7RU.cjs.map +0 -1
- package/dist/chunk-ANKOXPCC.cjs +0 -267
- package/dist/chunk-ANKOXPCC.cjs.map +0 -1
- package/dist/chunk-AT3MLMJV.js.map +0 -1
- package/dist/chunk-BEB4OFZF.cjs.map +0 -1
- package/dist/chunk-BQM5P2NO.cjs.map +0 -1
- package/dist/chunk-C2ZP5YS6.cjs.map +0 -1
- package/dist/chunk-C6KE2HSQ.cjs.map +0 -1
- package/dist/chunk-DSAWGCMP.js +0 -254
- package/dist/chunk-DSAWGCMP.js.map +0 -1
- package/dist/chunk-E4OSGBCM.cjs.map +0 -1
- package/dist/chunk-EDHV4CBU.js.map +0 -1
- package/dist/chunk-FA6Z7B5L.js +0 -244
- package/dist/chunk-FA6Z7B5L.js.map +0 -1
- package/dist/chunk-GVAMRRZR.cjs +0 -268
- package/dist/chunk-GVAMRRZR.cjs.map +0 -1
- package/dist/chunk-GWXXHOLU.js.map +0 -1
- package/dist/chunk-HNYLMPQQ.cjs.map +0 -1
- package/dist/chunk-IHRDX7K6.js.map +0 -1
- package/dist/chunk-KCH7WQA5.cjs.map +0 -1
- package/dist/chunk-KXSMF6PL.js.map +0 -1
- package/dist/chunk-LHYSY6SE.js.map +0 -1
- package/dist/chunk-N3QTGPC7.cjs.map +0 -1
- package/dist/chunk-QPOQX2DI.cjs.map +0 -1
- package/dist/chunk-R7EWHKID.js.map +0 -1
- package/dist/chunk-RFDEYLIK.js.map +0 -1
- package/dist/chunk-RHDOAQCT.cjs.map +0 -1
- package/dist/chunk-S3G5VLSW.js.map +0 -1
- package/dist/chunk-S7WIRFNS.cjs.map +0 -1
- package/dist/chunk-UTVQPZPN.cjs.map +0 -1
- package/dist/chunk-WEJHA7AD.js.map +0 -1
- package/dist/chunk-WYFB7BN3.js.map +0 -1
- package/dist/chunk-YQ4JONWE.js.map +0 -1
- package/dist/chunk-Z43KWLZF.js.map +0 -1
- package/dist/ddl-mE07h_EM.d.cts +0 -38
- package/dist/ddl-opIn8DfX.d.ts +0 -38
|
@@ -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 {
|
|
9
|
-
import {
|
|
1
|
+
import { createStoreWithSchema } from '../chunk-BRRP76WO.js';
|
|
2
|
+
import '../chunk-YIURNGXI.js';
|
|
3
|
+
import { importGraphStream, exportGraphStream, importGraph, exportGraph } from '../chunk-AX6JGVTZ.js';
|
|
4
|
+
import '../chunk-KF6K7TGH.js';
|
|
5
|
+
import { storeBackend, storeRuntime, transactionBackend } from '../chunk-I4GAZH5Z.js';
|
|
6
|
+
import { computeSchemaHash, serializeSchema } from '../chunk-2TYSSBU5.js';
|
|
7
|
+
import { defineNode } from '../chunk-SHFBB23Q.js';
|
|
8
|
+
import { forceWriteTransactionRevision, normalizeIdentityPair, identityReferenceKey, identityValidityWindowsOverlap, identityAssertionSemanticKey } from '../chunk-ZFMCDJGB.js';
|
|
9
|
+
import { compareStrings, generateId, encodeTupleKey, lockRecordedGraphWrite, readRevisionOrigin, readRecordedClock, forceRecordedGraphRevision, advanceRevisionClock, compareCodePoints } from '../chunk-S5TERQVW.js';
|
|
10
10
|
import { sortedReplacer } from '../chunk-JBQQ7THH.js';
|
|
11
|
-
import '../chunk-
|
|
12
|
-
import { createSqlSchema } from '../chunk-
|
|
13
|
-
import
|
|
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 { snapshotExportContention } from '../chunk-UGHG7PGX.js';
|
|
12
|
+
import { createSqlSchema } from '../chunk-NVY5CJFP.js';
|
|
13
|
+
import '../chunk-6LN7FIVU.js';
|
|
14
|
+
import { getNodeKinds, getEdgeKinds, defineInternalGraph } from '../chunk-HEV35TGT.js';
|
|
15
|
+
import { validateEdgeEndpoints, computeUniqueKey } from '../chunk-GLHXM7C4.js';
|
|
16
|
+
import '../chunk-DZAICA5J.js';
|
|
17
|
+
import { canonicalizeDatabaseTimestamp } from '../chunk-5K2BKEBD.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
|
},
|
|
@@ -1429,12 +1885,24 @@ function contentComponentOf(version) {
|
|
|
1429
1885
|
// src/graph-merge/working-copy.ts
|
|
1430
1886
|
var CLONE_IMPORT_BATCH_SIZE = 1e3;
|
|
1431
1887
|
function cloneWorkingCopyStrategy(makeBackend) {
|
|
1888
|
+
return cloneWorkingCopyWithGraphStrategy(
|
|
1889
|
+
makeBackend,
|
|
1890
|
+
(baseStore) => baseStore.graph
|
|
1891
|
+
);
|
|
1892
|
+
}
|
|
1893
|
+
function cloneIngestionWorkingCopyStrategy(makeBackend) {
|
|
1894
|
+
return cloneWorkingCopyWithGraphStrategy(
|
|
1895
|
+
makeBackend,
|
|
1896
|
+
(baseStore) => graphWithoutNodeUniqueness(baseStore.graph)
|
|
1897
|
+
);
|
|
1898
|
+
}
|
|
1899
|
+
function cloneWorkingCopyWithGraphStrategy(makeBackend, graphForClone) {
|
|
1432
1900
|
return {
|
|
1433
1901
|
create: async (baseStore) => {
|
|
1434
1902
|
const backend = await makeBackend();
|
|
1435
1903
|
try {
|
|
1436
1904
|
const [freshStore] = await createStoreWithSchema(
|
|
1437
|
-
baseStore
|
|
1905
|
+
graphForClone(baseStore),
|
|
1438
1906
|
backend,
|
|
1439
1907
|
{
|
|
1440
1908
|
// Keep descendants branchable with the same O(1) anchor contract,
|
|
@@ -1483,6 +1951,30 @@ function cloneWorkingCopyStrategy(makeBackend) {
|
|
|
1483
1951
|
}
|
|
1484
1952
|
};
|
|
1485
1953
|
}
|
|
1954
|
+
function graphWithoutNodeUniqueness(graph) {
|
|
1955
|
+
const nodes = Object.fromEntries(
|
|
1956
|
+
Object.entries(graph.nodes).map(([name, registration]) => {
|
|
1957
|
+
const { unique: _omitted, ...withoutUnique } = registration;
|
|
1958
|
+
return [name, Object.freeze(withoutUnique)];
|
|
1959
|
+
})
|
|
1960
|
+
);
|
|
1961
|
+
const extension = graph.extension?.nodes === void 0 ? graph.extension : Object.freeze({
|
|
1962
|
+
...graph.extension,
|
|
1963
|
+
nodes: Object.freeze(
|
|
1964
|
+
Object.fromEntries(
|
|
1965
|
+
Object.entries(graph.extension.nodes).map(([name, node]) => {
|
|
1966
|
+
const { unique: _omitted, ...withoutUnique } = node;
|
|
1967
|
+
return [name, Object.freeze(withoutUnique)];
|
|
1968
|
+
})
|
|
1969
|
+
)
|
|
1970
|
+
)
|
|
1971
|
+
});
|
|
1972
|
+
return Object.freeze({
|
|
1973
|
+
...graph,
|
|
1974
|
+
nodes: Object.freeze(nodes),
|
|
1975
|
+
extension
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1486
1978
|
|
|
1487
1979
|
// src/graph-merge/branch.ts
|
|
1488
1980
|
async function branch(baseStore, makeBackend, options, strategy) {
|
|
@@ -1491,7 +1983,7 @@ async function branch(baseStore, makeBackend, options, strategy) {
|
|
|
1491
1983
|
const id = options?.id ?? asBranchId(generateId());
|
|
1492
1984
|
const workingCopyStrategy = strategy ?? cloneWorkingCopyStrategy(makeBackend);
|
|
1493
1985
|
const store = await workingCopyStrategy.create(baseStore);
|
|
1494
|
-
const schemaAnchor = await
|
|
1986
|
+
const schemaAnchor = await captureBranchSchemaAnchor(store);
|
|
1495
1987
|
return ok({
|
|
1496
1988
|
id,
|
|
1497
1989
|
base,
|
|
@@ -1506,7 +1998,7 @@ async function branch(baseStore, makeBackend, options, strategy) {
|
|
|
1506
1998
|
);
|
|
1507
1999
|
}
|
|
1508
2000
|
}
|
|
1509
|
-
async function
|
|
2001
|
+
async function captureBranchSchemaAnchor(store) {
|
|
1510
2002
|
try {
|
|
1511
2003
|
const schemaRow = await storeBackend(store).getActiveSchema(store.graphId);
|
|
1512
2004
|
return schemaRow === void 0 ? void 0 : { version: schemaRow.version, hash: schemaRow.schema_hash };
|
|
@@ -1519,6 +2011,53 @@ async function readSchemaAnchor(store) {
|
|
|
1519
2011
|
}
|
|
1520
2012
|
}
|
|
1521
2013
|
|
|
2014
|
+
// src/graph-merge/ingestion-branch.ts
|
|
2015
|
+
var PRIVATE_BRANCHES = /* @__PURE__ */ new WeakMap();
|
|
2016
|
+
async function ingestionBranch(baseStore, makeBackend, options) {
|
|
2017
|
+
try {
|
|
2018
|
+
const created = await branch(
|
|
2019
|
+
baseStore,
|
|
2020
|
+
makeBackend,
|
|
2021
|
+
options,
|
|
2022
|
+
cloneIngestionWorkingCopyStrategy(makeBackend)
|
|
2023
|
+
);
|
|
2024
|
+
if (isErr(created)) {
|
|
2025
|
+
return err(
|
|
2026
|
+
new BranchError("Failed to create constraint-aware ingestion branch", {
|
|
2027
|
+
cause: created.error
|
|
2028
|
+
})
|
|
2029
|
+
);
|
|
2030
|
+
}
|
|
2031
|
+
const privateBranch = created.data;
|
|
2032
|
+
const { base, id, store } = privateBranch;
|
|
2033
|
+
const handle = Object.freeze({
|
|
2034
|
+
id,
|
|
2035
|
+
base,
|
|
2036
|
+
nodes: store.nodes,
|
|
2037
|
+
edges: store.edges,
|
|
2038
|
+
close: async () => storeBackend(store).close()
|
|
2039
|
+
});
|
|
2040
|
+
PRIVATE_BRANCHES.set(handle, privateBranch);
|
|
2041
|
+
return ok(handle);
|
|
2042
|
+
} catch (error) {
|
|
2043
|
+
return err(
|
|
2044
|
+
new BranchError("Failed to create constraint-aware ingestion branch", {
|
|
2045
|
+
cause: error
|
|
2046
|
+
})
|
|
2047
|
+
);
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
function unwrapMergeBranch(input) {
|
|
2051
|
+
const privateBranch = PRIVATE_BRANCHES.get(input);
|
|
2052
|
+
if (privateBranch !== void 0) {
|
|
2053
|
+
return privateBranch;
|
|
2054
|
+
}
|
|
2055
|
+
return input;
|
|
2056
|
+
}
|
|
2057
|
+
function unwrapMergeBranches(inputs) {
|
|
2058
|
+
return inputs.map((input) => unwrapMergeBranch(input));
|
|
2059
|
+
}
|
|
2060
|
+
|
|
1522
2061
|
// src/graph-merge/blocking.ts
|
|
1523
2062
|
var UNBLOCKED_BUCKET_KEY = "unblocked";
|
|
1524
2063
|
var KEY_PART_SEPARATOR = "\0";
|
|
@@ -1527,6 +2066,14 @@ var UNIQUE_KEY_PREFIX = "u";
|
|
|
1527
2066
|
function isUniqueBucketKey(key) {
|
|
1528
2067
|
return key.startsWith(`${UNIQUE_KEY_PREFIX}${KEY_PART_SEPARATOR}`);
|
|
1529
2068
|
}
|
|
2069
|
+
function uniqueConstraintNameForBucket(key) {
|
|
2070
|
+
if (!isUniqueBucketKey(key)) {
|
|
2071
|
+
return void 0;
|
|
2072
|
+
}
|
|
2073
|
+
const constraintStart = UNIQUE_KEY_PREFIX.length + KEY_PART_SEPARATOR.length;
|
|
2074
|
+
const constraintEnd = key.indexOf(KEY_PART_SEPARATOR, constraintStart);
|
|
2075
|
+
return constraintEnd === -1 ? void 0 : key.slice(constraintStart, constraintEnd);
|
|
2076
|
+
}
|
|
1530
2077
|
function constraintSignature(node, constraint) {
|
|
1531
2078
|
const props = node;
|
|
1532
2079
|
for (const field of constraint.fields) {
|
|
@@ -1874,7 +2421,10 @@ function canonicalizeCluster(cluster, members, policy, branchRank, weights, cano
|
|
|
1874
2421
|
canonicalId,
|
|
1875
2422
|
memberIds,
|
|
1876
2423
|
kind,
|
|
1877
|
-
branchOrigins
|
|
2424
|
+
branchOrigins,
|
|
2425
|
+
// The merge orchestrator replaces this with the witness selected from the
|
|
2426
|
+
// post-guard surviving edge set. Canonicalization never re-derives evidence.
|
|
2427
|
+
decisiveEdges: []
|
|
1878
2428
|
};
|
|
1879
2429
|
const windowSource = members.find(
|
|
1880
2430
|
(member) => member.branchId === COMMITTED_TARGET_BRANCH && member.kind === kind && member.id === canonicalId
|
|
@@ -1886,10 +2436,59 @@ function canonicalizeCluster(cluster, members, policy, branchRank, weights, cano
|
|
|
1886
2436
|
resolution,
|
|
1887
2437
|
conflicts,
|
|
1888
2438
|
...windowSource.validFrom === void 0 ? {} : { validFrom: windowSource.validFrom },
|
|
1889
|
-
...windowSource.validTo === void 0 ? {} : { validTo: windowSource.validTo }
|
|
2439
|
+
...windowSource.validTo === void 0 ? {} : { validTo: windowSource.validTo },
|
|
2440
|
+
...windowSource.validFrom === void 0 ? {} : { endpointValidFrom: windowSource.validFrom },
|
|
2441
|
+
...windowSource.validTo === void 0 ? {} : { endpointValidTo: windowSource.validTo }
|
|
1890
2442
|
};
|
|
1891
2443
|
}
|
|
1892
2444
|
|
|
2445
|
+
// src/graph-merge/union-find.ts
|
|
2446
|
+
var UnionFind = class {
|
|
2447
|
+
constructor(compare) {
|
|
2448
|
+
this.compare = compare;
|
|
2449
|
+
}
|
|
2450
|
+
compare;
|
|
2451
|
+
parent = /* @__PURE__ */ new Map();
|
|
2452
|
+
/** Adds `value` as its own singleton set if it is not already present. */
|
|
2453
|
+
add(value) {
|
|
2454
|
+
if (!this.parent.has(value)) {
|
|
2455
|
+
this.parent.set(value, value);
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
/** Returns the representative of `value`'s set, compressing the path to it. */
|
|
2459
|
+
find(value) {
|
|
2460
|
+
this.add(value);
|
|
2461
|
+
let root = value;
|
|
2462
|
+
while (this.parent.get(root) !== root) {
|
|
2463
|
+
root = requireDefined(this.parent.get(root));
|
|
2464
|
+
}
|
|
2465
|
+
let cursor = value;
|
|
2466
|
+
while (cursor !== root) {
|
|
2467
|
+
const next = requireDefined(this.parent.get(cursor));
|
|
2468
|
+
this.parent.set(cursor, root);
|
|
2469
|
+
cursor = next;
|
|
2470
|
+
}
|
|
2471
|
+
return root;
|
|
2472
|
+
}
|
|
2473
|
+
/** Merges the sets of `left` and `right`; the `compare`-minimal root wins. */
|
|
2474
|
+
union(left, right) {
|
|
2475
|
+
const leftRoot = this.find(left);
|
|
2476
|
+
const rightRoot = this.find(right);
|
|
2477
|
+
if (leftRoot === rightRoot) {
|
|
2478
|
+
return;
|
|
2479
|
+
}
|
|
2480
|
+
if (this.compare(leftRoot, rightRoot) <= 0) {
|
|
2481
|
+
this.parent.set(rightRoot, leftRoot);
|
|
2482
|
+
} else {
|
|
2483
|
+
this.parent.set(leftRoot, rightRoot);
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
/** Every value seen by {@link add}/{@link find}/{@link union}, insertion order. */
|
|
2487
|
+
members() {
|
|
2488
|
+
return [...this.parent.keys()];
|
|
2489
|
+
}
|
|
2490
|
+
};
|
|
2491
|
+
|
|
1893
2492
|
// src/graph-merge/closures.ts
|
|
1894
2493
|
var SUB_CLASS_OF_META_EDGE = "subClassOf";
|
|
1895
2494
|
var EQUIVALENCE_META_EDGES = /* @__PURE__ */ new Set([
|
|
@@ -1944,6 +2543,101 @@ function isReachable2(closure, from, to) {
|
|
|
1944
2543
|
return isReachable(closure.closure, fromRep, toRep);
|
|
1945
2544
|
}
|
|
1946
2545
|
|
|
2546
|
+
// src/graph-merge/evidence.ts
|
|
2547
|
+
function entityRef(key) {
|
|
2548
|
+
return { kind: kindOf(key), id: idOf(key) };
|
|
2549
|
+
}
|
|
2550
|
+
var SOURCE_KIND_ORDER = {
|
|
2551
|
+
block: 0,
|
|
2552
|
+
unique: 1,
|
|
2553
|
+
baseUnique: 2,
|
|
2554
|
+
baseIndex: 3,
|
|
2555
|
+
keyless: 4,
|
|
2556
|
+
retype: 5,
|
|
2557
|
+
custom: 6
|
|
2558
|
+
};
|
|
2559
|
+
function sourceKey(source) {
|
|
2560
|
+
switch (source.kind) {
|
|
2561
|
+
case "block":
|
|
2562
|
+
case "keyless":
|
|
2563
|
+
case "retype": {
|
|
2564
|
+
return JSON.stringify([SOURCE_KIND_ORDER[source.kind], source.sourceId]);
|
|
2565
|
+
}
|
|
2566
|
+
case "unique":
|
|
2567
|
+
case "baseUnique": {
|
|
2568
|
+
return JSON.stringify([
|
|
2569
|
+
SOURCE_KIND_ORDER[source.kind],
|
|
2570
|
+
source.sourceId,
|
|
2571
|
+
source.constraintName
|
|
2572
|
+
]);
|
|
2573
|
+
}
|
|
2574
|
+
case "baseIndex": {
|
|
2575
|
+
return JSON.stringify([
|
|
2576
|
+
SOURCE_KIND_ORDER[source.kind],
|
|
2577
|
+
source.sourceId,
|
|
2578
|
+
source.indexName
|
|
2579
|
+
]);
|
|
2580
|
+
}
|
|
2581
|
+
case "custom": {
|
|
2582
|
+
return JSON.stringify([
|
|
2583
|
+
SOURCE_KIND_ORDER.custom,
|
|
2584
|
+
source.sourceId,
|
|
2585
|
+
source.metadata === void 0 ? "" : canonicalValueKey(source.metadata)
|
|
2586
|
+
]);
|
|
2587
|
+
}
|
|
2588
|
+
default: {
|
|
2589
|
+
const exhaustive = source;
|
|
2590
|
+
return exhaustive;
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
function compareMatchSources(left, right) {
|
|
2595
|
+
return compareStrings(sourceKey(left), sourceKey(right));
|
|
2596
|
+
}
|
|
2597
|
+
function compareEntityRefs(left, right) {
|
|
2598
|
+
return compareMergeKeys(
|
|
2599
|
+
mergeKey(left.kind, left.id),
|
|
2600
|
+
mergeKey(right.kind, right.id)
|
|
2601
|
+
);
|
|
2602
|
+
}
|
|
2603
|
+
function normalizeMatchSources(sources) {
|
|
2604
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2605
|
+
for (const source of sources) {
|
|
2606
|
+
byKey.set(sourceKey(source), source);
|
|
2607
|
+
}
|
|
2608
|
+
return [...byKey].sort(([, left], [, right]) => compareMatchSources(left, right)).map(([, source]) => source);
|
|
2609
|
+
}
|
|
2610
|
+
var DEFAULT_HYBRID_WEIGHT = 0.5;
|
|
2611
|
+
function describeMatchStrategy(strategy) {
|
|
2612
|
+
switch (strategy.kind) {
|
|
2613
|
+
case "fulltext": {
|
|
2614
|
+
return { kind: "fulltext", fields: [...strategy.fields] };
|
|
2615
|
+
}
|
|
2616
|
+
case "vector": {
|
|
2617
|
+
return { kind: "vector", fields: [strategy.field] };
|
|
2618
|
+
}
|
|
2619
|
+
case "hybrid": {
|
|
2620
|
+
const vector = strategy.weights?.vector ?? DEFAULT_HYBRID_WEIGHT;
|
|
2621
|
+
const fulltext = strategy.weights?.fulltext ?? DEFAULT_HYBRID_WEIGHT;
|
|
2622
|
+
const total = vector + fulltext;
|
|
2623
|
+
const weights = total === 0 ? { vector: DEFAULT_HYBRID_WEIGHT, fulltext: DEFAULT_HYBRID_WEIGHT } : { vector: vector / total, fulltext: fulltext / total };
|
|
2624
|
+
return { kind: "hybrid", fields: [...strategy.fields], weights };
|
|
2625
|
+
}
|
|
2626
|
+
case "custom": {
|
|
2627
|
+
return { kind: "custom" };
|
|
2628
|
+
}
|
|
2629
|
+
default: {
|
|
2630
|
+
const exhaustive = strategy;
|
|
2631
|
+
return exhaustive;
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
function compareMatchEvidence(left, right) {
|
|
2636
|
+
const byA = compareEntityRefs(left.a, right.a);
|
|
2637
|
+
if (byA !== 0) return byA;
|
|
2638
|
+
return compareEntityRefs(left.b, right.b);
|
|
2639
|
+
}
|
|
2640
|
+
|
|
1947
2641
|
// src/graph-merge/similarity.ts
|
|
1948
2642
|
var MIN_SCORE = 0;
|
|
1949
2643
|
var MAX_SCORE = 1;
|
|
@@ -2073,18 +2767,18 @@ function cosineFromLookup(embeddings, leftText, rightText) {
|
|
|
2073
2767
|
}
|
|
2074
2768
|
return cosineSimilarity(leftVector, rightVector);
|
|
2075
2769
|
}
|
|
2076
|
-
var
|
|
2770
|
+
var DEFAULT_HYBRID_WEIGHT2 = 0.5;
|
|
2077
2771
|
function nonNegativeFinite(value) {
|
|
2078
2772
|
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
2079
2773
|
}
|
|
2080
2774
|
function normalizeHybridWeights(weights) {
|
|
2081
|
-
const vector = nonNegativeFinite(weights?.vector ??
|
|
2775
|
+
const vector = nonNegativeFinite(weights?.vector ?? DEFAULT_HYBRID_WEIGHT2);
|
|
2082
2776
|
const fulltext = nonNegativeFinite(
|
|
2083
|
-
weights?.fulltext ??
|
|
2777
|
+
weights?.fulltext ?? DEFAULT_HYBRID_WEIGHT2
|
|
2084
2778
|
);
|
|
2085
2779
|
const total = vector + fulltext;
|
|
2086
2780
|
if (total === 0) {
|
|
2087
|
-
return { vector:
|
|
2781
|
+
return { vector: DEFAULT_HYBRID_WEIGHT2, fulltext: DEFAULT_HYBRID_WEIGHT2 };
|
|
2088
2782
|
}
|
|
2089
2783
|
return { vector: vector / total, fulltext: fulltext / total };
|
|
2090
2784
|
}
|
|
@@ -2123,7 +2817,36 @@ function prepareNode(node, strategy) {
|
|
|
2123
2817
|
function scorePrepared(left, right, strategy, ctx) {
|
|
2124
2818
|
switch (strategy.kind) {
|
|
2125
2819
|
case "custom": {
|
|
2126
|
-
|
|
2820
|
+
let raw;
|
|
2821
|
+
try {
|
|
2822
|
+
raw = strategy.score(left.node, right.node);
|
|
2823
|
+
} catch (error) {
|
|
2824
|
+
return err(
|
|
2825
|
+
new MatchEvidenceError("Custom similarity scorer failed.", {
|
|
2826
|
+
details: {
|
|
2827
|
+
kind: left.node.kind,
|
|
2828
|
+
sourceId: "similarity:custom",
|
|
2829
|
+
operation: "score"
|
|
2830
|
+
},
|
|
2831
|
+
cause: error
|
|
2832
|
+
})
|
|
2833
|
+
);
|
|
2834
|
+
}
|
|
2835
|
+
if (!Number.isFinite(raw)) {
|
|
2836
|
+
return err(
|
|
2837
|
+
new MatchEvidenceError(
|
|
2838
|
+
"Custom similarity scorer returned a non-finite score.",
|
|
2839
|
+
{
|
|
2840
|
+
details: {
|
|
2841
|
+
kind: left.node.kind,
|
|
2842
|
+
sourceId: "similarity:custom",
|
|
2843
|
+
operation: "score",
|
|
2844
|
+
score: String(raw)
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
)
|
|
2848
|
+
);
|
|
2849
|
+
}
|
|
2127
2850
|
return ok(clampScore(raw));
|
|
2128
2851
|
}
|
|
2129
2852
|
case "fulltext": {
|
|
@@ -2191,25 +2914,51 @@ function compareCandidateEdges(left, right) {
|
|
|
2191
2914
|
function endpointKey(a, b) {
|
|
2192
2915
|
return JSON.stringify([a, b]);
|
|
2193
2916
|
}
|
|
2194
|
-
function
|
|
2917
|
+
function withSources(edge, sources) {
|
|
2918
|
+
return {
|
|
2919
|
+
...edge,
|
|
2920
|
+
evidence: {
|
|
2921
|
+
...edge.evidence,
|
|
2922
|
+
sources: normalizeMatchSources([...edge.evidence.sources, ...sources])
|
|
2923
|
+
}
|
|
2924
|
+
};
|
|
2925
|
+
}
|
|
2926
|
+
function scoreCandidates(input, resolveConfig, ctx, ceilingPolicy, maxComparisonsPerKind, diagnosticLimit = 0) {
|
|
2195
2927
|
const { similarity, threshold } = resolveConfig;
|
|
2196
|
-
const
|
|
2197
|
-
const forced = [];
|
|
2928
|
+
const forcedByKey = /* @__PURE__ */ new Map();
|
|
2198
2929
|
for (const edge of input.forcedEdges) {
|
|
2199
2930
|
const key = endpointKey(edge.a, edge.b);
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2931
|
+
const previous = forcedByKey.get(key);
|
|
2932
|
+
forcedByKey.set(
|
|
2933
|
+
key,
|
|
2934
|
+
previous === void 0 ? edge : withSources(previous, edge.evidence.sources)
|
|
2935
|
+
);
|
|
2204
2936
|
}
|
|
2205
|
-
const
|
|
2937
|
+
const fuzzyByKey = /* @__PURE__ */ new Map();
|
|
2206
2938
|
for (const pair of input.pairs) {
|
|
2207
2939
|
const key = endpointKey(pair.a, pair.b);
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2940
|
+
const forced2 = forcedByKey.get(key);
|
|
2941
|
+
if (forced2 !== void 0) {
|
|
2942
|
+
forcedByKey.set(key, withSources(forced2, pair.sources));
|
|
2943
|
+
continue;
|
|
2211
2944
|
}
|
|
2945
|
+
const previous = fuzzyByKey.get(key);
|
|
2946
|
+
fuzzyByKey.set(
|
|
2947
|
+
key,
|
|
2948
|
+
previous === void 0 ? pair : {
|
|
2949
|
+
...previous,
|
|
2950
|
+
sources: normalizeMatchSources([
|
|
2951
|
+
...previous.sources,
|
|
2952
|
+
...pair.sources
|
|
2953
|
+
])
|
|
2954
|
+
}
|
|
2955
|
+
);
|
|
2212
2956
|
}
|
|
2957
|
+
const forced = [...forcedByKey.values()];
|
|
2958
|
+
const fuzzy = [...fuzzyByKey.values()].sort((left, right) => {
|
|
2959
|
+
const byA = compareMergeKeys(left.a, right.a);
|
|
2960
|
+
return byA === 0 ? compareMergeKeys(left.b, right.b) : byA;
|
|
2961
|
+
});
|
|
2213
2962
|
if (maxComparisonsPerKind !== void 0 && fuzzy.length > maxComparisonsPerKind) {
|
|
2214
2963
|
if (ceilingPolicy === "error") {
|
|
2215
2964
|
return err(
|
|
@@ -2236,23 +2985,81 @@ function scoreCandidates(input, resolveConfig, ctx, ceilingPolicy, maxComparison
|
|
|
2236
2985
|
limit: maxComparisonsPerKind,
|
|
2237
2986
|
message: `Skipped similarity for this kind: ${fuzzy.length} candidate pairs exceeded maxComparisonsPerKind ${maxComparisonsPerKind}; nodes will merge by id and exact unique match only.`
|
|
2238
2987
|
}
|
|
2239
|
-
]
|
|
2988
|
+
],
|
|
2989
|
+
diagnostics: [],
|
|
2990
|
+
diagnosticsTotal: 0
|
|
2240
2991
|
});
|
|
2241
2992
|
}
|
|
2242
2993
|
const scorer = createPairScorer(similarity, ctx);
|
|
2243
2994
|
const edges = [...forced];
|
|
2244
|
-
|
|
2995
|
+
const diagnostics = [];
|
|
2996
|
+
let diagnosticsTotal = 0;
|
|
2997
|
+
for (const { left, right, a, b, sources } of fuzzy) {
|
|
2245
2998
|
const scored = scorer(a, left, b, right);
|
|
2246
2999
|
if (isErr(scored)) {
|
|
3000
|
+
if (similarity.kind === "custom" && scored.error instanceof MatchEvidenceError) {
|
|
3001
|
+
return err(
|
|
3002
|
+
new MatchEvidenceError(scored.error.message, {
|
|
3003
|
+
cause: scored.error,
|
|
3004
|
+
details: {
|
|
3005
|
+
kind: left.kind,
|
|
3006
|
+
operation: "score",
|
|
3007
|
+
sourceIds: normalizeMatchSources(sources).map(
|
|
3008
|
+
(source) => source.sourceId
|
|
3009
|
+
),
|
|
3010
|
+
endpoints: [entityRef(a), entityRef(b)]
|
|
3011
|
+
}
|
|
3012
|
+
})
|
|
3013
|
+
);
|
|
3014
|
+
}
|
|
2247
3015
|
return err(scored.error);
|
|
2248
3016
|
}
|
|
2249
|
-
if (scored.data
|
|
2250
|
-
|
|
3017
|
+
if (!Number.isFinite(scored.data)) {
|
|
3018
|
+
return err(
|
|
3019
|
+
new MatchEvidenceError(
|
|
3020
|
+
"Similarity scorer returned a non-finite score.",
|
|
3021
|
+
{
|
|
3022
|
+
details: {
|
|
3023
|
+
kind: left.kind,
|
|
3024
|
+
operation: "score",
|
|
3025
|
+
sourceIds: normalizeMatchSources(sources).map(
|
|
3026
|
+
(source) => source.sourceId
|
|
3027
|
+
),
|
|
3028
|
+
score: String(scored.data)
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
)
|
|
3032
|
+
);
|
|
3033
|
+
}
|
|
3034
|
+
const noComparableValues = scored.data < 0;
|
|
3035
|
+
const publicScore = noComparableValues ? 0 : scored.data;
|
|
3036
|
+
const accepted = !noComparableValues && scored.data >= threshold;
|
|
3037
|
+
diagnosticsTotal += 1;
|
|
3038
|
+
const retainsDiagnostic = diagnostics.length < diagnosticLimit;
|
|
3039
|
+
if (accepted || retainsDiagnostic) {
|
|
3040
|
+
const evidence = {
|
|
3041
|
+
a: entityRef(a),
|
|
3042
|
+
b: entityRef(b),
|
|
3043
|
+
sources: normalizeMatchSources(sources),
|
|
3044
|
+
decision: "scored",
|
|
3045
|
+
strategy: describeMatchStrategy(similarity),
|
|
3046
|
+
score: publicScore,
|
|
3047
|
+
threshold
|
|
3048
|
+
};
|
|
3049
|
+
if (accepted) edges.push({ a, b, score: scored.data, evidence });
|
|
3050
|
+
if (!retainsDiagnostic) continue;
|
|
3051
|
+
diagnostics.push({
|
|
3052
|
+
evidence,
|
|
3053
|
+
scoreDecision: accepted ? "accepted" : "rejected",
|
|
3054
|
+
...noComparableValues ? { reason: "noComparableValues" } : {}
|
|
3055
|
+
});
|
|
2251
3056
|
}
|
|
2252
3057
|
}
|
|
2253
3058
|
return ok({
|
|
2254
3059
|
edges: edges.sort((left, right) => compareCandidateEdges(left, right)),
|
|
2255
|
-
warnings: []
|
|
3060
|
+
warnings: [],
|
|
3061
|
+
diagnostics,
|
|
3062
|
+
diagnosticsTotal
|
|
2256
3063
|
});
|
|
2257
3064
|
}
|
|
2258
3065
|
|
|
@@ -2354,13 +3161,14 @@ function weakestEdge(edges) {
|
|
|
2354
3161
|
}
|
|
2355
3162
|
function splitUntil(members, edges, isOffending) {
|
|
2356
3163
|
let surviving = internalEdges(members, edges);
|
|
3164
|
+
const removedEdges = [];
|
|
2357
3165
|
for (; ; ) {
|
|
2358
3166
|
const components = buildComponents(surviving, members, true);
|
|
2359
3167
|
const offending = components.filter(
|
|
2360
3168
|
(component) => isOffending(component.members, surviving)
|
|
2361
3169
|
);
|
|
2362
3170
|
if (offending.length === 0) {
|
|
2363
|
-
return components;
|
|
3171
|
+
return { clusters: components, survivingEdges: surviving, removedEdges };
|
|
2364
3172
|
}
|
|
2365
3173
|
let splittable;
|
|
2366
3174
|
for (const component of offending) {
|
|
@@ -2372,11 +3180,16 @@ function splitUntil(members, edges, isOffending) {
|
|
|
2372
3180
|
}
|
|
2373
3181
|
if (splittable === void 0) {
|
|
2374
3182
|
const offendingSet = new Set(offending);
|
|
2375
|
-
return
|
|
2376
|
-
|
|
2377
|
-
|
|
3183
|
+
return {
|
|
3184
|
+
clusters: components.flatMap(
|
|
3185
|
+
(component) => offendingSet.has(component) ? component.members.map((id) => ({ members: [id] })) : [component]
|
|
3186
|
+
),
|
|
3187
|
+
survivingEdges: surviving,
|
|
3188
|
+
removedEdges
|
|
3189
|
+
};
|
|
2378
3190
|
}
|
|
2379
3191
|
const weakest = weakestEdge(splittable);
|
|
3192
|
+
removedEdges.push(weakest);
|
|
2380
3193
|
surviving = surviving.filter((edge) => edge !== weakest);
|
|
2381
3194
|
}
|
|
2382
3195
|
}
|
|
@@ -2390,16 +3203,30 @@ function splitByDropWeakest(members, edges, maxDiameter) {
|
|
|
2390
3203
|
function connectedComponents(candidateEdges, nodeIds) {
|
|
2391
3204
|
return buildComponents(candidateEdges, nodeIds);
|
|
2392
3205
|
}
|
|
2393
|
-
function
|
|
3206
|
+
function enforceDiameterWithEdges(clusters, candidateEdges, clusterMaxDiameter) {
|
|
2394
3207
|
const guarded = [];
|
|
3208
|
+
let survivingEdges = [...candidateEdges];
|
|
3209
|
+
const excludedEdges = [];
|
|
2395
3210
|
for (const component of clusters) {
|
|
2396
3211
|
if (exceedsDiameter(component.members, candidateEdges, clusterMaxDiameter)) {
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
3212
|
+
const split = splitByDropWeakest(
|
|
3213
|
+
component.members,
|
|
3214
|
+
survivingEdges,
|
|
3215
|
+
clusterMaxDiameter
|
|
3216
|
+
);
|
|
3217
|
+
guarded.push(...split.clusters);
|
|
3218
|
+
const memberSet = new Set(component.members);
|
|
3219
|
+
survivingEdges = [
|
|
3220
|
+
...survivingEdges.filter(
|
|
3221
|
+
(edge) => !(memberSet.has(edge.a) && memberSet.has(edge.b))
|
|
3222
|
+
),
|
|
3223
|
+
...split.survivingEdges
|
|
3224
|
+
].sort((left, right) => compareCandidateEdges(left, right));
|
|
3225
|
+
excludedEdges.push(
|
|
3226
|
+
...split.removedEdges.map((edge) => ({
|
|
3227
|
+
edge,
|
|
3228
|
+
reason: "diameter"
|
|
3229
|
+
}))
|
|
2403
3230
|
);
|
|
2404
3231
|
} else {
|
|
2405
3232
|
guarded.push(component);
|
|
@@ -2411,7 +3238,7 @@ function enforceDiameter(clusters, candidateEdges, clusterMaxDiameter) {
|
|
|
2411
3238
|
requireDefined(right.members[0])
|
|
2412
3239
|
)
|
|
2413
3240
|
);
|
|
2414
|
-
return guarded;
|
|
3241
|
+
return { clusters: guarded, survivingEdges, excludedEdges };
|
|
2415
3242
|
}
|
|
2416
3243
|
function countBaseMembers(members, baseIds) {
|
|
2417
3244
|
let count = 0;
|
|
@@ -2431,10 +3258,19 @@ function splitByBaseMultiplicity(members, edges, baseIds) {
|
|
|
2431
3258
|
}
|
|
2432
3259
|
function enforceBaseGuard(clusters, candidateEdges, baseIds) {
|
|
2433
3260
|
if (baseIds.size === 0) {
|
|
2434
|
-
return {
|
|
3261
|
+
return {
|
|
3262
|
+
clusters,
|
|
3263
|
+
events: [],
|
|
3264
|
+
survivingEdges: [...candidateEdges].sort(
|
|
3265
|
+
(left, right) => compareCandidateEdges(left, right)
|
|
3266
|
+
),
|
|
3267
|
+
excludedEdges: []
|
|
3268
|
+
};
|
|
2435
3269
|
}
|
|
2436
3270
|
const result = [];
|
|
2437
3271
|
const events = [];
|
|
3272
|
+
let survivingEdges = [...candidateEdges];
|
|
3273
|
+
const excludedEdges = [];
|
|
2438
3274
|
for (const cluster of clusters) {
|
|
2439
3275
|
const clusterBaseIds = cluster.members.filter((id) => baseIds.has(id));
|
|
2440
3276
|
if (clusterBaseIds.length < 2) {
|
|
@@ -2449,8 +3285,24 @@ function enforceBaseGuard(clusters, candidateEdges, baseIds) {
|
|
|
2449
3285
|
(left, right) => compareMergeKeys(left, right)
|
|
2450
3286
|
)
|
|
2451
3287
|
});
|
|
2452
|
-
|
|
2453
|
-
|
|
3288
|
+
const split = splitByBaseMultiplicity(
|
|
3289
|
+
cluster.members,
|
|
3290
|
+
survivingEdges,
|
|
3291
|
+
baseIds
|
|
3292
|
+
);
|
|
3293
|
+
result.push(...split.clusters);
|
|
3294
|
+
const memberSet = new Set(cluster.members);
|
|
3295
|
+
survivingEdges = [
|
|
3296
|
+
...survivingEdges.filter(
|
|
3297
|
+
(edge) => !(memberSet.has(edge.a) && memberSet.has(edge.b))
|
|
3298
|
+
),
|
|
3299
|
+
...split.survivingEdges
|
|
3300
|
+
].sort((left, right) => compareCandidateEdges(left, right));
|
|
3301
|
+
excludedEdges.push(
|
|
3302
|
+
...split.removedEdges.map((edge) => ({
|
|
3303
|
+
edge,
|
|
3304
|
+
reason: "baseAmbiguity"
|
|
3305
|
+
}))
|
|
2454
3306
|
);
|
|
2455
3307
|
}
|
|
2456
3308
|
result.sort(
|
|
@@ -2459,7 +3311,20 @@ function enforceBaseGuard(clusters, candidateEdges, baseIds) {
|
|
|
2459
3311
|
requireDefined(right.members[0])
|
|
2460
3312
|
)
|
|
2461
3313
|
);
|
|
2462
|
-
return { clusters: result, events };
|
|
3314
|
+
return { clusters: result, events, survivingEdges, excludedEdges };
|
|
3315
|
+
}
|
|
3316
|
+
function decisiveEdgesForCluster(cluster, survivingEdges) {
|
|
3317
|
+
const members = new Set(cluster.members);
|
|
3318
|
+
const forest = new UnionFind(compareMergeKeys);
|
|
3319
|
+
for (const member of cluster.members) forest.add(member);
|
|
3320
|
+
const decisive = [];
|
|
3321
|
+
for (const edge of internalEdges(cluster.members, survivingEdges)) {
|
|
3322
|
+
if (!members.has(edge.a) || !members.has(edge.b)) continue;
|
|
3323
|
+
if (forest.find(edge.a) === forest.find(edge.b)) continue;
|
|
3324
|
+
forest.union(edge.a, edge.b);
|
|
3325
|
+
decisive.push(edge.evidence);
|
|
3326
|
+
}
|
|
3327
|
+
return decisive;
|
|
2463
3328
|
}
|
|
2464
3329
|
|
|
2465
3330
|
// src/graph-merge/delete-modify.ts
|
|
@@ -2756,8 +3621,9 @@ var WINDOW_NOT_APPLICABLE_DROP_REASON = "window-not-applicable";
|
|
|
2756
3621
|
function earliestEnd(claims) {
|
|
2757
3622
|
let earliest;
|
|
2758
3623
|
for (const claim of claims) {
|
|
2759
|
-
if (
|
|
2760
|
-
|
|
3624
|
+
if (claim.change.kind !== "set") continue;
|
|
3625
|
+
if (earliest === void 0 || compareStrings(claim.change.validTo, earliest) < 0) {
|
|
3626
|
+
earliest = claim.change.validTo;
|
|
2761
3627
|
}
|
|
2762
3628
|
}
|
|
2763
3629
|
return earliest;
|
|
@@ -2766,18 +3632,26 @@ function resolveEndClaims(claims, preferredBranchId) {
|
|
|
2766
3632
|
const preferred = claims.filter(
|
|
2767
3633
|
(claim) => claim.branchId === preferredBranchId
|
|
2768
3634
|
);
|
|
2769
|
-
|
|
3635
|
+
const candidates = preferred.length > 0 ? preferred : claims;
|
|
3636
|
+
const earliest = earliestEnd(candidates);
|
|
3637
|
+
return earliest === void 0 ? candidates.some((claim) => claim.change.kind === "clear") ? { kind: "clear" } : void 0 : { kind: "set", validTo: earliest };
|
|
2770
3638
|
}
|
|
2771
3639
|
function claimingBranches(claims) {
|
|
2772
3640
|
return [...new Set(claims.map((claim) => claim.branchId))].sort(
|
|
2773
3641
|
(left, right) => compareStrings(left, right)
|
|
2774
3642
|
);
|
|
2775
3643
|
}
|
|
3644
|
+
function sameValidToChange(left, right) {
|
|
3645
|
+
if (left.kind !== right.kind) return false;
|
|
3646
|
+
if (left.kind === "clear") return true;
|
|
3647
|
+
return right.kind === "set" && left.validTo === right.validTo;
|
|
3648
|
+
}
|
|
2776
3649
|
function classifyDelta(base, fork) {
|
|
3650
|
+
const lowerBoundMoved = fork.validFrom !== base.validFrom;
|
|
2777
3651
|
const endCleared = fork.validTo === void 0 && base.validTo !== void 0;
|
|
2778
3652
|
return {
|
|
2779
|
-
applicableEnd: fork.validTo !== void 0 && fork.validTo !== base.validTo ? fork.validTo : void 0,
|
|
2780
|
-
unapplicable:
|
|
3653
|
+
applicableEnd: fork.validTo !== void 0 && fork.validTo !== base.validTo ? { kind: "set", validTo: fork.validTo } : endCleared && !lowerBoundMoved ? { kind: "clear" } : void 0,
|
|
3654
|
+
unapplicable: lowerBoundMoved
|
|
2781
3655
|
};
|
|
2782
3656
|
}
|
|
2783
3657
|
function resolvePopulation(entity, deltas, deletions, preferredBranchId, dropItem) {
|
|
@@ -2812,7 +3686,7 @@ function resolvePopulation(entity, deltas, deletions, preferredBranchId, dropIte
|
|
|
2812
3686
|
continue;
|
|
2813
3687
|
}
|
|
2814
3688
|
if (applicableEnd !== void 0) {
|
|
2815
|
-
claims.push({ branchId: delta.branchId,
|
|
3689
|
+
claims.push({ branchId: delta.branchId, change: applicableEnd });
|
|
2816
3690
|
}
|
|
2817
3691
|
if (unapplicable && !reportedUnapplicable) {
|
|
2818
3692
|
reportedUnapplicable = true;
|
|
@@ -2826,27 +3700,29 @@ function resolvePopulation(entity, deltas, deletions, preferredBranchId, dropIte
|
|
|
2826
3700
|
entity,
|
|
2827
3701
|
kind: first.kind,
|
|
2828
3702
|
id: first.id,
|
|
2829
|
-
validTo: targetEnd,
|
|
3703
|
+
...targetEnd.kind === "set" ? { validTo: targetEnd.validTo } : { clearValidTo: true },
|
|
2830
3704
|
claimedBy: claimingBranches(claims),
|
|
2831
3705
|
precedence: VALIDITY_END_TARGET_PRECEDENCE
|
|
2832
3706
|
});
|
|
2833
3707
|
}
|
|
2834
3708
|
continue;
|
|
2835
3709
|
}
|
|
2836
|
-
const resolved =
|
|
3710
|
+
const resolved = resolveEndClaims(claims, void 0);
|
|
2837
3711
|
if (resolved === void 0) {
|
|
2838
3712
|
continue;
|
|
2839
3713
|
}
|
|
2840
3714
|
ends.set(identity, resolved);
|
|
2841
3715
|
credits.set(
|
|
2842
3716
|
identity,
|
|
2843
|
-
claimingBranches(
|
|
3717
|
+
claimingBranches(
|
|
3718
|
+
claims.filter((claim) => sameValidToChange(claim.change, resolved))
|
|
3719
|
+
)
|
|
2844
3720
|
);
|
|
2845
3721
|
resolutions.push({
|
|
2846
3722
|
entity,
|
|
2847
3723
|
kind: first.kind,
|
|
2848
3724
|
id: first.id,
|
|
2849
|
-
validTo: resolved,
|
|
3725
|
+
...resolved.kind === "set" ? { validTo: resolved.validTo } : { clearValidTo: true },
|
|
2850
3726
|
claimedBy: claimingBranches(claims)
|
|
2851
3727
|
});
|
|
2852
3728
|
}
|
|
@@ -3070,17 +3946,19 @@ function foldEdgeSet(foldSet, context, branchRank, preferredBranchId) {
|
|
|
3070
3946
|
const fromKind = kindOf(survivor.fromKey);
|
|
3071
3947
|
const toId = idOf(survivor.toKey);
|
|
3072
3948
|
const toKind = kindOf(survivor.toKey);
|
|
3073
|
-
const preferredSurvivorEnd = survivor.staged.branchId === preferredBranchId ? survivor.staged.validTo : void 0;
|
|
3949
|
+
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
3950
|
const foldedEnd = preferredSurvivorEnd ?? resolveEndClaims(
|
|
3075
|
-
foldSet.filter(
|
|
3951
|
+
foldSet.filter(
|
|
3952
|
+
(edge) => edge.staged.validTo !== void 0 || edge.staged.clearValidTo === true
|
|
3953
|
+
).map((edge) => ({
|
|
3076
3954
|
branchId: edge.staged.branchId,
|
|
3077
|
-
validTo:
|
|
3955
|
+
change: typeof edge.staged.validTo === "string" ? { kind: "set", validTo: edge.staged.validTo } : { kind: "clear" }
|
|
3078
3956
|
})),
|
|
3079
3957
|
preferredBranchId
|
|
3080
3958
|
);
|
|
3081
3959
|
const window = {
|
|
3082
3960
|
...survivor.staged.validFrom === void 0 ? {} : { validFrom: survivor.staged.validFrom },
|
|
3083
|
-
...foldedEnd ===
|
|
3961
|
+
...foldedEnd?.kind === "set" ? { validTo: foldedEnd.validTo } : foldedEnd?.kind === "clear" ? { clearValidTo: true } : {}
|
|
3084
3962
|
};
|
|
3085
3963
|
const contentKeys = new Set(foldSet.map((edge) => edge.dedupeKey));
|
|
3086
3964
|
if (contentKeys.size === 1) {
|
|
@@ -3225,7 +4103,10 @@ var mergeOptionsScalarSchema = z.object({
|
|
|
3225
4103
|
provenance: z.boolean().default(MERGE_OPTION_DEFAULTS.provenance),
|
|
3226
4104
|
persistProvenance: z.boolean().default(MERGE_OPTION_DEFAULTS.persistProvenance),
|
|
3227
4105
|
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()
|
|
4106
|
+
clusterMaxDiameter: z.number().positive({ message: "clusterMaxDiameter must be positive" }).optional(),
|
|
4107
|
+
candidateDiagnostics: z.object({
|
|
4108
|
+
limit: z.number().int({ message: "candidateDiagnostics.limit must be an integer" }).min(0, { message: "candidateDiagnostics.limit must be >= 0" })
|
|
4109
|
+
}).strict().optional()
|
|
3229
4110
|
});
|
|
3230
4111
|
function validatePropertyConflictPolicy(policy, label) {
|
|
3231
4112
|
if (typeof policy === "string" && !propertyConflictPolicySchema.safeParse(policy).success) {
|
|
@@ -3288,7 +4169,8 @@ function normalizeMergeOptions(options = {}) {
|
|
|
3288
4169
|
provenance: options.provenance,
|
|
3289
4170
|
persistProvenance: options.persistProvenance,
|
|
3290
4171
|
...options.maxComparisonsPerKind === void 0 ? {} : { maxComparisonsPerKind: options.maxComparisonsPerKind },
|
|
3291
|
-
...options.clusterMaxDiameter === void 0 ? {} : { clusterMaxDiameter: options.clusterMaxDiameter }
|
|
4172
|
+
...options.clusterMaxDiameter === void 0 ? {} : { clusterMaxDiameter: options.clusterMaxDiameter },
|
|
4173
|
+
...options.candidateDiagnostics === void 0 ? {} : { candidateDiagnostics: options.candidateDiagnostics }
|
|
3292
4174
|
});
|
|
3293
4175
|
const onPropertyConflict = validatePropertyConflictPolicy(
|
|
3294
4176
|
options.onPropertyConflict ?? MERGE_OPTION_DEFAULTS.onPropertyConflict,
|
|
@@ -3319,78 +4201,693 @@ function normalizeMergeOptions(options = {}) {
|
|
|
3319
4201
|
...options.target === void 0 ? {} : { target: options.target },
|
|
3320
4202
|
...scalar.maxComparisonsPerKind === void 0 ? {} : { maxComparisonsPerKind: scalar.maxComparisonsPerKind },
|
|
3321
4203
|
...scalar.clusterMaxDiameter === void 0 ? {} : { clusterMaxDiameter: scalar.clusterMaxDiameter },
|
|
4204
|
+
...scalar.candidateDiagnostics === void 0 ? {} : { candidateDiagnostics: scalar.candidateDiagnostics },
|
|
3322
4205
|
...options.branchOrder === void 0 ? {} : { branchOrder: options.branchOrder },
|
|
3323
4206
|
...provenanceWeights === void 0 ? {} : { provenanceWeights }
|
|
3324
4207
|
};
|
|
3325
4208
|
}
|
|
3326
|
-
var
|
|
3327
|
-
var
|
|
3328
|
-
var
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
});
|
|
3338
|
-
var
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
4209
|
+
var MERGE_PLAN_FORMAT_VERSION = 1;
|
|
4210
|
+
var MERGE_PLAN_DIGEST_ALGORITHM = "sha256";
|
|
4211
|
+
var nonEmptyStringSchema = z.string().min(1);
|
|
4212
|
+
var finiteNumberSchema = z.number();
|
|
4213
|
+
var nonNegativeIntegerSchema = finiteNumberSchema.int().nonnegative();
|
|
4214
|
+
var jsonObjectSchema = z.record(z.string(), z.json());
|
|
4215
|
+
var mergePlanEntityRefSchema = z.object({ kind: nonEmptyStringSchema, id: nonEmptyStringSchema }).strict();
|
|
4216
|
+
var mergePlanSchemaFenceSchema = z.object({
|
|
4217
|
+
managed: z.boolean(),
|
|
4218
|
+
version: nonNegativeIntegerSchema,
|
|
4219
|
+
hash: nonEmptyStringSchema
|
|
4220
|
+
}).strict();
|
|
4221
|
+
var mergePlanRevisionFenceSchema = z.object({
|
|
4222
|
+
origin: nonEmptyStringSchema,
|
|
4223
|
+
revision: nonEmptyStringSchema.nullable()
|
|
4224
|
+
}).strict();
|
|
4225
|
+
var mergePlanTargetFenceSchema = z.object({
|
|
4226
|
+
graphId: nonEmptyStringSchema,
|
|
4227
|
+
schema: mergePlanSchemaFenceSchema,
|
|
4228
|
+
revision: mergePlanRevisionFenceSchema
|
|
4229
|
+
}).strict();
|
|
4230
|
+
var branchAnchorSchema = z.object({
|
|
4231
|
+
branchId: nonEmptyStringSchema,
|
|
4232
|
+
baseVersion: nonEmptyStringSchema
|
|
4233
|
+
}).strict();
|
|
4234
|
+
var snapshotAnchorsSchema = z.object({
|
|
4235
|
+
kind: z.literal("snapshot"),
|
|
4236
|
+
base: z.object({
|
|
4237
|
+
graphId: nonEmptyStringSchema,
|
|
4238
|
+
baseVersion: nonEmptyStringSchema
|
|
4239
|
+
}).strict(),
|
|
4240
|
+
branches: z.array(branchAnchorSchema)
|
|
4241
|
+
}).strict();
|
|
4242
|
+
var incrementalAnchorsSchema = z.object({
|
|
4243
|
+
kind: z.literal("incremental"),
|
|
4244
|
+
forkPoint: z.object({
|
|
4245
|
+
graphId: nonEmptyStringSchema,
|
|
4246
|
+
baseVersion: nonEmptyStringSchema,
|
|
4247
|
+
schema: mergePlanSchemaFenceSchema
|
|
4248
|
+
}).strict(),
|
|
4249
|
+
branches: z.array(branchAnchorSchema)
|
|
4250
|
+
}).strict();
|
|
4251
|
+
var mergePlanAnchorsSchema = z.discriminatedUnion("kind", [
|
|
4252
|
+
snapshotAnchorsSchema,
|
|
4253
|
+
incrementalAnchorsSchema
|
|
4254
|
+
]);
|
|
4255
|
+
var nodeUpsertSchema = z.object({
|
|
4256
|
+
kind: nonEmptyStringSchema,
|
|
4257
|
+
id: nonEmptyStringSchema,
|
|
4258
|
+
setProps: jsonObjectSchema,
|
|
4259
|
+
unsetProps: z.array(nonEmptyStringSchema),
|
|
4260
|
+
validFrom: nonEmptyStringSchema.optional(),
|
|
4261
|
+
validTo: nonEmptyStringSchema.optional()
|
|
4262
|
+
}).strict();
|
|
4263
|
+
var edgeUpsertSchema = z.object({
|
|
4264
|
+
kind: nonEmptyStringSchema,
|
|
4265
|
+
id: nonEmptyStringSchema,
|
|
4266
|
+
from: mergePlanEntityRefSchema,
|
|
4267
|
+
to: mergePlanEntityRefSchema,
|
|
4268
|
+
setProps: jsonObjectSchema,
|
|
4269
|
+
unsetProps: z.array(nonEmptyStringSchema),
|
|
4270
|
+
validFrom: nonEmptyStringSchema.optional(),
|
|
4271
|
+
validTo: nonEmptyStringSchema.optional()
|
|
4272
|
+
}).strict();
|
|
4273
|
+
var mergePlanIdentityAssertionSchema = z.object({
|
|
4274
|
+
id: nonEmptyStringSchema,
|
|
4275
|
+
relation: z.enum(["same", "different"]),
|
|
4276
|
+
a: mergePlanEntityRefSchema,
|
|
4277
|
+
b: mergePlanEntityRefSchema,
|
|
4278
|
+
validFrom: nonEmptyStringSchema,
|
|
4279
|
+
validTo: nonEmptyStringSchema.optional(),
|
|
4280
|
+
endedBy: mergePlanEntityRefSchema.optional()
|
|
4281
|
+
}).strict();
|
|
4282
|
+
var mergePlanWritesSchema = z.object({
|
|
4283
|
+
nodeDeletes: z.array(mergePlanEntityRefSchema),
|
|
4284
|
+
nodeUpserts: z.array(nodeUpsertSchema),
|
|
4285
|
+
edgeDeletes: z.array(mergePlanEntityRefSchema),
|
|
4286
|
+
edgeUpserts: z.array(edgeUpsertSchema),
|
|
4287
|
+
identityAssertions: z.array(mergePlanIdentityAssertionSchema),
|
|
4288
|
+
identityRetractions: z.array(mergePlanIdentityAssertionSchema)
|
|
4289
|
+
}).strict();
|
|
4290
|
+
var proposedRoleSchema = z.object({
|
|
4291
|
+
upserts: nonNegativeIntegerSchema,
|
|
4292
|
+
deletions: nonNegativeIntegerSchema
|
|
4293
|
+
}).strict();
|
|
4294
|
+
var mergePlanProposedSummarySchema = z.object({
|
|
4295
|
+
nodes: proposedRoleSchema,
|
|
4296
|
+
edges: proposedRoleSchema,
|
|
4297
|
+
identity: z.object({
|
|
4298
|
+
assertions: nonNegativeIntegerSchema,
|
|
4299
|
+
retractions: nonNegativeIntegerSchema
|
|
4300
|
+
}).strict()
|
|
4301
|
+
}).strict();
|
|
4302
|
+
var mergePlanGuardsSchema = z.object({
|
|
4303
|
+
canonicalMappings: z.array(
|
|
4304
|
+
z.object({
|
|
4305
|
+
member: mergePlanEntityRefSchema,
|
|
4306
|
+
canonical: mergePlanEntityRefSchema
|
|
4307
|
+
}).strict()
|
|
4308
|
+
),
|
|
4309
|
+
retypes: z.array(
|
|
4310
|
+
z.object({
|
|
4311
|
+
entity: mergePlanEntityRefSchema,
|
|
4312
|
+
toKind: nonEmptyStringSchema
|
|
4313
|
+
}).strict()
|
|
4314
|
+
),
|
|
4315
|
+
deletedNodes: z.array(mergePlanEntityRefSchema),
|
|
4316
|
+
incremental: z.object({
|
|
4317
|
+
tombstoneResurrection: z.literal("refuse"),
|
|
4318
|
+
lossyUpdates: z.literal("refuse"),
|
|
4319
|
+
edgeIdentity: z.literal("preserve")
|
|
4320
|
+
}).strict().optional()
|
|
4321
|
+
}).strict();
|
|
4322
|
+
var matchSourceSchema = z.discriminatedUnion("kind", [
|
|
4323
|
+
z.object({ kind: z.literal("block"), sourceId: nonEmptyStringSchema }).strict(),
|
|
4324
|
+
z.object({
|
|
4325
|
+
kind: z.literal("unique"),
|
|
4326
|
+
sourceId: nonEmptyStringSchema,
|
|
4327
|
+
constraintName: nonEmptyStringSchema
|
|
4328
|
+
}).strict(),
|
|
4329
|
+
z.object({
|
|
4330
|
+
kind: z.literal("baseUnique"),
|
|
4331
|
+
sourceId: nonEmptyStringSchema,
|
|
4332
|
+
constraintName: nonEmptyStringSchema
|
|
4333
|
+
}).strict(),
|
|
4334
|
+
z.object({
|
|
4335
|
+
kind: z.literal("baseIndex"),
|
|
4336
|
+
sourceId: nonEmptyStringSchema,
|
|
4337
|
+
indexName: nonEmptyStringSchema
|
|
4338
|
+
}).strict(),
|
|
4339
|
+
z.object({ kind: z.literal("keyless"), sourceId: nonEmptyStringSchema }).strict(),
|
|
4340
|
+
z.object({ kind: z.literal("retype"), sourceId: nonEmptyStringSchema }).strict(),
|
|
4341
|
+
z.object({
|
|
4342
|
+
kind: z.literal("custom"),
|
|
4343
|
+
sourceId: nonEmptyStringSchema,
|
|
4344
|
+
metadata: z.json().optional()
|
|
4345
|
+
}).strict()
|
|
4346
|
+
]);
|
|
4347
|
+
var similarityStrategySchema = z.discriminatedUnion("kind", [
|
|
4348
|
+
z.object({
|
|
4349
|
+
kind: z.literal("fulltext"),
|
|
4350
|
+
fields: z.array(nonEmptyStringSchema)
|
|
4351
|
+
}).strict(),
|
|
4352
|
+
z.object({
|
|
4353
|
+
kind: z.literal("vector"),
|
|
4354
|
+
fields: z.array(nonEmptyStringSchema)
|
|
4355
|
+
}).strict(),
|
|
4356
|
+
z.object({
|
|
4357
|
+
kind: z.literal("hybrid"),
|
|
4358
|
+
fields: z.array(nonEmptyStringSchema),
|
|
4359
|
+
weights: z.object({ vector: finiteNumberSchema, fulltext: finiteNumberSchema }).strict()
|
|
4360
|
+
}).strict(),
|
|
4361
|
+
z.object({ kind: z.literal("custom") }).strict()
|
|
4362
|
+
]);
|
|
4363
|
+
var definitionalEvidenceSchema = z.object({
|
|
4364
|
+
a: mergePlanEntityRefSchema,
|
|
4365
|
+
b: mergePlanEntityRefSchema,
|
|
4366
|
+
sources: z.array(matchSourceSchema),
|
|
4367
|
+
decision: z.literal("definitional")
|
|
4368
|
+
}).strict();
|
|
4369
|
+
var scoredEvidenceSchema = z.object({
|
|
4370
|
+
a: mergePlanEntityRefSchema,
|
|
4371
|
+
b: mergePlanEntityRefSchema,
|
|
4372
|
+
sources: z.array(matchSourceSchema),
|
|
4373
|
+
decision: z.literal("scored"),
|
|
4374
|
+
strategy: similarityStrategySchema,
|
|
4375
|
+
score: finiteNumberSchema,
|
|
4376
|
+
threshold: finiteNumberSchema
|
|
4377
|
+
}).strict();
|
|
4378
|
+
var mergePlanMatchEvidenceSchema = z.discriminatedUnion("decision", [
|
|
4379
|
+
definitionalEvidenceSchema,
|
|
4380
|
+
scoredEvidenceSchema
|
|
4381
|
+
]);
|
|
4382
|
+
var entityResolutionSchema = z.object({
|
|
4383
|
+
canonicalId: nonEmptyStringSchema,
|
|
4384
|
+
memberIds: z.array(nonEmptyStringSchema),
|
|
4385
|
+
kind: nonEmptyStringSchema,
|
|
4386
|
+
branchOrigins: z.array(nonEmptyStringSchema),
|
|
4387
|
+
decisiveEdges: z.array(mergePlanMatchEvidenceSchema)
|
|
4388
|
+
}).strict();
|
|
4389
|
+
var diagnosticsSchema = z.object({
|
|
4390
|
+
entries: z.array(
|
|
4391
|
+
z.object({
|
|
4392
|
+
evidence: mergePlanMatchEvidenceSchema,
|
|
4393
|
+
scoreDecision: z.enum(["accepted", "rejected"]),
|
|
4394
|
+
reason: z.literal("noComparableValues").optional(),
|
|
4395
|
+
clusterDisposition: z.union([
|
|
4396
|
+
z.literal("retained"),
|
|
4397
|
+
z.object({
|
|
4398
|
+
kind: z.literal("excluded"),
|
|
4399
|
+
reason: z.enum(["diameter", "baseAmbiguity"])
|
|
4400
|
+
}).strict()
|
|
4401
|
+
]).optional()
|
|
4402
|
+
}).strict()
|
|
4403
|
+
),
|
|
4404
|
+
total: nonNegativeIntegerSchema,
|
|
4405
|
+
limit: nonNegativeIntegerSchema,
|
|
4406
|
+
truncated: z.boolean()
|
|
4407
|
+
}).strict();
|
|
4408
|
+
var mergePlanReviewSchema = z.object({
|
|
4409
|
+
resolutions: z.array(entityResolutionSchema),
|
|
4410
|
+
conflicts: z.array(
|
|
4411
|
+
z.object({
|
|
4412
|
+
entityId: nonEmptyStringSchema,
|
|
4413
|
+
kind: nonEmptyStringSchema,
|
|
4414
|
+
property: nonEmptyStringSchema,
|
|
4415
|
+
values: z.array(
|
|
4416
|
+
z.object({ branchId: nonEmptyStringSchema, value: z.json() }).strict()
|
|
4417
|
+
),
|
|
4418
|
+
resolution: z.json()
|
|
4419
|
+
}).strict()
|
|
4420
|
+
),
|
|
4421
|
+
deleteModifyConflicts: z.array(
|
|
4422
|
+
z.object({
|
|
4423
|
+
entityId: nonEmptyStringSchema,
|
|
4424
|
+
kind: nonEmptyStringSchema,
|
|
4425
|
+
deletedBy: nonEmptyStringSchema,
|
|
4426
|
+
modifiedBy: nonEmptyStringSchema,
|
|
4427
|
+
resolution: z.enum(["deleteWins", "modifyWins", "flag"])
|
|
4428
|
+
}).strict()
|
|
4429
|
+
),
|
|
4430
|
+
typeReconciliations: z.array(
|
|
4431
|
+
z.object({
|
|
4432
|
+
entityId: nonEmptyStringSchema,
|
|
4433
|
+
fromTypes: z.array(nonEmptyStringSchema),
|
|
4434
|
+
toType: nonEmptyStringSchema,
|
|
4435
|
+
decisiveEdges: z.array(mergePlanMatchEvidenceSchema).optional()
|
|
4436
|
+
}).strict()
|
|
4437
|
+
),
|
|
4438
|
+
dropped: z.array(
|
|
4439
|
+
z.discriminatedUnion("kind", [
|
|
4440
|
+
z.object({
|
|
4441
|
+
kind: z.literal("node"),
|
|
4442
|
+
id: nonEmptyStringSchema,
|
|
4443
|
+
reason: nonEmptyStringSchema
|
|
4444
|
+
}).strict(),
|
|
4445
|
+
z.object({
|
|
4446
|
+
kind: z.literal("edge"),
|
|
4447
|
+
id: nonEmptyStringSchema,
|
|
4448
|
+
reason: nonEmptyStringSchema
|
|
4449
|
+
}).strict(),
|
|
4450
|
+
z.object({
|
|
4451
|
+
kind: z.literal("identity"),
|
|
4452
|
+
id: nonEmptyStringSchema,
|
|
4453
|
+
reason: nonEmptyStringSchema
|
|
4454
|
+
}).strict()
|
|
4455
|
+
])
|
|
4456
|
+
),
|
|
4457
|
+
validityEnds: z.array(
|
|
4458
|
+
z.object({
|
|
4459
|
+
entity: z.enum(["node", "edge"]),
|
|
4460
|
+
kind: nonEmptyStringSchema,
|
|
4461
|
+
id: nonEmptyStringSchema,
|
|
4462
|
+
validTo: nonEmptyStringSchema,
|
|
4463
|
+
claimedBy: z.array(nonEmptyStringSchema),
|
|
4464
|
+
precedence: z.literal("target").optional()
|
|
4465
|
+
}).strict()
|
|
4466
|
+
),
|
|
4467
|
+
baseAmbiguities: z.array(
|
|
4468
|
+
z.object({
|
|
4469
|
+
baseIds: z.array(mergePlanEntityRefSchema),
|
|
4470
|
+
memberIds: z.array(mergePlanEntityRefSchema)
|
|
4471
|
+
}).strict()
|
|
4472
|
+
),
|
|
4473
|
+
provenanceRecords: z.array(
|
|
4474
|
+
z.object({
|
|
4475
|
+
role: z.enum(["node", "edge"]),
|
|
4476
|
+
canonicalId: nonEmptyStringSchema,
|
|
4477
|
+
canonicalKind: nonEmptyStringSchema,
|
|
4478
|
+
branchId: nonEmptyStringSchema,
|
|
4479
|
+
sourceId: nonEmptyStringSchema
|
|
4480
|
+
}).strict()
|
|
4481
|
+
),
|
|
4482
|
+
warnings: z.array(z.string()),
|
|
4483
|
+
diagnostics: diagnosticsSchema.optional()
|
|
4484
|
+
}).strict();
|
|
4485
|
+
var mergePlanDigestSchema = z.object({
|
|
4486
|
+
algorithm: z.literal(MERGE_PLAN_DIGEST_ALGORITHM),
|
|
4487
|
+
value: z.string().regex(/^[\da-f]{64}$/)
|
|
4488
|
+
}).strict();
|
|
4489
|
+
var mergePlanArtifactV1BaseSchema = z.object({
|
|
4490
|
+
formatVersion: z.literal(MERGE_PLAN_FORMAT_VERSION),
|
|
4491
|
+
digest: mergePlanDigestSchema,
|
|
4492
|
+
mode: z.enum(["snapshot", "incremental"]),
|
|
4493
|
+
target: mergePlanTargetFenceSchema,
|
|
4494
|
+
anchors: mergePlanAnchorsSchema,
|
|
4495
|
+
proposed: mergePlanProposedSummarySchema,
|
|
4496
|
+
writes: mergePlanWritesSchema,
|
|
4497
|
+
guards: mergePlanGuardsSchema,
|
|
4498
|
+
review: mergePlanReviewSchema,
|
|
4499
|
+
provenance: z.object({ includeInReport: z.boolean(), persist: z.boolean() }).strict()
|
|
4500
|
+
}).strict();
|
|
4501
|
+
var mergePlanArtifactV1InputBaseSchema = mergePlanArtifactV1BaseSchema.omit({
|
|
4502
|
+
digest: true
|
|
3347
4503
|
});
|
|
3348
|
-
function
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
Provenance: { type: Provenance },
|
|
3356
|
-
ProvenanceOwner: { type: ProvenanceOwner }
|
|
3357
|
-
},
|
|
3358
|
-
edges: {}
|
|
3359
|
-
});
|
|
3360
|
-
}
|
|
3361
|
-
function buildProvenanceGraph(targetGraphId) {
|
|
3362
|
-
return defineInternalGraph({
|
|
3363
|
-
id: provenanceGraphId(targetGraphId),
|
|
3364
|
-
nodes: { Provenance: { type: Provenance } },
|
|
3365
|
-
edges: {}
|
|
3366
|
-
});
|
|
3367
|
-
}
|
|
3368
|
-
async function openProvenanceStore(...args) {
|
|
3369
|
-
const [backend, targetGraphId] = args.length === 1 ? [storeBackend(args[0]), args[0].graphId] : args;
|
|
3370
|
-
const graph = buildOwnedProvenanceGraph(targetGraphId);
|
|
3371
|
-
const preflight = await inspectSidecarGraphId(backend, graph, targetGraphId);
|
|
3372
|
-
if (preflight.state === "refuse") {
|
|
3373
|
-
throw provenanceGraphIdCollision(graph.id, targetGraphId, preflight.reason);
|
|
4504
|
+
function addSemanticIssues(artifact, ctx) {
|
|
4505
|
+
if (artifact.mode !== artifact.anchors.kind) {
|
|
4506
|
+
ctx.addIssue({
|
|
4507
|
+
code: "custom",
|
|
4508
|
+
path: ["anchors", "kind"],
|
|
4509
|
+
message: `Expected ${artifact.mode} anchors for a ${artifact.mode} plan.`
|
|
4510
|
+
});
|
|
3374
4511
|
}
|
|
3375
|
-
if (
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
4512
|
+
if (artifact.mode === "incremental" !== (artifact.guards.incremental !== void 0)) {
|
|
4513
|
+
ctx.addIssue({
|
|
4514
|
+
code: "custom",
|
|
4515
|
+
path: ["guards", "incremental"],
|
|
4516
|
+
message: "Incremental write guards must be present exactly for incremental plans."
|
|
4517
|
+
});
|
|
3380
4518
|
}
|
|
3381
|
-
const
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
4519
|
+
const expectedCounts = {
|
|
4520
|
+
nodeUpserts: artifact.writes.nodeUpserts.length,
|
|
4521
|
+
nodeDeletions: artifact.writes.nodeDeletes.length,
|
|
4522
|
+
edgeUpserts: artifact.writes.edgeUpserts.length,
|
|
4523
|
+
edgeDeletions: artifact.writes.edgeDeletes.length,
|
|
4524
|
+
assertions: artifact.writes.identityAssertions.length,
|
|
4525
|
+
retractions: artifact.writes.identityRetractions.length
|
|
4526
|
+
};
|
|
4527
|
+
const receivedCounts = {
|
|
4528
|
+
nodeUpserts: artifact.proposed.nodes.upserts,
|
|
4529
|
+
nodeDeletions: artifact.proposed.nodes.deletions,
|
|
4530
|
+
edgeUpserts: artifact.proposed.edges.upserts,
|
|
4531
|
+
edgeDeletions: artifact.proposed.edges.deletions,
|
|
4532
|
+
assertions: artifact.proposed.identity.assertions,
|
|
4533
|
+
retractions: artifact.proposed.identity.retractions
|
|
4534
|
+
};
|
|
4535
|
+
if (JSON.stringify(expectedCounts) !== JSON.stringify(receivedCounts)) {
|
|
4536
|
+
ctx.addIssue({
|
|
4537
|
+
code: "custom",
|
|
4538
|
+
path: ["proposed"],
|
|
4539
|
+
message: "Proposed counts must equal the resolved write-set counts."
|
|
4540
|
+
});
|
|
3388
4541
|
}
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
4542
|
+
addOperationIssues(
|
|
4543
|
+
artifact.writes.nodeDeletes,
|
|
4544
|
+
artifact.writes.nodeUpserts,
|
|
4545
|
+
"node",
|
|
4546
|
+
ctx
|
|
4547
|
+
);
|
|
4548
|
+
addOperationIssues(
|
|
4549
|
+
artifact.writes.edgeDeletes,
|
|
4550
|
+
artifact.writes.edgeUpserts,
|
|
4551
|
+
"edge",
|
|
4552
|
+
ctx
|
|
4553
|
+
);
|
|
4554
|
+
for (const [index, upsert] of artifact.writes.nodeUpserts.entries()) {
|
|
4555
|
+
addPropertyPatchIssues(upsert, ["writes", "nodeUpserts", index], ctx);
|
|
4556
|
+
}
|
|
4557
|
+
for (const [index, upsert] of artifact.writes.edgeUpserts.entries()) {
|
|
4558
|
+
addPropertyPatchIssues(upsert, ["writes", "edgeUpserts", index], ctx);
|
|
4559
|
+
}
|
|
4560
|
+
const canonicalByMember = /* @__PURE__ */ new Map();
|
|
4561
|
+
let invalidCanonicalMappings = false;
|
|
4562
|
+
for (const mapping of artifact.guards.canonicalMappings) {
|
|
4563
|
+
const member = entityKey(mapping.member);
|
|
4564
|
+
const canonical = entityKey(mapping.canonical);
|
|
4565
|
+
if (canonicalByMember.has(member)) invalidCanonicalMappings = true;
|
|
4566
|
+
canonicalByMember.set(member, canonical);
|
|
4567
|
+
}
|
|
4568
|
+
for (const canonical of canonicalByMember.values()) {
|
|
4569
|
+
if (canonicalByMember.get(canonical) !== canonical) {
|
|
4570
|
+
invalidCanonicalMappings = true;
|
|
4571
|
+
}
|
|
4572
|
+
}
|
|
4573
|
+
if (invalidCanonicalMappings) {
|
|
4574
|
+
ctx.addIssue({
|
|
4575
|
+
code: "custom",
|
|
4576
|
+
path: ["guards", "canonicalMappings"],
|
|
4577
|
+
message: "Canonical mappings must name each member once and include a self-mapped canonical root."
|
|
4578
|
+
});
|
|
4579
|
+
}
|
|
4580
|
+
const diagnostics = artifact.review.diagnostics;
|
|
4581
|
+
if (diagnostics !== void 0) {
|
|
4582
|
+
const expectedRetained = Math.min(diagnostics.total, diagnostics.limit);
|
|
4583
|
+
if (diagnostics.entries.length !== expectedRetained || diagnostics.truncated !== diagnostics.total > diagnostics.entries.length) {
|
|
4584
|
+
ctx.addIssue({
|
|
4585
|
+
code: "custom",
|
|
4586
|
+
path: ["review", "diagnostics"],
|
|
4587
|
+
message: "Diagnostic retention and truncation metadata must exactly match total and limit."
|
|
4588
|
+
});
|
|
4589
|
+
}
|
|
4590
|
+
for (const [index, diagnostic] of diagnostics.entries.entries()) {
|
|
4591
|
+
addEvidenceIssues(
|
|
4592
|
+
diagnostic.evidence,
|
|
4593
|
+
["review", "diagnostics", "entries", index, "evidence"],
|
|
4594
|
+
ctx
|
|
4595
|
+
);
|
|
4596
|
+
const accepted = diagnostic.evidence.decision === "definitional" || diagnostic.evidence.score >= diagnostic.evidence.threshold && diagnostic.reason === void 0;
|
|
4597
|
+
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) {
|
|
4598
|
+
ctx.addIssue({
|
|
4599
|
+
code: "custom",
|
|
4600
|
+
path: ["review", "diagnostics", "entries", index],
|
|
4601
|
+
message: "Diagnostic decision, reason, and cluster disposition must agree with its score evidence."
|
|
4602
|
+
});
|
|
4603
|
+
}
|
|
4604
|
+
}
|
|
4605
|
+
}
|
|
4606
|
+
for (const [index, resolution] of artifact.review.resolutions.entries()) {
|
|
4607
|
+
addResolutionEvidenceIssues(artifact, resolution, index, ctx);
|
|
4608
|
+
}
|
|
4609
|
+
for (const [
|
|
4610
|
+
reconciliationIndex,
|
|
4611
|
+
reconciliation
|
|
4612
|
+
] of artifact.review.typeReconciliations.entries()) {
|
|
4613
|
+
for (const [edgeIndex, evidence] of (reconciliation.decisiveEdges ?? []).entries()) {
|
|
4614
|
+
addEvidenceIssues(
|
|
4615
|
+
evidence,
|
|
4616
|
+
[
|
|
4617
|
+
"review",
|
|
4618
|
+
"typeReconciliations",
|
|
4619
|
+
reconciliationIndex,
|
|
4620
|
+
"decisiveEdges",
|
|
4621
|
+
edgeIndex
|
|
4622
|
+
],
|
|
4623
|
+
ctx
|
|
4624
|
+
);
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
function addResolutionEvidenceIssues(artifact, resolution, resolutionIndex, ctx) {
|
|
4629
|
+
const retypedKinds = new Map(
|
|
4630
|
+
artifact.guards.retypes.map((retype) => [
|
|
4631
|
+
entityKey(retype.entity),
|
|
4632
|
+
retype.toKind
|
|
4633
|
+
])
|
|
4634
|
+
);
|
|
4635
|
+
const clusterMembers = artifact.guards.canonicalMappings.filter((mapping) => {
|
|
4636
|
+
const canonicalKind = retypedKinds.get(entityKey(mapping.canonical)) ?? mapping.canonical.kind;
|
|
4637
|
+
return mapping.canonical.id === resolution.canonicalId && canonicalKind === resolution.kind;
|
|
4638
|
+
}).map((mapping) => mapping.member);
|
|
4639
|
+
const uniqueMembers = new Map(
|
|
4640
|
+
clusterMembers.map((member) => [entityKey(member), member])
|
|
4641
|
+
);
|
|
4642
|
+
const memberIds = new Set(
|
|
4643
|
+
[...uniqueMembers.values()].map((member) => member.id)
|
|
4644
|
+
);
|
|
4645
|
+
const declaredMemberIds = new Set(resolution.memberIds);
|
|
4646
|
+
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) {
|
|
4647
|
+
ctx.addIssue({
|
|
4648
|
+
code: "custom",
|
|
4649
|
+
path: ["review", "resolutions", resolutionIndex],
|
|
4650
|
+
message: "A resolution must name its complete guarded cluster and carry exactly N-1 decisive edges."
|
|
4651
|
+
});
|
|
4652
|
+
return;
|
|
4653
|
+
}
|
|
4654
|
+
const parent = new Map([...uniqueMembers.keys()].map((key) => [key, key]));
|
|
4655
|
+
const find = (key) => {
|
|
4656
|
+
const next = parent.get(key);
|
|
4657
|
+
if (next === void 0 || next === key) return key;
|
|
4658
|
+
const root = find(next);
|
|
4659
|
+
parent.set(key, root);
|
|
4660
|
+
return root;
|
|
4661
|
+
};
|
|
4662
|
+
let invalidTree = false;
|
|
4663
|
+
for (const [edgeIndex, evidence] of resolution.decisiveEdges.entries()) {
|
|
4664
|
+
addEvidenceIssues(
|
|
4665
|
+
evidence,
|
|
4666
|
+
["review", "resolutions", resolutionIndex, "decisiveEdges", edgeIndex],
|
|
4667
|
+
ctx
|
|
4668
|
+
);
|
|
4669
|
+
const a = entityKey(evidence.a);
|
|
4670
|
+
const b = entityKey(evidence.b);
|
|
4671
|
+
if (!uniqueMembers.has(a) || !uniqueMembers.has(b)) {
|
|
4672
|
+
invalidTree = true;
|
|
4673
|
+
continue;
|
|
4674
|
+
}
|
|
4675
|
+
const rootA = find(a);
|
|
4676
|
+
const rootB = find(b);
|
|
4677
|
+
if (rootA === rootB) invalidTree = true;
|
|
4678
|
+
else parent.set(rootB, rootA);
|
|
4679
|
+
}
|
|
4680
|
+
if (invalidTree || new Set([...uniqueMembers.keys()].map((key) => find(key))).size !== 1) {
|
|
4681
|
+
ctx.addIssue({
|
|
4682
|
+
code: "custom",
|
|
4683
|
+
path: ["review", "resolutions", resolutionIndex, "decisiveEdges"],
|
|
4684
|
+
message: "Decisive edges must be an acyclic connected witness over the guarded cluster."
|
|
4685
|
+
});
|
|
4686
|
+
}
|
|
4687
|
+
}
|
|
4688
|
+
function addEvidenceIssues(evidence, path, ctx) {
|
|
4689
|
+
const sourcesCanonical = evidence.sources.every((source, index) => {
|
|
4690
|
+
const previous = evidence.sources[index - 1];
|
|
4691
|
+
return previous === void 0 || compareMatchSources(previous, source) < 0;
|
|
4692
|
+
});
|
|
4693
|
+
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(
|
|
4694
|
+
evidence.strategy.weights.vector + evidence.strategy.weights.fulltext - 1
|
|
4695
|
+
) > Number.EPSILON));
|
|
4696
|
+
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) {
|
|
4697
|
+
ctx.addIssue({
|
|
4698
|
+
code: "custom",
|
|
4699
|
+
path: [...path],
|
|
4700
|
+
message: "Evidence endpoints and sources must be canonical, and scored values must be within [0, 1]."
|
|
4701
|
+
});
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
function addOperationIssues(deletions, upserts, role, ctx) {
|
|
4705
|
+
const deletionKeys = deletions.map((entry) => entityKey(entry));
|
|
4706
|
+
const upsertKeys = upserts.map((entry) => entityKey(entry));
|
|
4707
|
+
if (new Set(deletionKeys).size !== deletionKeys.length || new Set(upsertKeys).size !== upsertKeys.length) {
|
|
4708
|
+
ctx.addIssue({
|
|
4709
|
+
code: "custom",
|
|
4710
|
+
path: ["writes"],
|
|
4711
|
+
message: `The ${role} write set must not contain duplicate identities.`
|
|
4712
|
+
});
|
|
4713
|
+
}
|
|
4714
|
+
const deleted = new Set(deletionKeys);
|
|
4715
|
+
if (upsertKeys.some((key) => deleted.has(key))) {
|
|
4716
|
+
ctx.addIssue({
|
|
4717
|
+
code: "custom",
|
|
4718
|
+
path: ["writes"],
|
|
4719
|
+
message: `The same ${role} identity cannot be both deleted and upserted.`
|
|
4720
|
+
});
|
|
4721
|
+
}
|
|
4722
|
+
}
|
|
4723
|
+
function addPropertyPatchIssues(patch, path, ctx) {
|
|
4724
|
+
const unset = new Set(patch.unsetProps);
|
|
4725
|
+
if (unset.size !== patch.unsetProps.length || Object.keys(patch.setProps).some((key) => unset.has(key))) {
|
|
4726
|
+
ctx.addIssue({
|
|
4727
|
+
code: "custom",
|
|
4728
|
+
path: [...path],
|
|
4729
|
+
message: "unsetProps must be duplicate-free and disjoint from setProps keys."
|
|
4730
|
+
});
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4733
|
+
function entityKey(entity) {
|
|
4734
|
+
return JSON.stringify([entity.kind, entity.id]);
|
|
4735
|
+
}
|
|
4736
|
+
var mergePlanArtifactV1InputSchema = mergePlanArtifactV1InputBaseSchema.superRefine(
|
|
4737
|
+
(artifact, ctx) => addSemanticIssues(artifact, ctx)
|
|
4738
|
+
);
|
|
4739
|
+
var mergePlanArtifactV1Schema = mergePlanArtifactV1BaseSchema.superRefine(
|
|
4740
|
+
(artifact, ctx) => addSemanticIssues(artifact, ctx)
|
|
4741
|
+
);
|
|
4742
|
+
|
|
4743
|
+
// src/graph-merge/plan-canonical.ts
|
|
4744
|
+
function canonicalMergePlanJson(artifact) {
|
|
4745
|
+
const digestless = "digest" in artifact ? omitDigest(artifact) : artifact;
|
|
4746
|
+
return JSON.stringify(digestless, sortedReplacer);
|
|
4747
|
+
}
|
|
4748
|
+
async function computeMergePlanDigest(artifact) {
|
|
4749
|
+
return sha256Hex(canonicalMergePlanJson(artifact), 32);
|
|
4750
|
+
}
|
|
4751
|
+
async function finalizeMergePlanArtifact(artifact) {
|
|
4752
|
+
const finalized = {
|
|
4753
|
+
...artifact,
|
|
4754
|
+
digest: {
|
|
4755
|
+
algorithm: MERGE_PLAN_DIGEST_ALGORITHM,
|
|
4756
|
+
value: await computeMergePlanDigest(artifact)
|
|
4757
|
+
}
|
|
4758
|
+
};
|
|
4759
|
+
const canonical = JSON.parse(
|
|
4760
|
+
JSON.stringify(finalized, sortedReplacer)
|
|
4761
|
+
);
|
|
4762
|
+
return canonical;
|
|
4763
|
+
}
|
|
4764
|
+
function omitDigest(artifact) {
|
|
4765
|
+
const { digest: _digest, ...digestless } = artifact;
|
|
4766
|
+
return digestless;
|
|
4767
|
+
}
|
|
4768
|
+
|
|
4769
|
+
// src/graph-merge/plan-wire.ts
|
|
4770
|
+
function parseMergePlanArtifact(input) {
|
|
4771
|
+
const version = readFormatVersion(input);
|
|
4772
|
+
if (version !== MERGE_PLAN_FORMAT_VERSION) {
|
|
4773
|
+
return {
|
|
4774
|
+
success: false,
|
|
4775
|
+
error: { kind: "unsupported-version", received: version }
|
|
4776
|
+
};
|
|
4777
|
+
}
|
|
4778
|
+
const parsed = mergePlanArtifactV1Schema.safeParse(input);
|
|
4779
|
+
if (!parsed.success) {
|
|
4780
|
+
return {
|
|
4781
|
+
success: false,
|
|
4782
|
+
error: { kind: "malformed", issues: parsed.error.issues }
|
|
4783
|
+
};
|
|
4784
|
+
}
|
|
4785
|
+
return {
|
|
4786
|
+
success: true,
|
|
4787
|
+
artifact: parsed.data
|
|
4788
|
+
};
|
|
4789
|
+
}
|
|
4790
|
+
async function verifyMergePlanDigest(artifact) {
|
|
4791
|
+
const expected = await computeMergePlanDigest(artifact);
|
|
4792
|
+
const received = artifact.digest.value;
|
|
4793
|
+
return expected === received ? { valid: true } : { valid: false, expected, received };
|
|
4794
|
+
}
|
|
4795
|
+
async function validateMergePlanArtifact(input) {
|
|
4796
|
+
const parsed = parseMergePlanArtifact(input);
|
|
4797
|
+
if (!parsed.success) return parsed;
|
|
4798
|
+
const digest = await verifyMergePlanDigest(parsed.artifact);
|
|
4799
|
+
if (!digest.valid) {
|
|
4800
|
+
return {
|
|
4801
|
+
success: false,
|
|
4802
|
+
error: {
|
|
4803
|
+
kind: "digest-mismatch",
|
|
4804
|
+
expected: digest.expected,
|
|
4805
|
+
received: digest.received
|
|
4806
|
+
}
|
|
4807
|
+
};
|
|
4808
|
+
}
|
|
4809
|
+
return parsed;
|
|
4810
|
+
}
|
|
4811
|
+
async function constructMergePlanArtifact(input) {
|
|
4812
|
+
const validatedInput = mergePlanArtifactV1InputSchema.parse(input);
|
|
4813
|
+
return finalizeMergePlanArtifact(
|
|
4814
|
+
validatedInput
|
|
4815
|
+
);
|
|
4816
|
+
}
|
|
4817
|
+
function readFormatVersion(input) {
|
|
4818
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
4819
|
+
return void 0;
|
|
4820
|
+
}
|
|
4821
|
+
return Reflect.get(input, "formatVersion");
|
|
4822
|
+
}
|
|
4823
|
+
var PROVENANCE_KIND = "Provenance";
|
|
4824
|
+
var PROVENANCE_OWNER_KIND = "ProvenanceOwner";
|
|
4825
|
+
var Provenance = defineNode(PROVENANCE_KIND, {
|
|
4826
|
+
schema: z.object({
|
|
4827
|
+
targetGraphId: z.string(),
|
|
4828
|
+
role: z.enum(["node", "edge"]),
|
|
4829
|
+
canonicalId: z.string(),
|
|
4830
|
+
canonicalKind: z.string(),
|
|
4831
|
+
branchId: z.string(),
|
|
4832
|
+
sourceId: z.string()
|
|
4833
|
+
})
|
|
4834
|
+
});
|
|
4835
|
+
var PROVENANCE_OWNER = "@nicia-ai/typegraph/merge-provenance";
|
|
4836
|
+
var PROVENANCE_OWNER_VERSION = 1;
|
|
4837
|
+
var PROVENANCE_OWNER_ID = "merge-provenance-owner";
|
|
4838
|
+
var ProvenanceOwner = defineNode(PROVENANCE_OWNER_KIND, {
|
|
4839
|
+
schema: z.object({
|
|
4840
|
+
owner: z.literal(PROVENANCE_OWNER),
|
|
4841
|
+
version: z.literal(PROVENANCE_OWNER_VERSION),
|
|
4842
|
+
targetGraphId: z.string()
|
|
4843
|
+
})
|
|
4844
|
+
});
|
|
4845
|
+
function provenanceGraphId(targetGraphId) {
|
|
4846
|
+
return `${targetGraphId}::merge-provenance`;
|
|
4847
|
+
}
|
|
4848
|
+
function buildOwnedProvenanceGraph(targetGraphId) {
|
|
4849
|
+
return defineInternalGraph({
|
|
4850
|
+
id: provenanceGraphId(targetGraphId),
|
|
4851
|
+
nodes: {
|
|
4852
|
+
Provenance: { type: Provenance },
|
|
4853
|
+
ProvenanceOwner: { type: ProvenanceOwner }
|
|
4854
|
+
},
|
|
4855
|
+
edges: {}
|
|
4856
|
+
});
|
|
4857
|
+
}
|
|
4858
|
+
function buildProvenanceGraph(targetGraphId) {
|
|
4859
|
+
return defineInternalGraph({
|
|
4860
|
+
id: provenanceGraphId(targetGraphId),
|
|
4861
|
+
nodes: { Provenance: { type: Provenance } },
|
|
4862
|
+
edges: {}
|
|
4863
|
+
});
|
|
4864
|
+
}
|
|
4865
|
+
async function openProvenanceStore(...args) {
|
|
4866
|
+
const [backend, targetGraphId] = args.length === 1 ? [storeBackend(args[0]), args[0].graphId] : args;
|
|
4867
|
+
const graph = buildOwnedProvenanceGraph(targetGraphId);
|
|
4868
|
+
const preflight = await inspectSidecarGraphId(backend, graph, targetGraphId);
|
|
4869
|
+
if (preflight.state === "refuse") {
|
|
4870
|
+
throw provenanceGraphIdCollision(graph.id, targetGraphId, preflight.reason);
|
|
4871
|
+
}
|
|
4872
|
+
if (preflight.state !== "owned") {
|
|
4873
|
+
const claim = await claimSidecarOwnership(backend, graph, targetGraphId);
|
|
4874
|
+
if (claim.state === "refuse") {
|
|
4875
|
+
throw provenanceGraphIdCollision(graph.id, targetGraphId, claim.reason);
|
|
4876
|
+
}
|
|
4877
|
+
}
|
|
4878
|
+
const [store] = await createStoreWithSchema(graph, backend);
|
|
4879
|
+
return store;
|
|
4880
|
+
}
|
|
4881
|
+
async function claimSidecarOwnership(backend, graph, targetGraphId) {
|
|
4882
|
+
const fence = backend.schemaWriteTransaction;
|
|
4883
|
+
if (fence === void 0) {
|
|
4884
|
+
throw provenanceClaimUnfenced(graph.id, targetGraphId);
|
|
4885
|
+
}
|
|
4886
|
+
return fence(graph.id, async (tx) => {
|
|
4887
|
+
await drainUnfencedRowWriters(tx);
|
|
4888
|
+
const verdict = await inspectSidecarGraphId(tx, graph, targetGraphId);
|
|
4889
|
+
if (verdict.state === "refuse" || verdict.state === "owned") return verdict;
|
|
4890
|
+
await tx.insertNode({
|
|
3394
4891
|
graphId: graph.id,
|
|
3395
4892
|
kind: PROVENANCE_OWNER_KIND,
|
|
3396
4893
|
id: PROVENANCE_OWNER_ID,
|
|
@@ -3673,10 +5170,10 @@ async function readProvenance(store, query = {}) {
|
|
|
3673
5170
|
}
|
|
3674
5171
|
|
|
3675
5172
|
// src/graph-merge/sources.ts
|
|
3676
|
-
function orderEndpoints(left, right) {
|
|
5173
|
+
function orderEndpoints(left, right, sources) {
|
|
3677
5174
|
const leftKey = mergeKeyOf(left);
|
|
3678
5175
|
const rightKey = mergeKeyOf(right);
|
|
3679
|
-
return compareMergeKeys(leftKey, rightKey) <= 0 ? { a: leftKey, b: rightKey, left, right } : { a: rightKey, b: leftKey, left: right, right: left };
|
|
5176
|
+
return compareMergeKeys(leftKey, rightKey) <= 0 ? { a: leftKey, b: rightKey, left, right, sources } : { a: rightKey, b: leftKey, left: right, right: left, sources };
|
|
3680
5177
|
}
|
|
3681
5178
|
function sortMembersById(members) {
|
|
3682
5179
|
return [...members].sort((left, right) => compareStrings(left.id, right.id));
|
|
@@ -3719,7 +5216,8 @@ function windowedPairs(members, config) {
|
|
|
3719
5216
|
pairs.push(
|
|
3720
5217
|
orderEndpoints(
|
|
3721
5218
|
requireDefined(keyed[index]).node,
|
|
3722
|
-
requireDefined(keyed[index_]).node
|
|
5219
|
+
requireDefined(keyed[index_]).node,
|
|
5220
|
+
[{ kind: "keyless", sourceId: "keyless" }]
|
|
3723
5221
|
)
|
|
3724
5222
|
);
|
|
3725
5223
|
}
|
|
@@ -3733,8 +5231,22 @@ function pairsFromBlocks(blocks, keyless) {
|
|
|
3733
5231
|
continue;
|
|
3734
5232
|
}
|
|
3735
5233
|
const members = sortMembersById(blocks.get(bucketKey) ?? []);
|
|
3736
|
-
if (
|
|
3737
|
-
|
|
5234
|
+
if (bucketKey === UNBLOCKED_BUCKET_KEY) {
|
|
5235
|
+
if (keyless !== void 0) {
|
|
5236
|
+
pairs.push(...windowedPairs(members, keyless));
|
|
5237
|
+
continue;
|
|
5238
|
+
}
|
|
5239
|
+
for (let index = 0; index < members.length; index += 1) {
|
|
5240
|
+
for (let index_ = index + 1; index_ < members.length; index_ += 1) {
|
|
5241
|
+
pairs.push(
|
|
5242
|
+
orderEndpoints(
|
|
5243
|
+
requireDefined(members[index]),
|
|
5244
|
+
requireDefined(members[index_]),
|
|
5245
|
+
[{ kind: "keyless", sourceId: "keyless" }]
|
|
5246
|
+
)
|
|
5247
|
+
);
|
|
5248
|
+
}
|
|
5249
|
+
}
|
|
3738
5250
|
continue;
|
|
3739
5251
|
}
|
|
3740
5252
|
for (let index = 0; index < members.length; index += 1) {
|
|
@@ -3742,7 +5254,8 @@ function pairsFromBlocks(blocks, keyless) {
|
|
|
3742
5254
|
pairs.push(
|
|
3743
5255
|
orderEndpoints(
|
|
3744
5256
|
requireDefined(members[index]),
|
|
3745
|
-
requireDefined(members[index_])
|
|
5257
|
+
requireDefined(members[index_]),
|
|
5258
|
+
[{ kind: "block", sourceId: "exactKey" }]
|
|
3746
5259
|
)
|
|
3747
5260
|
);
|
|
3748
5261
|
}
|
|
@@ -3757,13 +5270,37 @@ function forcedEdgesFromBlocks(blocks) {
|
|
|
3757
5270
|
continue;
|
|
3758
5271
|
}
|
|
3759
5272
|
const members = sortMembersById(blocks.get(bucketKey) ?? []);
|
|
5273
|
+
const constraintName = uniqueConstraintNameForBucket(bucketKey);
|
|
5274
|
+
if (constraintName === void 0) {
|
|
5275
|
+
throw new CandidateSourceError(
|
|
5276
|
+
"Unique candidate bucket has no constraint name.",
|
|
5277
|
+
{
|
|
5278
|
+
details: {
|
|
5279
|
+
operation: "candidateEvidence",
|
|
5280
|
+
sourceId: "unique",
|
|
5281
|
+
kind: members[0]?.kind ?? "unknown"
|
|
5282
|
+
}
|
|
5283
|
+
}
|
|
5284
|
+
);
|
|
5285
|
+
}
|
|
3760
5286
|
for (let index = 0; index < members.length; index += 1) {
|
|
3761
5287
|
for (let index_ = index + 1; index_ < members.length; index_ += 1) {
|
|
3762
5288
|
const { a, b } = orderEndpoints(
|
|
3763
5289
|
requireDefined(members[index]),
|
|
3764
|
-
requireDefined(members[index_])
|
|
5290
|
+
requireDefined(members[index_]),
|
|
5291
|
+
[]
|
|
3765
5292
|
);
|
|
3766
|
-
edges.push({
|
|
5293
|
+
edges.push({
|
|
5294
|
+
a,
|
|
5295
|
+
b,
|
|
5296
|
+
score: FORCED_MATCH_SCORE,
|
|
5297
|
+
evidence: {
|
|
5298
|
+
a: entityRef(a),
|
|
5299
|
+
b: entityRef(b),
|
|
5300
|
+
sources: [{ kind: "unique", sourceId: "unique", constraintName }],
|
|
5301
|
+
decision: "definitional"
|
|
5302
|
+
}
|
|
5303
|
+
});
|
|
3767
5304
|
}
|
|
3768
5305
|
}
|
|
3769
5306
|
}
|
|
@@ -3797,7 +5334,13 @@ function ontologyRetypeEdges(stagedIdentities, isRetypeCompatible) {
|
|
|
3797
5334
|
edges.push({
|
|
3798
5335
|
a: requireDefined(members[0]),
|
|
3799
5336
|
b: requireDefined(members[index]),
|
|
3800
|
-
score: FORCED_MATCH_SCORE
|
|
5337
|
+
score: FORCED_MATCH_SCORE,
|
|
5338
|
+
evidence: {
|
|
5339
|
+
a: entityRef(requireDefined(members[0])),
|
|
5340
|
+
b: entityRef(requireDefined(members[index])),
|
|
5341
|
+
sources: [{ kind: "retype", sourceId: "retype" }],
|
|
5342
|
+
decision: "definitional"
|
|
5343
|
+
}
|
|
3801
5344
|
});
|
|
3802
5345
|
}
|
|
3803
5346
|
}
|
|
@@ -3864,9 +5407,16 @@ var baseUniqueSource = {
|
|
|
3864
5407
|
async generate(scope) {
|
|
3865
5408
|
const { kind, nodes, uniqueConstraints, store } = scope;
|
|
3866
5409
|
if (nodes === void 0 || uniqueConstraints === void 0 || store === void 0) {
|
|
3867
|
-
throw new
|
|
5410
|
+
throw new CandidateSourceError(
|
|
3868
5411
|
"baseUniqueSource requires nodes, uniqueConstraints, and store in the source scope.",
|
|
3869
|
-
{
|
|
5412
|
+
{
|
|
5413
|
+
details: {
|
|
5414
|
+
kind,
|
|
5415
|
+
source: "baseUnique",
|
|
5416
|
+
sourceId: "baseUnique",
|
|
5417
|
+
operation: "generate"
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
3870
5420
|
);
|
|
3871
5421
|
}
|
|
3872
5422
|
const collection = store.nodes[kind];
|
|
@@ -3884,14 +5434,23 @@ var baseUniqueSource = {
|
|
|
3884
5434
|
try {
|
|
3885
5435
|
return await collection.bulkFindByConstraint(constraint.name, items);
|
|
3886
5436
|
} catch (error) {
|
|
3887
|
-
throw new
|
|
5437
|
+
throw new CandidateSourceError(
|
|
3888
5438
|
`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
|
-
{
|
|
5439
|
+
{
|
|
5440
|
+
details: {
|
|
5441
|
+
kind,
|
|
5442
|
+
sourceId: "baseUnique",
|
|
5443
|
+
operation: "lookupConstraint",
|
|
5444
|
+
constraint: constraint.name
|
|
5445
|
+
},
|
|
5446
|
+
cause: error
|
|
5447
|
+
}
|
|
3890
5448
|
);
|
|
3891
5449
|
}
|
|
3892
5450
|
})
|
|
3893
5451
|
);
|
|
3894
|
-
for (const matches of matchesByConstraint) {
|
|
5452
|
+
for (const [constraintIndex, matches] of matchesByConstraint.entries()) {
|
|
5453
|
+
const constraint = requireDefined(constraints[constraintIndex]);
|
|
3895
5454
|
for (const [index, node] of nodes.entries()) {
|
|
3896
5455
|
const base = matches[index];
|
|
3897
5456
|
if (base === void 0) {
|
|
@@ -3905,14 +5464,32 @@ var baseUniqueSource = {
|
|
|
3905
5464
|
origin: "base",
|
|
3906
5465
|
id: base.id,
|
|
3907
5466
|
kind: base.kind,
|
|
3908
|
-
props: nodeProps(base)
|
|
5467
|
+
props: nodeProps(base),
|
|
5468
|
+
...base.meta.validFrom === void 0 ? {} : { validFrom: base.meta.validFrom },
|
|
5469
|
+
...base.meta.validTo === void 0 ? {} : { validTo: base.meta.validTo }
|
|
3909
5470
|
});
|
|
3910
5471
|
}
|
|
3911
5472
|
if (stagedKey === baseKey) {
|
|
3912
5473
|
continue;
|
|
3913
5474
|
}
|
|
3914
5475
|
const { a, b } = compareMergeKeys(stagedKey, baseKey) <= 0 ? { a: stagedKey, b: baseKey } : { a: baseKey, b: stagedKey };
|
|
3915
|
-
forcedEdges.push({
|
|
5476
|
+
forcedEdges.push({
|
|
5477
|
+
a,
|
|
5478
|
+
b,
|
|
5479
|
+
score: FORCED_MATCH_SCORE,
|
|
5480
|
+
evidence: {
|
|
5481
|
+
a: entityRef(a),
|
|
5482
|
+
b: entityRef(b),
|
|
5483
|
+
sources: [
|
|
5484
|
+
{
|
|
5485
|
+
kind: "baseUnique",
|
|
5486
|
+
sourceId: "baseUnique",
|
|
5487
|
+
constraintName: constraint.name
|
|
5488
|
+
}
|
|
5489
|
+
],
|
|
5490
|
+
decision: "definitional"
|
|
5491
|
+
}
|
|
5492
|
+
});
|
|
3916
5493
|
}
|
|
3917
5494
|
}
|
|
3918
5495
|
return {
|
|
@@ -3932,9 +5509,16 @@ var baseKeySource = {
|
|
|
3932
5509
|
return { pairs: [], forcedEdges: [], baseMembers: [] };
|
|
3933
5510
|
}
|
|
3934
5511
|
if (nodes === void 0 || store === void 0) {
|
|
3935
|
-
throw new
|
|
5512
|
+
throw new CandidateSourceError(
|
|
3936
5513
|
"baseKeySource requires nodes and store in the source scope.",
|
|
3937
|
-
{
|
|
5514
|
+
{
|
|
5515
|
+
details: {
|
|
5516
|
+
kind,
|
|
5517
|
+
source: "baseKey",
|
|
5518
|
+
sourceId: "baseKey",
|
|
5519
|
+
operation: "generate"
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
3938
5522
|
);
|
|
3939
5523
|
}
|
|
3940
5524
|
const collection = store.nodes[kind];
|
|
@@ -3946,9 +5530,17 @@ var baseKeySource = {
|
|
|
3946
5530
|
try {
|
|
3947
5531
|
matchesByNode = await collection.bulkFindByIndex(blockIndex, items);
|
|
3948
5532
|
} catch (error) {
|
|
3949
|
-
throw new
|
|
5533
|
+
throw new CandidateSourceError(
|
|
3950
5534
|
`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
|
-
{
|
|
5535
|
+
{
|
|
5536
|
+
details: {
|
|
5537
|
+
kind,
|
|
5538
|
+
sourceId: "baseKey",
|
|
5539
|
+
operation: "lookupIndex",
|
|
5540
|
+
index: blockIndex
|
|
5541
|
+
},
|
|
5542
|
+
cause: error
|
|
5543
|
+
}
|
|
3952
5544
|
);
|
|
3953
5545
|
}
|
|
3954
5546
|
const pairs = [];
|
|
@@ -3963,13 +5555,19 @@ var baseKeySource = {
|
|
|
3963
5555
|
origin: "base",
|
|
3964
5556
|
id: base.id,
|
|
3965
5557
|
kind: base.kind,
|
|
3966
|
-
props: nodeProps(base)
|
|
5558
|
+
props: nodeProps(base),
|
|
5559
|
+
...base.meta.validFrom === void 0 ? {} : { validFrom: base.meta.validFrom },
|
|
5560
|
+
...base.meta.validTo === void 0 ? {} : { validTo: base.meta.validTo }
|
|
3967
5561
|
});
|
|
3968
5562
|
}
|
|
3969
5563
|
if (stagedKey === baseKey) {
|
|
3970
5564
|
continue;
|
|
3971
5565
|
}
|
|
3972
|
-
pairs.push(
|
|
5566
|
+
pairs.push(
|
|
5567
|
+
orderEndpoints(stagedNode, base, [
|
|
5568
|
+
{ kind: "baseIndex", sourceId: "baseKey", indexName: blockIndex }
|
|
5569
|
+
])
|
|
5570
|
+
);
|
|
3973
5571
|
}
|
|
3974
5572
|
}
|
|
3975
5573
|
return {
|
|
@@ -4321,6 +5919,8 @@ async function generateAllCandidates(target, staging, options, introspectionKind
|
|
|
4321
5919
|
const allEdges = [];
|
|
4322
5920
|
const warnings = [];
|
|
4323
5921
|
const baseMembers = [];
|
|
5922
|
+
const diagnostics = [];
|
|
5923
|
+
let diagnosticsTotal = 0;
|
|
4324
5924
|
const byKind = newNodesByKind(staging);
|
|
4325
5925
|
const sources = useBaseSources ? [...CANDIDATE_SOURCES, baseUniqueSource, baseKeySource] : CANDIDATE_SOURCES;
|
|
4326
5926
|
const baseStore = target;
|
|
@@ -4329,7 +5929,13 @@ async function generateAllCandidates(target, staging, options, introspectionKind
|
|
|
4329
5929
|
async ([kind, items]) => {
|
|
4330
5930
|
const resolveConfig = options.resolve[kind];
|
|
4331
5931
|
if (resolveConfig === void 0) {
|
|
4332
|
-
return ok({
|
|
5932
|
+
return ok({
|
|
5933
|
+
edges: [],
|
|
5934
|
+
warnings: [],
|
|
5935
|
+
baseMembers: [],
|
|
5936
|
+
diagnostics: [],
|
|
5937
|
+
diagnosticsTotal: 0
|
|
5938
|
+
});
|
|
4333
5939
|
}
|
|
4334
5940
|
const nodes = items.map((staged) => asNode(staged));
|
|
4335
5941
|
const uniqueConstraints = uniqueConstraintsFor(
|
|
@@ -4370,7 +5976,8 @@ async function generateAllCandidates(target, staging, options, introspectionKind
|
|
|
4370
5976
|
resolveConfig,
|
|
4371
5977
|
kindCtx,
|
|
4372
5978
|
options.onComparisonCeiling,
|
|
4373
|
-
options.maxComparisonsPerKind
|
|
5979
|
+
options.maxComparisonsPerKind,
|
|
5980
|
+
options.candidateDiagnostics?.limit ?? 0
|
|
4374
5981
|
);
|
|
4375
5982
|
if (isErr(scored)) {
|
|
4376
5983
|
return err(scored.error);
|
|
@@ -4380,7 +5987,9 @@ async function generateAllCandidates(target, staging, options, introspectionKind
|
|
|
4380
5987
|
warnings: scored.data.warnings.map(
|
|
4381
5988
|
(warning) => `[${kind}] ${warning.message}`
|
|
4382
5989
|
),
|
|
4383
|
-
baseMembers: kindBaseMembers
|
|
5990
|
+
baseMembers: kindBaseMembers,
|
|
5991
|
+
diagnostics: scored.data.diagnostics,
|
|
5992
|
+
diagnosticsTotal: scored.data.diagnosticsTotal
|
|
4384
5993
|
});
|
|
4385
5994
|
}
|
|
4386
5995
|
)
|
|
@@ -4392,6 +6001,14 @@ async function generateAllCandidates(target, staging, options, introspectionKind
|
|
|
4392
6001
|
allEdges.push(...result.data.edges);
|
|
4393
6002
|
warnings.push(...result.data.warnings);
|
|
4394
6003
|
baseMembers.push(...result.data.baseMembers);
|
|
6004
|
+
if (options.candidateDiagnostics !== void 0) {
|
|
6005
|
+
diagnostics.push(...result.data.diagnostics);
|
|
6006
|
+
diagnostics.sort(
|
|
6007
|
+
(left, right) => compareMatchEvidence(left.evidence, right.evidence)
|
|
6008
|
+
);
|
|
6009
|
+
diagnostics.splice(options.candidateDiagnostics.limit);
|
|
6010
|
+
}
|
|
6011
|
+
diagnosticsTotal += result.data.diagnosticsTotal;
|
|
4395
6012
|
}
|
|
4396
6013
|
return ok({
|
|
4397
6014
|
// The ONE shared `(a, b)` edge comparator (id-first `(kind, id)` order), so this
|
|
@@ -4402,7 +6019,9 @@ async function generateAllCandidates(target, staging, options, introspectionKind
|
|
|
4402
6019
|
// base members would tie under a bare-id compare).
|
|
4403
6020
|
baseMembers: baseMembers.sort(
|
|
4404
6021
|
(left, right) => compareMergeKeys(mergeKeyOf(left), mergeKeyOf(right))
|
|
4405
|
-
)
|
|
6022
|
+
),
|
|
6023
|
+
diagnostics,
|
|
6024
|
+
diagnosticsTotal
|
|
4406
6025
|
});
|
|
4407
6026
|
}
|
|
4408
6027
|
function clusterMembersFor(cluster, newNodesById, baseMembersById) {
|
|
@@ -4426,7 +6045,9 @@ function clusterMembersFor(cluster, newNodesById, baseMembersById) {
|
|
|
4426
6045
|
id: base.id,
|
|
4427
6046
|
kind: base.kind,
|
|
4428
6047
|
branchId: BASE_PROVENANCE_BRANCH,
|
|
4429
|
-
props: base.props
|
|
6048
|
+
props: base.props,
|
|
6049
|
+
...base.validFrom === void 0 ? {} : { validFrom: base.validFrom },
|
|
6050
|
+
...base.validTo === void 0 ? {} : { validTo: base.validTo }
|
|
4430
6051
|
});
|
|
4431
6052
|
}
|
|
4432
6053
|
}
|
|
@@ -4459,7 +6080,7 @@ function buildStagedEdges(staging, modifiedEdges, edgeValidityEnds, edgeDeletion
|
|
|
4459
6080
|
for (const item of modifiedEdges) {
|
|
4460
6081
|
const identity = mergeKeyOf(item.edge);
|
|
4461
6082
|
stagedIdentities.add(identity);
|
|
4462
|
-
const
|
|
6083
|
+
const validToChange = edgeValidityEnds.get(identity);
|
|
4463
6084
|
staged.push({
|
|
4464
6085
|
id: item.edge.id,
|
|
4465
6086
|
kind: item.edge.kind,
|
|
@@ -4471,13 +6092,13 @@ function buildStagedEdges(staging, modifiedEdges, edgeValidityEnds, edgeDeletion
|
|
|
4471
6092
|
props: item.edge.forkProps,
|
|
4472
6093
|
baseProps: item.edge.baseProps,
|
|
4473
6094
|
branchId: item.branchId,
|
|
4474
|
-
...
|
|
6095
|
+
...validToChange?.kind === "set" ? { validTo: validToChange.validTo } : validToChange?.kind === "clear" ? { clearValidTo: true } : {}
|
|
4475
6096
|
});
|
|
4476
6097
|
}
|
|
4477
6098
|
for (const item of staging.windowedEdges) {
|
|
4478
6099
|
const identity = mergeKeyOf(item.edge);
|
|
4479
|
-
const
|
|
4480
|
-
if (
|
|
6100
|
+
const validToChange = edgeValidityEnds.get(identity);
|
|
6101
|
+
if (validToChange === void 0 || stagedIdentities.has(identity) || edgeDeletions.has(identity)) {
|
|
4481
6102
|
continue;
|
|
4482
6103
|
}
|
|
4483
6104
|
stagedIdentities.add(identity);
|
|
@@ -4493,7 +6114,7 @@ function buildStagedEdges(staging, modifiedEdges, edgeValidityEnds, edgeDeletion
|
|
|
4493
6114
|
props: item.edge.props,
|
|
4494
6115
|
baseProps: item.edge.baseProps,
|
|
4495
6116
|
branchId: item.branchId,
|
|
4496
|
-
validTo
|
|
6117
|
+
...validToChange.kind === "set" ? { validTo: validToChange.validTo } : { clearValidTo: true }
|
|
4497
6118
|
});
|
|
4498
6119
|
}
|
|
4499
6120
|
return { edges: staged, windowOnlyCarried };
|
|
@@ -4541,7 +6162,7 @@ function buildProvenanceIndex(records) {
|
|
|
4541
6162
|
};
|
|
4542
6163
|
}
|
|
4543
6164
|
var EMPTY_WEIGHTS = /* @__PURE__ */ new Map();
|
|
4544
|
-
function
|
|
6165
|
+
function buildInternalMergePlan(staging, candidateEdges, candidateWarnings, candidateDiagnostics, candidateDiagnosticsTotal, baseMembers, options, branchRank, subClassClosure, identityContext, storedIdentityRowsById, targetPeers, preferredBranchId) {
|
|
4545
6166
|
const identity = planIdentityChanges(staging, storedIdentityRowsById);
|
|
4546
6167
|
const provenanceRecords = [];
|
|
4547
6168
|
const recordedContributions = /* @__PURE__ */ new Set();
|
|
@@ -4593,7 +6214,56 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4593
6214
|
clusterEdges,
|
|
4594
6215
|
baseIds
|
|
4595
6216
|
);
|
|
4596
|
-
const
|
|
6217
|
+
const diameterGuard = options.clusterMaxDiameter === void 0 ? {
|
|
6218
|
+
clusters: guard.clusters,
|
|
6219
|
+
survivingEdges: guard.survivingEdges,
|
|
6220
|
+
excludedEdges: []
|
|
6221
|
+
} : enforceDiameterWithEdges(
|
|
6222
|
+
guard.clusters,
|
|
6223
|
+
clusterEdges,
|
|
6224
|
+
options.clusterMaxDiameter
|
|
6225
|
+
);
|
|
6226
|
+
const clusters = diameterGuard.clusters;
|
|
6227
|
+
const baseSurvivingEdges = new Set(guard.survivingEdges);
|
|
6228
|
+
const survivingEdges = diameterGuard.survivingEdges.filter(
|
|
6229
|
+
(edge) => baseSurvivingEdges.has(edge)
|
|
6230
|
+
);
|
|
6231
|
+
const excludedByEndpoints = /* @__PURE__ */ new Map();
|
|
6232
|
+
for (const excluded of [
|
|
6233
|
+
...guard.excludedEdges,
|
|
6234
|
+
...diameterGuard.excludedEdges
|
|
6235
|
+
]) {
|
|
6236
|
+
excludedByEndpoints.set(
|
|
6237
|
+
JSON.stringify([excluded.edge.a, excluded.edge.b]),
|
|
6238
|
+
excluded.reason
|
|
6239
|
+
);
|
|
6240
|
+
}
|
|
6241
|
+
const diagnosticsWithDisposition = candidateDiagnostics.map((diagnostic) => {
|
|
6242
|
+
if (diagnostic.evidence.decision === "definitional") return diagnostic;
|
|
6243
|
+
if (diagnostic.scoreDecision === "rejected") return diagnostic;
|
|
6244
|
+
const a = mergeKey(diagnostic.evidence.a.kind, diagnostic.evidence.a.id);
|
|
6245
|
+
const b = mergeKey(diagnostic.evidence.b.kind, diagnostic.evidence.b.id);
|
|
6246
|
+
const reason = excludedByEndpoints.get(JSON.stringify([a, b]));
|
|
6247
|
+
return {
|
|
6248
|
+
...diagnostic,
|
|
6249
|
+
clusterDisposition: reason === void 0 ? "retained" : { kind: "excluded", reason }
|
|
6250
|
+
};
|
|
6251
|
+
});
|
|
6252
|
+
const definitionalExclusions = [
|
|
6253
|
+
...guard.excludedEdges,
|
|
6254
|
+
...diameterGuard.excludedEdges
|
|
6255
|
+
].filter((excluded) => excluded.edge.evidence.decision === "definitional").map((excluded) => ({
|
|
6256
|
+
evidence: excluded.edge.evidence,
|
|
6257
|
+
scoreDecision: "accepted",
|
|
6258
|
+
clusterDisposition: {
|
|
6259
|
+
kind: "excluded",
|
|
6260
|
+
reason: excluded.reason
|
|
6261
|
+
}
|
|
6262
|
+
}));
|
|
6263
|
+
const retainedDiagnostics = [
|
|
6264
|
+
...diagnosticsWithDisposition,
|
|
6265
|
+
...definitionalExclusions
|
|
6266
|
+
].sort((left, right) => compareMatchEvidence(left.evidence, right.evidence));
|
|
4597
6267
|
const baseAmbiguities = guard.events.map((event) => ({
|
|
4598
6268
|
baseIds: event.baseIds.map((key) => ({ kind: kindOf(key), id: idOf(key) })),
|
|
4599
6269
|
memberIds: event.memberIds.map((key) => ({
|
|
@@ -4603,6 +6273,7 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4603
6273
|
}));
|
|
4604
6274
|
const canonicalEntities = [];
|
|
4605
6275
|
const resolutions = [];
|
|
6276
|
+
const clusterByCanonicalIdentity = /* @__PURE__ */ new Map();
|
|
4606
6277
|
const propertyConflicts = [];
|
|
4607
6278
|
const reconcileInputs = [];
|
|
4608
6279
|
for (const cluster of clusters) {
|
|
@@ -4622,6 +6293,10 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4622
6293
|
preferredBranchId
|
|
4623
6294
|
);
|
|
4624
6295
|
canonicalEntities.push(entity);
|
|
6296
|
+
clusterByCanonicalIdentity.set(
|
|
6297
|
+
mergeKey(entity.kind, entity.canonicalId),
|
|
6298
|
+
cluster
|
|
6299
|
+
);
|
|
4625
6300
|
reconcileInputs.push({
|
|
4626
6301
|
canonicalId: mergeKey(entity.kind, entity.canonicalId),
|
|
4627
6302
|
memberKinds: members.map((member) => member.kind)
|
|
@@ -4630,7 +6305,10 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4630
6305
|
cluster.members.map((member) => idOf(member))
|
|
4631
6306
|
);
|
|
4632
6307
|
if (distinctMergedIds.size > 1) {
|
|
4633
|
-
resolutions.push(
|
|
6308
|
+
resolutions.push({
|
|
6309
|
+
...entity.resolution,
|
|
6310
|
+
decisiveEdges: decisiveEdgesForCluster(cluster, survivingEdges)
|
|
6311
|
+
});
|
|
4634
6312
|
}
|
|
4635
6313
|
for (const conflict of entity.conflicts) {
|
|
4636
6314
|
propertyConflicts.push(conflict);
|
|
@@ -4761,6 +6439,21 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4761
6439
|
subClassClosure,
|
|
4762
6440
|
options.reconcileTypes
|
|
4763
6441
|
);
|
|
6442
|
+
const reconciledClusterByEntityId = /* @__PURE__ */ new Map();
|
|
6443
|
+
for (const identity2 of reconciliation.retypeMap.keys()) {
|
|
6444
|
+
const entityId = idOf(identity2);
|
|
6445
|
+
const cluster = clusterByCanonicalIdentity.get(identity2);
|
|
6446
|
+
if (cluster !== void 0 && !reconciledClusterByEntityId.has(entityId)) {
|
|
6447
|
+
reconciledClusterByEntityId.set(entityId, cluster);
|
|
6448
|
+
}
|
|
6449
|
+
}
|
|
6450
|
+
const typeReconciliations = reconciliation.reconciliations.map((item) => {
|
|
6451
|
+
const cluster = reconciledClusterByEntityId.get(item.entityId);
|
|
6452
|
+
return cluster === void 0 ? item : {
|
|
6453
|
+
...item,
|
|
6454
|
+
decisiveEdges: decisiveEdgesForCluster(cluster, survivingEdges)
|
|
6455
|
+
};
|
|
6456
|
+
});
|
|
4764
6457
|
const entityByIdentity = /* @__PURE__ */ new Map();
|
|
4765
6458
|
for (const entity of canonicalEntities) {
|
|
4766
6459
|
entityByIdentity.set(mergeKey(entity.kind, entity.canonicalId), entity);
|
|
@@ -4769,11 +6462,24 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4769
6462
|
clusters,
|
|
4770
6463
|
(cluster) => pickClusterCanonical(cluster, entityByIdentity)
|
|
4771
6464
|
);
|
|
6465
|
+
const canonicalEndpointWindows = /* @__PURE__ */ new Map();
|
|
6466
|
+
for (const entity of canonicalEntities) {
|
|
6467
|
+
if (entity.endpointValidFrom === void 0 && entity.endpointValidTo === void 0) {
|
|
6468
|
+
continue;
|
|
6469
|
+
}
|
|
6470
|
+
const sourceKey2 = mergeKey(entity.kind, entity.canonicalId);
|
|
6471
|
+
const finalKind = reconciliation.retypeMap.get(sourceKey2) ?? entity.kind;
|
|
6472
|
+
canonicalEndpointWindows.set(mergeKey(finalKind, entity.canonicalId), {
|
|
6473
|
+
...entity.endpointValidFrom === void 0 ? {} : { validFrom: entity.endpointValidFrom },
|
|
6474
|
+
...entity.endpointValidTo === void 0 ? {} : { validTo: entity.endpointValidTo }
|
|
6475
|
+
});
|
|
6476
|
+
}
|
|
4772
6477
|
const identityRemap = remapIdentityAssertionEndpoints(
|
|
4773
6478
|
identity.assertions,
|
|
4774
6479
|
canonicalOf,
|
|
4775
6480
|
reconciliation.retypeMap,
|
|
4776
|
-
storedIdentityRowsById
|
|
6481
|
+
storedIdentityRowsById,
|
|
6482
|
+
canonicalEndpointWindows
|
|
4777
6483
|
);
|
|
4778
6484
|
assertIdentityEndpointsNotDeleted(
|
|
4779
6485
|
identityRemap.assertions,
|
|
@@ -4897,7 +6603,7 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4897
6603
|
mergeKey(right.kind, right.entityId)
|
|
4898
6604
|
)
|
|
4899
6605
|
),
|
|
4900
|
-
typeReconciliations
|
|
6606
|
+
typeReconciliations,
|
|
4901
6607
|
dropped,
|
|
4902
6608
|
baseAmbiguities: baseAmbiguities.sort(
|
|
4903
6609
|
(left, right) => compareMergeKeys(
|
|
@@ -4912,7 +6618,18 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4912
6618
|
)
|
|
4913
6619
|
),
|
|
4914
6620
|
provenanceRecords,
|
|
4915
|
-
warnings: candidateWarnings,
|
|
6621
|
+
warnings: [...candidateWarnings, ...identityRemap.warnings],
|
|
6622
|
+
...options.candidateDiagnostics === void 0 ? {} : {
|
|
6623
|
+
candidateDiagnostics: {
|
|
6624
|
+
entries: retainedDiagnostics.slice(
|
|
6625
|
+
0,
|
|
6626
|
+
options.candidateDiagnostics.limit
|
|
6627
|
+
),
|
|
6628
|
+
total: candidateDiagnosticsTotal + definitionalExclusions.length,
|
|
6629
|
+
limit: options.candidateDiagnostics.limit,
|
|
6630
|
+
truncated: candidateDiagnosticsTotal + definitionalExclusions.length > options.candidateDiagnostics.limit
|
|
6631
|
+
}
|
|
6632
|
+
},
|
|
4916
6633
|
identityAssertions: identityRemap.assertions,
|
|
4917
6634
|
identityRetractions: survivingRetractions
|
|
4918
6635
|
};
|
|
@@ -4963,13 +6680,13 @@ function plannedNodeWrites(plan) {
|
|
|
4963
6680
|
continue;
|
|
4964
6681
|
}
|
|
4965
6682
|
written.add(identity);
|
|
4966
|
-
const
|
|
6683
|
+
const validToChange = plan.nodeValidityEnds.get(identity);
|
|
4967
6684
|
writes.push({
|
|
4968
6685
|
identity,
|
|
4969
6686
|
kind: modification.node.kind,
|
|
4970
6687
|
id: modification.node.id,
|
|
4971
6688
|
modification: modification.node,
|
|
4972
|
-
...
|
|
6689
|
+
...validToChange?.kind === "set" ? { validTo: validToChange.validTo } : validToChange?.kind === "clear" ? { clearValidTo: true } : {}
|
|
4973
6690
|
});
|
|
4974
6691
|
}
|
|
4975
6692
|
for (const entity of plan.canonicalEntities) {
|
|
@@ -4980,7 +6697,7 @@ function plannedNodeWrites(plan) {
|
|
|
4980
6697
|
const kind = plan.retypeMap.get(sourceIdentity) ?? entity.kind;
|
|
4981
6698
|
const modification = modificationsByIdentity.get(sourceIdentity);
|
|
4982
6699
|
written.add(sourceIdentity);
|
|
4983
|
-
const
|
|
6700
|
+
const validToChange = plan.nodeValidityEnds.get(sourceIdentity);
|
|
4984
6701
|
writes.push({
|
|
4985
6702
|
identity: mergeKey(kind, entity.canonicalId),
|
|
4986
6703
|
kind,
|
|
@@ -4988,7 +6705,7 @@ function plannedNodeWrites(plan) {
|
|
|
4988
6705
|
entity,
|
|
4989
6706
|
...modification === void 0 ? {} : { modification },
|
|
4990
6707
|
...entity.validFrom === void 0 ? {} : { validFrom: entity.validFrom },
|
|
4991
|
-
...validTo ===
|
|
6708
|
+
...typeof entity.validTo === "string" ? { validTo: entity.validTo } : validToChange?.kind === "set" ? { validTo: validToChange.validTo } : validToChange?.kind === "clear" ? { clearValidTo: true } : {}
|
|
4992
6709
|
});
|
|
4993
6710
|
}
|
|
4994
6711
|
for (const identity of [...plan.nodeValidityEnds.keys()].sort(
|
|
@@ -4998,11 +6715,12 @@ function plannedNodeWrites(plan) {
|
|
|
4998
6715
|
continue;
|
|
4999
6716
|
}
|
|
5000
6717
|
const kind = plan.retypeMap.get(identity) ?? kindOf(identity);
|
|
6718
|
+
const validToChange = requireDefined(plan.nodeValidityEnds.get(identity));
|
|
5001
6719
|
writes.push({
|
|
5002
6720
|
identity: mergeKey(kind, idOf(identity)),
|
|
5003
6721
|
kind,
|
|
5004
6722
|
id: idOf(identity),
|
|
5005
|
-
validTo:
|
|
6723
|
+
...validToChange.kind === "set" ? { validTo: validToChange.validTo } : { clearValidTo: true }
|
|
5006
6724
|
});
|
|
5007
6725
|
}
|
|
5008
6726
|
return writes;
|
|
@@ -5013,67 +6731,181 @@ function nodeWriteProps(write, modificationProps) {
|
|
|
5013
6731
|
...write.modification === void 0 ? void 0 : modificationProps(write.modification)
|
|
5014
6732
|
};
|
|
5015
6733
|
}
|
|
5016
|
-
|
|
5017
|
-
const
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
await collection.delete(idOf(identity));
|
|
6734
|
+
function nodeWriteWindowOptions(write) {
|
|
6735
|
+
const validFrom = write.validFrom === void 0 ? {} : { validFrom: write.validFrom };
|
|
6736
|
+
if (write.clearValidTo === true) {
|
|
6737
|
+
return { ...validFrom, clearValidTo: true };
|
|
5021
6738
|
}
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
6739
|
+
return {
|
|
6740
|
+
...validFrom,
|
|
6741
|
+
...write.validTo === void 0 ? {} : { validTo: write.validTo }
|
|
6742
|
+
};
|
|
6743
|
+
}
|
|
6744
|
+
async function applyNodeRows(target, txBackend, nodesApi, deletions, upserts) {
|
|
6745
|
+
const afterImages = /* @__PURE__ */ new Map();
|
|
6746
|
+
const upsertsByKind = /* @__PURE__ */ new Map();
|
|
6747
|
+
for (const upsert of upserts) {
|
|
6748
|
+
const items = upsertsByKind.get(upsert.kind);
|
|
6749
|
+
if (items === void 0) upsertsByKind.set(upsert.kind, [upsert]);
|
|
6750
|
+
else items.push(upsert);
|
|
6751
|
+
}
|
|
6752
|
+
for (const [kind, items] of upsertsByKind) {
|
|
6753
|
+
const currentRows = await nodeCollection(nodesApi, kind).getByIds(
|
|
6754
|
+
items.map((item) => item.id),
|
|
6755
|
+
INCLUDE_TOMBSTONES
|
|
5034
6756
|
);
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
6757
|
+
const currentById = /* @__PURE__ */ new Map();
|
|
6758
|
+
for (const node of currentRows) {
|
|
6759
|
+
if (node !== void 0) currentById.set(node.id, nodeProps2(node));
|
|
6760
|
+
}
|
|
6761
|
+
for (const item of items) {
|
|
6762
|
+
const props = createDataKeyedBag();
|
|
6763
|
+
const unset = new Set(
|
|
6764
|
+
Object.entries(item.props).filter(([, value]) => value === void 0).map(([key]) => key)
|
|
6765
|
+
);
|
|
6766
|
+
for (const [key, value] of Object.entries(
|
|
6767
|
+
currentById.get(item.id) ?? {}
|
|
6768
|
+
)) {
|
|
6769
|
+
if (!unset.has(key)) props[key] = value;
|
|
6770
|
+
}
|
|
6771
|
+
for (const [key, value] of Object.entries(item.props)) {
|
|
6772
|
+
if (value !== void 0) props[key] = value;
|
|
6773
|
+
}
|
|
6774
|
+
afterImages.set(mergeKey(kind, item.id), props);
|
|
6775
|
+
}
|
|
5039
6776
|
}
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
}
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
6777
|
+
return storeRuntime(target).applyResolvedNodeUniqueness(
|
|
6778
|
+
txBackend,
|
|
6779
|
+
{
|
|
6780
|
+
upserts: upserts.map((upsert) => ({
|
|
6781
|
+
kind: upsert.kind,
|
|
6782
|
+
id: upsert.id,
|
|
6783
|
+
props: requireDefined(
|
|
6784
|
+
afterImages.get(mergeKey(upsert.kind, upsert.id))
|
|
6785
|
+
)
|
|
6786
|
+
})),
|
|
6787
|
+
releases: deletions
|
|
6788
|
+
},
|
|
6789
|
+
async () => {
|
|
6790
|
+
for (const deletion of deletions) {
|
|
6791
|
+
await nodeCollection(nodesApi, deletion.kind).delete(deletion.id);
|
|
6792
|
+
}
|
|
6793
|
+
const committed = /* @__PURE__ */ new Set();
|
|
6794
|
+
for (const upsert of upserts) {
|
|
6795
|
+
await nodeCollection(nodesApi, upsert.kind).upsertByIdFromRecord(
|
|
6796
|
+
upsert.id,
|
|
6797
|
+
upsert.props,
|
|
6798
|
+
nodeWriteWindowOptions(upsert)
|
|
6799
|
+
);
|
|
6800
|
+
committed.add(mergeKey(upsert.kind, upsert.id));
|
|
6801
|
+
}
|
|
6802
|
+
return committed.size;
|
|
5057
6803
|
}
|
|
6804
|
+
);
|
|
6805
|
+
}
|
|
6806
|
+
async function applyEdgeRows(edgesApi, deletions, upserts) {
|
|
6807
|
+
for (const deletion of deletions) {
|
|
6808
|
+
await edgeCollection(edgesApi, deletion.kind).delete(deletion.id);
|
|
6809
|
+
}
|
|
6810
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
6811
|
+
for (const upsert of upserts) {
|
|
6812
|
+
const items = byKind.get(upsert.kind);
|
|
6813
|
+
if (items === void 0) byKind.set(upsert.kind, [upsert.item]);
|
|
6814
|
+
else items.push(upsert.item);
|
|
5058
6815
|
}
|
|
5059
|
-
let
|
|
5060
|
-
for (const [kind, items] of
|
|
6816
|
+
let committed = 0;
|
|
6817
|
+
for (const [kind, items] of byKind) {
|
|
5061
6818
|
await edgeCollection(edgesApi, kind).bulkUpsertById(items);
|
|
5062
|
-
|
|
6819
|
+
committed += items.length;
|
|
5063
6820
|
}
|
|
5064
|
-
|
|
6821
|
+
return committed;
|
|
6822
|
+
}
|
|
6823
|
+
async function applyIdentityRows(target, txBackend, assertions, retractions, assertConsistent) {
|
|
5065
6824
|
try {
|
|
5066
|
-
|
|
6825
|
+
const applied = await storeRuntime(target).applyIdentityMergeAtTarget(
|
|
5067
6826
|
txBackend,
|
|
5068
|
-
|
|
5069
|
-
|
|
6827
|
+
retractions,
|
|
6828
|
+
assertions
|
|
5070
6829
|
);
|
|
5071
|
-
|
|
6830
|
+
if (assertConsistent !== void 0) await assertConsistent();
|
|
6831
|
+
return { asserted: applied.created, retracted: applied.retracted };
|
|
5072
6832
|
} catch (error) {
|
|
5073
6833
|
throw translateIdentityCommitError(error);
|
|
5074
6834
|
}
|
|
5075
|
-
|
|
5076
|
-
|
|
6835
|
+
}
|
|
6836
|
+
async function applyInternalMergePlan(plan, nodesApi, edgesApi, target, txBackend) {
|
|
6837
|
+
const nodeDeletions = [...plan.nodeDeletions].map(([identity, kind]) => ({
|
|
6838
|
+
kind,
|
|
6839
|
+
id: idOf(identity)
|
|
6840
|
+
}));
|
|
6841
|
+
const nodeUpserts = plannedNodeWrites(plan).map((write) => ({
|
|
6842
|
+
kind: write.kind,
|
|
6843
|
+
id: write.id,
|
|
6844
|
+
props: nodeWriteProps(
|
|
6845
|
+
write,
|
|
6846
|
+
(modification) => commitModificationProps(modification.baseProps, modification.forkProps)
|
|
6847
|
+
),
|
|
6848
|
+
...write.validFrom === void 0 ? {} : { validFrom: write.validFrom },
|
|
6849
|
+
...write.clearValidTo === true ? { clearValidTo: true } : write.validTo === void 0 ? {} : { validTo: write.validTo }
|
|
6850
|
+
}));
|
|
6851
|
+
const validityEndedNodes = new Set(
|
|
6852
|
+
nodeUpserts.filter((write) => "validTo" in write).map((write) => mergeKey(write.kind, write.id))
|
|
6853
|
+
);
|
|
6854
|
+
const earlyIdentityRetractions = plan.identityRetractions.filter(
|
|
6855
|
+
(retraction) => validityEndedNodes.has(mergeKeyOf(retraction.a)) || validityEndedNodes.has(mergeKeyOf(retraction.b))
|
|
6856
|
+
);
|
|
6857
|
+
const earlyIdentity = await applyIdentityRows(
|
|
6858
|
+
target,
|
|
6859
|
+
txBackend,
|
|
6860
|
+
[],
|
|
6861
|
+
earlyIdentityRetractions
|
|
6862
|
+
);
|
|
6863
|
+
let committedNodes;
|
|
6864
|
+
try {
|
|
6865
|
+
committedNodes = await applyNodeRows(
|
|
6866
|
+
target,
|
|
6867
|
+
txBackend,
|
|
6868
|
+
nodesApi,
|
|
6869
|
+
nodeDeletions,
|
|
6870
|
+
nodeUpserts
|
|
6871
|
+
);
|
|
6872
|
+
} catch (error) {
|
|
6873
|
+
throw error instanceof IdentityEndpointValidityError ? translateIdentityCommitError(error) : error;
|
|
6874
|
+
}
|
|
6875
|
+
const edgeDeletions = [...plan.edgeDeletions].map(([identity, kind]) => ({
|
|
6876
|
+
kind,
|
|
6877
|
+
id: idOf(identity)
|
|
6878
|
+
}));
|
|
6879
|
+
const edgeUpserts = [];
|
|
6880
|
+
for (const edge of plan.mergedEdges) {
|
|
6881
|
+
const edgeBaseProps = plan.inheritedEdgeBaseProps.get(edge.id);
|
|
6882
|
+
const props = edgeBaseProps === void 0 ? edge.props : commitModificationProps(edgeBaseProps, edge.props);
|
|
6883
|
+
edgeUpserts.push({
|
|
6884
|
+
kind: edge.kind,
|
|
6885
|
+
item: {
|
|
6886
|
+
id: edge.id,
|
|
6887
|
+
from: finalEdgeEndpoint(plan, edge.fromKind, edge.fromId),
|
|
6888
|
+
to: finalEdgeEndpoint(plan, edge.toKind, edge.toId),
|
|
6889
|
+
props,
|
|
6890
|
+
...edge.validFrom === void 0 ? {} : { validFrom: edge.validFrom },
|
|
6891
|
+
...edge.clearValidTo === true ? { clearValidTo: true } : edge.validTo === void 0 ? {} : { validTo: edge.validTo }
|
|
6892
|
+
}
|
|
6893
|
+
});
|
|
6894
|
+
}
|
|
6895
|
+
const committedEdges = await applyEdgeRows(
|
|
6896
|
+
edgesApi,
|
|
6897
|
+
edgeDeletions,
|
|
6898
|
+
edgeUpserts
|
|
6899
|
+
);
|
|
6900
|
+
const appliedIdentity = await applyIdentityRows(
|
|
6901
|
+
target,
|
|
6902
|
+
txBackend,
|
|
6903
|
+
plan.identityAssertions,
|
|
6904
|
+
plan.identityRetractions,
|
|
6905
|
+
() => assertMergedIdentityClassesConsistent(target, txBackend, plan)
|
|
6906
|
+
);
|
|
6907
|
+
return {
|
|
6908
|
+
nodes: committedNodes,
|
|
5077
6909
|
edges: committedEdges,
|
|
5078
6910
|
// ACTUAL ledger effects from the applier: rows created (idempotent
|
|
5079
6911
|
// exact/pair matches the target already held are excluded — the normal
|
|
@@ -5082,8 +6914,8 @@ async function applyMergePlan(plan, nodesApi, edgesApi, target, txBackend) {
|
|
|
5082
6914
|
// failure aborts the whole merge, so the counts never describe a
|
|
5083
6915
|
// partial commit.
|
|
5084
6916
|
identity: {
|
|
5085
|
-
asserted: appliedIdentity.
|
|
5086
|
-
retracted: appliedIdentity.retracted
|
|
6917
|
+
asserted: appliedIdentity.asserted,
|
|
6918
|
+
retracted: earlyIdentity.retracted + appliedIdentity.retracted
|
|
5087
6919
|
}
|
|
5088
6920
|
};
|
|
5089
6921
|
}
|
|
@@ -5094,26 +6926,39 @@ async function commitPlan(target, plan, expectedBaseVersion) {
|
|
|
5094
6926
|
{ details: { capability: "transactions" } }
|
|
5095
6927
|
);
|
|
5096
6928
|
}
|
|
5097
|
-
return
|
|
5098
|
-
() =>
|
|
5099
|
-
|
|
5100
|
-
|
|
6929
|
+
return runMergeCommit(
|
|
6930
|
+
() => withTxConflictRetry(
|
|
6931
|
+
() => target.transaction(async (tx) => {
|
|
6932
|
+
if (expectedBaseVersion !== void 0) {
|
|
6933
|
+
await assertTargetUnchanged(
|
|
6934
|
+
transactionBackend(tx),
|
|
6935
|
+
target,
|
|
6936
|
+
expectedBaseVersion
|
|
6937
|
+
);
|
|
6938
|
+
}
|
|
6939
|
+
await assertPlannedIdentityIdsFresh(
|
|
6940
|
+
target,
|
|
5101
6941
|
transactionBackend(tx),
|
|
6942
|
+
plan
|
|
6943
|
+
);
|
|
6944
|
+
return applyInternalMergePlan(
|
|
6945
|
+
plan,
|
|
6946
|
+
tx.nodes,
|
|
6947
|
+
tx.edges,
|
|
5102
6948
|
target,
|
|
5103
|
-
|
|
6949
|
+
transactionBackend(tx)
|
|
5104
6950
|
);
|
|
5105
|
-
}
|
|
5106
|
-
|
|
5107
|
-
return applyMergePlan(
|
|
5108
|
-
plan,
|
|
5109
|
-
tx.nodes,
|
|
5110
|
-
tx.edges,
|
|
5111
|
-
target,
|
|
5112
|
-
transactionBackend(tx)
|
|
5113
|
-
);
|
|
5114
|
-
}, mergeCommitTransactionOptions(target))
|
|
6951
|
+
}, mergeCommitTransactionOptions(target))
|
|
6952
|
+
)
|
|
5115
6953
|
);
|
|
5116
6954
|
}
|
|
6955
|
+
async function runMergeCommit(commit) {
|
|
6956
|
+
try {
|
|
6957
|
+
return await commit();
|
|
6958
|
+
} catch (error) {
|
|
6959
|
+
throw translateMergeCommitError(error);
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
5117
6962
|
var MERGE_COMMIT_TX_OPTIONS = {
|
|
5118
6963
|
isolationLevel: "serializable"
|
|
5119
6964
|
};
|
|
@@ -5250,7 +7095,169 @@ async function tryOpenProvenanceStore(target) {
|
|
|
5250
7095
|
);
|
|
5251
7096
|
}
|
|
5252
7097
|
}
|
|
5253
|
-
|
|
7098
|
+
function assertPublicPlanCapability(target) {
|
|
7099
|
+
if (!storeBackend(target).capabilities.transactions) {
|
|
7100
|
+
throw new MergePlanCapabilityError(
|
|
7101
|
+
"Public merge plans require a transaction-capable target backend.",
|
|
7102
|
+
{ details: { capability: "transactions" } }
|
|
7103
|
+
);
|
|
7104
|
+
}
|
|
7105
|
+
if (!target.revisionTrackingEnabled) {
|
|
7106
|
+
throw new MergePlanCapabilityError(
|
|
7107
|
+
"Public merge plans require durable target revision tracking.",
|
|
7108
|
+
{
|
|
7109
|
+
details: { capability: "revisionTracking" },
|
|
7110
|
+
suggestion: "Create the target Store with { revisionTracking: true } (or history enabled), then create a new plan."
|
|
7111
|
+
}
|
|
7112
|
+
);
|
|
7113
|
+
}
|
|
7114
|
+
}
|
|
7115
|
+
async function captureMergePlanTargetFence(target) {
|
|
7116
|
+
assertPublicPlanCapability(target);
|
|
7117
|
+
const [activeSchema, schemaHash, origin, revision] = await Promise.all([
|
|
7118
|
+
storeBackend(target).getActiveSchema(target.graphId),
|
|
7119
|
+
computeSchemaComponent(target),
|
|
7120
|
+
target.revisionOriginNow(),
|
|
7121
|
+
target.revisionNow()
|
|
7122
|
+
]);
|
|
7123
|
+
return {
|
|
7124
|
+
graphId: target.graphId,
|
|
7125
|
+
schema: {
|
|
7126
|
+
managed: activeSchema !== void 0,
|
|
7127
|
+
version: activeSchema?.version ?? 1,
|
|
7128
|
+
hash: activeSchema?.schema_hash ?? schemaHash
|
|
7129
|
+
},
|
|
7130
|
+
revision: { origin, revision: revision ?? null }
|
|
7131
|
+
};
|
|
7132
|
+
}
|
|
7133
|
+
function sameMergePlanTargetFence(left, right) {
|
|
7134
|
+
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;
|
|
7135
|
+
}
|
|
7136
|
+
async function assertPlanningFenceUnchanged(target, startingFence) {
|
|
7137
|
+
const endingFence = await captureMergePlanTargetFence(target);
|
|
7138
|
+
if (sameMergePlanTargetFence(startingFence, endingFence)) return;
|
|
7139
|
+
throw new MergePlanningStaleError(
|
|
7140
|
+
"The merge target changed while the plan was being computed; no reviewable plan was produced.",
|
|
7141
|
+
{ details: { startingFence, endingFence } }
|
|
7142
|
+
);
|
|
7143
|
+
}
|
|
7144
|
+
function splitWireProps(props) {
|
|
7145
|
+
const setProps = createDataKeyedBag();
|
|
7146
|
+
const unsetProps = [];
|
|
7147
|
+
for (const key of Object.keys(props).sort(compareStrings)) {
|
|
7148
|
+
const value = props[key];
|
|
7149
|
+
if (value === void 0) {
|
|
7150
|
+
unsetProps.push(key);
|
|
7151
|
+
} else {
|
|
7152
|
+
setProps[key] = value;
|
|
7153
|
+
}
|
|
7154
|
+
}
|
|
7155
|
+
return { setProps, unsetProps };
|
|
7156
|
+
}
|
|
7157
|
+
function wireEntityRef(kind, id) {
|
|
7158
|
+
return { kind, id };
|
|
7159
|
+
}
|
|
7160
|
+
function resolvedNodeUpserts(plan) {
|
|
7161
|
+
return plannedNodeWrites(plan).map((write) => ({
|
|
7162
|
+
kind: write.kind,
|
|
7163
|
+
id: write.id,
|
|
7164
|
+
...splitWireProps(
|
|
7165
|
+
nodeWriteProps(
|
|
7166
|
+
write,
|
|
7167
|
+
(modification) => commitModificationProps(modification.baseProps, modification.forkProps)
|
|
7168
|
+
)
|
|
7169
|
+
),
|
|
7170
|
+
...write.validFrom === void 0 ? {} : { validFrom: write.validFrom },
|
|
7171
|
+
...write.validTo === void 0 ? {} : { validTo: write.validTo }
|
|
7172
|
+
}));
|
|
7173
|
+
}
|
|
7174
|
+
async function resolvedMergeArtifact(resolved, mode, targetFence, anchors) {
|
|
7175
|
+
const { plan, options } = resolved;
|
|
7176
|
+
const nodeUpserts = resolvedNodeUpserts(plan);
|
|
7177
|
+
const edgeUpserts = plan.mergedEdges.map((edge) => {
|
|
7178
|
+
const baseProps = plan.inheritedEdgeBaseProps.get(edge.id);
|
|
7179
|
+
const props = baseProps === void 0 ? edge.props : commitModificationProps(baseProps, edge.props);
|
|
7180
|
+
return {
|
|
7181
|
+
kind: edge.kind,
|
|
7182
|
+
id: edge.id,
|
|
7183
|
+
from: finalEdgeEndpoint(plan, edge.fromKind, edge.fromId),
|
|
7184
|
+
to: finalEdgeEndpoint(plan, edge.toKind, edge.toId),
|
|
7185
|
+
...splitWireProps(props),
|
|
7186
|
+
...edge.validFrom === void 0 ? {} : { validFrom: edge.validFrom },
|
|
7187
|
+
...edge.validTo === void 0 ? {} : { validTo: edge.validTo }
|
|
7188
|
+
};
|
|
7189
|
+
});
|
|
7190
|
+
const input = {
|
|
7191
|
+
formatVersion: 1,
|
|
7192
|
+
mode,
|
|
7193
|
+
target: targetFence,
|
|
7194
|
+
anchors,
|
|
7195
|
+
proposed: {
|
|
7196
|
+
nodes: {
|
|
7197
|
+
upserts: nodeUpserts.length,
|
|
7198
|
+
deletions: plan.nodeDeletions.size
|
|
7199
|
+
},
|
|
7200
|
+
edges: {
|
|
7201
|
+
upserts: edgeUpserts.length,
|
|
7202
|
+
deletions: plan.edgeDeletions.size
|
|
7203
|
+
},
|
|
7204
|
+
identity: {
|
|
7205
|
+
assertions: plan.identityAssertions.length,
|
|
7206
|
+
retractions: plan.identityRetractions.length
|
|
7207
|
+
}
|
|
7208
|
+
},
|
|
7209
|
+
writes: {
|
|
7210
|
+
nodeDeletes: [...plan.nodeDeletions].map(
|
|
7211
|
+
([identity, kind]) => wireEntityRef(kind, idOf(identity))
|
|
7212
|
+
),
|
|
7213
|
+
nodeUpserts,
|
|
7214
|
+
edgeDeletes: [...plan.edgeDeletions].map(
|
|
7215
|
+
([identity, kind]) => wireEntityRef(kind, idOf(identity))
|
|
7216
|
+
),
|
|
7217
|
+
edgeUpserts,
|
|
7218
|
+
identityAssertions: plan.identityAssertions,
|
|
7219
|
+
identityRetractions: plan.identityRetractions
|
|
7220
|
+
},
|
|
7221
|
+
guards: {
|
|
7222
|
+
canonicalMappings: [...plan.canonicalOf].sort(([left], [right]) => compareMergeKeys(left, right)).map(([member, canonical]) => ({
|
|
7223
|
+
member: wireEntityRef(kindOf(member), idOf(member)),
|
|
7224
|
+
canonical: wireEntityRef(kindOf(canonical), idOf(canonical))
|
|
7225
|
+
})),
|
|
7226
|
+
retypes: [...plan.retypeMap].sort(([left], [right]) => compareMergeKeys(left, right)).map(([entity, toKind]) => ({
|
|
7227
|
+
entity: wireEntityRef(kindOf(entity), idOf(entity)),
|
|
7228
|
+
toKind
|
|
7229
|
+
})),
|
|
7230
|
+
deletedNodes: [...plan.nodeDeletions].map(
|
|
7231
|
+
([identity, kind]) => wireEntityRef(kind, idOf(identity))
|
|
7232
|
+
),
|
|
7233
|
+
...resolved.incrementalGuard === void 0 ? {} : {
|
|
7234
|
+
incremental: {
|
|
7235
|
+
tombstoneResurrection: "refuse",
|
|
7236
|
+
lossyUpdates: "refuse",
|
|
7237
|
+
edgeIdentity: "preserve"
|
|
7238
|
+
}
|
|
7239
|
+
}
|
|
7240
|
+
},
|
|
7241
|
+
review: {
|
|
7242
|
+
resolutions: plan.resolutions,
|
|
7243
|
+
conflicts: plan.propertyConflicts,
|
|
7244
|
+
deleteModifyConflicts: plan.deleteModifyConflicts,
|
|
7245
|
+
typeReconciliations: plan.typeReconciliations,
|
|
7246
|
+
dropped: plan.dropped,
|
|
7247
|
+
validityEnds: plan.validityEnds,
|
|
7248
|
+
baseAmbiguities: plan.baseAmbiguities,
|
|
7249
|
+
provenanceRecords: plan.provenanceRecords,
|
|
7250
|
+
warnings: plan.warnings,
|
|
7251
|
+
...plan.candidateDiagnostics === void 0 ? {} : { diagnostics: plan.candidateDiagnostics }
|
|
7252
|
+
},
|
|
7253
|
+
provenance: {
|
|
7254
|
+
includeInReport: options.provenance,
|
|
7255
|
+
persist: options.persistProvenance
|
|
7256
|
+
}
|
|
7257
|
+
};
|
|
7258
|
+
return constructMergePlanArtifact(input);
|
|
7259
|
+
}
|
|
7260
|
+
async function resolveMerge(store, target, branches, options, useBaseSources, incremental, expectedBaseVersion, complete) {
|
|
5254
7261
|
let targetBranchSeen = false;
|
|
5255
7262
|
for (const branch2 of branches) {
|
|
5256
7263
|
if (branch2.id === BASE_PROVENANCE_BRANCH) {
|
|
@@ -5303,10 +7310,6 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
|
|
|
5303
7310
|
);
|
|
5304
7311
|
}
|
|
5305
7312
|
}
|
|
5306
|
-
const provenanceStore = options.persistProvenance ? await tryOpenProvenanceStore(target) : void 0;
|
|
5307
|
-
if (provenanceStore !== void 0 && isErr(provenanceStore)) {
|
|
5308
|
-
return provenanceStore;
|
|
5309
|
-
}
|
|
5310
7313
|
try {
|
|
5311
7314
|
const preferredBranchId = incremental?.targetBranchId;
|
|
5312
7315
|
const staging = await stageBranches(store, branches, preferredBranchId);
|
|
@@ -5375,10 +7378,12 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
|
|
|
5375
7378
|
sameIdAcrossKinds: target.graph.identity?.sameIdAcrossKinds,
|
|
5376
7379
|
areDisjoint: (left, right) => target.registry.areDisjoint(left, right)
|
|
5377
7380
|
};
|
|
5378
|
-
const plan =
|
|
7381
|
+
const plan = buildInternalMergePlan(
|
|
5379
7382
|
staging,
|
|
5380
7383
|
candidates.data.edges,
|
|
5381
7384
|
candidates.data.warnings,
|
|
7385
|
+
candidates.data.diagnostics,
|
|
7386
|
+
candidates.data.diagnosticsTotal,
|
|
5382
7387
|
candidates.data.baseMembers,
|
|
5383
7388
|
options,
|
|
5384
7389
|
branchRank,
|
|
@@ -5434,29 +7439,731 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
|
|
|
5434
7439
|
);
|
|
5435
7440
|
identityGuard = built.probe;
|
|
5436
7441
|
}
|
|
5437
|
-
const
|
|
7442
|
+
const incrementalGuard = incremental === void 0 ? void 0 : {
|
|
5438
7443
|
stagedNewByKind,
|
|
5439
7444
|
options,
|
|
5440
7445
|
introspectionKinds,
|
|
5441
7446
|
forkPoint: incremental.forkPoint,
|
|
5442
7447
|
...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
7448
|
plannedBaseMatchKeys: new Set(
|
|
5446
7449
|
candidates.data.baseMembers.map((member) => mergeKeyOf(member))
|
|
5447
7450
|
),
|
|
5448
7451
|
targetNodeVersions: staging.targetNodeVersions,
|
|
5449
7452
|
targetEdgeSignatures: staging.targetEdgeSignatures
|
|
5450
|
-
}
|
|
5451
|
-
|
|
5452
|
-
|
|
7453
|
+
};
|
|
7454
|
+
return ok(
|
|
7455
|
+
await complete({
|
|
7456
|
+
target,
|
|
7457
|
+
plan,
|
|
7458
|
+
options,
|
|
7459
|
+
...expectedBaseVersion === void 0 ? {} : { expectedBaseVersion },
|
|
7460
|
+
...incrementalGuard === void 0 ? {} : { incrementalGuard }
|
|
7461
|
+
})
|
|
7462
|
+
);
|
|
7463
|
+
} catch (error) {
|
|
7464
|
+
if (error instanceof MergeError) {
|
|
7465
|
+
return err(error);
|
|
7466
|
+
}
|
|
7467
|
+
return err(
|
|
7468
|
+
new MergeError(
|
|
7469
|
+
`Merge failed while staging or committing: ${describeCause(error)}`,
|
|
7470
|
+
{ cause: error }
|
|
7471
|
+
)
|
|
7472
|
+
);
|
|
7473
|
+
}
|
|
7474
|
+
}
|
|
7475
|
+
async function commitResolvedMerge(resolved) {
|
|
7476
|
+
const { target, plan, options } = resolved;
|
|
7477
|
+
const provenanceStore = options.persistProvenance ? await tryOpenProvenanceStore(target) : void 0;
|
|
7478
|
+
if (provenanceStore !== void 0 && isErr(provenanceStore)) {
|
|
7479
|
+
throw provenanceStore.error;
|
|
7480
|
+
}
|
|
7481
|
+
const merged = resolved.incrementalGuard === void 0 ? await commitPlan(target, plan, resolved.expectedBaseVersion) : await commitIncrementalPlan(target, plan, resolved.incrementalGuard);
|
|
7482
|
+
const provenance = options.provenance ? buildProvenanceIndex(plan.provenanceRecords) : { byBranch: () => ({ nodeIds: [], edgeIds: [] }) };
|
|
7483
|
+
const warnings = [...plan.warnings];
|
|
7484
|
+
let provenancePersisted;
|
|
7485
|
+
if (provenanceStore !== void 0 && !isErr(provenanceStore)) {
|
|
7486
|
+
try {
|
|
7487
|
+
const count = await persistProvenanceRecords(
|
|
7488
|
+
provenanceStore.data,
|
|
7489
|
+
target.graphId,
|
|
7490
|
+
plan.provenanceRecords
|
|
7491
|
+
);
|
|
7492
|
+
provenancePersisted = {
|
|
7493
|
+
graphId: provenanceGraphId(target.graphId),
|
|
7494
|
+
count
|
|
7495
|
+
};
|
|
7496
|
+
} catch (error) {
|
|
7497
|
+
warnings.push(
|
|
7498
|
+
"provenance persistence failed (graph committed; provenance not persisted): " + (error instanceof Error ? error.message : String(error))
|
|
7499
|
+
);
|
|
7500
|
+
}
|
|
7501
|
+
}
|
|
7502
|
+
return {
|
|
7503
|
+
merged,
|
|
7504
|
+
resolutions: plan.resolutions,
|
|
7505
|
+
conflicts: plan.propertyConflicts,
|
|
7506
|
+
deleteModifyConflicts: plan.deleteModifyConflicts,
|
|
7507
|
+
typeReconciliations: plan.typeReconciliations,
|
|
7508
|
+
dropped: plan.dropped,
|
|
7509
|
+
validityEnds: plan.validityEnds,
|
|
7510
|
+
baseAmbiguities: plan.baseAmbiguities,
|
|
7511
|
+
provenance,
|
|
7512
|
+
warnings,
|
|
7513
|
+
...plan.candidateDiagnostics === void 0 ? {} : { candidateDiagnostics: plan.candidateDiagnostics },
|
|
7514
|
+
...provenancePersisted === void 0 ? {} : { provenancePersisted }
|
|
7515
|
+
};
|
|
7516
|
+
}
|
|
7517
|
+
function incrementalBaseConflictPolicyError(options) {
|
|
7518
|
+
return new InvalidMergeOptionsError(
|
|
7519
|
+
'mergeIncremental() requires onBasePropertyConflict: "flag" (keep-base); a non-keep-base policy could overwrite a newer committed base value with a stale branch value.',
|
|
7520
|
+
{
|
|
7521
|
+
details: {
|
|
7522
|
+
option: "onBasePropertyConflict",
|
|
7523
|
+
accepted: "flag",
|
|
7524
|
+
received: options.onBasePropertyConflict
|
|
7525
|
+
}
|
|
7526
|
+
}
|
|
7527
|
+
);
|
|
7528
|
+
}
|
|
7529
|
+
function incrementalSchemaError() {
|
|
7530
|
+
return new MergeError(
|
|
7531
|
+
"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).",
|
|
7532
|
+
{ details: {} }
|
|
7533
|
+
);
|
|
7534
|
+
}
|
|
7535
|
+
async function planMerge(store, branchInputs, optionsInput = {}) {
|
|
7536
|
+
const branches = unwrapMergeBranches(branchInputs);
|
|
7537
|
+
const normalized = tryNormalize(optionsInput);
|
|
7538
|
+
if (isErr(normalized)) return err(normalized.error);
|
|
7539
|
+
const options = normalized.data;
|
|
7540
|
+
const target = options.target ?? store;
|
|
7541
|
+
let targetFence;
|
|
7542
|
+
try {
|
|
7543
|
+
targetFence = await captureMergePlanTargetFence(target);
|
|
7544
|
+
} catch (error) {
|
|
7545
|
+
return err(
|
|
7546
|
+
error instanceof MergeError ? error : new MergePlanCapabilityError(
|
|
7547
|
+
`Unable to capture the target's durable plan fence: ${describeCause(error)}`,
|
|
7548
|
+
{ cause: error }
|
|
7549
|
+
)
|
|
7550
|
+
);
|
|
7551
|
+
}
|
|
7552
|
+
const precondition = await validateBaseVersions(target, branches);
|
|
7553
|
+
if (isErr(precondition)) return err(precondition.error);
|
|
7554
|
+
const anchors = {
|
|
7555
|
+
kind: "snapshot",
|
|
7556
|
+
base: { graphId: store.graphId, baseVersion: precondition.data },
|
|
7557
|
+
branches: [...branches].sort((left, right) => compareStrings(left.id, right.id)).map((branch2) => ({
|
|
7558
|
+
branchId: branch2.id,
|
|
7559
|
+
baseVersion: branch2.base
|
|
7560
|
+
}))
|
|
7561
|
+
};
|
|
7562
|
+
return resolveMerge(
|
|
7563
|
+
store,
|
|
7564
|
+
target,
|
|
7565
|
+
branches,
|
|
7566
|
+
options,
|
|
7567
|
+
false,
|
|
7568
|
+
void 0,
|
|
7569
|
+
precondition.data,
|
|
7570
|
+
async (resolved) => {
|
|
7571
|
+
await assertPlanningFenceUnchanged(target, targetFence);
|
|
7572
|
+
return resolvedMergeArtifact(resolved, "snapshot", targetFence, anchors);
|
|
7573
|
+
}
|
|
7574
|
+
);
|
|
7575
|
+
}
|
|
7576
|
+
async function planMergeIncremental(args) {
|
|
7577
|
+
const { forkPoint, target } = args;
|
|
7578
|
+
const branches = unwrapMergeBranches(args.branches);
|
|
7579
|
+
const normalized = tryNormalize(args.options ?? {}, ["target"]);
|
|
7580
|
+
if (isErr(normalized)) return err(normalized.error);
|
|
7581
|
+
const options = normalized.data;
|
|
7582
|
+
if (options.onBasePropertyConflict !== "flag") {
|
|
7583
|
+
return err(incrementalBaseConflictPolicyError(options));
|
|
7584
|
+
}
|
|
7585
|
+
let targetFence;
|
|
7586
|
+
try {
|
|
7587
|
+
targetFence = await captureMergePlanTargetFence(target);
|
|
7588
|
+
} catch (error) {
|
|
7589
|
+
return err(
|
|
7590
|
+
error instanceof MergeError ? error : new MergePlanCapabilityError(
|
|
7591
|
+
`Unable to capture the target's durable plan fence: ${describeCause(error)}`,
|
|
7592
|
+
{ cause: error }
|
|
7593
|
+
)
|
|
7594
|
+
);
|
|
7595
|
+
}
|
|
7596
|
+
const forkPrecondition = await validateForkPointVersions(forkPoint, branches);
|
|
7597
|
+
if (isErr(forkPrecondition)) return err(forkPrecondition.error);
|
|
7598
|
+
const forkVersion = forkPrecondition.data;
|
|
7599
|
+
const [forkSchema, targetSchema] = await Promise.all([
|
|
7600
|
+
computeSchemaComponent(forkPoint),
|
|
7601
|
+
computeSchemaComponent(target)
|
|
7602
|
+
]);
|
|
7603
|
+
if (forkSchema !== targetSchema) return err(incrementalSchemaError());
|
|
7604
|
+
const forkActiveSchema = await storeBackend(forkPoint).getActiveSchema(
|
|
7605
|
+
forkPoint.graphId
|
|
7606
|
+
);
|
|
7607
|
+
const targetBranch = {
|
|
7608
|
+
id: COMMITTED_TARGET_BRANCH,
|
|
7609
|
+
base: forkVersion,
|
|
7610
|
+
store: target
|
|
7611
|
+
};
|
|
7612
|
+
const anchors = {
|
|
7613
|
+
kind: "incremental",
|
|
7614
|
+
forkPoint: {
|
|
7615
|
+
graphId: forkPoint.graphId,
|
|
7616
|
+
baseVersion: forkVersion,
|
|
7617
|
+
schema: {
|
|
7618
|
+
managed: forkActiveSchema !== void 0,
|
|
7619
|
+
version: forkActiveSchema?.version ?? 1,
|
|
7620
|
+
hash: forkActiveSchema?.schema_hash ?? forkSchema
|
|
7621
|
+
}
|
|
7622
|
+
},
|
|
7623
|
+
branches: [...branches].sort((left, right) => compareStrings(left.id, right.id)).map((branch2) => ({
|
|
7624
|
+
branchId: branch2.id,
|
|
7625
|
+
baseVersion: branch2.base
|
|
7626
|
+
}))
|
|
7627
|
+
};
|
|
7628
|
+
return resolveMerge(
|
|
7629
|
+
forkPoint,
|
|
7630
|
+
target,
|
|
7631
|
+
[targetBranch, ...branches],
|
|
7632
|
+
options,
|
|
7633
|
+
true,
|
|
7634
|
+
{
|
|
7635
|
+
targetBranchId: COMMITTED_TARGET_BRANCH,
|
|
7636
|
+
forkPoint: { store: forkPoint, version: forkVersion }
|
|
7637
|
+
},
|
|
7638
|
+
void 0,
|
|
7639
|
+
async (resolved) => {
|
|
7640
|
+
await assertPlanningFenceUnchanged(target, targetFence);
|
|
7641
|
+
await assertForkPointUnchanged({
|
|
7642
|
+
store: forkPoint,
|
|
7643
|
+
version: forkVersion
|
|
7644
|
+
});
|
|
7645
|
+
return resolvedMergeArtifact(
|
|
7646
|
+
resolved,
|
|
7647
|
+
"incremental",
|
|
7648
|
+
targetFence,
|
|
7649
|
+
anchors
|
|
7650
|
+
);
|
|
7651
|
+
}
|
|
7652
|
+
);
|
|
7653
|
+
}
|
|
7654
|
+
function mergePlanValidationError(failure) {
|
|
7655
|
+
switch (failure.kind) {
|
|
7656
|
+
case "unsupported-version": {
|
|
7657
|
+
return new UnsupportedMergePlanVersionError(
|
|
7658
|
+
`Unsupported merge plan format version ${String(failure.received)}.`,
|
|
7659
|
+
{ details: { received: failure.received } }
|
|
7660
|
+
);
|
|
7661
|
+
}
|
|
7662
|
+
case "digest-mismatch": {
|
|
7663
|
+
return new MergePlanDigestMismatchError(
|
|
7664
|
+
"The merge plan digest does not match its canonical content.",
|
|
7665
|
+
{ details: { expected: failure.expected, received: failure.received } }
|
|
7666
|
+
);
|
|
7667
|
+
}
|
|
7668
|
+
case "malformed": {
|
|
7669
|
+
return new InvalidMergePlanError("The merge plan is malformed.", {
|
|
7670
|
+
details: { issues: failure.issues }
|
|
7671
|
+
});
|
|
7672
|
+
}
|
|
7673
|
+
default: {
|
|
7674
|
+
const exhaustive = failure;
|
|
7675
|
+
return exhaustive;
|
|
7676
|
+
}
|
|
7677
|
+
}
|
|
7678
|
+
}
|
|
7679
|
+
function wireWriteProps(setProps, unsetProps) {
|
|
7680
|
+
const props = createDataKeyedBag();
|
|
7681
|
+
for (const [key, value] of Object.entries(setProps)) props[key] = value;
|
|
7682
|
+
for (const key of unsetProps) props[key] = void 0;
|
|
7683
|
+
return props;
|
|
7684
|
+
}
|
|
7685
|
+
function resolvedWireProps(current, setProps, unsetProps) {
|
|
7686
|
+
const props = createDataKeyedBag();
|
|
7687
|
+
const unset = new Set(unsetProps);
|
|
7688
|
+
for (const [key, value] of Object.entries(current)) {
|
|
7689
|
+
if (!unset.has(key)) props[key] = value;
|
|
7690
|
+
}
|
|
7691
|
+
for (const [key, value] of Object.entries(setProps)) props[key] = value;
|
|
7692
|
+
return props;
|
|
7693
|
+
}
|
|
7694
|
+
function invalidPlanForTarget(message, details) {
|
|
7695
|
+
return new InvalidMergePlanError(message, { details });
|
|
7696
|
+
}
|
|
7697
|
+
async function preflightWireMergeWrites(target, nodesApi, edgesApi, artifact) {
|
|
7698
|
+
const graphNodes = target.graph.nodes;
|
|
7699
|
+
const graphEdges = target.graph.edges;
|
|
7700
|
+
const assertNodeKind = (kind) => {
|
|
7701
|
+
if (!hasOwnKey(graphNodes, kind)) {
|
|
7702
|
+
throw invalidPlanForTarget(
|
|
7703
|
+
`The merge plan references unknown node kind "${kind}".`,
|
|
7704
|
+
{ kind, role: "node" }
|
|
7705
|
+
);
|
|
7706
|
+
}
|
|
7707
|
+
};
|
|
7708
|
+
const assertEdgeKind = (kind) => {
|
|
7709
|
+
if (!hasOwnKey(graphEdges, kind) || graphEdges[kind] === void 0) {
|
|
7710
|
+
throw invalidPlanForTarget(
|
|
7711
|
+
`The merge plan references unknown edge kind "${kind}".`,
|
|
7712
|
+
{ kind, role: "edge" }
|
|
7713
|
+
);
|
|
7714
|
+
}
|
|
7715
|
+
};
|
|
7716
|
+
for (const item of [
|
|
7717
|
+
...artifact.writes.nodeDeletes,
|
|
7718
|
+
...artifact.writes.nodeUpserts,
|
|
7719
|
+
...artifact.guards.deletedNodes,
|
|
7720
|
+
...artifact.guards.canonicalMappings.flatMap((mapping) => [
|
|
7721
|
+
mapping.member,
|
|
7722
|
+
mapping.canonical
|
|
7723
|
+
]),
|
|
7724
|
+
...artifact.guards.retypes.map((retype) => retype.entity),
|
|
7725
|
+
...artifact.writes.identityAssertions.flatMap((assertion) => [
|
|
7726
|
+
assertion.a,
|
|
7727
|
+
assertion.b
|
|
7728
|
+
]),
|
|
7729
|
+
...artifact.writes.identityRetractions.flatMap((assertion) => [
|
|
7730
|
+
assertion.a,
|
|
7731
|
+
assertion.b
|
|
7732
|
+
])
|
|
7733
|
+
]) {
|
|
7734
|
+
assertNodeKind(item.kind);
|
|
7735
|
+
}
|
|
7736
|
+
for (const retype of artifact.guards.retypes) assertNodeKind(retype.toKind);
|
|
7737
|
+
for (const item of [
|
|
7738
|
+
...artifact.writes.edgeDeletes,
|
|
7739
|
+
...artifact.writes.edgeUpserts
|
|
7740
|
+
]) {
|
|
7741
|
+
assertEdgeKind(item.kind);
|
|
7742
|
+
}
|
|
7743
|
+
const plannedNodes = new Set(
|
|
7744
|
+
artifact.writes.nodeUpserts.map(
|
|
7745
|
+
(upsert) => mergeKey(upsert.kind, upsert.id)
|
|
7746
|
+
)
|
|
7747
|
+
);
|
|
7748
|
+
const deletedNodes = new Set(
|
|
7749
|
+
artifact.writes.nodeDeletes.map(
|
|
7750
|
+
(deletion) => mergeKey(deletion.kind, deletion.id)
|
|
7751
|
+
)
|
|
7752
|
+
);
|
|
7753
|
+
const endpointRows = /* @__PURE__ */ new Map();
|
|
7754
|
+
const readEndpoint = async (endpoint) => {
|
|
7755
|
+
const key = mergeKey(endpoint.kind, endpoint.id);
|
|
7756
|
+
if (endpointRows.has(key)) return endpointRows.get(key);
|
|
7757
|
+
const row = (await nodeCollection(nodesApi, endpoint.kind).getByIds(
|
|
7758
|
+
[endpoint.id],
|
|
7759
|
+
INCLUDE_TOMBSTONES
|
|
7760
|
+
))[0];
|
|
7761
|
+
endpointRows.set(key, row);
|
|
7762
|
+
return row;
|
|
7763
|
+
};
|
|
7764
|
+
const nodeUpsertsByKind = /* @__PURE__ */ new Map();
|
|
7765
|
+
for (const upsert of artifact.writes.nodeUpserts) {
|
|
7766
|
+
const upserts = nodeUpsertsByKind.get(upsert.kind) ?? [];
|
|
7767
|
+
upserts.push(upsert);
|
|
7768
|
+
nodeUpsertsByKind.set(upsert.kind, upserts);
|
|
7769
|
+
}
|
|
7770
|
+
const currentNodesByIdentity = /* @__PURE__ */ new Map();
|
|
7771
|
+
for (const [kind, upserts] of nodeUpsertsByKind) {
|
|
7772
|
+
const rows = await nodeCollection(nodesApi, kind).getByIds(
|
|
7773
|
+
upserts.map((upsert) => upsert.id),
|
|
7774
|
+
INCLUDE_TOMBSTONES
|
|
7775
|
+
);
|
|
7776
|
+
for (const [index, upsert] of upserts.entries()) {
|
|
7777
|
+
const row = rows[index];
|
|
7778
|
+
const identity = mergeKey(kind, upsert.id);
|
|
7779
|
+
currentNodesByIdentity.set(identity, row);
|
|
7780
|
+
endpointRows.set(identity, row);
|
|
7781
|
+
}
|
|
7782
|
+
}
|
|
7783
|
+
for (const upsert of artifact.writes.nodeUpserts) {
|
|
7784
|
+
const current = currentNodesByIdentity.get(
|
|
7785
|
+
mergeKey(upsert.kind, upsert.id)
|
|
7786
|
+
);
|
|
7787
|
+
if (artifact.guards.incremental !== void 0 && current?.meta.deletedAt !== void 0) {
|
|
7788
|
+
throw invalidPlanForTarget(
|
|
7789
|
+
`The incremental merge plan would resurrect soft-deleted committed node "${upsert.id}".`,
|
|
7790
|
+
{ kind: upsert.kind, id: upsert.id, role: "node" }
|
|
7791
|
+
);
|
|
7792
|
+
}
|
|
7793
|
+
const props = resolvedWireProps(
|
|
7794
|
+
current === void 0 ? {} : nodeProps2(current),
|
|
7795
|
+
upsert.setProps,
|
|
7796
|
+
upsert.unsetProps
|
|
7797
|
+
);
|
|
7798
|
+
const schema = nodeSchemaFor(target, upsert.kind);
|
|
7799
|
+
if (schema !== void 0 && !schema.safeParse(props).success) {
|
|
7800
|
+
throw invalidPlanForTarget(
|
|
7801
|
+
`The merge plan's node write for "${upsert.id}" does not validate against the active schema.`,
|
|
7802
|
+
{ kind: upsert.kind, id: upsert.id, role: "node" }
|
|
7803
|
+
);
|
|
7804
|
+
}
|
|
7805
|
+
if (artifact.guards.incremental !== void 0 && current !== void 0) {
|
|
7806
|
+
const analysis = analyzeRevalidatingWrite(
|
|
7807
|
+
schema,
|
|
7808
|
+
nodeProps2(current),
|
|
7809
|
+
wireWriteProps(upsert.setProps, upsert.unsetProps)
|
|
7810
|
+
);
|
|
7811
|
+
assertValidRevalidatingWrite(
|
|
7812
|
+
analysis,
|
|
7813
|
+
`The incremental merge plan found committed node "${upsert.id}" with props that do not validate against the active schema.`,
|
|
7814
|
+
{ kind: upsert.kind, id: upsert.id, role: "node" }
|
|
7815
|
+
);
|
|
7816
|
+
if (analysis.stripsCurrentProps && analysis.schemaWouldChange) {
|
|
7817
|
+
throw invalidPlanForTarget(
|
|
7818
|
+
`The incremental merge plan would strip existing props while updating committed node "${upsert.id}".`,
|
|
7819
|
+
{ kind: upsert.kind, id: upsert.id, role: "node" }
|
|
7820
|
+
);
|
|
7821
|
+
}
|
|
7822
|
+
}
|
|
7823
|
+
}
|
|
7824
|
+
const currentEdgeIds = /* @__PURE__ */ new Set();
|
|
7825
|
+
const edgeUpsertsByKind = /* @__PURE__ */ new Map();
|
|
7826
|
+
for (const upsert of artifact.writes.edgeUpserts) {
|
|
7827
|
+
const upserts = edgeUpsertsByKind.get(upsert.kind) ?? [];
|
|
7828
|
+
upserts.push(upsert);
|
|
7829
|
+
edgeUpsertsByKind.set(upsert.kind, upserts);
|
|
7830
|
+
}
|
|
7831
|
+
const currentEdgesByIdentity = /* @__PURE__ */ new Map();
|
|
7832
|
+
for (const [kind, upserts] of edgeUpsertsByKind) {
|
|
7833
|
+
const rows = await edgeCollection(edgesApi, kind).getByIds(
|
|
7834
|
+
upserts.map((upsert) => upsert.id),
|
|
7835
|
+
INCLUDE_TOMBSTONES
|
|
7836
|
+
);
|
|
7837
|
+
for (const [index, upsert] of upserts.entries()) {
|
|
7838
|
+
const row = rows[index];
|
|
7839
|
+
currentEdgesByIdentity.set(mergeKey(kind, upsert.id), row);
|
|
7840
|
+
if (row !== void 0) currentEdgeIds.add(upsert.id);
|
|
7841
|
+
}
|
|
7842
|
+
}
|
|
7843
|
+
for (const upsert of artifact.writes.edgeUpserts) {
|
|
7844
|
+
const registration = requireDefined(graphEdges[upsert.kind]);
|
|
7845
|
+
const endpointError = validateEdgeEndpoints(
|
|
7846
|
+
upsert.kind,
|
|
7847
|
+
upsert.from.kind,
|
|
7848
|
+
upsert.to.kind,
|
|
7849
|
+
registration,
|
|
7850
|
+
target.registry
|
|
7851
|
+
);
|
|
7852
|
+
if (endpointError !== void 0) {
|
|
7853
|
+
throw invalidPlanForTarget(endpointError.message, {
|
|
7854
|
+
kind: upsert.kind,
|
|
7855
|
+
id: upsert.id,
|
|
7856
|
+
role: "edge"
|
|
7857
|
+
});
|
|
7858
|
+
}
|
|
7859
|
+
for (const endpoint of [upsert.from, upsert.to]) {
|
|
7860
|
+
assertNodeKind(endpoint.kind);
|
|
7861
|
+
const key = mergeKey(endpoint.kind, endpoint.id);
|
|
7862
|
+
if (deletedNodes.has(key)) {
|
|
7863
|
+
throw invalidPlanForTarget(
|
|
7864
|
+
`The merge plan deletes an endpoint required by edge "${upsert.id}".`,
|
|
7865
|
+
{ edgeKind: upsert.kind, edgeId: upsert.id, endpoint }
|
|
7866
|
+
);
|
|
7867
|
+
}
|
|
7868
|
+
if (plannedNodes.has(key)) continue;
|
|
7869
|
+
const row = await readEndpoint(endpoint);
|
|
7870
|
+
if (row === void 0 || row.meta.deletedAt !== void 0) {
|
|
7871
|
+
throw invalidPlanForTarget(
|
|
7872
|
+
`The merge plan references a missing edge endpoint "${endpoint.id}".`,
|
|
7873
|
+
{ edgeKind: upsert.kind, edgeId: upsert.id, endpoint }
|
|
7874
|
+
);
|
|
7875
|
+
}
|
|
7876
|
+
}
|
|
7877
|
+
const current = currentEdgesByIdentity.get(
|
|
7878
|
+
mergeKey(upsert.kind, upsert.id)
|
|
7879
|
+
);
|
|
7880
|
+
if (artifact.guards.incremental !== void 0 && current?.meta.deletedAt !== void 0) {
|
|
7881
|
+
throw invalidPlanForTarget(
|
|
7882
|
+
`The incremental merge plan would resurrect soft-deleted committed edge "${upsert.id}".`,
|
|
7883
|
+
{ kind: upsert.kind, id: upsert.id, role: "edge" }
|
|
7884
|
+
);
|
|
7885
|
+
}
|
|
7886
|
+
const props = resolvedWireProps(
|
|
7887
|
+
current === void 0 ? {} : edgeProps(current),
|
|
7888
|
+
upsert.setProps,
|
|
7889
|
+
upsert.unsetProps
|
|
7890
|
+
);
|
|
7891
|
+
const schema = edgeSchemaFor(target, upsert.kind);
|
|
7892
|
+
if (schema !== void 0 && !schema.safeParse(props).success) {
|
|
7893
|
+
throw invalidPlanForTarget(
|
|
7894
|
+
`The merge plan's edge write for "${upsert.id}" does not validate against the active schema.`,
|
|
7895
|
+
{ kind: upsert.kind, id: upsert.id, role: "edge" }
|
|
7896
|
+
);
|
|
7897
|
+
}
|
|
7898
|
+
if (artifact.guards.incremental !== void 0 && current !== void 0) {
|
|
7899
|
+
const analysis = analyzeRevalidatingWrite(
|
|
7900
|
+
schema,
|
|
7901
|
+
edgeProps(current),
|
|
7902
|
+
wireWriteProps(upsert.setProps, upsert.unsetProps)
|
|
7903
|
+
);
|
|
7904
|
+
assertValidRevalidatingWrite(
|
|
7905
|
+
analysis,
|
|
7906
|
+
`The incremental merge plan found committed edge "${upsert.id}" with props that do not validate against the active schema.`,
|
|
7907
|
+
{ kind: upsert.kind, id: upsert.id, role: "edge" }
|
|
7908
|
+
);
|
|
7909
|
+
if (analysis.stripsCurrentProps && analysis.schemaWouldChange) {
|
|
7910
|
+
throw invalidPlanForTarget(
|
|
7911
|
+
`The incremental merge plan would strip existing props while updating committed edge "${upsert.id}".`,
|
|
7912
|
+
{ kind: upsert.kind, id: upsert.id, role: "edge" }
|
|
7913
|
+
);
|
|
7914
|
+
}
|
|
7915
|
+
}
|
|
7916
|
+
if (current !== void 0 && (current.fromKind !== upsert.from.kind || current.fromId !== upsert.from.id || current.toKind !== upsert.to.kind || current.toId !== upsert.to.id)) {
|
|
7917
|
+
throw invalidPlanForTarget(
|
|
7918
|
+
`The merge plan would change the immutable endpoints of edge "${upsert.id}".`,
|
|
7919
|
+
{ kind: upsert.kind, id: upsert.id, role: "edge" }
|
|
7920
|
+
);
|
|
7921
|
+
}
|
|
7922
|
+
}
|
|
7923
|
+
const missingEdgeIds = artifact.writes.edgeUpserts.map((upsert) => upsert.id).filter((id) => !currentEdgeIds.has(id));
|
|
7924
|
+
if (missingEdgeIds.length > 0) {
|
|
7925
|
+
const plannedEdgeById = new Map(
|
|
7926
|
+
artifact.writes.edgeUpserts.map((upsert) => [upsert.id, upsert])
|
|
7927
|
+
);
|
|
7928
|
+
for (const kind of Object.keys(target.graph.edges)) {
|
|
7929
|
+
const rows = await edgeCollection(edgesApi, kind).getByIds(
|
|
7930
|
+
missingEdgeIds,
|
|
7931
|
+
INCLUDE_TOMBSTONES
|
|
7932
|
+
);
|
|
7933
|
+
for (const [index, row] of rows.entries()) {
|
|
7934
|
+
if (row === void 0) continue;
|
|
7935
|
+
const id = requireDefined(missingEdgeIds[index]);
|
|
7936
|
+
const planned = plannedEdgeById.get(id);
|
|
7937
|
+
if (planned !== void 0 && planned.kind !== row.kind) {
|
|
7938
|
+
throw invalidPlanForTarget(
|
|
7939
|
+
`The merge plan reuses edge id "${id}" across kinds.`,
|
|
7940
|
+
{ id, committedKind: row.kind, plannedKind: planned.kind }
|
|
7941
|
+
);
|
|
7942
|
+
}
|
|
7943
|
+
}
|
|
7944
|
+
}
|
|
7945
|
+
}
|
|
7946
|
+
}
|
|
7947
|
+
async function assertMergePlanFenceInsideTransaction(target, txBackend, artifact) {
|
|
7948
|
+
await lockRecordedGraphWrite(txBackend, target.graphId);
|
|
7949
|
+
const activeSchema = await txBackend.getActiveSchema(target.graphId);
|
|
7950
|
+
const liveSchema = {
|
|
7951
|
+
managed: activeSchema !== void 0,
|
|
7952
|
+
version: activeSchema?.version ?? 1,
|
|
7953
|
+
hash: activeSchema?.schema_hash ?? await computeSchemaComponent(target)
|
|
7954
|
+
};
|
|
7955
|
+
if (liveSchema.managed !== artifact.target.schema.managed || liveSchema.version !== artifact.target.schema.version || liveSchema.hash !== artifact.target.schema.hash) {
|
|
7956
|
+
throw new MergePlanSchemaMismatchError(
|
|
7957
|
+
"The target's active schema no longer matches the merge plan.",
|
|
7958
|
+
{ details: { expected: artifact.target.schema, live: liveSchema } }
|
|
7959
|
+
);
|
|
7960
|
+
}
|
|
7961
|
+
const liveOrigin = await readRevisionOrigin(
|
|
7962
|
+
txBackend,
|
|
7963
|
+
target.revisionSchema,
|
|
7964
|
+
target.graphId
|
|
7965
|
+
);
|
|
7966
|
+
if (liveOrigin !== artifact.target.revision.origin) {
|
|
7967
|
+
throw new MergePlanOriginMismatchError(
|
|
7968
|
+
"The merge plan belongs to a different target revision origin.",
|
|
7969
|
+
{
|
|
7970
|
+
details: {
|
|
7971
|
+
expectedOrigin: artifact.target.revision.origin,
|
|
7972
|
+
liveOrigin
|
|
7973
|
+
}
|
|
7974
|
+
}
|
|
7975
|
+
);
|
|
7976
|
+
}
|
|
7977
|
+
const liveRevision = await readRecordedClock(
|
|
7978
|
+
txBackend,
|
|
7979
|
+
target.revisionSchema,
|
|
7980
|
+
target.graphId
|
|
7981
|
+
) ?? null;
|
|
7982
|
+
if (liveRevision !== artifact.target.revision.revision) {
|
|
7983
|
+
throw new StaleMergePlanError(
|
|
7984
|
+
"The target revision changed after this merge plan was created; the plan was not applied.",
|
|
7985
|
+
{
|
|
7986
|
+
details: {
|
|
7987
|
+
expectedRevision: artifact.target.revision.revision,
|
|
7988
|
+
liveRevision
|
|
7989
|
+
}
|
|
7990
|
+
}
|
|
7991
|
+
);
|
|
7992
|
+
}
|
|
7993
|
+
}
|
|
7994
|
+
function identityConsistencySeeds(artifact, profile) {
|
|
7995
|
+
const deleted = new Set(
|
|
7996
|
+
artifact.writes.nodeDeletes.map(
|
|
7997
|
+
(entity) => mergeKey(entity.kind, entity.id)
|
|
7998
|
+
)
|
|
7999
|
+
);
|
|
8000
|
+
const byIdentity = /* @__PURE__ */ new Map();
|
|
8001
|
+
const add = (entity) => {
|
|
8002
|
+
const identity = mergeKey(entity.kind, entity.id);
|
|
8003
|
+
if (!deleted.has(identity)) byIdentity.set(identity, entity);
|
|
8004
|
+
};
|
|
8005
|
+
for (const assertion of [
|
|
8006
|
+
...artifact.writes.identityAssertions,
|
|
8007
|
+
...artifact.writes.identityRetractions
|
|
8008
|
+
]) {
|
|
8009
|
+
add(assertion.a);
|
|
8010
|
+
add(assertion.b);
|
|
8011
|
+
}
|
|
8012
|
+
if (profile === "fold") {
|
|
8013
|
+
for (const upsert of artifact.writes.nodeUpserts) add(upsert);
|
|
8014
|
+
}
|
|
8015
|
+
return [...byIdentity].sort(([left], [right]) => compareMergeKeys(left, right)).map(([, entity]) => entity);
|
|
8016
|
+
}
|
|
8017
|
+
async function applyWireMergeWrites(target, nodesApi, edgesApi, txBackend, artifact) {
|
|
8018
|
+
const committedNodes = await applyNodeRows(
|
|
8019
|
+
target,
|
|
8020
|
+
txBackend,
|
|
8021
|
+
nodesApi,
|
|
8022
|
+
artifact.writes.nodeDeletes,
|
|
8023
|
+
artifact.writes.nodeUpserts.map((upsert) => ({
|
|
8024
|
+
kind: upsert.kind,
|
|
8025
|
+
id: upsert.id,
|
|
8026
|
+
props: wireWriteProps(upsert.setProps, upsert.unsetProps),
|
|
8027
|
+
...upsert.validFrom === void 0 ? {} : { validFrom: upsert.validFrom },
|
|
8028
|
+
...upsert.validTo === void 0 ? {} : { validTo: upsert.validTo }
|
|
8029
|
+
}))
|
|
8030
|
+
);
|
|
8031
|
+
const committedEdges = await applyEdgeRows(
|
|
8032
|
+
edgesApi,
|
|
8033
|
+
artifact.writes.edgeDeletes,
|
|
8034
|
+
artifact.writes.edgeUpserts.map((upsert) => ({
|
|
8035
|
+
kind: upsert.kind,
|
|
8036
|
+
item: {
|
|
8037
|
+
id: upsert.id,
|
|
8038
|
+
from: upsert.from,
|
|
8039
|
+
to: upsert.to,
|
|
8040
|
+
props: wireWriteProps(upsert.setProps, upsert.unsetProps),
|
|
8041
|
+
...upsert.validFrom === void 0 ? {} : { validFrom: upsert.validFrom },
|
|
8042
|
+
...upsert.validTo === void 0 ? {} : { validTo: upsert.validTo }
|
|
8043
|
+
}
|
|
8044
|
+
}))
|
|
8045
|
+
);
|
|
8046
|
+
const identityAssertions = artifact.writes.identityAssertions;
|
|
8047
|
+
const identityRetractions = artifact.writes.identityRetractions;
|
|
8048
|
+
const appliedIdentity = await applyIdentityRows(
|
|
8049
|
+
target,
|
|
8050
|
+
txBackend,
|
|
8051
|
+
identityAssertions,
|
|
8052
|
+
identityRetractions,
|
|
8053
|
+
() => storeRuntime(target).assertIdentityClassesConsistentAtTarget(
|
|
8054
|
+
txBackend,
|
|
8055
|
+
identityConsistencySeeds(
|
|
8056
|
+
artifact,
|
|
8057
|
+
target.graph.identity?.sameIdAcrossKinds
|
|
8058
|
+
)
|
|
8059
|
+
)
|
|
8060
|
+
);
|
|
8061
|
+
if (!forceRecordedGraphRevision(txBackend, target.graphId) && !forceWriteTransactionRevision(txBackend)) {
|
|
8062
|
+
await advanceRevisionClock(
|
|
8063
|
+
txBackend,
|
|
8064
|
+
target.revisionSchema,
|
|
8065
|
+
target.graphId,
|
|
8066
|
+
true
|
|
8067
|
+
);
|
|
8068
|
+
}
|
|
8069
|
+
return {
|
|
8070
|
+
nodes: committedNodes,
|
|
8071
|
+
edges: committedEdges,
|
|
8072
|
+
identity: {
|
|
8073
|
+
asserted: appliedIdentity.asserted,
|
|
8074
|
+
retracted: appliedIdentity.retracted
|
|
8075
|
+
}
|
|
8076
|
+
};
|
|
8077
|
+
}
|
|
8078
|
+
function reportFromArtifact(artifact, merged, warnings, provenancePersisted) {
|
|
8079
|
+
const provenanceRecords = artifact.review.provenanceRecords;
|
|
8080
|
+
return {
|
|
8081
|
+
merged,
|
|
8082
|
+
resolutions: artifact.review.resolutions,
|
|
8083
|
+
conflicts: artifact.review.conflicts,
|
|
8084
|
+
deleteModifyConflicts: artifact.review.deleteModifyConflicts,
|
|
8085
|
+
typeReconciliations: artifact.review.typeReconciliations,
|
|
8086
|
+
dropped: artifact.review.dropped,
|
|
8087
|
+
validityEnds: artifact.review.validityEnds,
|
|
8088
|
+
baseAmbiguities: artifact.review.baseAmbiguities,
|
|
8089
|
+
provenance: artifact.provenance.includeInReport ? buildProvenanceIndex(provenanceRecords) : { byBranch: () => ({ nodeIds: [], edgeIds: [] }) },
|
|
8090
|
+
warnings,
|
|
8091
|
+
...artifact.review.diagnostics === void 0 ? {} : {
|
|
8092
|
+
candidateDiagnostics: artifact.review.diagnostics
|
|
8093
|
+
},
|
|
8094
|
+
...provenancePersisted === void 0 ? {} : { provenancePersisted }
|
|
8095
|
+
};
|
|
8096
|
+
}
|
|
8097
|
+
async function applyMergePlan(target, input) {
|
|
8098
|
+
let validation;
|
|
8099
|
+
try {
|
|
8100
|
+
validation = await validateMergePlanArtifact(input);
|
|
8101
|
+
} catch (error) {
|
|
8102
|
+
return err(
|
|
8103
|
+
new InvalidMergePlanError(
|
|
8104
|
+
`Merge plan validation failed: ${describeCause(error)}`,
|
|
8105
|
+
{ cause: error }
|
|
8106
|
+
)
|
|
8107
|
+
);
|
|
8108
|
+
}
|
|
8109
|
+
if (!validation.success)
|
|
8110
|
+
return err(mergePlanValidationError(validation.error));
|
|
8111
|
+
const artifact = validation.artifact;
|
|
8112
|
+
if (artifact.target.graphId !== target.graphId) {
|
|
8113
|
+
return err(
|
|
8114
|
+
new MergePlanTargetMismatchError(
|
|
8115
|
+
"The merge plan names a different target graph.",
|
|
8116
|
+
{
|
|
8117
|
+
details: {
|
|
8118
|
+
expectedGraphId: artifact.target.graphId,
|
|
8119
|
+
receivedGraphId: target.graphId
|
|
8120
|
+
}
|
|
8121
|
+
}
|
|
8122
|
+
)
|
|
8123
|
+
);
|
|
8124
|
+
}
|
|
8125
|
+
try {
|
|
8126
|
+
assertPublicPlanCapability(target);
|
|
8127
|
+
const provenanceStore = artifact.provenance.persist ? await tryOpenProvenanceStore(target) : void 0;
|
|
8128
|
+
if (provenanceStore !== void 0 && isErr(provenanceStore)) {
|
|
8129
|
+
return err(provenanceStore.error);
|
|
8130
|
+
}
|
|
8131
|
+
const merged = await withTxConflictRetry(
|
|
8132
|
+
() => target.transaction(async (tx) => {
|
|
8133
|
+
const txBackend = transactionBackend(tx);
|
|
8134
|
+
await assertMergePlanFenceInsideTransaction(
|
|
8135
|
+
target,
|
|
8136
|
+
txBackend,
|
|
8137
|
+
artifact
|
|
8138
|
+
);
|
|
8139
|
+
await preflightWireMergeWrites(
|
|
8140
|
+
target,
|
|
8141
|
+
tx.nodes,
|
|
8142
|
+
tx.edges,
|
|
8143
|
+
artifact
|
|
8144
|
+
);
|
|
8145
|
+
await assertPlannedIdentityIdsFresh(target, txBackend, {
|
|
8146
|
+
identityAssertions: artifact.writes.identityAssertions,
|
|
8147
|
+
identityRetractions: artifact.writes.identityRetractions
|
|
8148
|
+
});
|
|
8149
|
+
return applyWireMergeWrites(
|
|
8150
|
+
target,
|
|
8151
|
+
tx.nodes,
|
|
8152
|
+
tx.edges,
|
|
8153
|
+
txBackend,
|
|
8154
|
+
artifact
|
|
8155
|
+
);
|
|
8156
|
+
}, mergeCommitTransactionOptions(target))
|
|
8157
|
+
);
|
|
8158
|
+
const warnings = [...artifact.review.warnings];
|
|
5453
8159
|
let provenancePersisted;
|
|
5454
8160
|
if (provenanceStore !== void 0 && !isErr(provenanceStore)) {
|
|
5455
8161
|
try {
|
|
8162
|
+
const records = artifact.review.provenanceRecords;
|
|
5456
8163
|
const count = await persistProvenanceRecords(
|
|
5457
8164
|
provenanceStore.data,
|
|
5458
8165
|
target.graphId,
|
|
5459
|
-
|
|
8166
|
+
records
|
|
5460
8167
|
);
|
|
5461
8168
|
provenancePersisted = {
|
|
5462
8169
|
graphId: provenanceGraphId(target.graphId),
|
|
@@ -5464,37 +8171,27 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
|
|
|
5464
8171
|
};
|
|
5465
8172
|
} catch (error) {
|
|
5466
8173
|
warnings.push(
|
|
5467
|
-
"provenance persistence failed (graph committed; provenance not persisted): " + (error
|
|
8174
|
+
"provenance persistence failed (graph committed; provenance not persisted): " + describeCause(error)
|
|
5468
8175
|
);
|
|
5469
8176
|
}
|
|
5470
8177
|
}
|
|
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);
|
|
8178
|
+
return ok(
|
|
8179
|
+
reportFromArtifact(artifact, merged, warnings, provenancePersisted)
|
|
8180
|
+
);
|
|
5485
8181
|
} catch (error) {
|
|
5486
|
-
|
|
5487
|
-
return err(error);
|
|
5488
|
-
}
|
|
8182
|
+
const translated = translateMergeCommitError(error);
|
|
5489
8183
|
return err(
|
|
5490
|
-
new MergeError(
|
|
5491
|
-
`Merge
|
|
5492
|
-
{
|
|
8184
|
+
translated instanceof MergeError ? translated : new MergeError(
|
|
8185
|
+
`Merge plan apply failed: ${describeCause(translated)}`,
|
|
8186
|
+
{
|
|
8187
|
+
cause: translated
|
|
8188
|
+
}
|
|
5493
8189
|
)
|
|
5494
8190
|
);
|
|
5495
8191
|
}
|
|
5496
8192
|
}
|
|
5497
|
-
async function merge(store,
|
|
8193
|
+
async function merge(store, branchInputs, optionsInput = {}) {
|
|
8194
|
+
const branches = unwrapMergeBranches(branchInputs);
|
|
5498
8195
|
const normalized = tryNormalize(optionsInput);
|
|
5499
8196
|
if (isErr(normalized)) {
|
|
5500
8197
|
return err(normalized.error);
|
|
@@ -5512,7 +8209,8 @@ async function merge(store, branches, optionsInput = {}) {
|
|
|
5512
8209
|
options,
|
|
5513
8210
|
false,
|
|
5514
8211
|
void 0,
|
|
5515
|
-
precondition.data
|
|
8212
|
+
precondition.data,
|
|
8213
|
+
commitResolvedMerge
|
|
5516
8214
|
);
|
|
5517
8215
|
}
|
|
5518
8216
|
async function validateForkPointVersions(forkPoint, branches) {
|
|
@@ -5931,55 +8629,54 @@ async function commitIncrementalPlan(target, plan, guard) {
|
|
|
5931
8629
|
{ details: { capability: "transactions" } }
|
|
5932
8630
|
);
|
|
5933
8631
|
}
|
|
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
|
-
|
|
8632
|
+
return runMergeCommit(
|
|
8633
|
+
() => withTxConflictRetry(
|
|
8634
|
+
() => target.transaction(async (tx) => {
|
|
8635
|
+
if (target.revisionTrackingEnabled) {
|
|
8636
|
+
await lockRecordedGraphWrite(transactionBackend(tx), target.graphId);
|
|
8637
|
+
}
|
|
8638
|
+
await assertForkPointUnchanged(guard.forkPoint);
|
|
8639
|
+
const nodesApi = tx.nodes;
|
|
8640
|
+
const edgesApi = tx.edges;
|
|
8641
|
+
await assertBaseResolutionStable(
|
|
8642
|
+
tx,
|
|
8643
|
+
guard
|
|
8644
|
+
);
|
|
8645
|
+
await assertPlannedIdentityIdsFresh(
|
|
8646
|
+
target,
|
|
8647
|
+
transactionBackend(tx),
|
|
8648
|
+
plan
|
|
8649
|
+
);
|
|
8650
|
+
await assertIdentityPeersStable(
|
|
8651
|
+
target,
|
|
8652
|
+
transactionBackend(tx),
|
|
8653
|
+
guard.identityPeerProbe,
|
|
8654
|
+
plan
|
|
8655
|
+
);
|
|
8656
|
+
await assertInheritedTargetUnchanged(nodesApi, edgesApi, guard, plan);
|
|
8657
|
+
await validateIncrementalNodeWrites(target, nodesApi, plan);
|
|
8658
|
+
await validateIncrementalEdgeWrites(target, edgesApi, plan);
|
|
8659
|
+
return applyInternalMergePlan(
|
|
8660
|
+
plan,
|
|
8661
|
+
nodesApi,
|
|
8662
|
+
edgesApi,
|
|
8663
|
+
target,
|
|
8664
|
+
transactionBackend(tx)
|
|
8665
|
+
);
|
|
8666
|
+
}, mergeCommitTransactionOptions(target))
|
|
8667
|
+
)
|
|
5961
8668
|
);
|
|
5962
8669
|
}
|
|
5963
8670
|
async function mergeIncremental(args) {
|
|
5964
|
-
const { forkPoint, target
|
|
8671
|
+
const { forkPoint, target } = args;
|
|
8672
|
+
const branches = unwrapMergeBranches(args.branches);
|
|
5965
8673
|
const normalized = tryNormalize(args.options ?? {}, ["target"]);
|
|
5966
8674
|
if (isErr(normalized)) {
|
|
5967
8675
|
return err(normalized.error);
|
|
5968
8676
|
}
|
|
5969
8677
|
const options = normalized.data;
|
|
5970
8678
|
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
|
-
);
|
|
8679
|
+
return err(incrementalBaseConflictPolicyError(options));
|
|
5983
8680
|
}
|
|
5984
8681
|
const forkPrecondition = await validateForkPointVersions(forkPoint, branches);
|
|
5985
8682
|
if (isErr(forkPrecondition)) {
|
|
@@ -5991,12 +8688,7 @@ async function mergeIncremental(args) {
|
|
|
5991
8688
|
computeSchemaComponent(target)
|
|
5992
8689
|
]);
|
|
5993
8690
|
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
|
-
);
|
|
8691
|
+
return err(incrementalSchemaError());
|
|
6000
8692
|
}
|
|
6001
8693
|
const targetBranch = {
|
|
6002
8694
|
id: COMMITTED_TARGET_BRANCH,
|
|
@@ -6015,10 +8707,12 @@ async function mergeIncremental(args) {
|
|
|
6015
8707
|
// it is re-established there rather than assumed to have held for the
|
|
6016
8708
|
// whole of planning (see `assertForkPointUnchanged`).
|
|
6017
8709
|
forkPoint: { store: forkPoint, version: forkVersion }
|
|
6018
|
-
}
|
|
8710
|
+
},
|
|
8711
|
+
void 0,
|
|
8712
|
+
commitResolvedMerge
|
|
6019
8713
|
);
|
|
6020
8714
|
}
|
|
6021
8715
|
|
|
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 };
|
|
8716
|
+
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, ingestionBranch, merge, mergeIncremental, normalizeMergeOptions, openProvenanceStore, persistProvenanceRecords, planMerge, planMergeIncremental, provenanceGraphId, readProvenance };
|
|
6023
8717
|
//# sourceMappingURL=index.js.map
|
|
6024
8718
|
//# sourceMappingURL=index.js.map
|