@nicia-ai/typegraph 0.48.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend/drizzle/indexes.cjs +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 +56 -52
- package/dist/backend/index.d.cts +3 -3
- package/dist/backend/index.d.ts +3 -3
- package/dist/backend/index.js +11 -11
- package/dist/backend/postgres/index.cjs +34 -28
- package/dist/backend/postgres/index.d.cts +7 -7
- package/dist/backend/postgres/index.d.ts +7 -7
- package/dist/backend/postgres/index.js +17 -15
- package/dist/backend/postgres/pglite-store.cjs +36 -35
- 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 +32 -31
- package/dist/backend/postgres/pglite-store.js.map +1 -1
- package/dist/backend/postgres/pglite.cjs +19 -17
- package/dist/backend/postgres/pglite.d.cts +3 -3
- package/dist/backend/postgres/pglite.d.ts +3 -3
- package/dist/backend/postgres/pglite.js +18 -16
- package/dist/backend/sqlite/index.cjs +31 -25
- package/dist/backend/sqlite/index.d.cts +7 -7
- package/dist/backend/sqlite/index.d.ts +7 -7
- package/dist/backend/sqlite/index.js +15 -13
- package/dist/backend/sqlite/libsql.cjs +22 -20
- 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 +17 -15
- package/dist/backend/sqlite/libsql.js.map +1 -1
- package/dist/backend/sqlite/local-store.cjs +37 -36
- 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 +33 -32
- package/dist/backend/sqlite/local-store.js.map +1 -1
- package/dist/backend/sqlite/local.cjs +20 -18
- package/dist/backend/sqlite/local.d.cts +3 -3
- package/dist/backend/sqlite/local.d.ts +3 -3
- package/dist/backend/sqlite/local.js +18 -16
- package/dist/{chunk-AX6JGVTZ.js → chunk-2CYI5HRH.js} +276 -206
- package/dist/chunk-2CYI5HRH.js.map +1 -0
- package/dist/{chunk-KF6K7TGH.js → chunk-2GIV5X4P.js} +702 -422
- package/dist/chunk-2GIV5X4P.js.map +1 -0
- package/dist/{chunk-2TYSSBU5.js → chunk-2Q45FAIE.js} +15 -15
- package/dist/{chunk-2TYSSBU5.js.map → chunk-2Q45FAIE.js.map} +1 -1
- package/dist/{chunk-WODMN3Q2.cjs → chunk-45YZXI6X.cjs} +27 -27
- package/dist/{chunk-WODMN3Q2.cjs.map → chunk-45YZXI6X.cjs.map} +1 -1
- package/dist/{chunk-3DMMGT6S.js → chunk-46UGOVRW.js} +3 -3
- package/dist/{chunk-3DMMGT6S.js.map → chunk-46UGOVRW.js.map} +1 -1
- package/dist/{chunk-GLHXM7C4.js → chunk-5LKOVCPP.js} +60 -23
- package/dist/chunk-5LKOVCPP.js.map +1 -0
- package/dist/{chunk-JFRT7S3B.cjs → chunk-5OWHU7Z7.cjs} +261 -459
- package/dist/chunk-5OWHU7Z7.cjs.map +1 -0
- package/dist/{chunk-7SQSZN2J.js → chunk-5PP4KRPV.js} +5 -5
- package/dist/{chunk-7SQSZN2J.js.map → chunk-5PP4KRPV.js.map} +1 -1
- package/dist/chunk-6KQEICJ6.cjs +12 -0
- package/dist/{chunk-MKJ4KXL5.cjs.map → chunk-6KQEICJ6.cjs.map} +1 -1
- package/dist/{chunk-6SWPJEYN.js → chunk-6LEOAIIB.js} +40 -6
- package/dist/chunk-6LEOAIIB.js.map +1 -0
- package/dist/{chunk-DZAICA5J.js → chunk-6NKSE5AU.js} +4 -4
- package/dist/{chunk-DZAICA5J.js.map → chunk-6NKSE5AU.js.map} +1 -1
- package/dist/{chunk-OVI7IXYX.js → chunk-6S3PTHL4.js} +3 -3
- package/dist/{chunk-OVI7IXYX.js.map → chunk-6S3PTHL4.js.map} +1 -1
- package/dist/{chunk-3FPR224C.cjs → chunk-7CCMCMJO.cjs} +102 -102
- package/dist/{chunk-3FPR224C.cjs.map → chunk-7CCMCMJO.cjs.map} +1 -1
- package/dist/{chunk-SHFBB23Q.js → chunk-7OWE7WD6.js} +6 -4
- package/dist/chunk-7OWE7WD6.js.map +1 -0
- package/dist/chunk-7PRMDZIA.cjs +182 -0
- package/dist/chunk-7PRMDZIA.cjs.map +1 -0
- package/dist/{chunk-CHAOFCHV.js → chunk-AH2A74OZ.js} +3 -3
- package/dist/{chunk-CHAOFCHV.js.map → chunk-AH2A74OZ.js.map} +1 -1
- package/dist/{chunk-OORXGFL5.cjs → chunk-BYBDM423.cjs} +48 -48
- package/dist/{chunk-OORXGFL5.cjs.map → chunk-BYBDM423.cjs.map} +1 -1
- package/dist/{chunk-JB6T4GLL.cjs → chunk-C76WFIVZ.cjs} +808 -519
- package/dist/chunk-C76WFIVZ.cjs.map +1 -0
- package/dist/{chunk-OTUPEF2W.cjs → chunk-CS6JTQQ2.cjs} +328 -257
- package/dist/chunk-CS6JTQQ2.cjs.map +1 -0
- package/dist/{chunk-AOSSQDVO.cjs → chunk-CSNOYIB4.cjs} +13 -13
- package/dist/{chunk-AOSSQDVO.cjs.map → chunk-CSNOYIB4.cjs.map} +1 -1
- package/dist/{chunk-HEV35TGT.js → chunk-DI6VC62F.js} +13 -4
- package/dist/chunk-DI6VC62F.js.map +1 -0
- package/dist/{chunk-2QGYQBS2.cjs → chunk-DWUDZ66O.cjs} +10 -10
- package/dist/{chunk-2QGYQBS2.cjs.map → chunk-DWUDZ66O.cjs.map} +1 -1
- package/dist/{chunk-ZDKTPBGV.js → chunk-EDLKIQRI.js} +4 -4
- package/dist/{chunk-ZDKTPBGV.js.map → chunk-EDLKIQRI.js.map} +1 -1
- package/dist/chunk-EH3JZJUM.cjs +567 -0
- package/dist/chunk-EH3JZJUM.cjs.map +1 -0
- package/dist/{chunk-S5TERQVW.js → chunk-FX5ZW6PN.js} +196 -33
- package/dist/chunk-FX5ZW6PN.js.map +1 -0
- package/dist/{chunk-XVWOYKIK.js → chunk-FZHF5UWR.js} +3 -3
- package/dist/{chunk-XVWOYKIK.js.map → chunk-FZHF5UWR.js.map} +1 -1
- package/dist/{chunk-BFUOME67.cjs → chunk-G2TLHH6T.cjs} +20 -11
- package/dist/chunk-G2TLHH6T.cjs.map +1 -0
- package/dist/{chunk-5PRKMB24.cjs → chunk-GHZPZNFC.cjs} +12 -4
- package/dist/chunk-GHZPZNFC.cjs.map +1 -0
- package/dist/{chunk-ZFMCDJGB.js → chunk-GVYVGGPJ.js} +11 -199
- package/dist/chunk-GVYVGGPJ.js.map +1 -0
- package/dist/{chunk-SDBBMD4N.js → chunk-H3XP6BAV.js} +3 -3
- package/dist/{chunk-SDBBMD4N.js.map → chunk-H3XP6BAV.js.map} +1 -1
- package/dist/{chunk-ROR5ZR6A.cjs → chunk-HSEZ7X6Y.cjs} +45 -45
- package/dist/{chunk-ROR5ZR6A.cjs.map → chunk-HSEZ7X6Y.cjs.map} +1 -1
- package/dist/{chunk-LRQKXRXO.cjs → chunk-HY5BNOUN.cjs} +4 -4
- package/dist/{chunk-LRQKXRXO.cjs.map → chunk-HY5BNOUN.cjs.map} +1 -1
- package/dist/chunk-HZUB5CON.js +27 -0
- package/dist/chunk-HZUB5CON.js.map +1 -0
- package/dist/{chunk-MYKHJI2J.js → chunk-I5C6OTKR.js} +3 -3
- package/dist/{chunk-MYKHJI2J.js.map → chunk-I5C6OTKR.js.map} +1 -1
- package/dist/{chunk-3U5ABFDW.cjs → chunk-IHK5KA2Q.cjs} +31 -28
- package/dist/chunk-IHK5KA2Q.cjs.map +1 -0
- package/dist/{chunk-26YKDNUD.cjs → chunk-IPRP6TDL.cjs} +12 -10
- package/dist/chunk-IPRP6TDL.cjs.map +1 -0
- package/dist/{chunk-UGHG7PGX.js → chunk-IRPYOHRC.js} +26 -4
- package/dist/chunk-IRPYOHRC.js.map +1 -0
- package/dist/{chunk-7XUYFFR4.cjs → chunk-ISKCIQF2.cjs} +20 -20
- package/dist/{chunk-7XUYFFR4.cjs.map → chunk-ISKCIQF2.cjs.map} +1 -1
- package/dist/{chunk-OD4SSY2U.js → chunk-JG5XIJDY.js} +57 -31
- package/dist/chunk-JG5XIJDY.js.map +1 -0
- package/dist/{chunk-2HIYYMWY.cjs → chunk-K2XYLVVB.cjs} +134 -133
- package/dist/chunk-K2XYLVVB.cjs.map +1 -0
- package/dist/{chunk-5K2BKEBD.js → chunk-K46SDNQD.js} +10 -4
- package/dist/chunk-K46SDNQD.js.map +1 -0
- package/dist/{chunk-TZEGUG6D.cjs → chunk-K6VKTBST.cjs} +4 -4
- package/dist/{chunk-TZEGUG6D.cjs.map → chunk-K6VKTBST.cjs.map} +1 -1
- package/dist/chunk-KWNWG4RC.cjs +12 -0
- package/dist/{chunk-CICO5GXD.cjs.map → chunk-KWNWG4RC.cjs.map} +1 -1
- package/dist/{chunk-FQPEMEWX.cjs → chunk-LLVOQOWE.cjs} +80 -32
- package/dist/chunk-LLVOQOWE.cjs.map +1 -0
- package/dist/{chunk-TQHGE7UC.cjs → chunk-M5MSP3BI.cjs} +18 -10
- package/dist/chunk-M5MSP3BI.cjs.map +1 -0
- package/dist/{chunk-ACGSAMD3.js → chunk-MJPZNEJZ.js} +274 -292
- package/dist/chunk-MJPZNEJZ.js.map +1 -0
- package/dist/{chunk-TUNPBZY3.cjs → chunk-MM3JHVKZ.cjs} +4 -4
- package/dist/{chunk-TUNPBZY3.cjs.map → chunk-MM3JHVKZ.cjs.map} +1 -1
- package/dist/{chunk-BRRP76WO.js → chunk-MX7EKQPM.js} +457 -299
- package/dist/chunk-MX7EKQPM.js.map +1 -0
- package/dist/{chunk-2SYZXD54.js → chunk-MZ34E2GN.js} +13 -12
- package/dist/chunk-MZ34E2GN.js.map +1 -0
- package/dist/{chunk-EDN4Y4ZG.cjs → chunk-OJHD3LXJ.cjs} +9 -9
- package/dist/{chunk-EDN4Y4ZG.cjs.map → chunk-OJHD3LXJ.cjs.map} +1 -1
- package/dist/{chunk-DO7SAMEK.cjs → chunk-ON47CBE4.cjs} +47 -47
- package/dist/{chunk-DO7SAMEK.cjs.map → chunk-ON47CBE4.cjs.map} +1 -1
- package/dist/{chunk-6LN7FIVU.js → chunk-PALYTHGM.js} +11 -4
- package/dist/chunk-PALYTHGM.js.map +1 -0
- package/dist/{chunk-YVF2EM7B.js → chunk-R7MRFE5P.js} +3 -3
- package/dist/{chunk-YVF2EM7B.js.map → chunk-R7MRFE5P.js.map} +1 -1
- package/dist/{chunk-EZEEWCFJ.cjs → chunk-RDP6ZRSM.cjs} +435 -265
- package/dist/chunk-RDP6ZRSM.cjs.map +1 -0
- package/dist/{chunk-GYEG3ZLZ.cjs → chunk-RJMHU2MG.cjs} +121 -18
- package/dist/chunk-RJMHU2MG.cjs.map +1 -0
- package/dist/chunk-RWUWTJQY.cjs +30 -0
- package/dist/chunk-RWUWTJQY.cjs.map +1 -0
- package/dist/{chunk-CT3ZKTOF.js → chunk-S33YQEL7.js} +7 -7
- package/dist/{chunk-CT3ZKTOF.js.map → chunk-S33YQEL7.js.map} +1 -1
- package/dist/chunk-S3SDEQMC.js +532 -0
- package/dist/chunk-S3SDEQMC.js.map +1 -0
- package/dist/{chunk-OI2GQ25X.cjs → chunk-SHC7BCG5.cjs} +7 -7
- package/dist/{chunk-OI2GQ25X.cjs.map → chunk-SHC7BCG5.cjs.map} +1 -1
- package/dist/{chunk-L3LQNR34.cjs → chunk-TEC24ZDA.cjs} +161 -161
- package/dist/{chunk-L3LQNR34.cjs.map → chunk-TEC24ZDA.cjs.map} +1 -1
- package/dist/{chunk-HDL2ARHX.cjs → chunk-TP4NJTI3.cjs} +63 -27
- package/dist/chunk-TP4NJTI3.cjs.map +1 -0
- package/dist/{chunk-UMGZDVOA.cjs → chunk-TTCOPKUL.cjs} +124 -124
- package/dist/{chunk-UMGZDVOA.cjs.map → chunk-TTCOPKUL.cjs.map} +1 -1
- package/dist/chunk-TUVD4DFR.cjs +1207 -0
- package/dist/chunk-TUVD4DFR.cjs.map +1 -0
- package/dist/{chunk-PFBRJBUL.cjs → chunk-TYGGUWMS.cjs} +178 -152
- package/dist/chunk-TYGGUWMS.cjs.map +1 -0
- package/dist/{chunk-4WMMBDS5.js → chunk-U3Z7XKAG.js} +7 -7
- package/dist/{chunk-4WMMBDS5.js.map → chunk-U3Z7XKAG.js.map} +1 -1
- package/dist/{chunk-NACVVDWB.js → chunk-U5EL6JUV.js} +10 -10
- package/dist/{chunk-NACVVDWB.js.map → chunk-U5EL6JUV.js.map} +1 -1
- package/dist/{chunk-OMZ37BM2.js → chunk-V76FGHY3.js} +5 -5
- package/dist/{chunk-OMZ37BM2.js.map → chunk-V76FGHY3.js.map} +1 -1
- package/dist/{chunk-6NGW27EO.cjs → chunk-VLBTRXA2.cjs} +10 -10
- package/dist/{chunk-6NGW27EO.cjs.map → chunk-VLBTRXA2.cjs.map} +1 -1
- package/dist/chunk-VTJVMP4I.js +1182 -0
- package/dist/chunk-VTJVMP4I.js.map +1 -0
- package/dist/{chunk-QTD6HX4O.js → chunk-W5SOCUBL.js} +5 -5
- package/dist/{chunk-QTD6HX4O.js.map → chunk-W5SOCUBL.js.map} +1 -1
- package/dist/{chunk-SR725PWF.cjs → chunk-WCLMKNQE.cjs} +1750 -1592
- package/dist/chunk-WCLMKNQE.cjs.map +1 -0
- package/dist/{chunk-TKZXMWQF.cjs → chunk-WK23PGTP.cjs} +13 -13
- package/dist/{chunk-TKZXMWQF.cjs.map → chunk-WK23PGTP.cjs.map} +1 -1
- package/dist/{chunk-KKBDXCLQ.js → chunk-WQBAQZHR.js} +4 -4
- package/dist/{chunk-KKBDXCLQ.js.map → chunk-WQBAQZHR.js.map} +1 -1
- package/dist/{chunk-MRDBBHIZ.cjs → chunk-X4GDXBOP.cjs} +2 -2
- package/dist/chunk-X4GDXBOP.cjs.map +1 -0
- package/dist/{chunk-WCCATHMB.cjs → chunk-XMY2SXOH.cjs} +438 -456
- package/dist/chunk-XMY2SXOH.cjs.map +1 -0
- package/dist/{chunk-D5R42W5D.js → chunk-XZW2CTNO.js} +115 -12
- package/dist/chunk-XZW2CTNO.js.map +1 -0
- package/dist/{chunk-NVY5CJFP.js → chunk-Y55DLLEG.js} +9 -6
- package/dist/chunk-Y55DLLEG.js.map +1 -0
- package/dist/chunk-YA36ABIL.js +174 -0
- package/dist/chunk-YA36ABIL.js.map +1 -0
- package/dist/{chunk-ZLONH2UD.js → chunk-YDEJQWOL.js} +2 -2
- package/dist/chunk-YDEJQWOL.js.map +1 -0
- package/dist/{chunk-JONXFAPO.js → chunk-ZAQUKCUC.js} +5 -5
- package/dist/{chunk-JONXFAPO.js.map → chunk-ZAQUKCUC.js.map} +1 -1
- package/dist/{chunk-SIT26LAZ.cjs → chunk-ZBFZGLZE.cjs} +29 -5
- package/dist/chunk-ZBFZGLZE.cjs.map +1 -0
- package/dist/{chunk-P6YLZTRE.js → chunk-ZRJTM7MT.js} +3 -3
- package/dist/{chunk-P6YLZTRE.js.map → chunk-ZRJTM7MT.js.map} +1 -1
- package/dist/core/index.cjs +29 -28
- package/dist/core/index.d.cts +4 -4
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +9 -8
- package/dist/{ddl-D_oW-EVO.d.ts → ddl-B49O7dB_.d.ts} +3 -3
- package/dist/{ddl-BL1NZLMY.d.cts → ddl-DcZG660s.d.cts} +3 -3
- package/dist/graph-extension/index.cjs +21 -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 +5 -4
- package/dist/graph-merge/index.cjs +327 -302
- package/dist/graph-merge/index.cjs.map +1 -1
- package/dist/graph-merge/index.d.cts +14 -11
- package/dist/graph-merge/index.d.ts +14 -11
- package/dist/graph-merge/index.js +52 -27
- package/dist/graph-merge/index.js.map +1 -1
- package/dist/{index-D7d1u2gr.d.ts → index-B0PrmE3K.d.cts} +16 -2
- package/dist/{index-COs_z6nV.d.cts → index-CwcXsL5R.d.ts} +16 -2
- package/dist/index.cjs +223 -222
- 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 +37 -36
- 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/ingestion-import-target-CI8OWIkM.d.ts +17 -0
- package/dist/ingestion-import-target-DCpfM1PX.d.cts +17 -0
- package/dist/interchange/index.cjs +55 -54
- package/dist/interchange/index.cjs.map +1 -1
- package/dist/interchange/index.d.cts +26 -12
- package/dist/interchange/index.d.ts +26 -12
- package/dist/interchange/index.js +19 -18
- package/dist/interchange/index.js.map +1 -1
- package/dist/{manager-D9eRT_ns.d.ts → manager-Cfg1ENj1.d.ts} +30 -2
- package/dist/{manager-CYaff3n3.d.cts → manager-D348fa6W.d.cts} +30 -2
- package/dist/{materialize-indexes-DJ3ykH59.d.ts → materialize-indexes-C9GG3O3C.d.ts} +1 -1
- package/dist/{materialize-indexes-DTtAnvfY.d.cts → materialize-indexes-CrSiW_lT.d.cts} +1 -1
- package/dist/{postgres-DMj39MEu.d.cts → postgres-CH1kkM5J.d.cts} +279 -2
- package/dist/{postgres-CcL4bAHS.d.ts → postgres-UStOycLI.d.ts} +279 -2
- package/dist/profiler/index.d.cts +9 -9
- package/dist/profiler/index.d.ts +9 -9
- package/dist/provenance/index.cjs +46 -44
- package/dist/provenance/index.cjs.map +1 -1
- package/dist/provenance/index.d.cts +7 -7
- package/dist/provenance/index.d.ts +7 -7
- package/dist/provenance/index.js +12 -10
- package/dist/provenance/index.js.map +1 -1
- package/dist/{repair-validity-windows-DlUSqwCg.d.ts → repair-validity-windows-BtOEe2pS.d.ts} +2 -2
- package/dist/{repair-validity-windows-BFNZvd_H.d.cts → repair-validity-windows-aWX7VNvv.d.cts} +2 -2
- package/dist/schema/index.cjs +46 -46
- package/dist/schema/index.d.cts +6 -6
- package/dist/schema/index.d.ts +6 -6
- package/dist/schema/index.js +19 -19
- package/dist/{searchable-DHJMh_nj.d.cts → searchable-C8-RrL3l.d.cts} +1 -1
- package/dist/{searchable-bpE1bBfc.d.ts → searchable-z4BiFfHI.d.ts} +1 -1
- package/dist/{sqlite-D7Oz0SQh.d.ts → sqlite-Cyx9j9o6.d.cts} +309 -2
- package/dist/{sqlite-YppHrNFb.d.cts → sqlite-axLw5Lo5.d.ts} +309 -2
- package/dist/{store-JUVvtmR4.d.cts → store-CTfQ9taw.d.ts} +110 -13
- package/dist/{store-UzrGvZOo.d.ts → store-DR9J7JVB.d.cts} +110 -13
- package/dist/{temporal-5GqpYCk2.d.cts → temporal-B4Ky4wzh.d.cts} +1 -1
- package/dist/{temporal-CbnLzTmM.d.ts → temporal-CBOrERz5.d.ts} +1 -1
- package/dist/{types-CXAhpW-c.d.cts → types-BE921wRl.d.cts} +1 -1
- package/dist/{types-Bx74kaTt.d.ts → types-BScHXVM7.d.ts} +1 -1
- package/dist/{types-BqFZTQmV.d.cts → types-CWg4dTjj.d.cts} +257 -6
- package/dist/{types-BqFZTQmV.d.ts → types-CWg4dTjj.d.ts} +257 -6
- package/dist/{where-O-k2wgYm.d.ts → where-BWXUPV3C.d.ts} +1 -1
- package/dist/{where-DI_Fqz-X.d.cts → where-CNtx1aLc.d.cts} +1 -1
- package/package.json +4 -4
- package/dist/chunk-26YKDNUD.cjs.map +0 -1
- package/dist/chunk-2HIYYMWY.cjs.map +0 -1
- package/dist/chunk-2SYZXD54.js.map +0 -1
- package/dist/chunk-3KOY4FH3.cjs +0 -934
- package/dist/chunk-3KOY4FH3.cjs.map +0 -1
- package/dist/chunk-3U5ABFDW.cjs.map +0 -1
- package/dist/chunk-5K2BKEBD.js.map +0 -1
- package/dist/chunk-5PRKMB24.cjs.map +0 -1
- package/dist/chunk-6LN7FIVU.js.map +0 -1
- package/dist/chunk-6SWPJEYN.js.map +0 -1
- package/dist/chunk-ACGSAMD3.js.map +0 -1
- package/dist/chunk-AX6JGVTZ.js.map +0 -1
- package/dist/chunk-BFUOME67.cjs.map +0 -1
- package/dist/chunk-BRRP76WO.js.map +0 -1
- package/dist/chunk-CICO5GXD.cjs +0 -12
- package/dist/chunk-D5R42W5D.js.map +0 -1
- package/dist/chunk-EZEEWCFJ.cjs.map +0 -1
- package/dist/chunk-FA2AFWLP.cjs +0 -167
- package/dist/chunk-FA2AFWLP.cjs.map +0 -1
- package/dist/chunk-FQPEMEWX.cjs.map +0 -1
- package/dist/chunk-GLHXM7C4.js.map +0 -1
- package/dist/chunk-GYEG3ZLZ.cjs.map +0 -1
- package/dist/chunk-HDL2ARHX.cjs.map +0 -1
- package/dist/chunk-HEV35TGT.js.map +0 -1
- package/dist/chunk-I4GAZH5Z.js +0 -913
- package/dist/chunk-I4GAZH5Z.js.map +0 -1
- package/dist/chunk-JB6T4GLL.cjs.map +0 -1
- package/dist/chunk-JBQQ7THH.js +0 -20
- package/dist/chunk-JBQQ7THH.js.map +0 -1
- package/dist/chunk-JFRT7S3B.cjs.map +0 -1
- package/dist/chunk-KF6K7TGH.js.map +0 -1
- package/dist/chunk-MKJ4KXL5.cjs +0 -12
- package/dist/chunk-MRDBBHIZ.cjs.map +0 -1
- package/dist/chunk-NVY5CJFP.js.map +0 -1
- package/dist/chunk-OD4SSY2U.js.map +0 -1
- package/dist/chunk-OTUPEF2W.cjs.map +0 -1
- package/dist/chunk-PFBRJBUL.cjs.map +0 -1
- package/dist/chunk-PWLJ77UV.cjs +0 -23
- package/dist/chunk-PWLJ77UV.cjs.map +0 -1
- package/dist/chunk-S5TERQVW.js.map +0 -1
- package/dist/chunk-SHFBB23Q.js.map +0 -1
- package/dist/chunk-SIT26LAZ.cjs.map +0 -1
- package/dist/chunk-SR725PWF.cjs.map +0 -1
- package/dist/chunk-TQHGE7UC.cjs.map +0 -1
- package/dist/chunk-UGHG7PGX.js.map +0 -1
- package/dist/chunk-WCCATHMB.cjs.map +0 -1
- package/dist/chunk-YIURNGXI.js +0 -155
- package/dist/chunk-YIURNGXI.js.map +0 -1
- package/dist/chunk-ZFMCDJGB.js.map +0 -1
- package/dist/chunk-ZLONH2UD.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/query/dialect/vector/pagination.ts"],"names":["sql"],"mappings":";;;;;AAcO,SAAS,gBAAA,CAAiB,OAAe,MAAA,EAA8B;AAC5E,EAAA,OAAO,MAAA,KAAW,MAAA,IAAa,MAAA,KAAW,CAAA,GACtCA,qBAAA,CAAA,MAAA,EAAY,KAAK,CAAA,CAAA,GACjBA,qBAAA,CAAA,MAAA,EAAY,KAAK,CAAA,QAAA,EAAW,MAAM,CAAA,CAAA;AACxC","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/query/dialect/vector/pagination.ts"],"names":["sql"],"mappings":";;;;;AAcO,SAAS,gBAAA,CAAiB,OAAe,MAAA,EAA8B;AAC5E,EAAA,OAAO,MAAA,KAAW,MAAA,IAAa,MAAA,KAAW,CAAA,GACtCA,qBAAA,CAAA,MAAA,EAAY,KAAK,CAAA,CAAA,GACjBA,qBAAA,CAAA,MAAA,EAAY,KAAK,CAAA,QAAA,EAAW,MAAM,CAAA,CAAA;AACxC","file":"chunk-KWNWG4RC.cjs","sourcesContent":["/**\n * Shared vector-search pagination clause.\n *\n * Every vector strategy pages its ordered relevance scan the same way: emit a\n * bare `LIMIT` for the first page (`offset` unset or `0`), and `LIMIT … OFFSET`\n * only when a non-zero offset must discard the leading rows. The three engines'\n * `buildSearch` methods each repeated this ternary verbatim; it lives here once\n * so the emitted clause can never drift between dialects.\n *\n * `limit` and `offset` ride as bound parameters (never interpolated), so the\n * clause — string and bound values alike — is identical across engines.\n */\nimport { sql, type SqlFragment } from \"../../sql-fragment\";\n\nexport function vectorPageClause(limit: number, offset?: number): SqlFragment {\n return offset === undefined || offset === 0 ?\n sql`LIMIT ${limit}`\n : sql`LIMIT ${limit} OFFSET ${offset}`;\n}\n"]}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkRWUWTJQY_cjs = require('./chunk-RWUWTJQY.cjs');
|
|
4
|
+
var chunkX4GDXBOP_cjs = require('./chunk-X4GDXBOP.cjs');
|
|
4
5
|
var chunkN66HCGCY_cjs = require('./chunk-N66HCGCY.cjs');
|
|
6
|
+
var drizzleOrm = require('drizzle-orm');
|
|
5
7
|
|
|
6
8
|
// src/constraints/index.ts
|
|
7
9
|
var UNIQUE_KEY_SEPARATOR = "";
|
|
@@ -23,7 +25,7 @@ function checkWherePredicate(constraint, props) {
|
|
|
23
25
|
}
|
|
24
26
|
const predicate = captureWherePredicate(constraint.where);
|
|
25
27
|
if (predicate === void 0) {
|
|
26
|
-
throw new
|
|
28
|
+
throw new chunkX4GDXBOP_cjs.ConfigurationError(
|
|
27
29
|
`Unique constraint "${constraint.name}" has a \`where\` callback that does not return a predicate.`,
|
|
28
30
|
{ constraintName: constraint.name, fields: [...constraint.fields] },
|
|
29
31
|
{
|
|
@@ -52,7 +54,7 @@ function assertWhereFieldDeclared(kind, constraint, shape) {
|
|
|
52
54
|
if (!constraint.where) return;
|
|
53
55
|
const predicate = captureWherePredicate(constraint.where);
|
|
54
56
|
if (predicate === void 0) {
|
|
55
|
-
throw new
|
|
57
|
+
throw new chunkX4GDXBOP_cjs.ConfigurationError(
|
|
56
58
|
`Unique constraint "${constraint.name}" on node kind "${kind}" has a \`where\` callback that does not return a predicate.`,
|
|
57
59
|
{ kind, constraintName: constraint.name },
|
|
58
60
|
{
|
|
@@ -61,7 +63,7 @@ function assertWhereFieldDeclared(kind, constraint, shape) {
|
|
|
61
63
|
);
|
|
62
64
|
}
|
|
63
65
|
if (!chunkN66HCGCY_cjs.hasOwnKey(shape, predicate.field)) {
|
|
64
|
-
throw new
|
|
66
|
+
throw new chunkX4GDXBOP_cjs.ConfigurationError(
|
|
65
67
|
`Unique constraint "${constraint.name}" on node kind "${kind}" has a \`where\` clause on field "${predicate.field}", which is not declared in the kind's schema.`,
|
|
66
68
|
{
|
|
67
69
|
kind,
|
|
@@ -104,25 +106,8 @@ function evaluatePredicate(pred, props) {
|
|
|
104
106
|
}
|
|
105
107
|
return chunkN66HCGCY_cjs.isPresent(value);
|
|
106
108
|
}
|
|
107
|
-
function
|
|
108
|
-
|
|
109
|
-
return [baseKind];
|
|
110
|
-
}
|
|
111
|
-
const root = findRootAncestor(baseKind, registry);
|
|
112
|
-
return registry.expandSubClasses(root);
|
|
113
|
-
}
|
|
114
|
-
function findRootAncestor(kind, registry) {
|
|
115
|
-
const ancestors = registry.getAncestors(kind);
|
|
116
|
-
if (ancestors.size === 0) {
|
|
117
|
-
return kind;
|
|
118
|
-
}
|
|
119
|
-
for (const ancestor of ancestors) {
|
|
120
|
-
if (registry.getAncestors(ancestor).size === 0) {
|
|
121
|
-
return ancestor;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
const firstAncestor = [...ancestors][0];
|
|
125
|
-
return firstAncestor ? findRootAncestor(firstAncestor, registry) : kind;
|
|
109
|
+
function subClassComponent(kind, registry) {
|
|
110
|
+
return registry.getSubClassComponent(kind);
|
|
126
111
|
}
|
|
127
112
|
function checkCardinality(edgeKind, fromKind, fromId, cardinality, existingEdgeCount, hasActiveEdge) {
|
|
128
113
|
switch (cardinality) {
|
|
@@ -131,7 +116,7 @@ function checkCardinality(edgeKind, fromKind, fromId, cardinality, existingEdgeC
|
|
|
131
116
|
}
|
|
132
117
|
case "one": {
|
|
133
118
|
if (existingEdgeCount > 0) {
|
|
134
|
-
return new
|
|
119
|
+
return new chunkX4GDXBOP_cjs.CardinalityError({
|
|
135
120
|
edgeKind,
|
|
136
121
|
fromKind,
|
|
137
122
|
fromId,
|
|
@@ -146,7 +131,7 @@ function checkCardinality(edgeKind, fromKind, fromId, cardinality, existingEdgeC
|
|
|
146
131
|
}
|
|
147
132
|
case "oneActive": {
|
|
148
133
|
if (hasActiveEdge) {
|
|
149
|
-
return new
|
|
134
|
+
return new chunkX4GDXBOP_cjs.CardinalityError({
|
|
150
135
|
edgeKind,
|
|
151
136
|
fromKind,
|
|
152
137
|
fromId,
|
|
@@ -160,7 +145,7 @@ function checkCardinality(edgeKind, fromKind, fromId, cardinality, existingEdgeC
|
|
|
160
145
|
}
|
|
161
146
|
function checkUniqueEdge(edgeKind, fromKind, fromId, _toKind, _toId, existingCount) {
|
|
162
147
|
if (existingCount > 0) {
|
|
163
|
-
return new
|
|
148
|
+
return new chunkX4GDXBOP_cjs.CardinalityError({
|
|
164
149
|
edgeKind,
|
|
165
150
|
fromKind,
|
|
166
151
|
fromId,
|
|
@@ -173,7 +158,7 @@ function checkUniqueEdge(edgeKind, fromKind, fromId, _toKind, _toId, existingCou
|
|
|
173
158
|
function validateEdgeEndpoints(edgeKind, fromKind, toKind, registration, registry) {
|
|
174
159
|
const validFromKinds = registration.from.map((node) => node.kind);
|
|
175
160
|
if (!registry.isAssignableToAny(fromKind, validFromKinds)) {
|
|
176
|
-
return new
|
|
161
|
+
return new chunkX4GDXBOP_cjs.EndpointError({
|
|
177
162
|
edgeKind,
|
|
178
163
|
endpoint: "from",
|
|
179
164
|
actualKind: fromKind,
|
|
@@ -182,7 +167,7 @@ function validateEdgeEndpoints(edgeKind, fromKind, toKind, registration, registr
|
|
|
182
167
|
}
|
|
183
168
|
const validToKinds = registration.to.map((node) => node.kind);
|
|
184
169
|
if (!registry.isAssignableToAny(toKind, validToKinds)) {
|
|
185
|
-
return new
|
|
170
|
+
return new chunkX4GDXBOP_cjs.EndpointError({
|
|
186
171
|
edgeKind,
|
|
187
172
|
endpoint: "to",
|
|
188
173
|
actualKind: toKind,
|
|
@@ -194,7 +179,7 @@ function validateEdgeEndpoints(edgeKind, fromKind, toKind, registration, registr
|
|
|
194
179
|
function checkDisjointness(nodeId, nodeKind, existingKinds, registry) {
|
|
195
180
|
for (const existingKind of existingKinds) {
|
|
196
181
|
if (registry.areDisjoint(nodeKind, existingKind)) {
|
|
197
|
-
return new
|
|
182
|
+
return new chunkX4GDXBOP_cjs.DisjointError({
|
|
198
183
|
nodeId,
|
|
199
184
|
attemptedKind: nodeKind,
|
|
200
185
|
conflictingKind: existingKind
|
|
@@ -204,14 +189,77 @@ function checkDisjointness(nodeId, nodeKind, existingKinds, registry) {
|
|
|
204
189
|
return void 0;
|
|
205
190
|
}
|
|
206
191
|
|
|
192
|
+
// src/utils/tuple-key.ts
|
|
193
|
+
function encodeTupleKey(values) {
|
|
194
|
+
return JSON.stringify(values);
|
|
195
|
+
}
|
|
196
|
+
var AXIS_SEPARATOR = "";
|
|
197
|
+
var DISJOINT_AXIS_PREFIX = `${AXIS_SEPARATOR}disjoint${AXIS_SEPARATOR}`;
|
|
198
|
+
var DISJOINT_CONSTRAINT_NAME = `${AXIS_SEPARATOR}disjointWith`;
|
|
199
|
+
function disjointnessClaimAxis(kind, otherKind, registry) {
|
|
200
|
+
return `${DISJOINT_AXIS_PREFIX}${registry.disjointPairLabel(kind, otherKind)}`;
|
|
201
|
+
}
|
|
202
|
+
function assertClaimAxisSafe(name, subject) {
|
|
203
|
+
if (!name.includes(AXIS_SEPARATOR)) return;
|
|
204
|
+
throw new chunkX4GDXBOP_cjs.ConfigurationError(
|
|
205
|
+
`${subject} name ${JSON.stringify(name)} contains U+001E, which TypeGraph reserves for claim axes.`,
|
|
206
|
+
{ name },
|
|
207
|
+
{
|
|
208
|
+
suggestion: `Rename it without the U+001E (record separator) character.`
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
function uniquenessClaimKinds(kind, scope, registry) {
|
|
213
|
+
return scope === "kind" ? [kind] : subClassComponent(kind, registry);
|
|
214
|
+
}
|
|
215
|
+
function uniquenessClaimTarget(kind, scope, registry) {
|
|
216
|
+
const kinds = uniquenessClaimKinds(kind, scope, registry);
|
|
217
|
+
return { axis: kinds[0] ?? kind, crossKind: kinds.length > 1 };
|
|
218
|
+
}
|
|
219
|
+
function uniquenessClaimAxis(kind, scope, registry) {
|
|
220
|
+
return uniquenessClaimTarget(kind, scope, registry).axis;
|
|
221
|
+
}
|
|
222
|
+
function uniquenessProbeKinds(kind, scope, registry) {
|
|
223
|
+
const axis = uniquenessClaimAxis(kind, scope, registry);
|
|
224
|
+
const coveredKinds = uniquenessClaimKinds(kind, scope, registry);
|
|
225
|
+
const rest = coveredKinds.filter((candidate) => candidate !== axis).toSorted((left, right) => chunkRWUWTJQY_cjs.compareStrings(left, right));
|
|
226
|
+
return [axis, ...rest];
|
|
227
|
+
}
|
|
228
|
+
function edgeCardinalityAxis(cardinality, edgeKind) {
|
|
229
|
+
return `${cardinality}:${edgeKind}`;
|
|
230
|
+
}
|
|
231
|
+
function compareClaimTargets(left, right) {
|
|
232
|
+
return chunkRWUWTJQY_cjs.compareStrings(left.relation, right.relation) || chunkRWUWTJQY_cjs.compareStrings(left.graphId, right.graphId) || chunkRWUWTJQY_cjs.compareStrings(left.axis, right.axis) || chunkRWUWTJQY_cjs.compareStrings(left.constraintName ?? "", right.constraintName ?? "") || chunkRWUWTJQY_cjs.compareStrings(left.key, right.key);
|
|
233
|
+
}
|
|
234
|
+
function isSameClaimOwner(left, right) {
|
|
235
|
+
return left.nodeId === right.nodeId && left.concreteKind === right.concreteKind;
|
|
236
|
+
}
|
|
237
|
+
function claimOwnerKey(owner) {
|
|
238
|
+
return encodeTupleKey([owner.concreteKind, owner.nodeId]);
|
|
239
|
+
}
|
|
240
|
+
function claimOwnerMatchesSql(existing, proposed, columns) {
|
|
241
|
+
return drizzleOrm.sql`${existing(columns.nodeId)} = ${proposed(columns.nodeId)} AND ${existing(columns.concreteKind)} = ${proposed(columns.concreteKind)}`;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
exports.DISJOINT_CONSTRAINT_NAME = DISJOINT_CONSTRAINT_NAME;
|
|
245
|
+
exports.assertClaimAxisSafe = assertClaimAxisSafe;
|
|
207
246
|
exports.assertWhereFieldDeclared = assertWhereFieldDeclared;
|
|
208
247
|
exports.captureWherePredicate = captureWherePredicate;
|
|
209
248
|
exports.checkCardinality = checkCardinality;
|
|
210
249
|
exports.checkDisjointness = checkDisjointness;
|
|
211
250
|
exports.checkUniqueEdge = checkUniqueEdge;
|
|
212
251
|
exports.checkWherePredicate = checkWherePredicate;
|
|
252
|
+
exports.claimOwnerKey = claimOwnerKey;
|
|
253
|
+
exports.claimOwnerMatchesSql = claimOwnerMatchesSql;
|
|
254
|
+
exports.compareClaimTargets = compareClaimTargets;
|
|
213
255
|
exports.computeUniqueKey = computeUniqueKey;
|
|
214
|
-
exports.
|
|
256
|
+
exports.disjointnessClaimAxis = disjointnessClaimAxis;
|
|
257
|
+
exports.edgeCardinalityAxis = edgeCardinalityAxis;
|
|
258
|
+
exports.encodeTupleKey = encodeTupleKey;
|
|
259
|
+
exports.isSameClaimOwner = isSameClaimOwner;
|
|
260
|
+
exports.subClassComponent = subClassComponent;
|
|
261
|
+
exports.uniquenessClaimTarget = uniquenessClaimTarget;
|
|
262
|
+
exports.uniquenessProbeKinds = uniquenessProbeKinds;
|
|
215
263
|
exports.validateEdgeEndpoints = validateEdgeEndpoints;
|
|
216
|
-
//# sourceMappingURL=chunk-
|
|
217
|
-
//# sourceMappingURL=chunk-
|
|
264
|
+
//# sourceMappingURL=chunk-LLVOQOWE.cjs.map
|
|
265
|
+
//# sourceMappingURL=chunk-LLVOQOWE.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constraints/index.ts","../src/utils/tuple-key.ts","../src/store/claims/axis.ts"],"names":["readOwnProperty","ConfigurationError","hasOwnKey","isPresent","CardinalityError","EndpointError","DisjointError","compareStrings","sql"],"mappings":"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","file":"chunk-LLVOQOWE.cjs","sourcesContent":["/**\n * Constraint validation module.\n *\n * Provides validation functions for enforcing graph constraints:\n * - Uniqueness constraints on node properties\n * - Cardinality constraints on edges\n * - Endpoint type constraints on edges\n * - Disjointness constraints between node kinds\n */\n\n/**\n * Separator used between field values in composite unique keys.\n * Uses ASCII Record Separator (0x1E) — valid UTF-8 and safe for PostgreSQL\n * TEXT columns (unlike \\0 which PostgreSQL rejects).\n */\nconst UNIQUE_KEY_SEPARATOR = \"\\u001E\";\n\n/** Marker for undefined/null field values in unique keys. */\nconst UNIQUE_KEY_NULL_MARKER = \"\\u001F\"; // ASCII Unit Separator\nimport {\n type Cardinality,\n type Collation,\n type EdgeRegistration,\n type NullCheckOp,\n type UniqueConstraint,\n type UniquenessScope,\n} from \"../core/types\";\nimport {\n CardinalityError,\n ConfigurationError,\n DisjointError,\n EndpointError,\n UniquenessError,\n} from \"../errors\";\nimport { type KindRegistry } from \"../registry/kind-registry\";\nimport { hasOwnKey, readOwnProperty } from \"../utils/object\";\nimport { isPresent } from \"../utils/presence\";\n\n// ============================================================\n// Uniqueness Validation\n// ============================================================\n\n/**\n * Computes the unique key for a node's uniqueness constraint.\n *\n * The key is built by concatenating the specified field values,\n * optionally normalized for case-insensitive comparison.\n *\n * Field values are read by declared OWN key ({@link readOwnProperty}): a\n * constraint may name a field that a props bag does not carry (an absent\n * optional field), and a plain `props[field]` read would answer such a field\n * with the inherited `Object.prototype` member when the field is named after\n * one. A constraint over a field named `toString` then keyed on the inherited\n * function — `\"\"` under `binary` (the function stringified and dropped by\n * `JSON.stringify`) and a `TypeError` under `caseInsensitive` — instead of the\n * null marker every other absent value gets.\n */\nexport function computeUniqueKey(\n props: Record<string, unknown>,\n fields: readonly string[],\n collation: Collation,\n): string {\n const values = fields.map((field) => {\n const value = readOwnProperty(props, field);\n if (value === undefined || value === null) {\n return UNIQUE_KEY_NULL_MARKER;\n }\n // Convert to string, handling primitives safely\n const stringValue =\n typeof value === \"string\" ? value\n : typeof value === \"number\" || typeof value === \"boolean\" ?\n value.toString()\n : JSON.stringify(value);\n return collation === \"caseInsensitive\" ?\n stringValue.toLowerCase()\n : stringValue;\n });\n return values.join(UNIQUE_KEY_SEPARATOR);\n}\n\n/**\n * Checks if a uniqueness constraint's where predicate passes.\n *\n * A `where` callback that does not return a predicate is REFUSED here, not\n * treated as \"the constraint always applies\". The same malformed clause is a\n * hard `ConfigurationError` at definition time ({@link assertWhereFieldDeclared})\n * and at persistence time (`serializeWherePredicate`); a third reading that\n * silently widened a partial constraint to a total one would let the three\n * sites disagree about the same clause — the divergence\n * {@link captureWherePredicate} exists to prevent.\n *\n * The arm SHOULD be unreachable: `defineGraph` refuses a non-predicate callback\n * before any write can evaluate it, `validateGraphExtension` refuses the\n * document form, and a constraint reconstructed from a persisted schema carries\n * a callback this module built itself. It is reachable only by handing the\n * store a constraint object that never passed a definition gate — for which\n * failing loudly is the correct answer, since the alternative is enforcing\n * uniqueness over rows the author meant to exclude.\n */\nexport function checkWherePredicate(\n constraint: UniqueConstraint,\n props: Record<string, unknown>,\n): boolean {\n if (!constraint.where) {\n return true; // No where clause, always applies\n }\n\n const predicate = captureWherePredicate(constraint.where);\n if (predicate === undefined) {\n throw new ConfigurationError(\n `Unique constraint \"${constraint.name}\" has a \\`where\\` callback that does not return a predicate.`,\n { constraintName: constraint.name, fields: [...constraint.fields] },\n {\n suggestion: `Return a field predicate, e.g. \\`where: (fields) => fields.${constraint.fields[0] ?? \"someField\"}.isNotNull()\\`. A constraint built outside \\`defineGraph\\` bypasses the definition-time check that normally reports this.`,\n },\n );\n }\n\n return evaluatePredicate(predicate, props);\n}\n\ntype UniquePredicate = Readonly<{\n __type: \"unique_predicate\";\n field: string;\n op: NullCheckOp;\n}>;\n\n/**\n * The `where` callback as every caller of {@link captureWherePredicate} sees it:\n * a per-field builder in, whatever the author returned out.\n *\n * Deliberately looser than `UniqueConstraint[\"where\"]`, whose builder type is\n * generic in the kind's schema — the capture is the same operation whether the\n * callback came from a typed `defineGraph` registration, a compiled\n * graph-extension document, or an untyped JavaScript caller.\n */\ntype UniqueWhereCallback = (\n builder: Readonly<\n Record<\n string,\n Readonly<{\n isNull: () => UniquePredicate;\n isNotNull: () => UniquePredicate;\n }>\n >\n >,\n) => unknown;\n\n/**\n * Runs a `where` callback against the shared builder and returns the predicate\n * it named, or `undefined` when the callback returned something that is not a\n * predicate.\n *\n * The single owner of \"what does this `where` clause say\": constraint\n * EVALUATION ({@link checkWherePredicate}), definition-time VALIDATION\n * ({@link assertWhereFieldDeclared}), and persistence-time CAPTURE\n * (`serializeWherePredicate` in src/schema/serializer.ts) all read the clause\n * through this one function, so a persisted `where`, a validated `where`, and\n * an evaluated `where` cannot disagree about which field the author named.\n */\nexport function captureWherePredicate(\n where: UniqueWhereCallback,\n): UniquePredicate | undefined {\n const predicate = where(buildPredicateContext());\n if (\n typeof predicate !== \"object\" ||\n predicate === null ||\n !(\"__type\" in predicate)\n ) {\n return undefined;\n }\n\n const candidate = predicate as {\n __type: unknown;\n field: unknown;\n op: unknown;\n };\n if (\n candidate.__type !== \"unique_predicate\" ||\n typeof candidate.field !== \"string\" ||\n (candidate.op !== \"isNull\" && candidate.op !== \"isNotNull\")\n ) {\n return undefined;\n }\n\n return {\n __type: \"unique_predicate\",\n field: candidate.field,\n op: candidate.op,\n };\n}\n\n/**\n * Refuses a uniqueness constraint whose `where` clause names a field the kind\n * does not declare.\n *\n * The runtime half of the builder's guard. {@link buildPredicateContext} is\n * total by design — it must answer for a DECLARED-but-absent field, which is\n * the whole point of a partial constraint — so a typo'd name cannot be caught\n * there, and the type system catches it only for typed callers\n * (`UniqueConstraintPredicateBuilder` declares exactly the schema's fields and\n * has no index signature). An untyped caller naming an undeclared field\n * otherwise gets a predicate that quietly never applies.\n *\n * Called once per constraint at graph-definition time — the single point every\n * constraint passes through before any write can evaluate it — rather than at\n * each of the write paths' `checkWherePredicate` call sites, so the refusal\n * covers every path uniformly and costs nothing per write. Mirrors\n * `validateGraphExtension`'s `UNKNOWN_UNIQUE_WHERE_FIELD` refusal, which\n * already holds the same invariant for kinds declared as JSON documents.\n */\nexport function assertWhereFieldDeclared(\n kind: string,\n constraint: UniqueConstraint,\n shape: Readonly<Record<string, unknown>>,\n): void {\n if (!constraint.where) return;\n\n const predicate = captureWherePredicate(constraint.where);\n if (predicate === undefined) {\n throw new ConfigurationError(\n `Unique constraint \"${constraint.name}\" on node kind \"${kind}\" has a \\`where\\` callback that does not return a predicate.`,\n { kind, constraintName: constraint.name },\n {\n suggestion: `Return a field predicate, e.g. \\`where: (fields) => fields.${Object.keys(shape)[0] ?? \"someField\"}.isNotNull()\\`.`,\n },\n );\n }\n\n if (!hasOwnKey(shape, predicate.field)) {\n throw new ConfigurationError(\n `Unique constraint \"${constraint.name}\" on node kind \"${kind}\" has a \\`where\\` clause on field \"${predicate.field}\", which is not declared in the kind's schema.`,\n {\n kind,\n constraintName: constraint.name,\n field: predicate.field,\n declaredFields: Object.keys(shape),\n },\n {\n suggestion: `Name a declared field (${Object.keys(shape).join(\", \")}) or add \"${predicate.field}\" to the schema.`,\n },\n );\n }\n}\n\ntype PredicateContext = Readonly<\n Record<\n string,\n Readonly<{\n isNull: () => UniquePredicate;\n isNotNull: () => UniquePredicate;\n }>\n >\n>;\n\n/**\n * Builds the context a constraint's `where` callback names fields on.\n *\n * EVERY field name gets a member, because the builder type declares every\n * schema field as required (`-?` in `UniqueConstraintPredicateBuilder`) —\n * precisely so a partial constraint can ask whether an OPTIONAL field is\n * present. Populating the context from the props bag's own keys instead left a\n * declared-but-absent field with no member, and the callback's access then hit\n * whatever the context's prototype offered: `Object.prototype.toString` for a\n * field named `toString` (so naming it threw \"isNull is not a function\"), and\n * `undefined` for an ordinary field (so the everyday partial constraint over\n * `externalId` threw — \"Cannot read properties of undefined\", or \"Expected a\n * defined value\" through `requireDefined` — for every node written without it).\n *\n * Answering every name is safe because a name comes from the predicate author\n * (the code), never from data, and the field's VALUE is still read from the\n * props bag by own key when the predicate is evaluated — so an absent field\n * evaluates as null, which is what a partial constraint means by absent. Every\n * reader of a `where` clause goes through {@link captureWherePredicate}, which\n * builds this one context, so a persisted `where`, a validated `where`, and an\n * evaluated `where` cannot see different builders.\n *\n * The guard against a TYPO'D field name is NOT this Proxy — it must stay total\n * or a declared-but-absent field loses its member. It is\n * {@link assertWhereFieldDeclared}, which every constraint passes at\n * graph-definition time: an undeclared name is refused there with a typed\n * `ConfigurationError`, so no `where` clause naming one ever reaches\n * evaluation, from a typed or an untyped caller.\n */\nfunction buildPredicateContext(): PredicateContext {\n return new Proxy<PredicateContext>(\n {},\n {\n get(_target, property) {\n if (typeof property === \"symbol\") return;\n return {\n isNull: () => ({\n __type: \"unique_predicate\" as const,\n field: property,\n op: \"isNull\" as const,\n }),\n isNotNull: () => ({\n __type: \"unique_predicate\" as const,\n field: property,\n op: \"isNotNull\" as const,\n }),\n };\n },\n },\n );\n}\n\n/**\n * Evaluates a uniqueness predicate.\n *\n * Takes a CAPTURED predicate, not an `unknown`: the \"is this really a\n * predicate?\" question belongs to {@link captureWherePredicate}, which is its\n * single owner. The defensive `return true` arms this function used to carry\n * were a second, quieter answer to that question — and they answered it the\n * opposite way, widening a partial constraint into a total one where the owner\n * refuses.\n */\nfunction evaluatePredicate(\n pred: UniquePredicate,\n props: Record<string, unknown>,\n): boolean {\n // Own-key read: `pred.field` is a schema field name, and a props bag that\n // does not carry it must read as absent rather than as the inherited\n // `Object.prototype` member a field named after one would otherwise find\n // (which reads as present, inverting both `isNull` and `isNotNull`).\n const value = readOwnProperty(props, pred.field);\n if (pred.op === \"isNull\") {\n return value === null || value === undefined;\n }\n return isPresent(value);\n}\n\n/**\n * Gets all kinds that should be checked for a uniqueness constraint.\n *\n * For \"kindWithSubClasses\" scope, includes the entire subclass hierarchy:\n * - The kind itself\n * - All ancestors (parent classes)\n * - All descendants of those ancestors (sibling classes)\n *\n * For \"kind\" scope, only the specific kind.\n */\nexport function getKindsForUniquenessCheck(\n baseKind: string,\n scope: UniquenessScope,\n registry: KindRegistry,\n): readonly string[] {\n if (scope === \"kind\") {\n return [baseKind];\n }\n\n // Get the entire connected subclass hierarchy by finding the root ancestor\n const root = findRootAncestor(baseKind, registry);\n\n // Return the root and all its descendants (which includes baseKind and siblings)\n return registry.expandSubClasses(root);\n}\n\n/**\n * THE definition of \"which kinds a `kindWithSubClasses` scope covers\": the\n * registry's precomputed connected component of the UNDIRECTED `subClassOf`\n * graph containing `kind`, in code-point order.\n *\n * Kind-independent by construction — every member of a component computes the\n * same set — which is what makes a claim axis folded from it deterministic: two\n * writers of two different kinds in one hierarchy reserve the SAME row, so the\n * uniques primary key fences them against each other.\n *\n * {@link getKindsForUniquenessCheck} is deliberately NOT folded into this: it\n * walks one root's descendants, so on a multi-root hierarchy (`Employee`\n * subclassing both `Alpha` and `Zeta`) it answers differently depending on which\n * member asks, and a fold of a kind-dependent set cannot be a canonical axis.\n * This is the documented intent of that function, so the component is a superset\n * of the set it walks: the fence is never weaker than the probe, and is stronger\n * in exactly the multi-root case where the probe is inconsistent.\n */\nexport function subClassComponent(\n kind: string,\n registry: KindRegistry,\n): readonly string[] {\n return registry.getSubClassComponent(kind);\n}\n\n/**\n * Finds the topmost ancestor of a kind, or the kind itself if it has no ancestors.\n */\nfunction findRootAncestor(kind: string, registry: KindRegistry): string {\n const ancestors = registry.getAncestors(kind);\n\n if (ancestors.size === 0) {\n return kind;\n }\n\n // Find an ancestor with no ancestors (the root)\n for (const ancestor of ancestors) {\n if (registry.getAncestors(ancestor).size === 0) {\n return ancestor;\n }\n }\n\n // If all ancestors have ancestors, recurse up\n const firstAncestor = [...ancestors][0];\n return firstAncestor ? findRootAncestor(firstAncestor, registry) : kind;\n}\n\n/**\n * Creates a uniqueness error.\n */\nexport function createUniquenessError(\n constraintName: string,\n kind: string,\n existingId: string,\n newId: string,\n fields: readonly string[],\n): UniquenessError {\n return new UniquenessError({\n constraintName,\n kind,\n existingId,\n newId,\n fields: [...fields],\n });\n}\n\n// ============================================================\n// Cardinality Validation\n// ============================================================\n\n/**\n * Checks if adding an edge would violate cardinality constraints.\n *\n * @param edgeKind - The edge kind being added\n * @param fromKind - The source node kind\n * @param fromId - The source node ID\n * @param cardinality - The cardinality constraint\n * @param existingEdgeCount - Number of existing edges of this kind from this source\n * @param hasActiveEdge - Whether there's an active (valid_to IS NULL) edge\n * @returns Error if violation, undefined if valid\n */\nexport function checkCardinality(\n edgeKind: string,\n fromKind: string,\n fromId: string,\n cardinality: Cardinality,\n existingEdgeCount: number,\n hasActiveEdge: boolean,\n): CardinalityError | undefined {\n switch (cardinality) {\n case \"many\": {\n // No constraint\n return undefined;\n }\n case \"one\": {\n // At most one edge of this kind from any source node\n if (existingEdgeCount > 0) {\n return new CardinalityError({\n edgeKind,\n fromKind,\n fromId,\n cardinality: \"one\",\n existingCount: existingEdgeCount,\n });\n }\n return undefined;\n }\n case \"unique\": {\n // unique is checked separately per (source, target) pair\n return undefined;\n }\n case \"oneActive\": {\n // At most one edge with valid_to IS NULL from any source\n if (hasActiveEdge) {\n return new CardinalityError({\n edgeKind,\n fromKind,\n fromId,\n cardinality: \"oneActive\",\n existingCount: 1,\n });\n }\n return undefined;\n }\n }\n}\n\n/**\n * Checks unique edge constraint (at most one edge between any source-target pair).\n */\nexport function checkUniqueEdge(\n edgeKind: string,\n fromKind: string,\n fromId: string,\n _toKind: string,\n _toId: string,\n existingCount: number,\n): CardinalityError | undefined {\n if (existingCount > 0) {\n return new CardinalityError({\n edgeKind,\n fromKind,\n fromId,\n cardinality: \"unique\",\n existingCount,\n });\n }\n return undefined;\n}\n\n// ============================================================\n// Endpoint Validation\n// ============================================================\n\n/**\n * Validates that an edge's endpoints are valid node kinds.\n */\nexport function validateEdgeEndpoints(\n edgeKind: string,\n fromKind: string,\n toKind: string,\n registration: EdgeRegistration,\n registry: KindRegistry,\n): EndpointError | undefined {\n // Check from kinds\n const validFromKinds = registration.from.map((node) => node.kind);\n if (!registry.isAssignableToAny(fromKind, validFromKinds)) {\n return new EndpointError({\n edgeKind,\n endpoint: \"from\",\n actualKind: fromKind,\n expectedKinds: validFromKinds,\n });\n }\n\n // Check to kinds\n const validToKinds = registration.to.map((node) => node.kind);\n if (!registry.isAssignableToAny(toKind, validToKinds)) {\n return new EndpointError({\n edgeKind,\n endpoint: \"to\",\n actualKind: toKind,\n expectedKinds: validToKinds,\n });\n }\n\n return undefined;\n}\n\n// ============================================================\n// Disjointness Validation\n// ============================================================\n\n/**\n * Checks if creating a node would violate disjointness constraints.\n *\n * @param nodeId - The node ID being created\n * @param nodeKind - The kind of the new node\n * @param existingKinds - Kinds of existing nodes with the same ID\n * @param registry - The kind registry for disjointness checks\n * @returns Error if disjoint violation, undefined if valid\n */\nexport function checkDisjointness(\n nodeId: string,\n nodeKind: string,\n existingKinds: readonly string[],\n registry: KindRegistry,\n): DisjointError | undefined {\n for (const existingKind of existingKinds) {\n if (registry.areDisjoint(nodeKind, existingKind)) {\n return new DisjointError({\n nodeId,\n attemptedKind: nodeKind,\n conflictingKind: existingKind,\n });\n }\n }\n return undefined;\n}\n\n/**\n * Gets all disjoint kinds for a given kind.\n */\nexport function getDisjointKinds(\n kind: string,\n registry: KindRegistry,\n): readonly string[] {\n return registry.getDisjointKinds(kind);\n}\n","/**\n * Encodes an ordered tuple of strings as an injective map key.\n *\n * Delimiter joins are unsafe because every delimiter is also a legal value\n * character. JSON's string-array grammar preserves both field boundaries and\n * string contents, so distinct tuples cannot collapse onto the same key.\n */\nexport function encodeTupleKey(values: readonly string[]): string {\n return JSON.stringify(values);\n}\n","/**\n * Claim axis vocabulary.\n *\n * A declared constraint is a CLAIM on an AXIS: the value bound to a claim row's\n * `node_kind` column, which is what the relation's primary key fences on. The\n * axis is not always a kind — it is whatever set of kinds the constraint spans —\n * and deciding it is this module's only job, so no write path spells its own.\n *\n * The other half of a claim row is its OWNER, `(concrete_kind, node_id)`. Ids\n * are unique only per kind, so an id alone cannot answer \"is this row mine?\":\n * `Employee \"X\"` and `Contractor \"X\"` are two rows under the nodes primary key\n * and would read as one owner. The predicate lives here once, in the two\n * renderings the code needs (TypeScript and SQL), so a claim's accept/refuse\n * verdict cannot differ between the layer that probes it and the layer that\n * writes it.\n */\nimport { type Column, type SQL, sql } from \"drizzle-orm\";\n\nimport { subClassComponent } from \"../../constraints\";\nimport { type UniquenessScope } from \"../../core/types\";\nimport { ConfigurationError } from \"../../errors\";\nimport { type KindRegistry } from \"../../registry/kind-registry\";\nimport { compareStrings } from \"../../utils/compare\";\nimport { encodeTupleKey } from \"../../utils/tuple-key\";\n\n/**\n * The one code point an axis component may not contain, written as an escape\n * so it can never be mistaken for whitespace in a diff.\n *\n * Axes that are not kinds are built by joining with it — the disjointness pair\n * axis below is the first — so a kind or constraint name containing it could\n * spell a reserved axis and take a claim row the fence assigns to something\n * else. {@link assertClaimAxisSafe} is what makes that unspellable, at\n * graph-definition time.\n */\nconst AXIS_SEPARATOR = \"\\u001E\";\n\n/** The prefix marking an axis as a disjoint PAIR rather than as a kind. */\nconst DISJOINT_AXIS_PREFIX = `${AXIS_SEPARATOR}disjoint${AXIS_SEPARATOR}`;\n\n/**\n * The `constraint_name` every disjointness claim is written under.\n *\n * Reserved rather than derived: it is what tells the claim seam that a refusal\n * on this row is a `DisjointError` and not a `UniquenessError`, and what tells\n * a reader of the relation that this row's `node_kind` is a pair label rather\n * than a kind. {@link assertClaimAxisSafe} is what guarantees no declared\n * constraint can carry the same name.\n */\nexport const DISJOINT_CONSTRAINT_NAME = `${AXIS_SEPARATOR}disjointWith`;\n\n/**\n * THE axis a disjointness claim is written at: the registry's own canonical\n * pair label, prefixed so it cannot collide with a kind.\n *\n * A fold of `KindRegistry.disjointPairLabel` rather than a second\n * normalization of the same pair — the two kinds of one disjoint pair must\n * compute ONE string, or their claims sit on two rows that can never collide\n * and the fence refuses nothing.\n *\n * Pairwise, and deliberately not per component: the registry's disjoint pairs\n * are literal unordered pairs and disjointness is not transitive here, so an\n * axis keyed on a connected component would refuse an `A`/`C` pair under\n * `A⊥B, B⊥C` that the graph never declared disjoint.\n */\nexport function disjointnessClaimAxis(\n kind: string,\n otherKind: string,\n registry: KindRegistry,\n): string {\n return `${DISJOINT_AXIS_PREFIX}${registry.disjointPairLabel(kind, otherKind)}`;\n}\n\n/**\n * Refuses a kind name or constraint name that could spell a reserved claim\n * axis or the reserved disjointness constraint name.\n *\n * Runs at graph-definition time, the one gate every kind and every declared\n * constraint passes before a claim can be written for it — so the reserved\n * vocabulary is unspellable by construction rather than re-checked at each\n * write, and the claim seam can read \"this refusal is a disjointness refusal\"\n * off the reserved constraint name without a caller being able to forge it. A\n * new refusal, on an input no real schema carries.\n *\n * `subject` names the ROLE (`Node kind`, `Unique constraint`); the name itself\n * is quoted through `JSON.stringify` so the offending code point reads as an\n * escape instead of as an invisible character.\n */\nexport function assertClaimAxisSafe(name: string, subject: string): void {\n if (!name.includes(AXIS_SEPARATOR)) return;\n throw new ConfigurationError(\n `${subject} name ${JSON.stringify(name)} contains U+001E, which TypeGraph reserves for claim axes.`,\n { name },\n {\n suggestion: `Rename it without the U+001E (record separator) character.`,\n },\n );\n}\n\n/**\n * WHERE a uniqueness claim for this kind and scope is written, and whether that\n * target spans kinds beyond the writer's own.\n *\n * Both facts come out of one computation on purpose. The axis is the code-point\n * minimum of the set the scope covers, and \"does this site also need the\n * per-graph write lock?\" is that same set having more than one member — so a\n * caller cannot pick up one without the other, and the lock trigger cannot\n * drift away from the claim target. Asking the covered SET rather than the\n * scope token is what keeps a `kindWithSubClasses` constraint on a kind with no\n * hierarchy classified like the `kind` scope it is equivalent to.\n */\nexport type UniquenessClaimTarget = Readonly<{\n axis: string;\n crossKind: boolean;\n}>;\n\n/** The kinds one uniqueness claim axis spans, in canonical order. */\nfunction uniquenessClaimKinds(\n kind: string,\n scope: UniquenessScope,\n registry: KindRegistry,\n): readonly string[] {\n return scope === \"kind\" ? [kind] : subClassComponent(kind, registry);\n}\n\n/** THE decision above — the one owner of both readings. */\nexport function uniquenessClaimTarget(\n kind: string,\n scope: UniquenessScope,\n registry: KindRegistry,\n): UniquenessClaimTarget {\n const kinds = uniquenessClaimKinds(kind, scope, registry);\n return { axis: kinds[0] ?? kind, crossKind: kinds.length > 1 };\n}\n\n/**\n * THE axis a uniqueness claim is written at: the kind itself for\n * `scope: \"kind\"`, and the code-point minimum of the subclass component for\n * `scope: \"kindWithSubClasses\"`.\n *\n * The minimum is a fold of a kind-independent set, so every kind in one\n * hierarchy folds to the same axis and their claims collide on the uniques\n * primary key — which is the whole fence. Reserving under each writer's own\n * kind (what this replaces) put sibling kinds in rows that can never collide.\n */\nexport function uniquenessClaimAxis(\n kind: string,\n scope: UniquenessScope,\n registry: KindRegistry,\n): string {\n return uniquenessClaimTarget(kind, scope, registry).axis;\n}\n\n/**\n * THE order a uniqueness probe reads claim rows in: the axis first, then every\n * remaining kind the scope covers, in code-point order.\n *\n * Two things make this list wider than the axis alone and narrower than\n * arbitrary:\n *\n * - The axis is where this version writes, so it is read first.\n * - Rows written before the axis move sit under their own concrete kind, so the\n * probe visits the full undirected component, including every root of a\n * multi-root hierarchy. That is what makes the axis move need no data\n * migration.\n *\n * The remainder is sorted rather than left in registry-iteration order so two\n * processes reading the same scope read it in the same order.\n */\nexport function uniquenessProbeKinds(\n kind: string,\n scope: UniquenessScope,\n registry: KindRegistry,\n): readonly string[] {\n const axis = uniquenessClaimAxis(kind, scope, registry);\n const coveredKinds = uniquenessClaimKinds(kind, scope, registry);\n const rest = coveredKinds\n .filter((candidate) => candidate !== axis)\n .toSorted((left, right) => compareStrings(left, right));\n return [axis, ...rest];\n}\n\n/**\n * THE axis an edge cardinality claim is written at: the declared cardinality\n * and the edge kind, which together name the population the constraint counts.\n *\n * `one` and `oneActive` on one kind are DIFFERENT axes on purpose — they count\n * different populations (every live edge from a source vs every active one) —\n * so a kind whose declaration changed cannot inherit rows the old declaration\n * wrote. The cardinality tokens contain no `:`, so the pair is injective over\n * arbitrary edge kind names.\n */\nexport function edgeCardinalityAxis(\n cardinality: string,\n edgeKind: string,\n): string {\n return `${cardinality}:${edgeKind}`;\n}\n\n/** The relation a claim row lives in. */\ntype ClaimRelation = \"uniques\" | \"edgeClaims\";\n\n/** A claim row named in full — the row a statement is about to lock. */\nexport type ClaimTarget = Readonly<{\n relation: ClaimRelation;\n graphId: string;\n axis: string;\n /**\n * `uniques.constraint_name`. The edge claim relation keys on\n * `(graph_id, axis, key)` and has no such column, so its targets omit it and\n * sort as the empty string. Absent means \"this relation does not key on a\n * constraint name\", never \"not known yet\".\n */\n constraintName?: string;\n key: string;\n}>;\n\n/**\n * THE canonical order claims are acquired in: code-point compare on\n * `(relation, graphId, axis, constraintName, key)`.\n *\n * Two writers that take the same two claim rows in opposite orders deadlock,\n * and PostgreSQL resolves that by aborting one with `40P01` — which would turn\n * an import's per-row recovery into a whole-batch abort. Sorting every claim\n * statement's entries by one comparator removes the commonest cycle, exactly as\n * multi-graph lock acquisition already does (see `recorded-capture.ts`, whose\n * comment states the same rule: every process must acquire in the same order).\n *\n * The order it establishes is per claim SET and per statement, not per\n * transaction: rows and batches inside one import claim in input order, so two\n * concurrent lock-free imports into one graph can still deadlock. That residual\n * is declared out of contract rather than fenced here.\n */\nexport function compareClaimTargets(\n left: ClaimTarget,\n right: ClaimTarget,\n): number {\n return (\n compareStrings(left.relation, right.relation) ||\n compareStrings(left.graphId, right.graphId) ||\n compareStrings(left.axis, right.axis) ||\n compareStrings(left.constraintName ?? \"\", right.constraintName ?? \"\") ||\n compareStrings(left.key, right.key)\n );\n}\n\n/**\n * WHO holds a claim. A node, not an id: ids are unique only per kind, so\n * `(concrete_kind, node_id)` is the smallest thing that identifies a claimant.\n */\nexport type ClaimOwner = Readonly<{ concreteKind: string; nodeId: string }>;\n\n/**\n * THE ownership predicate. Every reader of \"is this claim row mine?\" calls it —\n * the probe, the single-row upsert's verdict, the batch upsert's verdict, and\n * the batch-validation cache's pending answer.\n *\n * Comparing ids alone accepts a claim held by a namesake under another kind,\n * which is precisely the collision `disjointWith` forbids and precisely the one\n * a shared uniqueness scope exists to catch.\n */\nexport function isSameClaimOwner(left: ClaimOwner, right: ClaimOwner): boolean {\n return (\n left.nodeId === right.nodeId && left.concreteKind === right.concreteKind\n );\n}\n\n/**\n * THE Set/Map key rendering of {@link isSameClaimOwner}, for a path that decides\n * ownership against MANY owners at once rather than pairwise.\n *\n * `encodeTupleKey`, not a delimiter join: kind names are constrained but node\n * ids are arbitrary caller data, and a delimiter a value may contain would make\n * two different owners collapse onto one key — which, where this is used, would\n * silently whitelist a foreign owner. Third rendering of one predicate, in the\n * module that owns it and beside {@link claimOwnerMatchesSql}: keys are equal\n * exactly when `isSameClaimOwner` holds and exactly when the SQL matches.\n */\nexport function claimOwnerKey(owner: ClaimOwner): string {\n return encodeTupleKey([owner.concreteKind, owner.nodeId]);\n}\n\n/** The owner columns of the uniques relation, as the SQL renderer needs them. */\nexport type ClaimOwnerColumns = Readonly<{\n nodeId: Column;\n concreteKind: Column;\n}>;\n\n/**\n * THE SQL rendering of {@link isSameClaimOwner}, for the two upsert builders.\n *\n * `existing` qualifies a column of the conflicting row the way the dialect\n * requires (PostgreSQL needs the table name, SQLite takes the bare quoted\n * column); `proposed` renders the value being claimed — a bound parameter for\n * the single-row builder, an `excluded.` reference for the batch one. Two\n * renderings, one definition: the arms both builders decide ownership with are\n * this fragment, so a builder cannot quietly compare fewer columns than the\n * TypeScript predicate does.\n */\nexport function claimOwnerMatchesSql(\n existing: (column: Column) => SQL,\n proposed: (column: Column) => SQL,\n columns: ClaimOwnerColumns,\n): SQL {\n return sql`${existing(columns.nodeId)} = ${proposed(columns.nodeId)} AND ${existing(columns.concreteKind)} = ${proposed(columns.concreteKind)}`;\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkX4GDXBOP_cjs = require('./chunk-X4GDXBOP.cjs');
|
|
4
4
|
|
|
5
5
|
// src/utils/date.ts
|
|
6
6
|
var CANONICAL_ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
|
@@ -51,7 +51,7 @@ function isCanonicalIsoDate(value) {
|
|
|
51
51
|
}
|
|
52
52
|
function validateCanonicalIsoDate(value, fieldName) {
|
|
53
53
|
if (!isCanonicalIsoDate(value)) {
|
|
54
|
-
throw new
|
|
54
|
+
throw new chunkX4GDXBOP_cjs.ValidationError(
|
|
55
55
|
`Invalid canonical ISO 8601 datetime for "${fieldName}": "${value}". Expected fixed-width UTC: YYYY-MM-DDTHH:mm:ss.sssZ`,
|
|
56
56
|
{
|
|
57
57
|
issues: [
|
|
@@ -96,13 +96,13 @@ function resolveStatedValidityLowerBound(validFrom) {
|
|
|
96
96
|
}
|
|
97
97
|
function assertEffectiveValidityLowerBound(subject, effectiveValidFrom, validTo) {
|
|
98
98
|
if (!isInvertedValidityWindow(effectiveValidFrom, validTo)) return;
|
|
99
|
-
throw new
|
|
99
|
+
throw new chunkX4GDXBOP_cjs.ValidationError(
|
|
100
100
|
`Inverted validity window for ${subject}: the effective validFrom "${effectiveValidFrom}" is after validTo "${validTo}".`,
|
|
101
101
|
{
|
|
102
102
|
issues: [
|
|
103
103
|
{
|
|
104
104
|
path: "validTo",
|
|
105
|
-
code:
|
|
105
|
+
code: chunkX4GDXBOP_cjs.INVERTED_VALIDITY_WINDOW_CODE,
|
|
106
106
|
message: `Expected the effective validFrom <= validTo, got "${effectiveValidFrom}" > "${validTo}". The effective validFrom is the row's stored lower bound, or the write instant where the write stamps one.`
|
|
107
107
|
}
|
|
108
108
|
]
|
|
@@ -114,13 +114,13 @@ function assertEffectiveValidityLowerBound(subject, effectiveValidFrom, validTo)
|
|
|
114
114
|
}
|
|
115
115
|
function assertOrderedValidityWindow(subject, validFrom, validTo) {
|
|
116
116
|
if (!isInvertedValidityWindow(validFrom, validTo)) return;
|
|
117
|
-
throw new
|
|
117
|
+
throw new chunkX4GDXBOP_cjs.ValidationError(
|
|
118
118
|
`Inverted validity window for ${subject}: validFrom "${validFrom}" is after validTo "${validTo}".`,
|
|
119
119
|
{
|
|
120
120
|
issues: [
|
|
121
121
|
{
|
|
122
122
|
path: "validFrom",
|
|
123
|
-
code:
|
|
123
|
+
code: chunkX4GDXBOP_cjs.INVERTED_VALIDITY_WINDOW_CODE,
|
|
124
124
|
message: `Expected validFrom <= validTo, got "${validFrom}" > "${validTo}"`
|
|
125
125
|
}
|
|
126
126
|
]
|
|
@@ -130,19 +130,25 @@ function assertOrderedValidityWindow(subject, validFrom, validTo) {
|
|
|
130
130
|
}
|
|
131
131
|
);
|
|
132
132
|
}
|
|
133
|
+
function assertsStoredLowerBound(fence) {
|
|
134
|
+
return fence.expectedValidFrom !== void 0;
|
|
135
|
+
}
|
|
136
|
+
function assertsStoredUpperBound(fence) {
|
|
137
|
+
return fence.expectedValidTo !== void 0;
|
|
138
|
+
}
|
|
133
139
|
function preservesImmutableLowerBound(policy) {
|
|
134
140
|
return policy === "preserve";
|
|
135
141
|
}
|
|
136
142
|
function assertStatedLowerBoundIsApplicable(subject, statedValidFrom, storedValidFrom) {
|
|
137
143
|
if (statedBoundMatchesStored(statedValidFrom, storedValidFrom)) return;
|
|
138
144
|
const storedDescription = storedValidFrom === void 0 ? "the row has no lower bound" : `the row's stored lower bound is "${storedValidFrom}"`;
|
|
139
|
-
throw new
|
|
145
|
+
throw new chunkX4GDXBOP_cjs.ValidationError(
|
|
140
146
|
`Unappliable validFrom for ${subject}: stated "${statedValidFrom}", but ${storedDescription} and the row is live.`,
|
|
141
147
|
{
|
|
142
148
|
issues: [
|
|
143
149
|
{
|
|
144
150
|
path: "validFrom",
|
|
145
|
-
code:
|
|
151
|
+
code: chunkX4GDXBOP_cjs.IMMUTABLE_VALIDITY_LOWER_BOUND_CODE,
|
|
146
152
|
message: `A live row's lower bound is history and an in-place update never rewrites it, so "${statedValidFrom}" could not be applied.`
|
|
147
153
|
}
|
|
148
154
|
]
|
|
@@ -182,6 +188,8 @@ function nowIso() {
|
|
|
182
188
|
|
|
183
189
|
exports.assertOrderedValidityWindow = assertOrderedValidityWindow;
|
|
184
190
|
exports.assertWritableValidityWindow = assertWritableValidityWindow;
|
|
191
|
+
exports.assertsStoredLowerBound = assertsStoredLowerBound;
|
|
192
|
+
exports.assertsStoredUpperBound = assertsStoredUpperBound;
|
|
185
193
|
exports.canonicalizeDatabaseTimestamp = canonicalizeDatabaseTimestamp;
|
|
186
194
|
exports.isCanonicalIsoDate = isCanonicalIsoDate;
|
|
187
195
|
exports.isInvertedValidityWindow = isInvertedValidityWindow;
|
|
@@ -193,5 +201,5 @@ exports.statedBoundMatchesStored = statedBoundMatchesStored;
|
|
|
193
201
|
exports.validateCanonicalIsoDate = validateCanonicalIsoDate;
|
|
194
202
|
exports.validateOptionalCanonicalIsoDate = validateOptionalCanonicalIsoDate;
|
|
195
203
|
exports.validityWindowContainsInstant = validityWindowContainsInstant;
|
|
196
|
-
//# sourceMappingURL=chunk-
|
|
197
|
-
//# sourceMappingURL=chunk-
|
|
204
|
+
//# sourceMappingURL=chunk-M5MSP3BI.cjs.map
|
|
205
|
+
//# sourceMappingURL=chunk-M5MSP3BI.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/date.ts"],"names":["ValidationError","INVERTED_VALIDITY_WINDOW_CODE","IMMUTABLE_VALIDITY_LOWER_BOUND_CODE"],"mappings":"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","file":"chunk-M5MSP3BI.cjs","sourcesContent":["/**\n * Date encoding utilities for consistent storage.\n *\n * Contract: All dates are stored as ISO 8601 strings in UTC.\n * - Always includes milliseconds\n * - Always UTC (Z suffix)\n * - Sorts correctly as strings\n */\n\nimport {\n IMMUTABLE_VALIDITY_LOWER_BOUND_CODE,\n INVERTED_VALIDITY_WINDOW_CODE,\n ValidationError,\n} from \"../errors\";\n\n/**\n * ISO 8601 datetime pattern.\n * Matches formats like:\n * - 2024-01-15T10:30:00.000Z\n * - 2024-01-15T10:30:00Z\n * - 2024-01-15T10:30:00.123Z\n */\nconst ISO_DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,3})?Z$/;\n\n/**\n * Checks if a string is a valid ISO 8601 datetime.\n *\n * @param value - String to validate\n * @returns True if valid ISO 8601 datetime\n */\nexport function isValidIsoDate(value: string): boolean {\n if (!ISO_DATE_PATTERN.test(value)) {\n return false;\n }\n // Also check that Date parsing produces a valid date\n const date = new Date(value);\n return !Number.isNaN(date.getTime());\n}\n\n/**\n * Validates that a string is a valid ISO 8601 datetime.\n * Throws ValidationError if invalid.\n *\n * @param value - String to validate\n * @param fieldName - Name of field for error message\n * @returns The validated string\n * @throws ValidationError if not a valid ISO datetime\n */\nexport function validateIsoDate(value: string, fieldName: string): string {\n if (!isValidIsoDate(value)) {\n throw new ValidationError(\n `Invalid ISO 8601 datetime for \"${fieldName}\": \"${value}\". ` +\n `Expected format: YYYY-MM-DDTHH:mm:ss.sssZ`,\n {\n issues: [\n {\n path: fieldName,\n message: `Invalid ISO 8601 datetime format. Expected: YYYY-MM-DDTHH:mm:ss.sssZ, got: \"${value}\"`,\n },\n ],\n },\n {\n suggestion: `Use a valid ISO 8601 UTC datetime like \"2024-01-15T10:30:00.000Z\"`,\n },\n );\n }\n return value;\n}\n\n/**\n * Strict canonical ISO 8601 pattern: fixed-width UTC with exactly\n * millisecond precision (`YYYY-MM-DDTHH:mm:ss.sssZ`). Unlike\n * {@link ISO_DATE_PATTERN}, which tolerates a missing or variable-width\n * fractional part, this is the only shape whose lexicographic text order\n * matches chronological order — avoiding cases like `\"...:00.1Z\"` (= `.100`)\n * sorting *after* `\"...:00.101Z\"`.\n */\nconst CANONICAL_ISO_DATE_PATTERN =\n /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$/;\n\nconst ZONELESS_DATETIME_PATTERN =\n /^\\d{4}-\\d{2}-\\d{2}[ T]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?$/;\nconst OFFSET_DATETIME_PATTERN =\n /^(\\d{4}-\\d{2}-\\d{2})[ T](\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?)(Z|[+-]\\d{2}(?::?\\d{2})?)$/i;\nconst DATE_ONLY_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nfunction normalizedOffset(offset: string): string {\n if (offset.toUpperCase() === \"Z\") return \"Z\";\n if (/^[+-]\\d{2}$/.test(offset)) return `${offset}:00`;\n if (/^[+-]\\d{4}$/.test(offset)) {\n return `${offset.slice(0, 3)}:${offset.slice(3)}`;\n }\n return offset;\n}\n\nfunction databaseTimestampMilliseconds(value: string): number {\n if (ZONELESS_DATETIME_PATTERN.test(value)) {\n return Date.parse(`${value.replace(\" \", \"T\")}Z`);\n }\n const offsetMatch = OFFSET_DATETIME_PATTERN.exec(value);\n if (offsetMatch !== null) {\n const date = offsetMatch[1];\n const time = offsetMatch[2];\n const offset = offsetMatch[3];\n if (date === undefined || time === undefined || offset === undefined) {\n return Number.NaN;\n }\n return Date.parse(`${date}T${time}${normalizedOffset(offset)}`);\n }\n if (DATE_ONLY_PATTERN.test(value)) {\n return Date.parse(`${value}T00:00:00.000Z`);\n }\n return Number.NaN;\n}\n\n/**\n * Canonicalizes a database-driver timestamp without interpreting a zoneless\n * string in the host timezone. PostgreSQL drivers can return values such as\n * `2026-06-25 12:00:00`; TypeGraph treats that shape as UTC so the same stored\n * clock value cannot move across hosts or daylight-saving boundaries.\n *\n * Returns `undefined` for an unsupported or unrepresentable value so callers\n * can raise an error specific to their storage boundary.\n */\nexport function canonicalizeDatabaseTimestamp(\n value: unknown,\n): string | undefined {\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return undefined;\n return value.toISOString();\n }\n if (typeof value !== \"string\") return undefined;\n const milliseconds = databaseTimestampMilliseconds(value);\n if (Number.isNaN(milliseconds)) return undefined;\n return new Date(milliseconds).toISOString();\n}\n\n/**\n * Checks if a string is a canonical UTC ISO 8601 datetime with fixed\n * millisecond width (`YYYY-MM-DDTHH:mm:ss.sssZ`).\n */\nexport function isCanonicalIsoDate(value: string): boolean {\n if (!CANONICAL_ISO_DATE_PATTERN.test(value)) return false;\n const date = new Date(value);\n return !Number.isNaN(date.getTime()) && date.toISOString() === value;\n}\n\n/**\n * Validates that a string is a canonical UTC ISO 8601 datetime with fixed\n * millisecond width. Stricter than {@link validateIsoDate} (which also\n * accepts missing or 1–2 digit milliseconds): required wherever the value\n * is compared **as text** against stored timestamps and must sort\n * chronologically — e.g. a temporal `asOf` coordinate, where a\n * variable-width value would mis-order and silently include or exclude the\n * wrong rows. Produce one with `new Date(value).toISOString()`.\n *\n * @param value - String to validate\n * @param fieldName - Name of field for error message\n * @returns The validated string\n * @throws ValidationError if not a canonical UTC ISO datetime\n */\nexport function validateCanonicalIsoDate(\n value: string,\n fieldName: string,\n): string {\n if (!isCanonicalIsoDate(value)) {\n throw new ValidationError(\n `Invalid canonical ISO 8601 datetime for \"${fieldName}\": \"${value}\". ` +\n `Expected fixed-width UTC: YYYY-MM-DDTHH:mm:ss.sssZ`,\n {\n issues: [\n {\n path: fieldName,\n message: `Expected canonical UTC ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ), got: \"${value}\"`,\n },\n ],\n },\n {\n suggestion: `Use a fixed-width UTC datetime like \"2024-01-15T10:30:00.000Z\" (e.g. new Date(value).toISOString()).`,\n },\n );\n }\n return value;\n}\n\n/**\n * Validates an optional canonical ISO date string. Returns `undefined` for an\n * absent value; otherwise enforces canonical fixed-width UTC ISO 8601 via\n * {@link validateCanonicalIsoDate}. Used for stored validity-window inputs\n * (`validFrom` / `validTo`) so every timestamp the temporal filters compare as\n * text is canonical and sorts chronologically — the same contract the `asOf`\n * read coordinate already enforces.\n */\nexport function validateOptionalCanonicalIsoDate(\n value: string | undefined,\n fieldName: string,\n): string | undefined {\n if (value === undefined) return undefined;\n return validateCanonicalIsoDate(value, fieldName);\n}\n\n/**\n * Whether a CANONICAL stated bound names the same instant a row already stores.\n *\n * The one comparison of a caller's bound against a stored one, so the coalesce\n * dirty check (\"would this write move the window?\") and the write guard (\"can\n * this stated bound be applied?\") can never disagree about what \"the same bound\"\n * means. Two copies of it did disagree — one comparing driver text, one\n * comparing instants — which is how a re-stated window wrote on one backend and\n * coalesced on the other.\n *\n * Only the STORED side is canonicalized. It arrives as the driver rendered it,\n * and the dialects do not render a timestamp the same way (SQLite returns the\n * written text; a Postgres driver may hand back a zoned string equivalent to the\n * canonical form without being identical to it). The stated side needs no\n * canonicalization because every write path validates it as canonical first —\n * which is a PRECONDITION here, not an assumption: a non-canonical stated bound\n * would compare unequal to an equivalent stored instant and must be rejected by\n * {@link validateCanonicalIsoDate} rather than silently treated as a mismatch.\n */\nexport function statedBoundMatchesStored(\n stated: string,\n stored: string | undefined,\n): boolean {\n return stated === canonicalizeDatabaseTimestamp(stored);\n}\n\n/**\n * Whether a stated validity window has NEGATIVE width — a row that stopped\n * being true before it started. The one comparison every window refusal is\n * built on, so paths that must report the fault through their own error type\n * (trusted import) decide it identically to the ones that throw a\n * `ValidationError`.\n *\n * PRECONDITION: both endpoints are canonical fixed-width UTC ISO 8601, which is\n * what makes a lexicographic compare a chronological one. Every caller\n * establishes that before comparing, with no exception: the paths that throw a\n * `ValidationError` reach this through {@link validateOptionalCanonicalIsoDate},\n * and trusted import — which skips schema validation for throughput —\n * format-checks the stated window fields of every streamed row against\n * {@link isCanonicalIsoDate} before reaching here. A bound read back from a row\n * needs no check of its own: it is canonical because the write that stored it\n * was held to this same contract — SQLite hands that text back verbatim, and the\n * PostgreSQL row mapper normalizes the driver's shape back to it. So no caller\n * can compare a value that would mis-sort, here or later against an `asOf`\n * coordinate.\n *\n * ZERO width (`validFrom === validTo`) is not inverted: it is what a\n * same-instant retraction produces at millisecond precision, and the store's own\n * output must round-trip.\n */\nexport function isInvertedValidityWindow(\n validFrom: string | undefined,\n validTo: string | undefined,\n): boolean {\n if (validFrom === undefined || validTo === undefined) return false;\n return validFrom > validTo;\n}\n\n/**\n * Whether a validity window is readable at NO instant — inverted OR zero width.\n * The CHOICE predicate, sibling to {@link isInvertedValidityWindow}'s REFUSAL\n * predicate, and the two answer different questions on purpose:\n *\n * - a window a caller STATED in full is refused only when it is inverted, because\n * zero width is what a same-instant retraction produces and the store's own\n * output must round-trip;\n * - a bound a write CHOOSES for a caller who stated none must never satisfy this\n * one, because a row nobody can read at any coordinate is not a window the\n * caller asked for.\n *\n * `inverted ⇒ empty`, and `empty ∧ ¬inverted ⟺ zero width`. That law is a\n * property in `tests/property/temporal-window.test.ts`, so the pair cannot drift\n * into disagreeing about anything but the boundary they deliberately differ on.\n *\n * PRECONDITION: as {@link isInvertedValidityWindow} — both endpoints canonical\n * fixed-width UTC ISO 8601, so the lexicographic compare is a chronological one.\n */\nexport function isEmptyValidityWindow(\n validFrom: string | undefined,\n validTo: string | undefined,\n): boolean {\n if (validFrom === undefined || validTo === undefined) return false;\n return validFrom >= validTo;\n}\n\n/**\n * Whether a canonical instant belongs to the half-open validity interval\n * `[validFrom, validTo)`. An omitted bound is unbounded on that side.\n *\n * This is the single JavaScript owner of validity-window membership. SQL read\n * paths express the same decision through the shared temporal compiler.\n */\nexport function validityWindowContainsInstant(\n validFrom: string | undefined,\n validTo: string | undefined,\n instant: string,\n): boolean {\n return (\n (validFrom === undefined || validFrom <= instant) &&\n (validTo === undefined || instant < validTo)\n );\n}\n\n/**\n * THE lower bound a write that STAMPS its own start stores, decided against the\n * instant it is about to stamp. One owner for every such write, so no two of them\n * can spell the decision differently. Three inputs, three outcomes:\n *\n * - a stated `null` → no lower bound (a CONFIRMED open-left window, which is\n * how interchange round-trips a row that has none);\n * - a stated string → that bound, the caller's own assertion, verbatim;\n * - nothing stated → the write instant, UNLESS stamping it would leave the\n * window readable at no instant. A row carrying only a\n * `validTo` at or before the write instant is \"born already\n * ended\": its start is UNKNOWN, not at-or-after its end, so\n * it stores no lower bound and reads back at every `asOf`\n * before that end (issue #407).\n *\n * The comparison against `validTo` is NON-STRICT: `validTo === writeInstant`\n * stores no bound too. A stamped `[T, T)` is a successful write no coordinate can\n * observe, and the millisecond of skew between a caller's own `Date.now()` and the\n * backend's sample is enough to land there — so the rule is total, and there is no\n * input for which a stamped bound yields an empty window.\n *\n * The write instant is a PARAMETER, not a clock read: the caller passes the very\n * value it binds into `created_at`/`updated_at` (or, for a resurrection, the\n * instant its guard judged), so the decision and the stamp cannot come from two\n * different samples (issue #413's failure mode). This function has no way to\n * sample a clock, which is what makes that structural rather than tested.\n *\n * PRECONDITION: as {@link isEmptyValidityWindow}. This is called BELOW the\n * validation boundary, on a public `GraphBackend` surface, so canonicality is\n * established by whoever reached the backend: store paths through\n * {@link validateOptionalCanonicalIsoDate}, interchange import through its own\n * window validator, trusted import through its per-chunk format check. A direct\n * `GraphBackend` caller establishes it itself — a pre-existing property of that\n * surface, written down here rather than left implicit.\n */\nexport function resolveStampedValidityLowerBound(\n statedValidFrom: string | null | undefined,\n validTo: string | undefined,\n writeInstant: string,\n): string | undefined {\n if (statedValidFrom === null) return undefined;\n if (statedValidFrom !== undefined) return statedValidFrom;\n return isEmptyValidityWindow(writeInstant, validTo) ? undefined : (\n writeInstant\n );\n}\n\n/**\n * The lower bound a write that PASSES THROUGH a stated one stores: `null` — the\n * interchange spelling of a confirmed open-left window — becomes \"no bound\", and\n * a string is stored verbatim.\n *\n * Separate from {@link resolveStampedValidityLowerBound} because it answers a\n * different question. Its one caller (`buildUpdateEdge`'s resurrection leg) writes\n * the window only when the caller NAMED a lower bound; an edge resurrection that\n * names none RETAINS the stored window rather than choosing a new one, so there is\n * no instant to judge and none is offered.\n */\nexport function resolveStatedValidityLowerBound(\n validFrom: string | null,\n): string | undefined {\n return validFrom ?? undefined;\n}\n\n/**\n * Refuses a `validTo` whose EFFECTIVE lower bound would invert the window. On an\n * in-place update the row's stored lower bound is the effective one and must stay\n * <= the new `validTo`; on a resurrection that RESETS `valid_from` it is the\n * write instant, so a lone past `validTo` would be born inverted.\n *\n * A ZERO-width window (`validTo === effectiveValidFrom`) is legal and stays\n * legal: it is what a same-instant retraction produces at millisecond\n * precision, and the identity-import window check sanctions it for the same\n * reason. Only NEGATIVE width refuses.\n *\n * Reached through {@link assertWritableValidityWindow} rather than called\n * directly, so no writer can check the effective bound without also checking a\n * stated pair.\n */\nfunction assertEffectiveValidityLowerBound(\n subject: string,\n effectiveValidFrom: string | undefined,\n validTo: string | undefined,\n): void {\n if (!isInvertedValidityWindow(effectiveValidFrom, validTo)) return;\n throw new ValidationError(\n `Inverted validity window for ${subject}: the effective validFrom \"${effectiveValidFrom}\" is after validTo \"${validTo}\".`,\n {\n issues: [\n {\n path: \"validTo\",\n code: INVERTED_VALIDITY_WINDOW_CODE,\n message: `Expected the effective validFrom <= validTo, got \"${effectiveValidFrom}\" > \"${validTo}\". The effective validFrom is the row's stored lower bound, or the write instant where the write stamps one.`,\n },\n ],\n },\n {\n suggestion:\n \"Provide both validFrom and validTo for a historical window, or drop validTo to keep the row current.\",\n },\n );\n}\n\n/**\n * Rejects an inverted validity window. `validFrom > validTo` describes a row\n * that stopped being true before it started, so no current-mode or `asOf` read\n * can ever observe it — the write succeeds and the row is then reachable only\n * through `includeEnded`, which reads as data loss rather than as the error it\n * is. Call this wherever both endpoints are supplied together; both are\n * canonical ISO 8601 by then, so a lexicographic compare is a chronological\n * compare.\n *\n * @param subject - Human-readable identification of the row, for the message\n * (e.g. `Person \"01H...\"`).\n * @throws ValidationError if both endpoints are present and out of order\n */\nexport function assertOrderedValidityWindow(\n subject: string,\n validFrom: string | undefined,\n validTo: string | undefined,\n): void {\n if (!isInvertedValidityWindow(validFrom, validTo)) return;\n throw new ValidationError(\n `Inverted validity window for ${subject}: validFrom \"${validFrom}\" is after validTo \"${validTo}\".`,\n {\n issues: [\n {\n path: \"validFrom\",\n code: INVERTED_VALIDITY_WINDOW_CODE,\n message: `Expected validFrom <= validTo, got \"${validFrom}\" > \"${validTo}\"`,\n },\n ],\n },\n {\n suggestion:\n \"Pass a validFrom at or before validTo, or omit validTo to leave the window open.\",\n },\n );\n}\n\n/**\n * What an UPDATE will do with the row's validity lower bound — the two facts the\n * write guard needs, stated by the writer that knows them.\n *\n * Every update path must decide `appliesStatedValidFrom` explicitly, because the\n * answer is not a property of the guard's other arguments: the same\n * `(validFrom, storedBound, validTo)` triple is a legal window restatement on a\n * resurrection and an unappliable bound on an in-place update.\n */\nexport type UpdateValidityLowerBound = Readonly<{\n /**\n * The bound the row will hold when the caller states no `validFrom`: the\n * stored `valid_from` for an in-place update, the write instant for a\n * resurrection that stamps a new one, `undefined` where there is no bound to\n * invert against.\n */\n effectiveValidFrom: string | undefined;\n /**\n * Whether the write STORES a stated `validFrom`. False for an in-place update\n * of a live row — `buildUpdateNode` / `buildUpdateEdge` rewrite `valid_from`\n * only on a resurrection — which is what makes a differing stated bound a\n * refusal rather than a silent drop.\n */\n appliesStatedValidFrom: boolean;\n /**\n * Whether `effectiveValidFrom` IS the row's stored `valid_from` — the value an\n * `expectedValidFrom` predicate would name — rather than a bound this write is\n * about to stamp itself.\n *\n * A node resurrection resets `valid_from` to the write instant and passes THAT\n * as the effective bound, so its verdict never looked at what the row holds and\n * a fence on the stored value would predicate the write on something nothing\n * read. An edge retains its bound through a resurrection, so its effective\n * bound is the stored one on both legs.\n */\n effectiveBoundIsStored: boolean;\n}>;\n\n/**\n * The `expectedValidFrom` predicate a write owes its verdict, shaped for\n * spreading straight into the backend's update params.\n *\n * `{}` means \"assert nothing\"; `{ expectedValidFrom: null }` means \"assert the\n * row still has NO lower bound\". The two are different claims and the object\n * form keeps them distinguishable at every call site — see\n * `UpdateNodeParams.expectedValidFrom`.\n */\nexport type ValidityLowerBoundFence = Readonly<{\n expectedValidFrom?: string | null;\n}>;\n\n/**\n * Whether a {@link ValidityLowerBoundFence} STATES anything. Returns a\n * boolean, despite the `asserts` in its name reading like a TypeScript\n * assertion function: it answers \"does this fence assert the row's stored\n * lower bound?\".\n *\n * THE SINGLE OWNER of that question, placed beside the type it decides about.\n * Two spellings of it existed — `args.expectedValidFrom !== undefined` at the\n * write step that carries the fence into the UPDATE, and a key-membership test\n * wherever a fence had to be recognised as empty. They agree today under\n * `exactOptionalPropertyTypes`, which is exactly the state in which a second\n * implementation of an existing decision is still a defect: the copies WILL\n * drift, and this one decides whether a write is fenced at all.\n *\n * `{ expectedValidFrom: null }` is a STATED fence — \"assert the row still has\n * NO lower bound\" — not an empty one, which is why the test is against\n * `undefined` and not against nullishness.\n */\nexport function assertsStoredLowerBound<T extends ValidityLowerBoundFence>(\n fence: T,\n): fence is T & StatedValidityLowerBoundFence {\n return fence.expectedValidFrom !== undefined;\n}\n\n/**\n * A {@link ValidityLowerBoundFence} that states a bound — what\n * {@link assertsStoredLowerBound} narrows to, so a caller that has asked the\n * question does not have to re-ask it to satisfy `exactOptionalPropertyTypes`.\n * Re-asking is how the second spelling gets reintroduced.\n */\nexport type StatedValidityLowerBoundFence = Readonly<{\n expectedValidFrom: string | null;\n}>;\n\n/**\n * The `expectedValidTo` predicate a write owes a verdict that read the row's\n * stored window END, shaped for spreading straight into the update params.\n *\n * The counterpart of {@link ValidityLowerBoundFence}, and stated separately for\n * the same reason: `{}` means \"assert nothing\" and `{ expectedValidTo: null }`\n * means \"assert the row is still open-ended\". A reopen of an `oneActive` edge\n * judged the end the row carried, so its UPDATE asserts it.\n */\nexport type ValidityUpperBoundFence = Readonly<{\n expectedValidTo?: string | null;\n}>;\n\n/**\n * Whether a {@link ValidityUpperBoundFence} STATES anything — the single owner\n * of that question, for the same reason {@link assertsStoredLowerBound} is the\n * single owner of its own.\n */\nexport function assertsStoredUpperBound<T extends ValidityUpperBoundFence>(\n fence: T,\n): fence is T & Readonly<{ expectedValidTo: string | null }> {\n return fence.expectedValidTo !== undefined;\n}\n\n/**\n * What {@link assertWritableValidityWindow} reports back about the verdict it\n * just reached — not about the window, but about what the verdict DEPENDED ON.\n *\n * THE SINGLE OWNER of the question \"must this write assert the row's stored\n * lower bound?\", and it answers by handing over the predicate ITSELF rather than\n * a flag a caller then turns into one. A write asserts every component its\n * verdict read (see `UpdateNodeParams.expectedValidFrom`), and which components\n * those are is decided by which branches the guard actually took — so a caller\n * that could still spell the fence on its own would eventually spell a different\n * one. Interchange import did exactly that: it asserted the probed\n * `valid_from` unconditionally, over-fencing props-only updates the store paths\n * (correctly) left unfenced. There is now nothing to re-derive.\n *\n * Too FEW assertions is a silent race; too many is a refusal of writes that are\n * legitimate. Both failures come from a second spelling.\n */\nexport type ValidityWindowVerdict = Readonly<{\n /**\n * The fence this verdict obliges the write to carry. Non-empty when the\n * verdict consulted the row's STORED `valid_from` — either by comparing a\n * stated bound against it, or by inverting a lone `validTo` against it — and\n * empty when the caller named no window at all, when the write applies the\n * bound it stated, or when the effective bound was one this write will stamp\n * rather than one the row holds. In those cases the verdict is independent of\n * what the row carries, and predicating the write on it would refuse a\n * concurrent recreate that changed nothing this decision looked at.\n */\n storedLowerBoundFence: ValidityLowerBoundFence;\n}>;\n\n/**\n * Whether an upsert explicitly treats its stated lower bound as\n * create/resurrection-only input.\n *\n * This predicate owns the policy decision for both write execution and\n * unchanged-upsert coalescing, so those paths cannot drift on whether a live\n * row's stored lower bound is preserved.\n */\nexport function preservesImmutableLowerBound(\n policy: \"preserve\" | \"refuse\" | undefined,\n): policy is \"preserve\" {\n return policy === \"preserve\";\n}\n\n/**\n * Refuses a stated `validFrom` the write would not apply.\n *\n * A live row's lower bound is HISTORY: it records when the row started being\n * true, and an in-place update never moves it. Accepting a bound that names a\n * different instant and then dropping it is the API lying — the caller's stated\n * window silently is not the stored one, while the write still spends a version\n * bump and a history row moving nothing. Restating the bound the row already\n * holds stays legal, so a caller that echoes a row's own window back (a merge\n * commit, a re-delivered upsert) is unaffected.\n *\n * Reached through {@link assertWritableValidityWindow} rather than called\n * directly, so no writer can accept a lower bound without declaring whether it\n * applies one.\n */\nfunction assertStatedLowerBoundIsApplicable(\n subject: string,\n statedValidFrom: string,\n storedValidFrom: string | undefined,\n): void {\n if (statedBoundMatchesStored(statedValidFrom, storedValidFrom)) return;\n const storedDescription =\n storedValidFrom === undefined ?\n \"the row has no lower bound\"\n : `the row's stored lower bound is \"${storedValidFrom}\"`;\n throw new ValidationError(\n `Unappliable validFrom for ${subject}: stated \"${statedValidFrom}\", but ${storedDescription} and the row is live.`,\n {\n issues: [\n {\n path: \"validFrom\",\n code: IMMUTABLE_VALIDITY_LOWER_BOUND_CODE,\n message: `A live row's lower bound is history and an in-place update never rewrites it, so \"${statedValidFrom}\" could not be applied.`,\n },\n ],\n },\n {\n suggestion:\n storedValidFrom === undefined ?\n \"Omit validFrom: this row has no lower bound to restate, and only a resurrection can give it one.\"\n : `Restate the stored bound (\"${storedValidFrom}\") or omit validFrom.`,\n },\n );\n}\n\n/**\n * THE window-ordering guard a valid-time UPDATE goes through, identically for\n * nodes and edges. Refuses a window of negative width — a row that stopped\n * being true before it started — whether the caller states both endpoints or\n * only the new end. Also refuses a stated lower bound the write cannot apply,\n * so no update accepts a `validFrom` it will ignore.\n *\n * Three checks, because the failures deserve different messages: the caller who\n * supplied both endpoints out of order gets told about the pair, the caller who\n * stated a lower bound this write cannot store gets told which bound the row\n * holds, and the caller whose lone `validTo` inverts against the bound the row\n * will actually carry gets told which bound that is.\n *\n * The stated pair is judged FIRST — an inverted pair is wrong whatever the row\n * holds — and the applicability of the lower bound before the effective-bound\n * check, so a caller is told their bound will not be stored rather than being\n * told about a bound they did not name.\n *\n * INSERTS use {@link assertOrderedValidityWindow} alone, and that is now the\n * COMPLETE stated-pair rule rather than a partial one. An insert carrying a lone\n * historical `validTo` means \"born already ended\", and there is no bound to hold\n * the caller to because the write stamps none:\n * {@link resolveStampedValidityLowerBound} stores no lower bound whenever the\n * instant it would stamp cannot precede the stated end (I1b). So no insert path\n * can store a window readable at no coordinate, on any `GraphBackend` caller —\n * the decision lives in the SQL builders, below the store, below interchange and\n * below trusted import (I5). Only an UPDATE has a lower bound the caller can be\n * held to.\n *\n * @param subject - Human-readable identification of the row, for the message\n * (e.g. `Person \"01H...\"`).\n * @param validFrom - The caller's explicit lower bound, if any.\n * @param lowerBound - What the write will do with the row's lower bound; see\n * {@link UpdateValidityLowerBound}.\n * @returns {@link ValidityWindowVerdict} — the `expectedValidFrom` fence this\n * verdict obliges the write to carry, empty when it read no stored bound.\n * @throws ValidationError with issue code {@link INVERTED_VALIDITY_WINDOW_CODE}\n * or {@link IMMUTABLE_VALIDITY_LOWER_BOUND_CODE}\n */\nexport function assertWritableValidityWindow(\n subject: string,\n validFrom: string | undefined,\n lowerBound: UpdateValidityLowerBound,\n validTo: string | undefined,\n): ValidityWindowVerdict {\n assertOrderedValidityWindow(subject, validFrom, validTo);\n\n // The stated bound is COMPARED against the stored one only here — a write\n // that applies what the caller stated has nothing to compare it to.\n const comparedStatedBound =\n validFrom !== undefined && !lowerBound.appliesStatedValidFrom;\n if (comparedStatedBound) {\n assertStatedLowerBoundIsApplicable(\n subject,\n validFrom,\n lowerBound.effectiveValidFrom,\n );\n }\n\n assertEffectiveValidityLowerBound(\n subject,\n validFrom ?? lowerBound.effectiveValidFrom,\n validTo,\n );\n // ...and the inversion check falls back to the row's bound only when the\n // caller named an end without a start. Note this is true whether or not the\n // row HAS a bound: \"no lower bound to invert against\" is itself a reading of\n // the row, and a recreate that gives the row one changes the answer.\n const comparedStoredBoundAgainstEnd =\n validFrom === undefined && validTo !== undefined;\n\n const readEffectiveLowerBound =\n comparedStatedBound || comparedStoredBoundAgainstEnd;\n return {\n storedLowerBoundFence:\n readEffectiveLowerBound && lowerBound.effectiveBoundIsStored ?\n // eslint-disable-next-line unicorn/no-null -- `expectedValidFrom` distinguishes \"assert IS NULL\" (null) from \"assert nothing\" (an absent key); see UpdateNodeParams.\n { expectedValidFrom: lowerBound.effectiveValidFrom ?? null }\n : {},\n };\n}\n\n/**\n * Encodes a Date to an ISO 8601 string for storage.\n */\nexport function encodeDate(date: Date): string {\n return date.toISOString();\n}\n\n/**\n * Decodes an ISO 8601 string to a Date.\n * Validates the string format first.\n *\n * @throws ValidationError if not a valid ISO datetime\n */\nexport function decodeDate(isoString: string): Date {\n validateIsoDate(isoString, \"date\");\n return new Date(isoString);\n}\n\n/**\n * Returns the current timestamp as an ISO string.\n */\nexport function nowIso(): string {\n return new Date().toISOString();\n}\n"]}
|