@nicia-ai/typegraph 0.45.0 → 0.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend/drizzle/indexes.cjs +11 -11
- package/dist/backend/drizzle/indexes.d.cts +1 -1
- package/dist/backend/drizzle/indexes.d.ts +1 -1
- package/dist/backend/drizzle/indexes.js +7 -7
- package/dist/backend/index.cjs +53 -54
- package/dist/backend/index.d.cts +3 -3
- package/dist/backend/index.d.ts +3 -3
- package/dist/backend/index.js +13 -14
- package/dist/backend/postgres/index.cjs +29 -30
- package/dist/backend/postgres/index.d.cts +7 -7
- package/dist/backend/postgres/index.d.ts +7 -7
- package/dist/backend/postgres/index.js +16 -17
- package/dist/backend/postgres/pglite-store.cjs +48 -42
- package/dist/backend/postgres/pglite-store.cjs.map +1 -1
- package/dist/backend/postgres/pglite-store.d.cts +15 -9
- package/dist/backend/postgres/pglite-store.d.ts +15 -9
- package/dist/backend/postgres/pglite-store.js +44 -38
- package/dist/backend/postgres/pglite-store.js.map +1 -1
- package/dist/backend/postgres/pglite.cjs +18 -19
- package/dist/backend/postgres/pglite.d.cts +4 -4
- package/dist/backend/postgres/pglite.d.ts +4 -4
- package/dist/backend/postgres/pglite.js +17 -18
- package/dist/backend/sqlite/index.cjs +26 -27
- package/dist/backend/sqlite/index.d.cts +6 -6
- package/dist/backend/sqlite/index.d.ts +6 -6
- package/dist/backend/sqlite/index.js +14 -15
- package/dist/backend/sqlite/libsql.cjs +21 -22
- package/dist/backend/sqlite/libsql.cjs.map +1 -1
- package/dist/backend/sqlite/libsql.d.cts +15 -8
- package/dist/backend/sqlite/libsql.d.ts +15 -8
- package/dist/backend/sqlite/libsql.js +17 -18
- package/dist/backend/sqlite/libsql.js.map +1 -1
- package/dist/backend/sqlite/local-store.cjs +49 -43
- package/dist/backend/sqlite/local-store.cjs.map +1 -1
- package/dist/backend/sqlite/local-store.d.cts +15 -10
- package/dist/backend/sqlite/local-store.d.ts +15 -10
- package/dist/backend/sqlite/local-store.js +45 -39
- package/dist/backend/sqlite/local-store.js.map +1 -1
- package/dist/backend/sqlite/local.cjs +19 -20
- package/dist/backend/sqlite/local.d.cts +3 -3
- package/dist/backend/sqlite/local.d.ts +3 -3
- package/dist/backend/sqlite/local.js +17 -18
- package/dist/{chunk-S3ZRKJHY.js → chunk-2LG2XB2M.js} +3 -3
- package/dist/{chunk-S3ZRKJHY.js.map → chunk-2LG2XB2M.js.map} +1 -1
- package/dist/{chunk-7PF2L3H6.cjs → chunk-2NA5DYLO.cjs} +4 -4
- package/dist/{chunk-7PF2L3H6.cjs.map → chunk-2NA5DYLO.cjs.map} +1 -1
- package/dist/chunk-2Z2Z3ZD6.js +58 -0
- package/dist/chunk-2Z2Z3ZD6.js.map +1 -0
- package/dist/{chunk-VVQM4CZD.cjs → chunk-3DHZCOT7.cjs} +20 -20
- package/dist/{chunk-VVQM4CZD.cjs.map → chunk-3DHZCOT7.cjs.map} +1 -1
- package/dist/{chunk-JXUTCSXX.cjs → chunk-4UGOKMI5.cjs} +250 -158
- package/dist/chunk-4UGOKMI5.cjs.map +1 -0
- package/dist/{chunk-RFRM3Q5K.js → chunk-5262ZMTR.js} +14 -5
- package/dist/chunk-5262ZMTR.js.map +1 -0
- package/dist/{chunk-QRNYDV5Z.js → chunk-5763DULU.js} +214 -69
- package/dist/chunk-5763DULU.js.map +1 -0
- package/dist/{chunk-BGYJW37L.js → chunk-5UVRGXD3.js} +277 -7
- package/dist/chunk-5UVRGXD3.js.map +1 -0
- package/dist/{chunk-QR24DJDF.cjs → chunk-62ZP23AW.cjs} +15 -15
- package/dist/{chunk-QR24DJDF.cjs.map → chunk-62ZP23AW.cjs.map} +1 -1
- package/dist/{chunk-ZI6VQSZA.cjs → chunk-65E3XWGF.cjs} +724 -577
- package/dist/chunk-65E3XWGF.cjs.map +1 -0
- package/dist/{chunk-S4KQ5LV4.js → chunk-775A4ZMN.js} +40 -6
- package/dist/chunk-775A4ZMN.js.map +1 -0
- package/dist/{chunk-J2ICGNHO.cjs → chunk-7BQXPCGN.cjs} +830 -169
- package/dist/chunk-7BQXPCGN.cjs.map +1 -0
- package/dist/{chunk-FJMPR3OH.cjs → chunk-7EO3TCQB.cjs} +33 -7
- package/dist/chunk-7EO3TCQB.cjs.map +1 -0
- package/dist/{chunk-DXEEHSGN.js → chunk-7VQXLT5O.js} +436 -43
- package/dist/chunk-7VQXLT5O.js.map +1 -0
- package/dist/chunk-A4OC4DVN.cjs +12 -0
- package/dist/{chunk-4SZ6XPTG.cjs.map → chunk-A4OC4DVN.cjs.map} +1 -1
- package/dist/{chunk-TDXIBCGY.cjs → chunk-A7J2YDDI.cjs} +5 -3
- package/dist/chunk-A7J2YDDI.cjs.map +1 -0
- package/dist/{chunk-55ITBQJI.js → chunk-AKLS3BYZ.js} +56 -5
- package/dist/chunk-AKLS3BYZ.js.map +1 -0
- package/dist/{chunk-D7PG5D5K.js → chunk-AMKPPFUO.js} +32 -6
- package/dist/chunk-AMKPPFUO.js.map +1 -0
- package/dist/{chunk-WUFASQUO.js → chunk-ASTI57H3.js} +3 -3
- package/dist/{chunk-WUFASQUO.js.map → chunk-ASTI57H3.js.map} +1 -1
- package/dist/{chunk-4QIKLIGG.js → chunk-BKCUOV7A.js} +15 -5
- package/dist/chunk-BKCUOV7A.js.map +1 -0
- package/dist/{chunk-JQXSZHF2.cjs → chunk-BSFJMMV3.cjs} +134 -7
- package/dist/chunk-BSFJMMV3.cjs.map +1 -0
- package/dist/{chunk-4GUTQPVO.js → chunk-C23WSN5E.js} +185 -24
- package/dist/chunk-C23WSN5E.js.map +1 -0
- package/dist/{chunk-QLUXRWKL.js → chunk-CBTIUB76.js} +3 -3
- package/dist/{chunk-QLUXRWKL.js.map → chunk-CBTIUB76.js.map} +1 -1
- package/dist/chunk-CMWHLBVN.cjs +2082 -0
- package/dist/chunk-CMWHLBVN.cjs.map +1 -0
- package/dist/{chunk-QB6VMOJ2.cjs → chunk-CXBHOOIQ.cjs} +45 -10
- package/dist/chunk-CXBHOOIQ.cjs.map +1 -0
- package/dist/{chunk-FL5UQRES.js → chunk-DCQVHAYQ.js} +5 -5
- package/dist/{chunk-FL5UQRES.js.map → chunk-DCQVHAYQ.js.map} +1 -1
- package/dist/chunk-DTVWX77L.cjs +3432 -0
- package/dist/chunk-DTVWX77L.cjs.map +1 -0
- package/dist/chunk-DYATYRY4.js +47 -0
- package/dist/chunk-DYATYRY4.js.map +1 -0
- package/dist/chunk-E4WNQBEF.cjs +217 -0
- package/dist/chunk-E4WNQBEF.cjs.map +1 -0
- package/dist/{chunk-G6Q5UM7U.js → chunk-FWJKWH6U.js} +5 -3
- package/dist/chunk-FWJKWH6U.js.map +1 -0
- package/dist/chunk-GF3KXUTW.js +3393 -0
- package/dist/chunk-GF3KXUTW.js.map +1 -0
- package/dist/{chunk-VTN3C3CH.js → chunk-HFHTMECK.js} +7 -7
- package/dist/{chunk-VTN3C3CH.js.map → chunk-HFHTMECK.js.map} +1 -1
- package/dist/{chunk-FLEQ2HBK.js → chunk-I5MOXA4H.js} +20 -14
- package/dist/chunk-I5MOXA4H.js.map +1 -0
- package/dist/{chunk-4MB3AQAQ.js → chunk-J3WZ3FA2.js} +77 -3
- package/dist/chunk-J3WZ3FA2.js.map +1 -0
- package/dist/{chunk-UR7W5YQK.js → chunk-JBQQ7THH.js} +5 -3
- package/dist/chunk-JBQQ7THH.js.map +1 -0
- package/dist/{chunk-TKP5T34P.js → chunk-JXA2ZP2Q.js} +6 -16
- package/dist/chunk-JXA2ZP2Q.js.map +1 -0
- package/dist/{chunk-GYA4JRP5.js → chunk-JXXDZTMN.js} +2 -2
- package/dist/chunk-JXXDZTMN.js.map +1 -0
- package/dist/chunk-JZUR7ZWE.js +254 -0
- package/dist/chunk-JZUR7ZWE.js.map +1 -0
- package/dist/{chunk-42PYUGLS.js → chunk-K5ELX2GT.js} +108 -8
- package/dist/chunk-K5ELX2GT.js.map +1 -0
- package/dist/{chunk-Q6EDCUXV.js → chunk-L65OU2U3.js} +556 -204
- package/dist/chunk-L65OU2U3.js.map +1 -0
- package/dist/{chunk-D54WH24V.cjs → chunk-LBJKZXDE.cjs} +115 -13
- package/dist/chunk-LBJKZXDE.cjs.map +1 -0
- package/dist/{chunk-FQ7ZPOTZ.js → chunk-LGHCOKTT.js} +637 -493
- package/dist/chunk-LGHCOKTT.js.map +1 -0
- package/dist/chunk-M3WP7RQZ.js +736 -0
- package/dist/chunk-M3WP7RQZ.js.map +1 -0
- package/dist/{chunk-4MEWSUVM.cjs → chunk-M4BDU5RX.cjs} +12 -12
- package/dist/chunk-M4BDU5RX.cjs.map +1 -0
- package/dist/chunk-MCLYU6BN.cjs +332 -0
- package/dist/chunk-MCLYU6BN.cjs.map +1 -0
- package/dist/{chunk-MIJKEVV7.js → chunk-MSY7FSX4.js} +37 -4
- package/dist/chunk-MSY7FSX4.js.map +1 -0
- package/dist/{chunk-H7VE3FL4.cjs → chunk-MUK4BU7K.cjs} +519 -125
- package/dist/chunk-MUK4BU7K.cjs.map +1 -0
- package/dist/{chunk-D56XG4EI.cjs → chunk-MXJDTH5B.cjs} +56 -45
- package/dist/chunk-MXJDTH5B.cjs.map +1 -0
- package/dist/{chunk-5EKPX6QU.js → chunk-MZLPX2R7.js} +8 -9
- package/dist/chunk-MZLPX2R7.js.map +1 -0
- package/dist/chunk-N66HCGCY.cjs +57 -0
- package/dist/chunk-N66HCGCY.cjs.map +1 -0
- package/dist/{chunk-CPEL3YIR.cjs → chunk-NFCVHQ5P.cjs} +55 -22
- package/dist/chunk-NFCVHQ5P.cjs.map +1 -0
- package/dist/{chunk-C4F7VJ5D.cjs → chunk-NI72YOJB.cjs} +84 -2
- package/dist/chunk-NI72YOJB.cjs.map +1 -0
- package/dist/{chunk-BNS75YNF.cjs → chunk-NK5Q7UNE.cjs} +10 -20
- package/dist/chunk-NK5Q7UNE.cjs.map +1 -0
- package/dist/{chunk-TKORQCG4.js → chunk-NLI2AVEF.js} +386 -862
- package/dist/chunk-NLI2AVEF.js.map +1 -0
- package/dist/{chunk-YMTXMPV6.js → chunk-NMWDXFPH.js} +4 -4
- package/dist/{chunk-YMTXMPV6.js.map → chunk-NMWDXFPH.js.map} +1 -1
- package/dist/{chunk-J7LXTUTP.cjs → chunk-PF6F5NLJ.cjs} +604 -1094
- package/dist/chunk-PF6F5NLJ.cjs.map +1 -0
- package/dist/{chunk-NAAMUYMS.cjs → chunk-PNN7RHVR.cjs} +58 -48
- package/dist/chunk-PNN7RHVR.cjs.map +1 -0
- package/dist/{chunk-YOALGKRE.cjs → chunk-PNTCATDE.cjs} +3528 -1763
- package/dist/chunk-PNTCATDE.cjs.map +1 -0
- package/dist/{chunk-6DCB664D.js → chunk-PQBQG7IM.js} +3 -3
- package/dist/{chunk-6DCB664D.js.map → chunk-PQBQG7IM.js.map} +1 -1
- package/dist/{chunk-UCQ3BFSY.cjs → chunk-PT7RIRIZ.cjs} +684 -332
- package/dist/chunk-PT7RIRIZ.cjs.map +1 -0
- package/dist/{chunk-ACSVZPK7.cjs → chunk-PWLJ77UV.cjs} +5 -3
- package/dist/chunk-PWLJ77UV.cjs.map +1 -0
- package/dist/chunk-Q3G6A4I3.cjs +755 -0
- package/dist/chunk-Q3G6A4I3.cjs.map +1 -0
- package/dist/{chunk-Z3WO6MND.cjs → chunk-RMQLSPA2.cjs} +297 -27
- package/dist/chunk-RMQLSPA2.cjs.map +1 -0
- package/dist/{chunk-SJDMVITW.cjs → chunk-RT5ZY4XA.cjs} +6 -5
- package/dist/chunk-RT5ZY4XA.cjs.map +1 -0
- package/dist/{chunk-ID4WXTKL.js → chunk-S6PW7VV5.js} +2382 -617
- package/dist/chunk-S6PW7VV5.js.map +1 -0
- package/dist/{chunk-4GUBTVPV.js → chunk-T4XNKYXB.js} +780 -126
- package/dist/chunk-T4XNKYXB.js.map +1 -0
- package/dist/{chunk-YDWLUT5V.cjs → chunk-TQR4KBY2.cjs} +323 -178
- package/dist/chunk-TQR4KBY2.cjs.map +1 -0
- package/dist/{chunk-OOA273IJ.cjs → chunk-U5SON7R2.cjs} +27 -27
- package/dist/{chunk-OOA273IJ.cjs.map → chunk-U5SON7R2.cjs.map} +1 -1
- package/dist/{chunk-IX64T5OL.cjs → chunk-U63GDNRC.cjs} +32 -26
- package/dist/chunk-U63GDNRC.cjs.map +1 -0
- package/dist/{chunk-WDBXJOU5.cjs → chunk-UDISNXDL.cjs} +56 -47
- package/dist/chunk-UDISNXDL.cjs.map +1 -0
- package/dist/{chunk-EFEOOCJ3.js → chunk-UV3SN74I.js} +5 -5
- package/dist/{chunk-EFEOOCJ3.js.map → chunk-UV3SN74I.js.map} +1 -1
- package/dist/{chunk-U5W4LKZV.cjs → chunk-VCRRKBLN.cjs} +9 -9
- package/dist/{chunk-U5W4LKZV.cjs.map → chunk-VCRRKBLN.cjs.map} +1 -1
- package/dist/{chunk-ISLPNELU.js → chunk-VGTJPTIC.js} +16 -5
- package/dist/chunk-VGTJPTIC.js.map +1 -0
- package/dist/chunk-VLT6PEWR.cjs +60 -0
- package/dist/chunk-VLT6PEWR.cjs.map +1 -0
- package/dist/chunk-VQH4VAWD.cjs +12 -0
- package/dist/{chunk-B3F2D3SP.cjs.map → chunk-VQH4VAWD.cjs.map} +1 -1
- package/dist/{chunk-SNXVDZAJ.js → chunk-W6OG3HDG.js} +5 -4
- package/dist/chunk-W6OG3HDG.js.map +1 -0
- package/dist/chunk-WDGA7IYB.js +207 -0
- package/dist/chunk-WDGA7IYB.js.map +1 -0
- package/dist/chunk-WQZLUGVR.js +2057 -0
- package/dist/chunk-WQZLUGVR.js.map +1 -0
- package/dist/{chunk-EKFUG4G7.cjs → chunk-XBYVV3MP.cjs} +60 -5
- package/dist/chunk-XBYVV3MP.cjs.map +1 -0
- package/dist/{chunk-5GAONX5G.cjs → chunk-XCAGZAKP.cjs} +7 -7
- package/dist/{chunk-5GAONX5G.cjs.map → chunk-XCAGZAKP.cjs.map} +1 -1
- package/dist/chunk-XL7SLCXS.js +309 -0
- package/dist/chunk-XL7SLCXS.js.map +1 -0
- package/dist/{chunk-SVJ5GEXW.cjs → chunk-XUO3KQFO.cjs} +2 -2
- package/dist/chunk-XUO3KQFO.cjs.map +1 -0
- package/dist/{chunk-F4NHQHEZ.cjs → chunk-YAAM3M2S.cjs} +10 -10
- package/dist/{chunk-F4NHQHEZ.cjs.map → chunk-YAAM3M2S.cjs.map} +1 -1
- package/dist/{chunk-RUQS6M7G.js → chunk-YBFHZUXX.js} +6 -6
- package/dist/chunk-YBFHZUXX.js.map +1 -0
- package/dist/{chunk-KMXWZLQX.cjs → chunk-YBQOHKHN.cjs} +99 -100
- package/dist/chunk-YBQOHKHN.cjs.map +1 -0
- package/dist/{chunk-PVUI6KGR.js → chunk-YDFDBE6D.js} +93 -6
- package/dist/chunk-YDFDBE6D.js.map +1 -0
- package/dist/{chunk-P7GTYOLB.cjs → chunk-YVWGEQJE.cjs} +297 -135
- package/dist/chunk-YVWGEQJE.cjs.map +1 -0
- package/dist/{chunk-QT6V3YRH.js → chunk-YWGVVALJ.js} +3 -3
- package/dist/{chunk-QT6V3YRH.js.map → chunk-YWGVVALJ.js.map} +1 -1
- package/dist/core/index.cjs +40 -40
- package/dist/core/index.d.cts +4 -5
- package/dist/core/index.d.ts +4 -5
- package/dist/core/index.js +8 -8
- package/dist/{ddl-BG4J805L.d.cts → ddl-BJcOp4WZ.d.cts} +3 -3
- package/dist/{ddl-CgiS9QLj.d.ts → ddl-BXfGHbPN.d.ts} +3 -3
- package/dist/graph-extension/index.cjs +26 -22
- package/dist/graph-extension/index.d.cts +16 -6
- package/dist/graph-extension/index.d.ts +16 -6
- package/dist/graph-extension/index.js +7 -7
- package/dist/graph-merge/index.cjs +2459 -654
- package/dist/graph-merge/index.cjs.map +1 -1
- package/dist/graph-merge/index.d.cts +170 -33
- package/dist/graph-merge/index.d.ts +170 -33
- package/dist/graph-merge/index.js +2269 -467
- package/dist/graph-merge/index.js.map +1 -1
- package/dist/{index-C3y_98iN.d.cts → index-BSHvKfok.d.ts} +221 -10
- package/dist/{index-BBxZCUdu.d.ts → index-DxaYCv8Y.d.cts} +221 -10
- package/dist/index.cjs +330 -268
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +590 -576
- package/dist/index.d.ts +590 -576
- package/dist/index.js +73 -49
- package/dist/index.js.map +1 -1
- package/dist/indexes/index.cjs +17 -17
- package/dist/indexes/index.d.cts +4 -4
- package/dist/indexes/index.d.ts +4 -4
- package/dist/indexes/index.js +10 -10
- package/dist/interchange/index.cjs +165 -51
- package/dist/interchange/index.cjs.map +1 -1
- package/dist/interchange/index.d.cts +261 -16
- package/dist/interchange/index.d.ts +261 -16
- package/dist/interchange/index.js +128 -25
- package/dist/interchange/index.js.map +1 -1
- package/dist/{manager-YoLEfcc1.d.ts → manager-CfZAimti.d.ts} +36 -5
- package/dist/{manager-KS7CGQQT.d.cts → manager-HGk6E_HW.d.cts} +36 -5
- package/dist/{materialize-indexes-Bhhyb1B3.d.ts → materialize-indexes-6A7dWGtb.d.cts} +10 -2
- package/dist/{materialize-indexes-OHRWypW6.d.cts → materialize-indexes-BMJP6Tms.d.ts} +10 -2
- package/dist/{postgres-DpkW3R5Y.d.cts → postgres-DFYWENdR.d.cts} +1961 -439
- package/dist/{postgres-DwqyIniy.d.ts → postgres-e7a9TdvR.d.ts} +1961 -439
- package/dist/profiler/index.cjs +15 -15
- package/dist/profiler/index.d.cts +9 -10
- package/dist/profiler/index.d.ts +9 -10
- package/dist/profiler/index.js +4 -4
- package/dist/provenance/index.cjs +46 -48
- package/dist/provenance/index.cjs.map +1 -1
- package/dist/provenance/index.d.cts +7 -7
- package/dist/provenance/index.d.ts +7 -7
- package/dist/provenance/index.js +12 -14
- package/dist/provenance/index.js.map +1 -1
- package/dist/schema/index.cjs +50 -48
- package/dist/schema/index.d.cts +9 -16
- package/dist/schema/index.d.ts +9 -16
- package/dist/schema/index.js +23 -21
- package/dist/{searchable-DLWEfC4a.d.ts → searchable-9OQoPuRs.d.ts} +1 -2
- package/dist/{searchable-CIZL8Hyw.d.cts → searchable-CIje5XYD.d.cts} +1 -2
- package/dist/{sqlite-DY0WODUK.d.cts → sqlite-DerU5tc7.d.ts} +2029 -347
- package/dist/{sqlite-L-VIRzqA.d.ts → sqlite-DitnISa9.d.cts} +2029 -347
- package/dist/{store-view-A85FFRnk.d.cts → store-B0h6x7wo.d.cts} +1289 -767
- package/dist/{store-view-66p92IPi.d.ts → store-ClQ-MZQS.d.ts} +1289 -767
- package/dist/{temporal-BNbjXqer.d.cts → temporal-Bn-Fi8FJ.d.cts} +1 -1
- package/dist/{temporal-Bob6Nfbp.d.ts → temporal-BtM4UALv.d.ts} +1 -1
- package/dist/{types-DeORZhxn.d.cts → types-CoIx8HX5.d.cts} +1 -1
- package/dist/{types-Ch9a0Dx6.d.ts → types-HkveCCx0.d.cts} +726 -5
- package/dist/{types-Ch9a0Dx6.d.cts → types-HkveCCx0.d.ts} +726 -5
- package/dist/{types-ClCzT-dT.d.ts → types-KFfk-8Ah.d.ts} +1 -1
- package/dist/{where-DPw52ksb.d.cts → where--fk7_0kv.d.cts} +1 -1
- package/dist/{where-kWUZPvEA.d.ts → where-ws5hf-Ov.d.ts} +1 -1
- package/package.json +3 -3
- package/dist/chunk-2L54C2C2.js +0 -144
- package/dist/chunk-2L54C2C2.js.map +0 -1
- package/dist/chunk-35S6P5ZU.js +0 -27
- package/dist/chunk-35S6P5ZU.js.map +0 -1
- package/dist/chunk-42PYUGLS.js.map +0 -1
- package/dist/chunk-4GUBTVPV.js.map +0 -1
- package/dist/chunk-4GUTQPVO.js.map +0 -1
- package/dist/chunk-4MB3AQAQ.js.map +0 -1
- package/dist/chunk-4MEWSUVM.cjs.map +0 -1
- package/dist/chunk-4QIKLIGG.js.map +0 -1
- package/dist/chunk-4SZ6XPTG.cjs +0 -12
- package/dist/chunk-55ITBQJI.js.map +0 -1
- package/dist/chunk-5EKPX6QU.js.map +0 -1
- package/dist/chunk-6RYPD3NJ.cjs +0 -1282
- package/dist/chunk-6RYPD3NJ.cjs.map +0 -1
- package/dist/chunk-764JHTLT.cjs +0 -15
- package/dist/chunk-764JHTLT.cjs.map +0 -1
- package/dist/chunk-ACSVZPK7.cjs.map +0 -1
- package/dist/chunk-B3F2D3SP.cjs +0 -12
- package/dist/chunk-BGYJW37L.js.map +0 -1
- package/dist/chunk-BNS75YNF.cjs.map +0 -1
- package/dist/chunk-C4F7VJ5D.cjs.map +0 -1
- package/dist/chunk-CBKG6HEP.js +0 -1263
- package/dist/chunk-CBKG6HEP.js.map +0 -1
- package/dist/chunk-CPEL3YIR.cjs.map +0 -1
- package/dist/chunk-D54WH24V.cjs.map +0 -1
- package/dist/chunk-D56XG4EI.cjs.map +0 -1
- package/dist/chunk-D7PG5D5K.js.map +0 -1
- package/dist/chunk-DI74SMXS.cjs +0 -29
- package/dist/chunk-DI74SMXS.cjs.map +0 -1
- package/dist/chunk-DK2S42XB.js +0 -77
- package/dist/chunk-DK2S42XB.js.map +0 -1
- package/dist/chunk-DRACN6XG.js +0 -27
- package/dist/chunk-DRACN6XG.js.map +0 -1
- package/dist/chunk-DXEEHSGN.js.map +0 -1
- package/dist/chunk-EKFUG4G7.cjs.map +0 -1
- package/dist/chunk-EQ7JKLZO.cjs +0 -157
- package/dist/chunk-EQ7JKLZO.cjs.map +0 -1
- package/dist/chunk-FJMPR3OH.cjs.map +0 -1
- package/dist/chunk-FLEQ2HBK.js.map +0 -1
- package/dist/chunk-FQ7ZPOTZ.js.map +0 -1
- package/dist/chunk-G6Q5UM7U.js.map +0 -1
- package/dist/chunk-GYA4JRP5.js.map +0 -1
- package/dist/chunk-H7VE3FL4.cjs.map +0 -1
- package/dist/chunk-ID4WXTKL.js.map +0 -1
- package/dist/chunk-ISLPNELU.js.map +0 -1
- package/dist/chunk-ITE4IXAK.js +0 -25
- package/dist/chunk-ITE4IXAK.js.map +0 -1
- package/dist/chunk-IX64T5OL.cjs.map +0 -1
- package/dist/chunk-J2ICGNHO.cjs.map +0 -1
- package/dist/chunk-J7LXTUTP.cjs.map +0 -1
- package/dist/chunk-JQXSZHF2.cjs.map +0 -1
- package/dist/chunk-JXUTCSXX.cjs.map +0 -1
- package/dist/chunk-KCO5EFJU.js +0 -79
- package/dist/chunk-KCO5EFJU.js.map +0 -1
- package/dist/chunk-KKYELPZ2.cjs +0 -83
- package/dist/chunk-KKYELPZ2.cjs.map +0 -1
- package/dist/chunk-KMXWZLQX.cjs.map +0 -1
- package/dist/chunk-MIJKEVV7.js.map +0 -1
- package/dist/chunk-NAAMUYMS.cjs.map +0 -1
- package/dist/chunk-P7GTYOLB.cjs.map +0 -1
- package/dist/chunk-PVUI6KGR.js.map +0 -1
- package/dist/chunk-Q6EDCUXV.js.map +0 -1
- package/dist/chunk-QB6VMOJ2.cjs.map +0 -1
- package/dist/chunk-QC4MCOFV.cjs +0 -83
- package/dist/chunk-QC4MCOFV.cjs.map +0 -1
- package/dist/chunk-QMQLUA66.js +0 -12
- package/dist/chunk-QMQLUA66.js.map +0 -1
- package/dist/chunk-QRNYDV5Z.js.map +0 -1
- package/dist/chunk-RFRM3Q5K.js.map +0 -1
- package/dist/chunk-RUQS6M7G.js.map +0 -1
- package/dist/chunk-S4KQ5LV4.js.map +0 -1
- package/dist/chunk-SJDMVITW.cjs.map +0 -1
- package/dist/chunk-SNXVDZAJ.js.map +0 -1
- package/dist/chunk-SVJ5GEXW.cjs.map +0 -1
- package/dist/chunk-TDXIBCGY.cjs.map +0 -1
- package/dist/chunk-TKORQCG4.js.map +0 -1
- package/dist/chunk-TKP5T34P.js.map +0 -1
- package/dist/chunk-TRF4EBJT.cjs +0 -30
- package/dist/chunk-TRF4EBJT.cjs.map +0 -1
- package/dist/chunk-UCQ3BFSY.cjs.map +0 -1
- package/dist/chunk-UR7W5YQK.js.map +0 -1
- package/dist/chunk-V34A6R5V.js +0 -134
- package/dist/chunk-V34A6R5V.js.map +0 -1
- package/dist/chunk-WDAVPX46.js +0 -81
- package/dist/chunk-WDAVPX46.js.map +0 -1
- package/dist/chunk-WDBXJOU5.cjs.map +0 -1
- package/dist/chunk-YDWLUT5V.cjs.map +0 -1
- package/dist/chunk-YOALGKRE.cjs.map +0 -1
- package/dist/chunk-Z3WO6MND.cjs.map +0 -1
- package/dist/chunk-ZDH3DX3C.cjs +0 -30
- package/dist/chunk-ZDH3DX3C.cjs.map +0 -1
- package/dist/chunk-ZI6VQSZA.cjs.map +0 -1
- package/dist/chunk-ZTAJL4DT.cjs +0 -85
- package/dist/chunk-ZTAJL4DT.cjs.map +0 -1
- package/dist/define-graph-BGrJahvp.d.ts +0 -173
- package/dist/define-graph-alREY2wj.d.cts +0 -173
|
@@ -0,0 +1,2057 @@
|
|
|
1
|
+
import { createNodeBatchValidationBackend, primeBatchValidationCaches } from './chunk-L65OU2U3.js';
|
|
2
|
+
import { storeBackend, storeRuntime, applyNodeInsertSideEffectsBatch, applyNodeUpdate, checkUniquenessConstraints, applyNodeInsertSideEffects } from './chunk-M3WP7RQZ.js';
|
|
3
|
+
import { runInWriteTransaction, encodeTupleKey, IDENTITY_IMPORT_PROGRESS, IDENTITY_IMPORT_FAILED_ASSERTION } from './chunk-GF3KXUTW.js';
|
|
4
|
+
import { acquireSerializedStreamLease, snapshotExportContention, isLiveNodeRow, rowPropsToObject } from './chunk-AKLS3BYZ.js';
|
|
5
|
+
import { getNodeKinds, getEdgeKinds } from './chunk-775A4ZMN.js';
|
|
6
|
+
import { validateEdgeEndpoints } from './chunk-WDGA7IYB.js';
|
|
7
|
+
import { isCanonicalIsoDate, nowIso, assertWritableValidityWindow, validateOptionalCanonicalIsoDate, assertOrderedValidityWindow } from './chunk-XL7SLCXS.js';
|
|
8
|
+
import { ConfigurationError, ValidationError, ExportStreamCancelledError, NodeNotFoundError, IdentityContradictionError, DatabaseOperationError, UniquenessError, INVERTED_VALIDITY_WINDOW_CODE, IMMUTABLE_VALIDITY_LOWER_BOUND_CODE } from './chunk-J3WZ3FA2.js';
|
|
9
|
+
import { requireDefined, createDataKeyedBag, hasOwnKey } from './chunk-DYATYRY4.js';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
|
|
12
|
+
var FORMAT_VERSION = "2.0";
|
|
13
|
+
var AcceptedFormatVersionSchema = z.enum(["1.0", "2.0"]);
|
|
14
|
+
var ValidityTimestampSchema = z.iso.datetime().refine((value) => isCanonicalIsoDate(value), {
|
|
15
|
+
message: "Expected canonical UTC ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ); convert with new Date(value).toISOString()."
|
|
16
|
+
});
|
|
17
|
+
var InterchangeNodeSchema = z.object({
|
|
18
|
+
kind: z.string().min(1),
|
|
19
|
+
id: z.string().min(1),
|
|
20
|
+
properties: z.record(z.string(), z.unknown()),
|
|
21
|
+
/**
|
|
22
|
+
* `undefined` (key absent): not requested (`includeTemporal: false`) —
|
|
23
|
+
* import defaults it to the import's own creation timestamp. `null`:
|
|
24
|
+
* requested and confirmed the row has no lower bound (e.g. a legacy row
|
|
25
|
+
* predating the "omitted validFrom defaults to creation time" fix) —
|
|
26
|
+
* import preserves that open-left validity instead of re-stamping it.
|
|
27
|
+
*/
|
|
28
|
+
validFrom: ValidityTimestampSchema.nullable().optional(),
|
|
29
|
+
validTo: ValidityTimestampSchema.optional(),
|
|
30
|
+
meta: z.object({
|
|
31
|
+
version: z.number().int().positive().optional(),
|
|
32
|
+
createdAt: z.iso.datetime().optional(),
|
|
33
|
+
updatedAt: z.iso.datetime().optional()
|
|
34
|
+
}).optional()
|
|
35
|
+
});
|
|
36
|
+
var InterchangeEdgeSchema = z.object({
|
|
37
|
+
kind: z.string().min(1),
|
|
38
|
+
id: z.string().min(1),
|
|
39
|
+
from: z.object({
|
|
40
|
+
kind: z.string().min(1),
|
|
41
|
+
id: z.string().min(1)
|
|
42
|
+
}),
|
|
43
|
+
to: z.object({
|
|
44
|
+
kind: z.string().min(1),
|
|
45
|
+
id: z.string().min(1)
|
|
46
|
+
}),
|
|
47
|
+
properties: z.record(z.string(), z.unknown()).default({}),
|
|
48
|
+
/** See {@link InterchangeNodeSchema}'s `validFrom` for the null/undefined contract. */
|
|
49
|
+
validFrom: ValidityTimestampSchema.nullable().optional(),
|
|
50
|
+
validTo: ValidityTimestampSchema.optional(),
|
|
51
|
+
meta: z.object({
|
|
52
|
+
createdAt: z.iso.datetime().optional(),
|
|
53
|
+
updatedAt: z.iso.datetime().optional()
|
|
54
|
+
}).optional()
|
|
55
|
+
});
|
|
56
|
+
var IdentityInterchangeModeSchema = z.enum(["state", "archival"]);
|
|
57
|
+
var InterchangeIdentityAssertionSchema = z.object({
|
|
58
|
+
id: z.string().min(1),
|
|
59
|
+
relation: z.enum(["same", "different"]),
|
|
60
|
+
a: z.object({ kind: z.string().min(1), id: z.string().min(1) }),
|
|
61
|
+
b: z.object({ kind: z.string().min(1), id: z.string().min(1) }),
|
|
62
|
+
validFrom: ValidityTimestampSchema,
|
|
63
|
+
validTo: ValidityTimestampSchema.optional(),
|
|
64
|
+
/**
|
|
65
|
+
* The node whose soft-delete cascade ended this assertion — the ledger's
|
|
66
|
+
* stored cause, carried so an `archival` round-trip preserves WHY each
|
|
67
|
+
* ending happened and not merely that it did. Absent on open rows and on
|
|
68
|
+
* explicit retractions; when present it names one of `a` / `b` and requires
|
|
69
|
+
* `validTo`. Import rejects both violations with an attributed error.
|
|
70
|
+
*/
|
|
71
|
+
endedBy: z.object({ kind: z.string().min(1), id: z.string().min(1) }).optional()
|
|
72
|
+
});
|
|
73
|
+
var InterchangeIdentitySchema = z.object({
|
|
74
|
+
profile: z.literal("typegraph-identity-v1"),
|
|
75
|
+
mode: IdentityInterchangeModeSchema,
|
|
76
|
+
assertions: z.array(InterchangeIdentityAssertionSchema)
|
|
77
|
+
});
|
|
78
|
+
var InterchangeIdentityHeaderSchema = InterchangeIdentitySchema.omit({
|
|
79
|
+
assertions: true
|
|
80
|
+
});
|
|
81
|
+
var ConflictStrategySchema = z.enum(["skip", "update", "error"]);
|
|
82
|
+
var UnknownPropertyStrategySchema = z.enum([
|
|
83
|
+
"error",
|
|
84
|
+
"strip",
|
|
85
|
+
"allow"
|
|
86
|
+
]);
|
|
87
|
+
var ImportOptionsSchema = z.object({
|
|
88
|
+
/** How to handle existing nodes/edges with the same ID */
|
|
89
|
+
onConflict: ConflictStrategySchema,
|
|
90
|
+
/** How to handle properties not in the schema */
|
|
91
|
+
onUnknownProperty: UnknownPropertyStrategySchema.default("error"),
|
|
92
|
+
/** Whether to validate that edge endpoints exist */
|
|
93
|
+
validateReferences: z.boolean().default(true),
|
|
94
|
+
/**
|
|
95
|
+
* Number of items to process in each batch. Each batch pays fixed
|
|
96
|
+
* per-round-trip costs (existence probe, unique check, one multi-row
|
|
97
|
+
* insert), so undersized batches dominate import time on client/server
|
|
98
|
+
* engines: measured on PostgreSQL, 20k nodes + 5k edges import in
|
|
99
|
+
* 1,515ms at 100 vs 781ms at 1,000. Above ~1,000 the multi-row insert
|
|
100
|
+
* itself dominates and larger batches stop paying; SQLite is
|
|
101
|
+
* insensitive to the value (in-process, no round trips). The backend
|
|
102
|
+
* further splits inserts by its bind-parameter budget, so a large
|
|
103
|
+
* batch never overruns driver limits.
|
|
104
|
+
*/
|
|
105
|
+
batchSize: z.number().int().positive().default(1e3),
|
|
106
|
+
/**
|
|
107
|
+
* Refresh planner statistics (ANALYZE) after an import that created or
|
|
108
|
+
* updated rows. Bulk loads otherwise run against stale statistics until
|
|
109
|
+
* the engine catches up on its own. Default: true.
|
|
110
|
+
*/
|
|
111
|
+
refreshStatistics: z.boolean().optional(),
|
|
112
|
+
/**
|
|
113
|
+
* How a streamed import reacts when one chunk reports entity-level errors.
|
|
114
|
+
* `abort` is the safe default: earlier chunks are already committed, so
|
|
115
|
+
* callers must not mistake a partial stream for a successful retryable load.
|
|
116
|
+
* `continue` is reserved for explicit best-effort ingestion.
|
|
117
|
+
*/
|
|
118
|
+
onStreamChunkError: z.enum(["abort", "continue"]).default("abort")
|
|
119
|
+
});
|
|
120
|
+
var GraphDataSourceSchema = z.discriminatedUnion("type", [
|
|
121
|
+
z.object({
|
|
122
|
+
type: z.literal("typegraph-export"),
|
|
123
|
+
graphId: z.string(),
|
|
124
|
+
schemaVersion: z.number().int().positive()
|
|
125
|
+
}),
|
|
126
|
+
z.object({
|
|
127
|
+
type: z.literal("external"),
|
|
128
|
+
description: z.string().optional()
|
|
129
|
+
})
|
|
130
|
+
]);
|
|
131
|
+
var GraphDataSchema = z.object({
|
|
132
|
+
formatVersion: AcceptedFormatVersionSchema,
|
|
133
|
+
exportedAt: z.iso.datetime(),
|
|
134
|
+
source: GraphDataSourceSchema,
|
|
135
|
+
nodes: z.array(InterchangeNodeSchema),
|
|
136
|
+
edges: z.array(InterchangeEdgeSchema),
|
|
137
|
+
identity: InterchangeIdentitySchema.optional()
|
|
138
|
+
});
|
|
139
|
+
var GraphDataHeaderSchema = GraphDataSchema.omit({
|
|
140
|
+
nodes: true,
|
|
141
|
+
edges: true,
|
|
142
|
+
identity: true
|
|
143
|
+
}).extend({
|
|
144
|
+
identity: InterchangeIdentityHeaderSchema.optional()
|
|
145
|
+
});
|
|
146
|
+
var GraphInterchangeChunkSchema = z.discriminatedUnion("type", [
|
|
147
|
+
z.object({ type: z.literal("header"), header: GraphDataHeaderSchema }),
|
|
148
|
+
z.object({ type: z.literal("nodes"), nodes: z.array(InterchangeNodeSchema) }),
|
|
149
|
+
z.object({ type: z.literal("edges"), edges: z.array(InterchangeEdgeSchema) }),
|
|
150
|
+
z.object({
|
|
151
|
+
type: z.literal("identity"),
|
|
152
|
+
assertions: z.array(InterchangeIdentityAssertionSchema)
|
|
153
|
+
})
|
|
154
|
+
]);
|
|
155
|
+
var ImportErrorSchema = z.object({
|
|
156
|
+
entityType: z.enum(["node", "edge", "identity"]),
|
|
157
|
+
kind: z.string(),
|
|
158
|
+
id: z.string(),
|
|
159
|
+
error: z.string()
|
|
160
|
+
});
|
|
161
|
+
var ImportResultSchema = z.object({
|
|
162
|
+
success: z.boolean(),
|
|
163
|
+
nodes: z.object({
|
|
164
|
+
created: z.number().int().nonnegative(),
|
|
165
|
+
updated: z.number().int().nonnegative(),
|
|
166
|
+
skipped: z.number().int().nonnegative()
|
|
167
|
+
}),
|
|
168
|
+
edges: z.object({
|
|
169
|
+
created: z.number().int().nonnegative(),
|
|
170
|
+
updated: z.number().int().nonnegative(),
|
|
171
|
+
skipped: z.number().int().nonnegative()
|
|
172
|
+
}),
|
|
173
|
+
identity: z.object({
|
|
174
|
+
created: z.number().int().nonnegative(),
|
|
175
|
+
skipped: z.number().int().nonnegative()
|
|
176
|
+
}),
|
|
177
|
+
errors: z.array(ImportErrorSchema)
|
|
178
|
+
});
|
|
179
|
+
var ExportOptionsSchema = z.object({
|
|
180
|
+
/** Filter to specific node kinds (undefined = all) */
|
|
181
|
+
nodeKinds: z.array(z.string()).optional(),
|
|
182
|
+
/** Filter to specific edge kinds (undefined = all) */
|
|
183
|
+
edgeKinds: z.array(z.string()).optional(),
|
|
184
|
+
/** Include temporal fields (validFrom, validTo) */
|
|
185
|
+
includeTemporal: z.boolean().default(false),
|
|
186
|
+
/** Include metadata (version, timestamps) */
|
|
187
|
+
includeMeta: z.boolean().default(false),
|
|
188
|
+
/** Include soft-deleted records */
|
|
189
|
+
includeDeleted: z.boolean().default(false),
|
|
190
|
+
/** Operational Identity export mode. Defaults to current state only. */
|
|
191
|
+
identityMode: IdentityInterchangeModeSchema.default("state"),
|
|
192
|
+
/**
|
|
193
|
+
* Cancels the export: whatever it had opened is settled — on a backend with
|
|
194
|
+
* `capabilities.transactions` that means its repeatable-read snapshot is
|
|
195
|
+
* rolled back and the serialized connection's stream lease released; on one
|
|
196
|
+
* without, there is no transaction and no lease, and the remaining reads are
|
|
197
|
+
* simply abandoned. Either way the consumer is rejected with an
|
|
198
|
+
* `ExportStreamCancelledError` (`code:
|
|
199
|
+
* "INTERCHANGE_EXPORT_STREAM_ABORTED"`), whose message states which of the
|
|
200
|
+
* two happened, carrying the signal's own `reason` as `cause`. Aborting
|
|
201
|
+
* before the first read refuses the export outright: nothing is opened and
|
|
202
|
+
* nothing claimed.
|
|
203
|
+
*
|
|
204
|
+
* On `exportGraph` this is a convenience — the call rejects instead of
|
|
205
|
+
* running to completion. On {@link exportGraphStream} it is the ONLY
|
|
206
|
+
* contract-grade way to stop a stream a consumer will not finish pulling:
|
|
207
|
+
* an async generator's `finally` does not run on garbage collection, so a
|
|
208
|
+
* consumer that pulls `next()` and then drops the iterator — the
|
|
209
|
+
* `Promise.race([iterator.next(), timeout])` pattern — otherwise holds the
|
|
210
|
+
* snapshot transaction, and with it the connection's exclusive stream lease,
|
|
211
|
+
* until the process exits. `break` / `throw` out of a `for await` and an
|
|
212
|
+
* explicit `iterator.return()` settle the stream cooperatively and need no
|
|
213
|
+
* signal; a race that abandons the iterator needs one.
|
|
214
|
+
*/
|
|
215
|
+
signal: z.instanceof(AbortSignal).optional()
|
|
216
|
+
});
|
|
217
|
+
var ExportStreamOptionsSchema = ExportOptionsSchema.extend({
|
|
218
|
+
batchSize: z.number().int().positive().default(1e3)
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// src/interchange/stream-source.ts
|
|
222
|
+
var EXPORT_STREAM_BACKENDS = /* @__PURE__ */ new WeakMap();
|
|
223
|
+
function markExportStreamBackend(stream, backend) {
|
|
224
|
+
EXPORT_STREAM_BACKENDS.set(stream, backend);
|
|
225
|
+
return stream;
|
|
226
|
+
}
|
|
227
|
+
function exportStreamBackend(stream) {
|
|
228
|
+
return EXPORT_STREAM_BACKENDS.get(stream);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/interchange/import.ts
|
|
232
|
+
async function importGraph(store, data, rawOptions) {
|
|
233
|
+
const options = ImportOptionsSchema.parse(rawOptions);
|
|
234
|
+
return withImportStreamLease(
|
|
235
|
+
store,
|
|
236
|
+
() => importGraphData(store, data, options)
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
async function withImportStreamLease(store, run) {
|
|
240
|
+
const lease = acquireSerializedStreamLease(
|
|
241
|
+
storeBackend(store),
|
|
242
|
+
"import-stream"
|
|
243
|
+
);
|
|
244
|
+
if (!lease.acquired) {
|
|
245
|
+
throw serializedStreamRefusal({
|
|
246
|
+
graphId: store.graphId,
|
|
247
|
+
requested: "import-stream",
|
|
248
|
+
heldBy: lease.heldBy
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
return await run();
|
|
253
|
+
} finally {
|
|
254
|
+
lease.release();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async function importGraphData(store, data, options) {
|
|
258
|
+
assertIdentityImportSupported(store, data.identity !== void 0);
|
|
259
|
+
validateIdentitySection(data.identity);
|
|
260
|
+
const result = emptyImportResult();
|
|
261
|
+
const errors = [];
|
|
262
|
+
const graph = store.graph;
|
|
263
|
+
const graphId = store.graphId;
|
|
264
|
+
const backend = storeBackend(store);
|
|
265
|
+
const runtime = storeRuntime(store);
|
|
266
|
+
const registry = store.registry;
|
|
267
|
+
const nodeSchemas = buildNodeSchemaMap(graph);
|
|
268
|
+
const edgeSchemas = buildEdgeSchemaMap(graph);
|
|
269
|
+
const importedNodeIds = /* @__PURE__ */ new Set();
|
|
270
|
+
await runInWriteTransaction(
|
|
271
|
+
{
|
|
272
|
+
graphId,
|
|
273
|
+
schemaVersion: store.introspect().schemaVersion,
|
|
274
|
+
historyEnabled: store.historyEnabled,
|
|
275
|
+
revisionTrackingEnabled: store.revisionTrackingEnabled,
|
|
276
|
+
revisionSchema: store.revisionSchema
|
|
277
|
+
},
|
|
278
|
+
backend,
|
|
279
|
+
async (target, lock) => {
|
|
280
|
+
await runtime.lockIdentityImportTarget(target);
|
|
281
|
+
await processNodes(
|
|
282
|
+
target,
|
|
283
|
+
graphId,
|
|
284
|
+
registry,
|
|
285
|
+
data.nodes,
|
|
286
|
+
nodeSchemas,
|
|
287
|
+
options,
|
|
288
|
+
result,
|
|
289
|
+
errors,
|
|
290
|
+
importedNodeIds,
|
|
291
|
+
lock
|
|
292
|
+
);
|
|
293
|
+
await runtime.foldImportedIdentityNodes(
|
|
294
|
+
target,
|
|
295
|
+
data.nodes.filter(
|
|
296
|
+
(node) => importedNodeIds.has(makeNodeKey(node.kind, node.id))
|
|
297
|
+
).map((node) => ({ kind: node.kind, id: node.id }))
|
|
298
|
+
);
|
|
299
|
+
await processEdges(
|
|
300
|
+
target,
|
|
301
|
+
graphId,
|
|
302
|
+
registry,
|
|
303
|
+
data.edges,
|
|
304
|
+
edgeSchemas,
|
|
305
|
+
nodeSchemas,
|
|
306
|
+
options,
|
|
307
|
+
result,
|
|
308
|
+
errors,
|
|
309
|
+
importedNodeIds
|
|
310
|
+
);
|
|
311
|
+
if (data.identity !== void 0) {
|
|
312
|
+
await importIdentitySection(
|
|
313
|
+
runtime,
|
|
314
|
+
target,
|
|
315
|
+
graphId,
|
|
316
|
+
data.identity,
|
|
317
|
+
result,
|
|
318
|
+
errors
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
await refreshStatisticsAfterImport(store, options, result, "importGraph");
|
|
324
|
+
return {
|
|
325
|
+
...result,
|
|
326
|
+
success: errors.length === 0,
|
|
327
|
+
errors
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
async function importGraphStream(store, chunks, rawOptions) {
|
|
331
|
+
const options = ImportOptionsSchema.parse(rawOptions);
|
|
332
|
+
const sourceBackend = exportStreamBackend(chunks);
|
|
333
|
+
const targetBackend = storeBackend(store);
|
|
334
|
+
const contention = sourceBackend === void 0 ? void 0 : snapshotExportContention(sourceBackend, targetBackend);
|
|
335
|
+
if (contention !== void 0) {
|
|
336
|
+
throw serializedStreamRefusal({
|
|
337
|
+
graphId: store.graphId,
|
|
338
|
+
requested: "import-stream",
|
|
339
|
+
heldBy: "export-snapshot",
|
|
340
|
+
detector: contention
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
const result = emptyImportResult();
|
|
344
|
+
let header;
|
|
345
|
+
let receivedEdges = false;
|
|
346
|
+
let receivedIdentity = false;
|
|
347
|
+
let releaseImportLease;
|
|
348
|
+
try {
|
|
349
|
+
for await (const chunk of chunks) {
|
|
350
|
+
if (releaseImportLease === void 0) {
|
|
351
|
+
const lease = acquireSerializedStreamLease(
|
|
352
|
+
targetBackend,
|
|
353
|
+
"import-stream"
|
|
354
|
+
);
|
|
355
|
+
if (!lease.acquired) {
|
|
356
|
+
throw serializedStreamRefusal({
|
|
357
|
+
graphId: store.graphId,
|
|
358
|
+
requested: "import-stream",
|
|
359
|
+
heldBy: lease.heldBy
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
releaseImportLease = lease.release;
|
|
363
|
+
}
|
|
364
|
+
switch (chunk.type) {
|
|
365
|
+
case "header": {
|
|
366
|
+
if (header !== void 0) {
|
|
367
|
+
throw new Error(
|
|
368
|
+
"Graph interchange stream emitted more than one header."
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
validateStreamHeader(chunk.header);
|
|
372
|
+
assertIdentityImportSupported(
|
|
373
|
+
store,
|
|
374
|
+
chunk.header.identity !== void 0
|
|
375
|
+
);
|
|
376
|
+
header = chunk.header;
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
case "nodes": {
|
|
380
|
+
if (header === void 0) {
|
|
381
|
+
throw new Error(
|
|
382
|
+
"Graph interchange stream must start with a header."
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
if (receivedEdges || receivedIdentity) {
|
|
386
|
+
throw new Error(
|
|
387
|
+
`Graph interchange stream cannot emit nodes after ${receivedEdges ? "edges" : "identity assertions"}.`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
if (chunk.nodes.length === 0) break;
|
|
391
|
+
mergeImportResult(
|
|
392
|
+
result,
|
|
393
|
+
await importGraphData(
|
|
394
|
+
store,
|
|
395
|
+
graphDataForChunk(header, chunk.nodes, [], []),
|
|
396
|
+
{
|
|
397
|
+
...options,
|
|
398
|
+
refreshStatistics: false
|
|
399
|
+
}
|
|
400
|
+
)
|
|
401
|
+
);
|
|
402
|
+
throwIfStreamChunkFailed(result, options);
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
case "edges": {
|
|
406
|
+
if (header === void 0) {
|
|
407
|
+
throw new Error(
|
|
408
|
+
"Graph interchange stream must start with a header."
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
if (receivedIdentity) {
|
|
412
|
+
throw new Error(
|
|
413
|
+
"Graph interchange stream cannot emit edges after identity assertions."
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
receivedEdges = true;
|
|
417
|
+
if (chunk.edges.length === 0) break;
|
|
418
|
+
mergeImportResult(
|
|
419
|
+
result,
|
|
420
|
+
await importGraphData(
|
|
421
|
+
store,
|
|
422
|
+
graphDataForChunk(header, [], chunk.edges, []),
|
|
423
|
+
{
|
|
424
|
+
...options,
|
|
425
|
+
refreshStatistics: false
|
|
426
|
+
}
|
|
427
|
+
)
|
|
428
|
+
);
|
|
429
|
+
throwIfStreamChunkFailed(result, options);
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
case "identity": {
|
|
433
|
+
if (header === void 0) {
|
|
434
|
+
throw new Error(
|
|
435
|
+
"Graph interchange stream must start with a header."
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
if (header.identity === void 0) {
|
|
439
|
+
throw new Error(
|
|
440
|
+
"Graph interchange stream emitted identity rows without an identity header."
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
receivedIdentity = true;
|
|
444
|
+
if (chunk.assertions.length === 0) break;
|
|
445
|
+
mergeImportResult(
|
|
446
|
+
result,
|
|
447
|
+
await importGraphData(
|
|
448
|
+
store,
|
|
449
|
+
graphDataForChunk(header, [], [], chunk.assertions),
|
|
450
|
+
{ ...options, refreshStatistics: false }
|
|
451
|
+
)
|
|
452
|
+
);
|
|
453
|
+
throwIfStreamChunkFailed(result, options);
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (header === void 0) {
|
|
459
|
+
throw new Error(
|
|
460
|
+
"Graph interchange stream ended before emitting a header."
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
await refreshStatisticsAfterImport(
|
|
464
|
+
store,
|
|
465
|
+
options,
|
|
466
|
+
result,
|
|
467
|
+
"importGraphStream"
|
|
468
|
+
);
|
|
469
|
+
} finally {
|
|
470
|
+
releaseImportLease?.();
|
|
471
|
+
}
|
|
472
|
+
return { ...result, success: result.errors.length === 0 };
|
|
473
|
+
}
|
|
474
|
+
function serializedStreamRefusal(input) {
|
|
475
|
+
const { graphId, requested, heldBy, detector } = input;
|
|
476
|
+
const code = heldBy === "import-stream" ? SERIALIZED_IMPORT_IN_PROGRESS_CODE : detector === "same-sqlite-backend" ? "INTERCHANGE_SAME_SQLITE_BACKEND_SNAPSHOT" : "INTERCHANGE_SHARED_SERIALIZED_BACKEND_SNAPSHOT";
|
|
477
|
+
const { message, suggestion } = serializedStreamRefusalText(
|
|
478
|
+
requested,
|
|
479
|
+
heldBy
|
|
480
|
+
);
|
|
481
|
+
return new ConfigurationError(
|
|
482
|
+
message,
|
|
483
|
+
{ code, graphId, requested, heldBy },
|
|
484
|
+
{ suggestion }
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
var SERIALIZED_IMPORT_IN_PROGRESS_CODE = "INTERCHANGE_SERIALIZED_IMPORT_IN_PROGRESS";
|
|
488
|
+
function serializedStreamRefusalText(requested, heldBy) {
|
|
489
|
+
if (heldBy === "export-snapshot") {
|
|
490
|
+
return requested === "import-stream" ? {
|
|
491
|
+
message: "A snapshot export cannot be streamed into a target that writes through the same serialized database connection: the export's read transaction holds that connection open for the whole stream, so this import can never take the writer slot it needs.",
|
|
492
|
+
suggestion: "Export to a file first, or import the stream into an independent backend."
|
|
493
|
+
} : {
|
|
494
|
+
message: "A snapshot export cannot open on a serialized database connection while another snapshot export is streaming from it: the second export's read transaction would have to nest inside the first one on the single connection they share.",
|
|
495
|
+
suggestion: "Await the export already in flight before starting the next one, or export from an independent backend."
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
return requested === "export-snapshot" ? {
|
|
499
|
+
message: "A snapshot export cannot open on a serialized database connection while a streaming import is writing through it: the export's read transaction would hold the one connection the import's next chunk has to write on.",
|
|
500
|
+
suggestion: "Await the import before exporting, or export from an independent backend."
|
|
501
|
+
} : {
|
|
502
|
+
message: "A streaming import cannot open on a serialized database connection while another streaming import is writing through it: the two imports commit their chunks on the single connection they share, so the second one's chunk transaction would have to nest inside the first one's.",
|
|
503
|
+
suggestion: "Await the import already in flight before starting the next one, or import into an independent backend."
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
function throwIfStreamChunkFailed(result, options) {
|
|
507
|
+
if (options.onStreamChunkError === "continue" || result.errors.length === 0) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
throw new Error(
|
|
511
|
+
'Graph interchange stream aborted after a chunk reported import errors. Earlier chunks remain committed; use onStreamChunkError: "continue" for best-effort ingestion.'
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
function assertIdentityImportSupported(store, hasIdentitySection) {
|
|
515
|
+
if (hasIdentitySection && store.graph.identity === void 0) {
|
|
516
|
+
throw new ConfigurationError(
|
|
517
|
+
"Cannot import identity assertions into an identity-disabled graph.",
|
|
518
|
+
{ code: "IDENTITY_IMPORT_REQUIRES_PROFILE", graphId: store.graphId }
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function validateIdentitySection(identity) {
|
|
523
|
+
if (identity === void 0) return;
|
|
524
|
+
const parsed = InterchangeIdentitySchema.safeParse(identity);
|
|
525
|
+
if (parsed.success) return;
|
|
526
|
+
throw new ValidationError(
|
|
527
|
+
`Invalid identity interchange section: ${parsed.error.message}`,
|
|
528
|
+
{
|
|
529
|
+
issues: parsed.error.issues.map((issue) => ({
|
|
530
|
+
path: issue.path.join("."),
|
|
531
|
+
message: issue.message,
|
|
532
|
+
code: issue.code
|
|
533
|
+
}))
|
|
534
|
+
},
|
|
535
|
+
{ cause: parsed.error }
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
var IDENTITY_IMPORT_ERROR_ENTITY_TYPE = "identity";
|
|
539
|
+
var IDENTITY_IMPORT_ERROR_PATH = "identity.assertions";
|
|
540
|
+
var UNATTRIBUTED_IDENTITY_ERROR_KIND = "assertion";
|
|
541
|
+
var IDENTITY_IMPORT_ID_CONFLICT_CODE = "IDENTITY_IMPORT_ID_CONFLICT";
|
|
542
|
+
function identityImportError(assertion, graphId, message) {
|
|
543
|
+
const path = assertion === void 0 ? IDENTITY_IMPORT_ERROR_PATH : `${IDENTITY_IMPORT_ERROR_PATH}[${assertion.id}]`;
|
|
544
|
+
return {
|
|
545
|
+
entityType: IDENTITY_IMPORT_ERROR_ENTITY_TYPE,
|
|
546
|
+
kind: assertion?.relation ?? UNATTRIBUTED_IDENTITY_ERROR_KIND,
|
|
547
|
+
id: assertion?.id ?? graphId,
|
|
548
|
+
error: `${path}: ${message}`
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function isIdConflictError(error) {
|
|
552
|
+
return error instanceof ConfigurationError && error.details["code"] === IDENTITY_IMPORT_ID_CONFLICT_CODE;
|
|
553
|
+
}
|
|
554
|
+
function touchesRef(assertion, ref) {
|
|
555
|
+
return assertion.a.kind === ref.kind && assertion.a.id === ref.id || assertion.b.kind === ref.kind && assertion.b.id === ref.id;
|
|
556
|
+
}
|
|
557
|
+
function asIdentityImportError(assertions, graphId, error) {
|
|
558
|
+
const tagged = taggedAssertion(assertions, error);
|
|
559
|
+
if (error instanceof NodeNotFoundError) {
|
|
560
|
+
const ref = error.details;
|
|
561
|
+
const assertion = tagged ?? assertions.find((candidate) => touchesRef(candidate, ref));
|
|
562
|
+
return identityImportError(assertion, graphId, error.message);
|
|
563
|
+
}
|
|
564
|
+
if (error instanceof IdentityContradictionError) {
|
|
565
|
+
const { a, b } = error.details;
|
|
566
|
+
const assertion = tagged ?? assertions.find(
|
|
567
|
+
(candidate) => touchesRef(candidate, a) && touchesRef(candidate, b)
|
|
568
|
+
);
|
|
569
|
+
return identityImportError(assertion, graphId, error.message);
|
|
570
|
+
}
|
|
571
|
+
if (isIdConflictError(error)) {
|
|
572
|
+
const assertion = assertions.find(
|
|
573
|
+
(candidate) => candidate.id === error.details["assertionId"]
|
|
574
|
+
);
|
|
575
|
+
return identityImportError(assertion, graphId, error.message);
|
|
576
|
+
}
|
|
577
|
+
if (isIdentityAssertionValidationError(error)) {
|
|
578
|
+
const issue = error.details.issues[0];
|
|
579
|
+
const message = issue === void 0 ? error.message : `${error.message} (${issue.message})`;
|
|
580
|
+
return identityImportError(
|
|
581
|
+
assertions.find((candidate) => candidate.id === issue?.assertionId),
|
|
582
|
+
graphId,
|
|
583
|
+
message
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
return void 0;
|
|
587
|
+
}
|
|
588
|
+
function taggedAssertion(assertions, error) {
|
|
589
|
+
if (typeof error !== "object" || error === null) return void 0;
|
|
590
|
+
const id = error[IDENTITY_IMPORT_FAILED_ASSERTION];
|
|
591
|
+
if (typeof id !== "string") return void 0;
|
|
592
|
+
return assertions.find((candidate) => candidate.id === id);
|
|
593
|
+
}
|
|
594
|
+
function isIdentityAssertionValidationError(error) {
|
|
595
|
+
return error instanceof ValidationError && error.details.issues.length > 0 && error.details.issues.every(
|
|
596
|
+
(issue) => issue.path === "identity.assertions" && issue.assertionId !== void 0
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
async function importIdentitySection(runtime, target, graphId, identity, result, errors) {
|
|
600
|
+
try {
|
|
601
|
+
const summary = await runtime.importIdentityAssertionsAtTarget(
|
|
602
|
+
target,
|
|
603
|
+
identity.assertions,
|
|
604
|
+
identity.mode
|
|
605
|
+
);
|
|
606
|
+
result.identity.created += summary.created;
|
|
607
|
+
result.identity.skipped += summary.skipped;
|
|
608
|
+
} catch (error) {
|
|
609
|
+
const entry = asIdentityImportError(identity.assertions, graphId, error);
|
|
610
|
+
if (entry === void 0) throw error;
|
|
611
|
+
const progress = identityImportProgress(error);
|
|
612
|
+
result.identity.created += progress.created;
|
|
613
|
+
result.identity.skipped += progress.skipped;
|
|
614
|
+
errors.push(entry);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function identityImportProgress(error) {
|
|
618
|
+
if (typeof error !== "object" || error === null) {
|
|
619
|
+
return { created: 0, skipped: 0 };
|
|
620
|
+
}
|
|
621
|
+
const progress = error[IDENTITY_IMPORT_PROGRESS];
|
|
622
|
+
if (typeof progress !== "object" || progress === null || !("created" in progress) || typeof progress.created !== "number" || !("skipped" in progress) || typeof progress.skipped !== "number") {
|
|
623
|
+
return { created: 0, skipped: 0 };
|
|
624
|
+
}
|
|
625
|
+
return { created: progress.created, skipped: progress.skipped };
|
|
626
|
+
}
|
|
627
|
+
function validateStreamHeader(header) {
|
|
628
|
+
const parsed = GraphDataHeaderSchema.safeParse(header);
|
|
629
|
+
if (parsed.success) return;
|
|
630
|
+
throw new ValidationError(
|
|
631
|
+
`Invalid graph interchange stream header: ${parsed.error.message}`,
|
|
632
|
+
{
|
|
633
|
+
issues: parsed.error.issues.map((issue) => ({
|
|
634
|
+
path: issue.path.join("."),
|
|
635
|
+
message: issue.message,
|
|
636
|
+
code: issue.code
|
|
637
|
+
}))
|
|
638
|
+
},
|
|
639
|
+
{ cause: parsed.error }
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
function emptyImportResult() {
|
|
643
|
+
return {
|
|
644
|
+
success: true,
|
|
645
|
+
nodes: { created: 0, updated: 0, skipped: 0 },
|
|
646
|
+
edges: { created: 0, updated: 0, skipped: 0 },
|
|
647
|
+
identity: { created: 0, skipped: 0 },
|
|
648
|
+
errors: []
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function graphDataForChunk(header, nodes, edges, assertions) {
|
|
652
|
+
const { identity, ...headerWithoutIdentity } = header;
|
|
653
|
+
return {
|
|
654
|
+
...headerWithoutIdentity,
|
|
655
|
+
nodes,
|
|
656
|
+
edges,
|
|
657
|
+
...identity === void 0 ? {} : {
|
|
658
|
+
identity: { ...identity, assertions: [...assertions] }
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function mergeImportResult(target, source) {
|
|
663
|
+
target.nodes.created += source.nodes.created;
|
|
664
|
+
target.nodes.updated += source.nodes.updated;
|
|
665
|
+
target.nodes.skipped += source.nodes.skipped;
|
|
666
|
+
target.edges.created += source.edges.created;
|
|
667
|
+
target.edges.updated += source.edges.updated;
|
|
668
|
+
target.edges.skipped += source.edges.skipped;
|
|
669
|
+
target.identity.created += source.identity.created;
|
|
670
|
+
target.identity.skipped += source.identity.skipped;
|
|
671
|
+
target.errors.push(...source.errors);
|
|
672
|
+
}
|
|
673
|
+
async function refreshStatisticsAfterImport(store, options, result, surface) {
|
|
674
|
+
const mutationCount = result.nodes.created + result.nodes.updated + result.edges.created + result.edges.updated + result.identity.created;
|
|
675
|
+
if ((options.refreshStatistics ?? true) && mutationCount > 0) {
|
|
676
|
+
try {
|
|
677
|
+
await store.refreshStatistics();
|
|
678
|
+
} catch (error) {
|
|
679
|
+
if (typeof console !== "undefined" && typeof console.warn === "function") {
|
|
680
|
+
console.warn(
|
|
681
|
+
`[typegraph] ${surface} committed its rows but the follow-up statistics refresh failed; run store.refreshStatistics() to give the planner fresh statistics.`,
|
|
682
|
+
error
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
function buildNodeSchemaMap(graph) {
|
|
689
|
+
const map = /* @__PURE__ */ new Map();
|
|
690
|
+
for (const kindName of getNodeKinds(graph)) {
|
|
691
|
+
const registration = graph.nodes[kindName];
|
|
692
|
+
map.set(kindName, {
|
|
693
|
+
registration,
|
|
694
|
+
schema: registration.type.schema
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
return map;
|
|
698
|
+
}
|
|
699
|
+
function buildEdgeSchemaMap(graph) {
|
|
700
|
+
const map = /* @__PURE__ */ new Map();
|
|
701
|
+
for (const kindName of getEdgeKinds(graph)) {
|
|
702
|
+
const registration = graph.edges[kindName];
|
|
703
|
+
map.set(kindName, {
|
|
704
|
+
registration,
|
|
705
|
+
schema: registration.type.schema
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
return map;
|
|
709
|
+
}
|
|
710
|
+
async function processNodes(backend, graphId, registry, nodes, schemas, options, result, errors, importedNodeIds, lock) {
|
|
711
|
+
const batchSize = options.batchSize;
|
|
712
|
+
for (let index = 0; index < nodes.length; index += batchSize) {
|
|
713
|
+
const batch = nodes.slice(index, index + batchSize);
|
|
714
|
+
await processNodeSlice(
|
|
715
|
+
backend,
|
|
716
|
+
graphId,
|
|
717
|
+
registry,
|
|
718
|
+
batch,
|
|
719
|
+
schemas,
|
|
720
|
+
options,
|
|
721
|
+
result,
|
|
722
|
+
errors,
|
|
723
|
+
importedNodeIds,
|
|
724
|
+
lock
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
function recordNodeOutcome(node, outcome, result, errors, importedNodeIds) {
|
|
729
|
+
switch (outcome.status) {
|
|
730
|
+
case "created": {
|
|
731
|
+
result.nodes.created++;
|
|
732
|
+
importedNodeIds.add(makeNodeKey(node.kind, node.id));
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
case "updated": {
|
|
736
|
+
result.nodes.updated++;
|
|
737
|
+
importedNodeIds.add(makeNodeKey(node.kind, node.id));
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
case "skipped": {
|
|
741
|
+
result.nodes.skipped++;
|
|
742
|
+
if (outcome.liveTarget) {
|
|
743
|
+
importedNodeIds.add(makeNodeKey(node.kind, node.id));
|
|
744
|
+
}
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
case "error": {
|
|
748
|
+
errors.push({
|
|
749
|
+
entityType: "node",
|
|
750
|
+
kind: node.kind,
|
|
751
|
+
id: node.id,
|
|
752
|
+
error: outcome.error
|
|
753
|
+
});
|
|
754
|
+
break;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
async function processNodeSlice(backend, graphId, registry, batch, schemas, options, result, errors, importedNodeIds, lock) {
|
|
759
|
+
const record = (node, outcome) => {
|
|
760
|
+
recordNodeOutcome(node, outcome, result, errors, importedNodeIds);
|
|
761
|
+
};
|
|
762
|
+
const candidates = [];
|
|
763
|
+
const deferred = [];
|
|
764
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
765
|
+
for (const node of batch) {
|
|
766
|
+
const schemaEntry = schemas.get(node.kind);
|
|
767
|
+
if (!schemaEntry) {
|
|
768
|
+
record(node, {
|
|
769
|
+
status: "error",
|
|
770
|
+
error: `Unknown node kind: ${node.kind}`
|
|
771
|
+
});
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
const propsResult = validateProperties(
|
|
775
|
+
node.properties,
|
|
776
|
+
schemaEntry.schema,
|
|
777
|
+
options.onUnknownProperty
|
|
778
|
+
);
|
|
779
|
+
if (!propsResult.success) {
|
|
780
|
+
record(node, { status: "error", error: propsResult.error });
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
const validityError = validateValidityWindow(node);
|
|
784
|
+
if (validityError !== void 0) {
|
|
785
|
+
record(node, { status: "error", error: validityError });
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
const key = makeNodeKey(node.kind, node.id);
|
|
789
|
+
if (seenKeys.has(key)) {
|
|
790
|
+
deferred.push(node);
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
seenKeys.add(key);
|
|
794
|
+
candidates.push({
|
|
795
|
+
node,
|
|
796
|
+
schemaEntry,
|
|
797
|
+
props: propsResult.data,
|
|
798
|
+
draft: {
|
|
799
|
+
kind: node.kind,
|
|
800
|
+
id: node.id,
|
|
801
|
+
// Interchange rows always carry an explicit id.
|
|
802
|
+
idProvided: true,
|
|
803
|
+
nodeKind: schemaEntry.registration.type,
|
|
804
|
+
uniqueConstraints: schemaEntry.registration.unique ?? [],
|
|
805
|
+
validatedProps: propsResult.data,
|
|
806
|
+
// NodeCreateDraft.validFrom is string | undefined (never null) — it
|
|
807
|
+
// only feeds batch validation-cache priming, which never inspects
|
|
808
|
+
// it, so normalizing the explicit-NULL sentinel away here is inert.
|
|
809
|
+
// The actual insert (buildImportInsertParams) reads node.validFrom
|
|
810
|
+
// directly and preserves the null.
|
|
811
|
+
validFrom: node.validFrom ?? void 0,
|
|
812
|
+
validTo: node.validTo
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
const {
|
|
817
|
+
backend: validationBackend,
|
|
818
|
+
registerPendingNode,
|
|
819
|
+
registerPendingUniqueEntries,
|
|
820
|
+
registerAppliedNodeUpdate,
|
|
821
|
+
seedNodeRow,
|
|
822
|
+
seedUniqueRow
|
|
823
|
+
} = createNodeBatchValidationBackend(graphId, registry, backend);
|
|
824
|
+
await primeBatchValidationCaches(
|
|
825
|
+
{ graphId, registry },
|
|
826
|
+
candidates.map((candidate) => candidate.draft),
|
|
827
|
+
backend,
|
|
828
|
+
{ seedNodeRow, seedUniqueRow }
|
|
829
|
+
);
|
|
830
|
+
const writeContext = { graphId, registry, lock };
|
|
831
|
+
const accepted = [];
|
|
832
|
+
for (const candidate of candidates) {
|
|
833
|
+
const { node, schemaEntry, props } = candidate;
|
|
834
|
+
const uniqueConstraints = schemaEntry.registration.unique ?? [];
|
|
835
|
+
const existing = await validationBackend.getNode(
|
|
836
|
+
graphId,
|
|
837
|
+
node.kind,
|
|
838
|
+
node.id
|
|
839
|
+
);
|
|
840
|
+
if (existing) {
|
|
841
|
+
switch (options.onConflict) {
|
|
842
|
+
case "skip": {
|
|
843
|
+
record(node, {
|
|
844
|
+
status: "skipped",
|
|
845
|
+
liveTarget: isLiveNodeRow(existing)
|
|
846
|
+
});
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
849
|
+
case "error": {
|
|
850
|
+
record(node, {
|
|
851
|
+
status: "error",
|
|
852
|
+
error: `Node already exists: ${node.kind}:${node.id}`
|
|
853
|
+
});
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
856
|
+
case "update": {
|
|
857
|
+
if (!isLiveNodeRow(existing)) {
|
|
858
|
+
record(node, { status: "skipped", liveTarget: false });
|
|
859
|
+
break;
|
|
860
|
+
}
|
|
861
|
+
const updateWindow = validateUpdateValidityWindow(
|
|
862
|
+
node,
|
|
863
|
+
existing.valid_from
|
|
864
|
+
);
|
|
865
|
+
if (!updateWindow.ok) {
|
|
866
|
+
record(node, { status: "error", error: updateWindow.error });
|
|
867
|
+
break;
|
|
868
|
+
}
|
|
869
|
+
const updateError = await updateImportedNode(
|
|
870
|
+
writeContext,
|
|
871
|
+
// The pending-aware overlay, not the raw backend: the update's
|
|
872
|
+
// uniqueness pre-check must see a unique value already reserved by
|
|
873
|
+
// an unflushed create EARLIER in this slice, so it degrades to a
|
|
874
|
+
// per-row error exactly as the sequential path does — rather than
|
|
875
|
+
// claiming the key on the real backend and colliding with that
|
|
876
|
+
// create at flush (which would throw and roll back the whole
|
|
877
|
+
// import). Writes still delegate to the real backend.
|
|
878
|
+
validationBackend,
|
|
879
|
+
node,
|
|
880
|
+
{
|
|
881
|
+
existing,
|
|
882
|
+
schema: schemaEntry.registration.type.schema,
|
|
883
|
+
validatedProps: props,
|
|
884
|
+
uniqueConstraints,
|
|
885
|
+
windowFence: updateWindow.value
|
|
886
|
+
}
|
|
887
|
+
);
|
|
888
|
+
if (updateError === void 0) {
|
|
889
|
+
registerAppliedNodeUpdate(
|
|
890
|
+
node.kind,
|
|
891
|
+
node.id,
|
|
892
|
+
rowPropsToObject(existing.props),
|
|
893
|
+
props,
|
|
894
|
+
uniqueConstraints
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
record(
|
|
898
|
+
node,
|
|
899
|
+
updateError === void 0 ? { status: "updated" } : { status: "error", error: updateError }
|
|
900
|
+
);
|
|
901
|
+
break;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
const uniquenessResult = await catchUniquenessError(
|
|
907
|
+
() => checkUniquenessConstraints(
|
|
908
|
+
{ graphId, registry, backend: validationBackend },
|
|
909
|
+
node.kind,
|
|
910
|
+
node.id,
|
|
911
|
+
props,
|
|
912
|
+
uniqueConstraints
|
|
913
|
+
)
|
|
914
|
+
);
|
|
915
|
+
if (!uniquenessResult.ok) {
|
|
916
|
+
record(node, { status: "error", error: uniquenessResult.error });
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
registerPendingNode(buildImportInsertParams(graphId, candidate));
|
|
920
|
+
registerPendingUniqueEntries(node.kind, node.id, props, uniqueConstraints);
|
|
921
|
+
accepted.push(candidate);
|
|
922
|
+
}
|
|
923
|
+
if (accepted.length > 0) {
|
|
924
|
+
const insertParamsList = accepted.map(
|
|
925
|
+
(candidate) => buildImportInsertParams(graphId, candidate)
|
|
926
|
+
);
|
|
927
|
+
if (backend.insertNodesBatch === void 0) {
|
|
928
|
+
for (const params of insertParamsList) {
|
|
929
|
+
await backend.insertNode(params);
|
|
930
|
+
}
|
|
931
|
+
} else {
|
|
932
|
+
await backend.insertNodesBatch(insertParamsList);
|
|
933
|
+
}
|
|
934
|
+
await applyNodeInsertSideEffectsBatch(
|
|
935
|
+
writeContext,
|
|
936
|
+
accepted.map((candidate) => ({
|
|
937
|
+
kind: candidate.node.kind,
|
|
938
|
+
id: candidate.node.id,
|
|
939
|
+
schema: candidate.schemaEntry.registration.type.schema,
|
|
940
|
+
props: candidate.props,
|
|
941
|
+
uniqueConstraints: candidate.schemaEntry.registration.unique ?? []
|
|
942
|
+
})),
|
|
943
|
+
backend
|
|
944
|
+
);
|
|
945
|
+
for (const candidate of accepted) {
|
|
946
|
+
record(candidate.node, { status: "created" });
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
for (const node of deferred) {
|
|
950
|
+
record(
|
|
951
|
+
node,
|
|
952
|
+
await processNode(
|
|
953
|
+
backend,
|
|
954
|
+
graphId,
|
|
955
|
+
registry,
|
|
956
|
+
node,
|
|
957
|
+
schemas,
|
|
958
|
+
options,
|
|
959
|
+
lock
|
|
960
|
+
)
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
function buildImportInsertParams(graphId, candidate) {
|
|
965
|
+
const { node, props } = candidate;
|
|
966
|
+
return {
|
|
967
|
+
graphId,
|
|
968
|
+
kind: node.kind,
|
|
969
|
+
id: node.id,
|
|
970
|
+
props,
|
|
971
|
+
...node.validFrom !== void 0 && { validFrom: node.validFrom },
|
|
972
|
+
...node.validTo !== void 0 && { validTo: node.validTo }
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
async function catchUniquenessError(fn) {
|
|
976
|
+
try {
|
|
977
|
+
return { ok: true, value: await fn() };
|
|
978
|
+
} catch (error) {
|
|
979
|
+
if (error instanceof UniquenessError) {
|
|
980
|
+
return { ok: false, error: error.message };
|
|
981
|
+
}
|
|
982
|
+
throw error;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
var NODE_UPDATE_TARGET_CHANGED_CODE = "INTERCHANGE_NODE_UPDATE_TARGET_CHANGED";
|
|
986
|
+
async function updateImportedNode(writeContext, backend, node, args) {
|
|
987
|
+
try {
|
|
988
|
+
const result = await catchUniquenessError(
|
|
989
|
+
() => applyNodeUpdate(
|
|
990
|
+
writeContext,
|
|
991
|
+
{
|
|
992
|
+
existing: args.existing,
|
|
993
|
+
schema: args.schema,
|
|
994
|
+
validatedProps: args.validatedProps,
|
|
995
|
+
uniqueConstraints: args.uniqueConstraints,
|
|
996
|
+
...args.windowFence,
|
|
997
|
+
...node.validTo !== void 0 && { validTo: node.validTo }
|
|
998
|
+
},
|
|
999
|
+
backend
|
|
1000
|
+
)
|
|
1001
|
+
);
|
|
1002
|
+
return result.ok ? void 0 : result.error;
|
|
1003
|
+
} catch (error) {
|
|
1004
|
+
if (!(error instanceof DatabaseOperationError) || error.details.reason !== "no_row_returned") {
|
|
1005
|
+
throw error;
|
|
1006
|
+
}
|
|
1007
|
+
return `${NODE_UPDATE_TARGET_CHANGED_CODE}: Node "${node.kind}:${node.id}" was not updated: no live node matching the id and the validity bound this import checked remained when the write ran, so the row changed or was removed after it was checked. Re-export the source and retry.`;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
var EDGE_IDENTITY_CONFLICT_CODE = "INTERCHANGE_EDGE_KIND_CONFLICT";
|
|
1011
|
+
function edgeIdentityConflict(edge, existing) {
|
|
1012
|
+
const differences = [
|
|
1013
|
+
...existing.kind === edge.kind ? [] : [`kind "${existing.kind}" (document states "${edge.kind}")`],
|
|
1014
|
+
...existing.from_kind === edge.from.kind && existing.from_id === edge.from.id ? [] : [
|
|
1015
|
+
`from "${existing.from_kind}:${existing.from_id}" (document states "${edge.from.kind}:${edge.from.id}")`
|
|
1016
|
+
],
|
|
1017
|
+
...existing.to_kind === edge.to.kind && existing.to_id === edge.to.id ? [] : [
|
|
1018
|
+
`to "${existing.to_kind}:${existing.to_id}" (document states "${edge.to.kind}:${edge.to.id}")`
|
|
1019
|
+
]
|
|
1020
|
+
];
|
|
1021
|
+
if (differences.length === 0) return void 0;
|
|
1022
|
+
return `${EDGE_IDENTITY_CONFLICT_CODE}: Edge "${edge.id}" already exists with a different immutable identity \u2014 ${differences.join("; ")}. Edge ids are unique per graph, so the incoming edge can neither update nor be created under that id. Give it a distinct id, or import it under the identity the stored row already carries.`;
|
|
1023
|
+
}
|
|
1024
|
+
async function updateImportedEdge(backend, graphId, edge, props, windowFence) {
|
|
1025
|
+
try {
|
|
1026
|
+
await backend.updateEdge({
|
|
1027
|
+
graphId,
|
|
1028
|
+
id: edge.id,
|
|
1029
|
+
kind: edge.kind,
|
|
1030
|
+
fromKind: edge.from.kind,
|
|
1031
|
+
fromId: edge.from.id,
|
|
1032
|
+
toKind: edge.to.kind,
|
|
1033
|
+
toId: edge.to.id,
|
|
1034
|
+
props,
|
|
1035
|
+
...windowFence,
|
|
1036
|
+
...edge.validTo !== void 0 && { validTo: edge.validTo }
|
|
1037
|
+
});
|
|
1038
|
+
return void 0;
|
|
1039
|
+
} catch (error) {
|
|
1040
|
+
if (!(error instanceof DatabaseOperationError) || error.details.reason !== "no_row_returned") {
|
|
1041
|
+
throw error;
|
|
1042
|
+
}
|
|
1043
|
+
return `${EDGE_IDENTITY_CONFLICT_CODE}: Edge "${edge.id}" of kind "${edge.kind}" from "${edge.from.kind}:${edge.from.id}" to "${edge.to.kind}:${edge.to.id}" was not updated: no live edge matching the identity and the validity bound this import checked remained when the write ran, so the row changed or was removed after it was checked. Re-export the source and retry.`;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
function windowResultOf(assert) {
|
|
1047
|
+
try {
|
|
1048
|
+
return { ok: true, value: assert() };
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
if (error instanceof ValidationError) {
|
|
1051
|
+
const coded = error.details.issues.find(
|
|
1052
|
+
(issue) => issue.code === INVERTED_VALIDITY_WINDOW_CODE || issue.code === IMMUTABLE_VALIDITY_LOWER_BOUND_CODE
|
|
1053
|
+
);
|
|
1054
|
+
if (coded !== void 0) {
|
|
1055
|
+
return { ok: false, error: `${coded.code}: ${error.message}` };
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
return {
|
|
1059
|
+
ok: false,
|
|
1060
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
function windowErrorOf(assert) {
|
|
1065
|
+
const result = windowResultOf(assert);
|
|
1066
|
+
return result.ok ? void 0 : result.error;
|
|
1067
|
+
}
|
|
1068
|
+
function validateValidityWindow(entity) {
|
|
1069
|
+
return windowErrorOf(() => {
|
|
1070
|
+
validateOptionalCanonicalIsoDate(
|
|
1071
|
+
entity.validFrom ?? void 0,
|
|
1072
|
+
"validFrom"
|
|
1073
|
+
);
|
|
1074
|
+
validateOptionalCanonicalIsoDate(entity.validTo, "validTo");
|
|
1075
|
+
assertOrderedValidityWindow(
|
|
1076
|
+
`${entity.kind} "${entity.id}"`,
|
|
1077
|
+
entity.validFrom ?? void 0,
|
|
1078
|
+
entity.validTo
|
|
1079
|
+
);
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
function validateUpdateValidityWindow(entity, storedValidFrom) {
|
|
1083
|
+
const verdict = windowResultOf(
|
|
1084
|
+
() => assertWritableValidityWindow(
|
|
1085
|
+
`${entity.kind} "${entity.id}"`,
|
|
1086
|
+
entity.validFrom ?? void 0,
|
|
1087
|
+
{
|
|
1088
|
+
effectiveValidFrom: storedValidFrom,
|
|
1089
|
+
appliesStatedValidFrom: false,
|
|
1090
|
+
// Import updates a LIVE row in place, so the effective bound is always
|
|
1091
|
+
// the one the row already stores.
|
|
1092
|
+
effectiveBoundIsStored: true
|
|
1093
|
+
},
|
|
1094
|
+
entity.validTo
|
|
1095
|
+
)
|
|
1096
|
+
);
|
|
1097
|
+
return verdict.ok ? { ok: true, value: verdict.value.storedLowerBoundFence } : verdict;
|
|
1098
|
+
}
|
|
1099
|
+
async function processNode(backend, graphId, registry, node, schemas, options, lock) {
|
|
1100
|
+
const schemaEntry = schemas.get(node.kind);
|
|
1101
|
+
if (!schemaEntry) {
|
|
1102
|
+
return { status: "error", error: `Unknown node kind: ${node.kind}` };
|
|
1103
|
+
}
|
|
1104
|
+
const propsResult = validateProperties(
|
|
1105
|
+
node.properties,
|
|
1106
|
+
schemaEntry.schema,
|
|
1107
|
+
options.onUnknownProperty
|
|
1108
|
+
);
|
|
1109
|
+
if (!propsResult.success) {
|
|
1110
|
+
return { status: "error", error: propsResult.error };
|
|
1111
|
+
}
|
|
1112
|
+
const validityError = validateValidityWindow(node);
|
|
1113
|
+
if (validityError !== void 0) {
|
|
1114
|
+
return { status: "error", error: validityError };
|
|
1115
|
+
}
|
|
1116
|
+
const { registration } = schemaEntry;
|
|
1117
|
+
const uniqueConstraints = registration.unique ?? [];
|
|
1118
|
+
const writeContext = { graphId, registry, lock };
|
|
1119
|
+
const existing = await backend.getNode(graphId, node.kind, node.id);
|
|
1120
|
+
if (existing) {
|
|
1121
|
+
switch (options.onConflict) {
|
|
1122
|
+
case "skip": {
|
|
1123
|
+
return { status: "skipped", liveTarget: isLiveNodeRow(existing) };
|
|
1124
|
+
}
|
|
1125
|
+
case "error": {
|
|
1126
|
+
return {
|
|
1127
|
+
status: "error",
|
|
1128
|
+
error: `Node already exists: ${node.kind}:${node.id}`
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
case "update": {
|
|
1132
|
+
if (!isLiveNodeRow(existing)) {
|
|
1133
|
+
return { status: "skipped", liveTarget: false };
|
|
1134
|
+
}
|
|
1135
|
+
const updateWindow = validateUpdateValidityWindow(
|
|
1136
|
+
node,
|
|
1137
|
+
existing.valid_from
|
|
1138
|
+
);
|
|
1139
|
+
if (!updateWindow.ok) {
|
|
1140
|
+
return { status: "error", error: updateWindow.error };
|
|
1141
|
+
}
|
|
1142
|
+
const updateError = await updateImportedNode(
|
|
1143
|
+
writeContext,
|
|
1144
|
+
backend,
|
|
1145
|
+
node,
|
|
1146
|
+
{
|
|
1147
|
+
existing,
|
|
1148
|
+
schema: registration.type.schema,
|
|
1149
|
+
validatedProps: propsResult.data,
|
|
1150
|
+
uniqueConstraints,
|
|
1151
|
+
windowFence: updateWindow.value
|
|
1152
|
+
}
|
|
1153
|
+
);
|
|
1154
|
+
if (updateError !== void 0) {
|
|
1155
|
+
return { status: "error", error: updateError };
|
|
1156
|
+
}
|
|
1157
|
+
return { status: "updated" };
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
const uniquenessResult = await catchUniquenessError(
|
|
1162
|
+
() => checkUniquenessConstraints(
|
|
1163
|
+
{ graphId, registry, backend },
|
|
1164
|
+
node.kind,
|
|
1165
|
+
node.id,
|
|
1166
|
+
propsResult.data,
|
|
1167
|
+
uniqueConstraints
|
|
1168
|
+
)
|
|
1169
|
+
);
|
|
1170
|
+
if (!uniquenessResult.ok) {
|
|
1171
|
+
return { status: "error", error: uniquenessResult.error };
|
|
1172
|
+
}
|
|
1173
|
+
await backend.insertNode({
|
|
1174
|
+
graphId,
|
|
1175
|
+
kind: node.kind,
|
|
1176
|
+
id: node.id,
|
|
1177
|
+
props: propsResult.data,
|
|
1178
|
+
...node.validFrom !== void 0 && { validFrom: node.validFrom },
|
|
1179
|
+
...node.validTo !== void 0 && { validTo: node.validTo }
|
|
1180
|
+
});
|
|
1181
|
+
await applyNodeInsertSideEffects(
|
|
1182
|
+
writeContext,
|
|
1183
|
+
{
|
|
1184
|
+
kind: node.kind,
|
|
1185
|
+
id: node.id,
|
|
1186
|
+
schema: registration.type.schema,
|
|
1187
|
+
props: propsResult.data,
|
|
1188
|
+
uniqueConstraints
|
|
1189
|
+
},
|
|
1190
|
+
backend
|
|
1191
|
+
);
|
|
1192
|
+
return { status: "created" };
|
|
1193
|
+
}
|
|
1194
|
+
async function processEdges(backend, graphId, registry, edges, edgeSchemas, nodeSchemas, options, result, errors, importedNodeIds) {
|
|
1195
|
+
const batchSize = options.batchSize;
|
|
1196
|
+
for (let index = 0; index < edges.length; index += batchSize) {
|
|
1197
|
+
const batch = edges.slice(index, index + batchSize);
|
|
1198
|
+
await processEdgeSlice(
|
|
1199
|
+
backend,
|
|
1200
|
+
graphId,
|
|
1201
|
+
registry,
|
|
1202
|
+
batch,
|
|
1203
|
+
edgeSchemas,
|
|
1204
|
+
nodeSchemas,
|
|
1205
|
+
options,
|
|
1206
|
+
result,
|
|
1207
|
+
errors,
|
|
1208
|
+
importedNodeIds
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
function recordEdgeOutcome(edge, outcome, result, errors) {
|
|
1213
|
+
switch (outcome.status) {
|
|
1214
|
+
case "created": {
|
|
1215
|
+
result.edges.created++;
|
|
1216
|
+
break;
|
|
1217
|
+
}
|
|
1218
|
+
case "updated": {
|
|
1219
|
+
result.edges.updated++;
|
|
1220
|
+
break;
|
|
1221
|
+
}
|
|
1222
|
+
case "skipped": {
|
|
1223
|
+
result.edges.skipped++;
|
|
1224
|
+
break;
|
|
1225
|
+
}
|
|
1226
|
+
case "error": {
|
|
1227
|
+
errors.push({
|
|
1228
|
+
entityType: "edge",
|
|
1229
|
+
kind: edge.kind,
|
|
1230
|
+
id: edge.id,
|
|
1231
|
+
error: outcome.error
|
|
1232
|
+
});
|
|
1233
|
+
break;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
async function processEdgeSlice(backend, graphId, registry, batch, edgeSchemas, nodeSchemas, options, result, errors, importedNodeIds) {
|
|
1238
|
+
const record = (edge, outcome) => {
|
|
1239
|
+
recordEdgeOutcome(edge, outcome, result, errors);
|
|
1240
|
+
};
|
|
1241
|
+
const candidates = [];
|
|
1242
|
+
const deferred = [];
|
|
1243
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
1244
|
+
for (const edge of batch) {
|
|
1245
|
+
const schemaEntry = edgeSchemas.get(edge.kind);
|
|
1246
|
+
if (!schemaEntry) {
|
|
1247
|
+
record(edge, {
|
|
1248
|
+
status: "error",
|
|
1249
|
+
error: `Unknown edge kind: ${edge.kind}`
|
|
1250
|
+
});
|
|
1251
|
+
continue;
|
|
1252
|
+
}
|
|
1253
|
+
if (!nodeSchemas.has(edge.from.kind)) {
|
|
1254
|
+
record(edge, {
|
|
1255
|
+
status: "error",
|
|
1256
|
+
error: `Unknown from node kind: ${edge.from.kind}`
|
|
1257
|
+
});
|
|
1258
|
+
continue;
|
|
1259
|
+
}
|
|
1260
|
+
if (!nodeSchemas.has(edge.to.kind)) {
|
|
1261
|
+
record(edge, {
|
|
1262
|
+
status: "error",
|
|
1263
|
+
error: `Unknown to node kind: ${edge.to.kind}`
|
|
1264
|
+
});
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
const endpointError = validateEdgeEndpoints(
|
|
1268
|
+
edge.kind,
|
|
1269
|
+
edge.from.kind,
|
|
1270
|
+
edge.to.kind,
|
|
1271
|
+
schemaEntry.registration,
|
|
1272
|
+
registry
|
|
1273
|
+
);
|
|
1274
|
+
if (endpointError !== void 0) {
|
|
1275
|
+
record(edge, { status: "error", error: endpointError.message });
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
const propsResult = validateProperties(
|
|
1279
|
+
edge.properties,
|
|
1280
|
+
schemaEntry.schema,
|
|
1281
|
+
options.onUnknownProperty
|
|
1282
|
+
);
|
|
1283
|
+
if (!propsResult.success) {
|
|
1284
|
+
record(edge, { status: "error", error: propsResult.error });
|
|
1285
|
+
continue;
|
|
1286
|
+
}
|
|
1287
|
+
const validityError = validateValidityWindow(edge);
|
|
1288
|
+
if (validityError !== void 0) {
|
|
1289
|
+
record(edge, { status: "error", error: validityError });
|
|
1290
|
+
continue;
|
|
1291
|
+
}
|
|
1292
|
+
if (seenIds.has(edge.id)) {
|
|
1293
|
+
deferred.push(edge);
|
|
1294
|
+
continue;
|
|
1295
|
+
}
|
|
1296
|
+
seenIds.add(edge.id);
|
|
1297
|
+
candidates.push({ edge, props: propsResult.data });
|
|
1298
|
+
}
|
|
1299
|
+
const liveEndpointKeys = /* @__PURE__ */ new Set();
|
|
1300
|
+
const checkedEndpointKeys = /* @__PURE__ */ new Set();
|
|
1301
|
+
if (options.validateReferences && backend.getNodes !== void 0) {
|
|
1302
|
+
const idsByKind = /* @__PURE__ */ new Map();
|
|
1303
|
+
for (const { edge } of candidates) {
|
|
1304
|
+
for (const endpoint of [edge.from, edge.to]) {
|
|
1305
|
+
const key = makeNodeKey(endpoint.kind, endpoint.id);
|
|
1306
|
+
if (importedNodeIds.has(key) || checkedEndpointKeys.has(key)) continue;
|
|
1307
|
+
checkedEndpointKeys.add(key);
|
|
1308
|
+
const ids = idsByKind.get(endpoint.kind) ?? /* @__PURE__ */ new Set();
|
|
1309
|
+
ids.add(endpoint.id);
|
|
1310
|
+
idsByKind.set(endpoint.kind, ids);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
for (const [kind, ids] of idsByKind) {
|
|
1314
|
+
const rows = await backend.getNodes(graphId, kind, [...ids]);
|
|
1315
|
+
for (const row of rows) {
|
|
1316
|
+
if (isLiveNodeRow(row)) {
|
|
1317
|
+
liveEndpointKeys.add(makeNodeKey(kind, row.id));
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
const endpointIsLive = async (endpoint) => {
|
|
1323
|
+
const key = makeNodeKey(endpoint.kind, endpoint.id);
|
|
1324
|
+
if (importedNodeIds.has(key)) return true;
|
|
1325
|
+
if (checkedEndpointKeys.has(key)) return liveEndpointKeys.has(key);
|
|
1326
|
+
const row = await backend.getNode(graphId, endpoint.kind, endpoint.id);
|
|
1327
|
+
return row !== void 0 && isLiveNodeRow(row);
|
|
1328
|
+
};
|
|
1329
|
+
const existingById = /* @__PURE__ */ new Map();
|
|
1330
|
+
if (candidates.length > 0 && backend.getEdges !== void 0) {
|
|
1331
|
+
const rows = await backend.getEdges(
|
|
1332
|
+
graphId,
|
|
1333
|
+
candidates.map((candidate) => candidate.edge.id)
|
|
1334
|
+
);
|
|
1335
|
+
for (const row of rows) {
|
|
1336
|
+
existingById.set(row.id, row);
|
|
1337
|
+
}
|
|
1338
|
+
for (const { edge } of candidates) {
|
|
1339
|
+
if (!existingById.has(edge.id)) existingById.set(edge.id, void 0);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
const accepted = [];
|
|
1343
|
+
for (const candidate of candidates) {
|
|
1344
|
+
const { edge, props } = candidate;
|
|
1345
|
+
if (options.validateReferences) {
|
|
1346
|
+
if (!await endpointIsLive(edge.from)) {
|
|
1347
|
+
record(edge, {
|
|
1348
|
+
status: "error",
|
|
1349
|
+
error: `From node not found: ${edge.from.kind}:${edge.from.id}`
|
|
1350
|
+
});
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
if (!await endpointIsLive(edge.to)) {
|
|
1354
|
+
record(edge, {
|
|
1355
|
+
status: "error",
|
|
1356
|
+
error: `To node not found: ${edge.to.kind}:${edge.to.id}`
|
|
1357
|
+
});
|
|
1358
|
+
continue;
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
const existing = existingById.has(edge.id) ? existingById.get(edge.id) : await backend.getEdge(graphId, edge.id);
|
|
1362
|
+
if (existing) {
|
|
1363
|
+
const identityConflict = edgeIdentityConflict(edge, existing);
|
|
1364
|
+
if (identityConflict !== void 0) {
|
|
1365
|
+
record(edge, { status: "error", error: identityConflict });
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1368
|
+
switch (options.onConflict) {
|
|
1369
|
+
case "skip": {
|
|
1370
|
+
record(edge, {
|
|
1371
|
+
status: "skipped",
|
|
1372
|
+
liveTarget: existing.deleted_at === void 0
|
|
1373
|
+
});
|
|
1374
|
+
break;
|
|
1375
|
+
}
|
|
1376
|
+
case "error": {
|
|
1377
|
+
record(edge, {
|
|
1378
|
+
status: "error",
|
|
1379
|
+
error: `Edge already exists: ${edge.id}`
|
|
1380
|
+
});
|
|
1381
|
+
break;
|
|
1382
|
+
}
|
|
1383
|
+
case "update": {
|
|
1384
|
+
if (existing.deleted_at !== void 0) {
|
|
1385
|
+
record(edge, { status: "skipped"});
|
|
1386
|
+
break;
|
|
1387
|
+
}
|
|
1388
|
+
const updateWindow = validateUpdateValidityWindow(
|
|
1389
|
+
edge,
|
|
1390
|
+
existing.valid_from
|
|
1391
|
+
);
|
|
1392
|
+
if (!updateWindow.ok) {
|
|
1393
|
+
record(edge, { status: "error", error: updateWindow.error });
|
|
1394
|
+
break;
|
|
1395
|
+
}
|
|
1396
|
+
const updateError = await updateImportedEdge(
|
|
1397
|
+
backend,
|
|
1398
|
+
graphId,
|
|
1399
|
+
edge,
|
|
1400
|
+
props,
|
|
1401
|
+
updateWindow.value
|
|
1402
|
+
);
|
|
1403
|
+
if (updateError !== void 0) {
|
|
1404
|
+
record(edge, { status: "error", error: updateError });
|
|
1405
|
+
break;
|
|
1406
|
+
}
|
|
1407
|
+
record(edge, { status: "updated" });
|
|
1408
|
+
break;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1413
|
+
accepted.push(candidate);
|
|
1414
|
+
}
|
|
1415
|
+
if (accepted.length > 0) {
|
|
1416
|
+
const insertParamsList = accepted.map(({ edge, props }) => ({
|
|
1417
|
+
graphId,
|
|
1418
|
+
id: edge.id,
|
|
1419
|
+
kind: edge.kind,
|
|
1420
|
+
fromKind: edge.from.kind,
|
|
1421
|
+
fromId: edge.from.id,
|
|
1422
|
+
toKind: edge.to.kind,
|
|
1423
|
+
toId: edge.to.id,
|
|
1424
|
+
props,
|
|
1425
|
+
...edge.validFrom !== void 0 && { validFrom: edge.validFrom },
|
|
1426
|
+
...edge.validTo !== void 0 && { validTo: edge.validTo }
|
|
1427
|
+
}));
|
|
1428
|
+
if (backend.insertEdgesBatch === void 0) {
|
|
1429
|
+
for (const params of insertParamsList) {
|
|
1430
|
+
await backend.insertEdge(params);
|
|
1431
|
+
}
|
|
1432
|
+
} else {
|
|
1433
|
+
await backend.insertEdgesBatch(insertParamsList);
|
|
1434
|
+
}
|
|
1435
|
+
for (const candidate of accepted) {
|
|
1436
|
+
record(candidate.edge, { status: "created" });
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
for (const edge of deferred) {
|
|
1440
|
+
record(
|
|
1441
|
+
edge,
|
|
1442
|
+
await processEdge(
|
|
1443
|
+
backend,
|
|
1444
|
+
graphId,
|
|
1445
|
+
registry,
|
|
1446
|
+
edge,
|
|
1447
|
+
edgeSchemas,
|
|
1448
|
+
nodeSchemas,
|
|
1449
|
+
options,
|
|
1450
|
+
importedNodeIds
|
|
1451
|
+
)
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
async function processEdge(backend, graphId, registry, edge, edgeSchemas, nodeSchemas, options, importedNodeIds) {
|
|
1456
|
+
const schemaEntry = edgeSchemas.get(edge.kind);
|
|
1457
|
+
if (!schemaEntry) {
|
|
1458
|
+
return { status: "error", error: `Unknown edge kind: ${edge.kind}` };
|
|
1459
|
+
}
|
|
1460
|
+
if (!nodeSchemas.has(edge.from.kind)) {
|
|
1461
|
+
return {
|
|
1462
|
+
status: "error",
|
|
1463
|
+
error: `Unknown from node kind: ${edge.from.kind}`
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
if (!nodeSchemas.has(edge.to.kind)) {
|
|
1467
|
+
return { status: "error", error: `Unknown to node kind: ${edge.to.kind}` };
|
|
1468
|
+
}
|
|
1469
|
+
const endpointError = validateEdgeEndpoints(
|
|
1470
|
+
edge.kind,
|
|
1471
|
+
edge.from.kind,
|
|
1472
|
+
edge.to.kind,
|
|
1473
|
+
schemaEntry.registration,
|
|
1474
|
+
registry
|
|
1475
|
+
);
|
|
1476
|
+
if (endpointError !== void 0) {
|
|
1477
|
+
return { status: "error", error: endpointError.message };
|
|
1478
|
+
}
|
|
1479
|
+
if (options.validateReferences) {
|
|
1480
|
+
const fromKey = makeNodeKey(edge.from.kind, edge.from.id);
|
|
1481
|
+
const toKey = makeNodeKey(edge.to.kind, edge.to.id);
|
|
1482
|
+
if (!importedNodeIds.has(fromKey)) {
|
|
1483
|
+
const fromExists = await backend.getNode(
|
|
1484
|
+
graphId,
|
|
1485
|
+
edge.from.kind,
|
|
1486
|
+
edge.from.id
|
|
1487
|
+
);
|
|
1488
|
+
if (fromExists === void 0 || !isLiveNodeRow(fromExists)) {
|
|
1489
|
+
return {
|
|
1490
|
+
status: "error",
|
|
1491
|
+
error: `From node not found: ${edge.from.kind}:${edge.from.id}`
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
if (!importedNodeIds.has(toKey)) {
|
|
1496
|
+
const toExists = await backend.getNode(graphId, edge.to.kind, edge.to.id);
|
|
1497
|
+
if (toExists === void 0 || !isLiveNodeRow(toExists)) {
|
|
1498
|
+
return {
|
|
1499
|
+
status: "error",
|
|
1500
|
+
error: `To node not found: ${edge.to.kind}:${edge.to.id}`
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
const propsResult = validateProperties(
|
|
1506
|
+
edge.properties,
|
|
1507
|
+
schemaEntry.schema,
|
|
1508
|
+
options.onUnknownProperty
|
|
1509
|
+
);
|
|
1510
|
+
if (!propsResult.success) {
|
|
1511
|
+
return { status: "error", error: propsResult.error };
|
|
1512
|
+
}
|
|
1513
|
+
const validityError = validateValidityWindow(edge);
|
|
1514
|
+
if (validityError !== void 0) {
|
|
1515
|
+
return { status: "error", error: validityError };
|
|
1516
|
+
}
|
|
1517
|
+
const existing = await backend.getEdge(graphId, edge.id);
|
|
1518
|
+
if (existing) {
|
|
1519
|
+
const identityConflict = edgeIdentityConflict(edge, existing);
|
|
1520
|
+
if (identityConflict !== void 0) {
|
|
1521
|
+
return { status: "error", error: identityConflict };
|
|
1522
|
+
}
|
|
1523
|
+
switch (options.onConflict) {
|
|
1524
|
+
case "skip": {
|
|
1525
|
+
return {
|
|
1526
|
+
status: "skipped",
|
|
1527
|
+
liveTarget: existing.deleted_at === void 0
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
case "error": {
|
|
1531
|
+
return { status: "error", error: `Edge already exists: ${edge.id}` };
|
|
1532
|
+
}
|
|
1533
|
+
case "update": {
|
|
1534
|
+
if (existing.deleted_at !== void 0) {
|
|
1535
|
+
return { status: "skipped", liveTarget: false };
|
|
1536
|
+
}
|
|
1537
|
+
const updateWindow = validateUpdateValidityWindow(
|
|
1538
|
+
edge,
|
|
1539
|
+
existing.valid_from
|
|
1540
|
+
);
|
|
1541
|
+
if (!updateWindow.ok) {
|
|
1542
|
+
return { status: "error", error: updateWindow.error };
|
|
1543
|
+
}
|
|
1544
|
+
const updateError = await updateImportedEdge(
|
|
1545
|
+
backend,
|
|
1546
|
+
graphId,
|
|
1547
|
+
edge,
|
|
1548
|
+
propsResult.data,
|
|
1549
|
+
updateWindow.value
|
|
1550
|
+
);
|
|
1551
|
+
if (updateError !== void 0) {
|
|
1552
|
+
return { status: "error", error: updateError };
|
|
1553
|
+
}
|
|
1554
|
+
return { status: "updated" };
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
await backend.insertEdge({
|
|
1559
|
+
graphId,
|
|
1560
|
+
id: edge.id,
|
|
1561
|
+
kind: edge.kind,
|
|
1562
|
+
fromKind: edge.from.kind,
|
|
1563
|
+
fromId: edge.from.id,
|
|
1564
|
+
toKind: edge.to.kind,
|
|
1565
|
+
toId: edge.to.id,
|
|
1566
|
+
props: propsResult.data,
|
|
1567
|
+
...edge.validFrom !== void 0 && { validFrom: edge.validFrom },
|
|
1568
|
+
...edge.validTo !== void 0 && { validTo: edge.validTo }
|
|
1569
|
+
});
|
|
1570
|
+
return { status: "created" };
|
|
1571
|
+
}
|
|
1572
|
+
function validateProperties(properties, schema, unknownStrategy) {
|
|
1573
|
+
try {
|
|
1574
|
+
const knownKeys = new Set(Object.keys(schema.shape));
|
|
1575
|
+
const unknownKeys = Object.keys(properties).filter(
|
|
1576
|
+
(key) => !knownKeys.has(key)
|
|
1577
|
+
);
|
|
1578
|
+
if (unknownKeys.length > 0) {
|
|
1579
|
+
switch (unknownStrategy) {
|
|
1580
|
+
case "error": {
|
|
1581
|
+
return {
|
|
1582
|
+
success: false,
|
|
1583
|
+
error: `Unknown properties: ${unknownKeys.join(", ")}`
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
case "strip": {
|
|
1587
|
+
const stripped = createDataKeyedBag();
|
|
1588
|
+
for (const key of knownKeys) {
|
|
1589
|
+
if (hasOwnKey(properties, key)) {
|
|
1590
|
+
stripped[key] = properties[key];
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
const result2 = schema.safeParse(stripped);
|
|
1594
|
+
if (!result2.success) {
|
|
1595
|
+
return {
|
|
1596
|
+
success: false,
|
|
1597
|
+
error: formatZodError(result2.error)
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
return {
|
|
1601
|
+
success: true,
|
|
1602
|
+
data: result2.data
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
case "allow": {
|
|
1606
|
+
const result2 = schema.safeParse(properties);
|
|
1607
|
+
if (!result2.success) {
|
|
1608
|
+
return {
|
|
1609
|
+
success: false,
|
|
1610
|
+
error: formatZodError(result2.error)
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
return { success: true, data: properties };
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
const result = schema.safeParse(properties);
|
|
1618
|
+
if (!result.success) {
|
|
1619
|
+
return { success: false, error: formatZodError(result.error) };
|
|
1620
|
+
}
|
|
1621
|
+
return { success: true, data: result.data };
|
|
1622
|
+
} catch (error) {
|
|
1623
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1624
|
+
return { success: false, error: `Validation error: ${message}` };
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
function formatZodError(error) {
|
|
1628
|
+
return error.issues.map((issue) => {
|
|
1629
|
+
const path = issue.path.join(".");
|
|
1630
|
+
return path ? `${path}: ${issue.message}` : issue.message;
|
|
1631
|
+
}).join("; ");
|
|
1632
|
+
}
|
|
1633
|
+
function makeNodeKey(kind, id) {
|
|
1634
|
+
return encodeTupleKey([kind, id]);
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// src/interchange/export.ts
|
|
1638
|
+
async function exportGraph(store, options) {
|
|
1639
|
+
const nodes = [];
|
|
1640
|
+
const edges = [];
|
|
1641
|
+
const identityAssertions = [];
|
|
1642
|
+
let header;
|
|
1643
|
+
for await (const chunk of exportGraphStream(store, options)) {
|
|
1644
|
+
switch (chunk.type) {
|
|
1645
|
+
case "header": {
|
|
1646
|
+
header = chunk.header;
|
|
1647
|
+
break;
|
|
1648
|
+
}
|
|
1649
|
+
case "nodes": {
|
|
1650
|
+
nodes.push(...chunk.nodes);
|
|
1651
|
+
break;
|
|
1652
|
+
}
|
|
1653
|
+
case "edges": {
|
|
1654
|
+
edges.push(...chunk.edges);
|
|
1655
|
+
break;
|
|
1656
|
+
}
|
|
1657
|
+
case "identity": {
|
|
1658
|
+
identityAssertions.push(...chunk.assertions);
|
|
1659
|
+
break;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
if (header === void 0) {
|
|
1664
|
+
throw new Error("Graph export stream ended before emitting its header.");
|
|
1665
|
+
}
|
|
1666
|
+
const { identity, ...headerWithoutIdentity } = header;
|
|
1667
|
+
return {
|
|
1668
|
+
...headerWithoutIdentity,
|
|
1669
|
+
nodes,
|
|
1670
|
+
edges,
|
|
1671
|
+
...identity === void 0 ? {} : {
|
|
1672
|
+
identity: { ...identity, assertions: identityAssertions }
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
function exportGraphStream(store, options) {
|
|
1677
|
+
const backend = storeBackend(store);
|
|
1678
|
+
return markExportStreamBackend(
|
|
1679
|
+
exportGraphStreamFromBackend(store, backend, options),
|
|
1680
|
+
backend
|
|
1681
|
+
);
|
|
1682
|
+
}
|
|
1683
|
+
async function* exportGraphStreamFromBackend(store, backend, options) {
|
|
1684
|
+
const resolved = ExportStreamOptionsSchema.parse(options ?? {});
|
|
1685
|
+
const signal = resolved.signal;
|
|
1686
|
+
const channel = createRendezvousChannel();
|
|
1687
|
+
const abortStream = () => {
|
|
1688
|
+
channel.abort(
|
|
1689
|
+
exportStreamCancelled(
|
|
1690
|
+
"mid-stream",
|
|
1691
|
+
store.graphId,
|
|
1692
|
+
signal,
|
|
1693
|
+
backend.capabilities.transactions
|
|
1694
|
+
)
|
|
1695
|
+
);
|
|
1696
|
+
};
|
|
1697
|
+
const unsubscribeFromSignal = () => {
|
|
1698
|
+
signal?.removeEventListener("abort", abortStream);
|
|
1699
|
+
};
|
|
1700
|
+
signal?.addEventListener("abort", abortStream, { once: true });
|
|
1701
|
+
if (signal?.aborted === true) {
|
|
1702
|
+
unsubscribeFromSignal();
|
|
1703
|
+
throw exportStreamCancelled(
|
|
1704
|
+
"before-open",
|
|
1705
|
+
store.graphId,
|
|
1706
|
+
signal,
|
|
1707
|
+
backend.capabilities.transactions
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
const produce = async (target) => {
|
|
1711
|
+
await produceExportChunks(
|
|
1712
|
+
store,
|
|
1713
|
+
target,
|
|
1714
|
+
resolved,
|
|
1715
|
+
(chunk) => channel.push(chunk)
|
|
1716
|
+
);
|
|
1717
|
+
};
|
|
1718
|
+
const beginProducer = () => {
|
|
1719
|
+
try {
|
|
1720
|
+
const releaseSnapshotExport = backend.capabilities.transactions ? claimSnapshotExportLeaseOrRefuse(store.graphId, backend) : () => {
|
|
1721
|
+
};
|
|
1722
|
+
try {
|
|
1723
|
+
const started = backend.capabilities.transactions ? backend.transaction((target) => produce(target), {
|
|
1724
|
+
isolationLevel: "repeatable_read",
|
|
1725
|
+
accessMode: "read_only"
|
|
1726
|
+
}) : produce(backend);
|
|
1727
|
+
return started.then(
|
|
1728
|
+
() => {
|
|
1729
|
+
releaseSnapshotExport();
|
|
1730
|
+
channel.finish();
|
|
1731
|
+
},
|
|
1732
|
+
(error) => {
|
|
1733
|
+
releaseSnapshotExport();
|
|
1734
|
+
channel.fail(error);
|
|
1735
|
+
}
|
|
1736
|
+
);
|
|
1737
|
+
} catch (error) {
|
|
1738
|
+
releaseSnapshotExport();
|
|
1739
|
+
throw error;
|
|
1740
|
+
}
|
|
1741
|
+
} catch (error) {
|
|
1742
|
+
unsubscribeFromSignal();
|
|
1743
|
+
throw error;
|
|
1744
|
+
}
|
|
1745
|
+
};
|
|
1746
|
+
const producer = beginProducer();
|
|
1747
|
+
try {
|
|
1748
|
+
for (; ; ) {
|
|
1749
|
+
const delivery = await channel.take();
|
|
1750
|
+
if (delivery === void 0) return;
|
|
1751
|
+
yield delivery.value;
|
|
1752
|
+
delivery.acknowledge();
|
|
1753
|
+
}
|
|
1754
|
+
} finally {
|
|
1755
|
+
unsubscribeFromSignal();
|
|
1756
|
+
channel.cancel();
|
|
1757
|
+
await producer;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
function exportStreamCancelled(phase, graphId, signal, framed) {
|
|
1761
|
+
const message = phase === "before-open" ? "The graph export stream was aborted through its AbortSignal before it opened: no snapshot transaction was started and no database connection claimed." : framed ? "The graph export stream was aborted through its AbortSignal: its repeatable-read snapshot has been rolled back and the connection it held released." : "The graph export stream was aborted through its AbortSignal: its remaining reads were abandoned. This backend does not support transactions, so the export held no snapshot and no connection \u2014 and the chunks already delivered were never guaranteed to agree with one another.";
|
|
1762
|
+
const cause = signal?.reason;
|
|
1763
|
+
const suggestion = phase === "before-open" ? "Start a new export; the aborted one claimed nothing." : framed ? "Start a new export; the aborted one released its snapshot transaction and the connection it held." : "Start a new export; nothing was held, but discard the chunks already delivered unless partial, mutually-inconsistent data is acceptable.";
|
|
1764
|
+
return new ExportStreamCancelledError(
|
|
1765
|
+
message,
|
|
1766
|
+
{ graphId },
|
|
1767
|
+
{
|
|
1768
|
+
suggestion,
|
|
1769
|
+
...cause === void 0 ? {} : { cause }
|
|
1770
|
+
}
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
function claimSnapshotExportLeaseOrRefuse(graphId, backend) {
|
|
1774
|
+
const lease = acquireSerializedStreamLease(backend, "export-snapshot");
|
|
1775
|
+
if (!lease.acquired) {
|
|
1776
|
+
throw serializedStreamRefusal({
|
|
1777
|
+
graphId,
|
|
1778
|
+
requested: "export-snapshot",
|
|
1779
|
+
heldBy: lease.heldBy
|
|
1780
|
+
});
|
|
1781
|
+
}
|
|
1782
|
+
return lease.release;
|
|
1783
|
+
}
|
|
1784
|
+
async function produceExportChunks(store, backend, options, emit) {
|
|
1785
|
+
const graphId = store.graphId;
|
|
1786
|
+
const nodeKinds = options.nodeKinds ?? getNodeKinds(store.graph);
|
|
1787
|
+
const edgeKinds = options.edgeKinds ?? getEdgeKinds(store.graph);
|
|
1788
|
+
const schemaVersion = await backend.getActiveSchema(graphId);
|
|
1789
|
+
await emit({
|
|
1790
|
+
type: "header",
|
|
1791
|
+
header: {
|
|
1792
|
+
formatVersion: FORMAT_VERSION,
|
|
1793
|
+
exportedAt: nowIso(),
|
|
1794
|
+
source: {
|
|
1795
|
+
type: "typegraph-export",
|
|
1796
|
+
graphId,
|
|
1797
|
+
schemaVersion: schemaVersion?.version ?? 1
|
|
1798
|
+
},
|
|
1799
|
+
...store.graph.identity === void 0 ? {} : {
|
|
1800
|
+
identity: {
|
|
1801
|
+
profile: "typegraph-identity-v1",
|
|
1802
|
+
mode: options.identityMode
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
});
|
|
1807
|
+
for (const kind of nodeKinds) {
|
|
1808
|
+
for await (const chunk of exportNodeChunks(
|
|
1809
|
+
backend,
|
|
1810
|
+
graphId,
|
|
1811
|
+
kind,
|
|
1812
|
+
options
|
|
1813
|
+
)) {
|
|
1814
|
+
await emit(chunk);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
for (const kind of edgeKinds) {
|
|
1818
|
+
for await (const chunk of exportEdgeChunks(
|
|
1819
|
+
backend,
|
|
1820
|
+
graphId,
|
|
1821
|
+
kind,
|
|
1822
|
+
options
|
|
1823
|
+
)) {
|
|
1824
|
+
await emit(chunk);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
if (store.graph.identity === void 0) return;
|
|
1828
|
+
let after;
|
|
1829
|
+
for (; ; ) {
|
|
1830
|
+
const page = await storeRuntime(store).readIdentityAssertionPageAtTarget(
|
|
1831
|
+
backend,
|
|
1832
|
+
options.identityMode,
|
|
1833
|
+
{
|
|
1834
|
+
...options.nodeKinds === void 0 ? {} : { nodeKinds },
|
|
1835
|
+
includeDeleted: options.includeDeleted,
|
|
1836
|
+
...after === void 0 ? {} : { after },
|
|
1837
|
+
limit: options.batchSize
|
|
1838
|
+
}
|
|
1839
|
+
);
|
|
1840
|
+
if (page.assertions.length > 0) {
|
|
1841
|
+
await emit({ type: "identity", assertions: [...page.assertions] });
|
|
1842
|
+
}
|
|
1843
|
+
if (page.done) return;
|
|
1844
|
+
after = requireDefined(page.nextAfter);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
function createRendezvousChannel() {
|
|
1848
|
+
const consumerCancelledError = new Error("Export stream consumer cancelled.");
|
|
1849
|
+
let pendingPush;
|
|
1850
|
+
let pendingTake;
|
|
1851
|
+
let inFlightReject;
|
|
1852
|
+
let terminal;
|
|
1853
|
+
let producerUnwindError;
|
|
1854
|
+
function push(value) {
|
|
1855
|
+
if (producerUnwindError !== void 0) {
|
|
1856
|
+
return Promise.reject(producerUnwindError);
|
|
1857
|
+
}
|
|
1858
|
+
if (terminal !== void 0 || pendingPush !== void 0) {
|
|
1859
|
+
return Promise.reject(
|
|
1860
|
+
new Error("Cannot push to a completed or occupied export channel.")
|
|
1861
|
+
);
|
|
1862
|
+
}
|
|
1863
|
+
return new Promise((resolve, reject) => {
|
|
1864
|
+
const delivery = {
|
|
1865
|
+
value,
|
|
1866
|
+
acknowledge: () => {
|
|
1867
|
+
inFlightReject = void 0;
|
|
1868
|
+
resolve();
|
|
1869
|
+
}
|
|
1870
|
+
};
|
|
1871
|
+
if (pendingTake !== void 0) {
|
|
1872
|
+
const take2 = pendingTake;
|
|
1873
|
+
pendingTake = void 0;
|
|
1874
|
+
inFlightReject = reject;
|
|
1875
|
+
take2.resolve(delivery);
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
pendingPush = { delivery, reject };
|
|
1879
|
+
});
|
|
1880
|
+
}
|
|
1881
|
+
function take() {
|
|
1882
|
+
if (pendingPush !== void 0) {
|
|
1883
|
+
const push2 = pendingPush;
|
|
1884
|
+
pendingPush = void 0;
|
|
1885
|
+
inFlightReject = push2.reject;
|
|
1886
|
+
return Promise.resolve(push2.delivery);
|
|
1887
|
+
}
|
|
1888
|
+
if (terminal !== void 0) {
|
|
1889
|
+
return terminal.error === void 0 ? Promise.resolve(void 0) : Promise.reject(terminal.error);
|
|
1890
|
+
}
|
|
1891
|
+
if (pendingTake !== void 0) {
|
|
1892
|
+
return Promise.reject(
|
|
1893
|
+
new Error("Export channel already has a consumer.")
|
|
1894
|
+
);
|
|
1895
|
+
}
|
|
1896
|
+
return new Promise((resolve, reject) => {
|
|
1897
|
+
pendingTake = { resolve, reject };
|
|
1898
|
+
});
|
|
1899
|
+
}
|
|
1900
|
+
function finish() {
|
|
1901
|
+
if (producerUnwindError !== void 0) return;
|
|
1902
|
+
terminal = {};
|
|
1903
|
+
if (pendingTake !== void 0) {
|
|
1904
|
+
const take2 = pendingTake;
|
|
1905
|
+
pendingTake = void 0;
|
|
1906
|
+
take2.resolve(void 0);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
function fail(error) {
|
|
1910
|
+
if (producerUnwindError !== void 0 && error === producerUnwindError) {
|
|
1911
|
+
return;
|
|
1912
|
+
}
|
|
1913
|
+
if (terminal !== void 0) return;
|
|
1914
|
+
const exportError = error instanceof Error ? error : new Error("Graph export failed.", { cause: error });
|
|
1915
|
+
terminal = { error: exportError };
|
|
1916
|
+
if (pendingTake !== void 0) {
|
|
1917
|
+
const take2 = pendingTake;
|
|
1918
|
+
pendingTake = void 0;
|
|
1919
|
+
take2.reject(exportError);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
function cancel() {
|
|
1923
|
+
if (producerUnwindError !== void 0) return;
|
|
1924
|
+
producerUnwindError = consumerCancelledError;
|
|
1925
|
+
if (pendingPush !== void 0) {
|
|
1926
|
+
const push2 = pendingPush;
|
|
1927
|
+
pendingPush = void 0;
|
|
1928
|
+
push2.reject(consumerCancelledError);
|
|
1929
|
+
}
|
|
1930
|
+
if (inFlightReject !== void 0) {
|
|
1931
|
+
const reject = inFlightReject;
|
|
1932
|
+
inFlightReject = void 0;
|
|
1933
|
+
reject(consumerCancelledError);
|
|
1934
|
+
}
|
|
1935
|
+
if (pendingTake !== void 0) {
|
|
1936
|
+
const take2 = pendingTake;
|
|
1937
|
+
pendingTake = void 0;
|
|
1938
|
+
take2.resolve(void 0);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
function abort(error) {
|
|
1942
|
+
if (terminal !== void 0 || producerUnwindError !== void 0) return;
|
|
1943
|
+
producerUnwindError = error;
|
|
1944
|
+
terminal = { error };
|
|
1945
|
+
if (pendingPush !== void 0) {
|
|
1946
|
+
const push2 = pendingPush;
|
|
1947
|
+
pendingPush = void 0;
|
|
1948
|
+
push2.reject(error);
|
|
1949
|
+
}
|
|
1950
|
+
if (inFlightReject !== void 0) {
|
|
1951
|
+
const reject = inFlightReject;
|
|
1952
|
+
inFlightReject = void 0;
|
|
1953
|
+
reject(error);
|
|
1954
|
+
}
|
|
1955
|
+
if (pendingTake !== void 0) {
|
|
1956
|
+
const take2 = pendingTake;
|
|
1957
|
+
pendingTake = void 0;
|
|
1958
|
+
take2.reject(error);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
return { push, take, finish, fail, cancel, abort };
|
|
1962
|
+
}
|
|
1963
|
+
async function* exportNodeChunks(backend, graphId, kind, options) {
|
|
1964
|
+
let after;
|
|
1965
|
+
for (; ; ) {
|
|
1966
|
+
const rows = await backend.findNodesByKind({
|
|
1967
|
+
graphId,
|
|
1968
|
+
kind,
|
|
1969
|
+
excludeDeleted: !options.includeDeleted,
|
|
1970
|
+
orderBy: "id",
|
|
1971
|
+
limit: options.batchSize,
|
|
1972
|
+
...after === void 0 ? {} : { after }
|
|
1973
|
+
});
|
|
1974
|
+
if (rows.length === 0) return;
|
|
1975
|
+
const nodes = rows.map((row) => {
|
|
1976
|
+
const node = {
|
|
1977
|
+
kind: row.kind,
|
|
1978
|
+
id: row.id,
|
|
1979
|
+
properties: rowPropsToObject(row.props)
|
|
1980
|
+
};
|
|
1981
|
+
if (options.includeTemporal) {
|
|
1982
|
+
const validFrom = (
|
|
1983
|
+
// eslint-disable-next-line unicorn/no-null
|
|
1984
|
+
row.valid_from ?? null
|
|
1985
|
+
);
|
|
1986
|
+
node.validFrom = validFrom;
|
|
1987
|
+
if (row.valid_to) {
|
|
1988
|
+
node.validTo = row.valid_to;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
if (options.includeMeta) {
|
|
1992
|
+
node.meta = {
|
|
1993
|
+
version: row.version,
|
|
1994
|
+
createdAt: row.created_at,
|
|
1995
|
+
updatedAt: row.updated_at
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
return node;
|
|
1999
|
+
});
|
|
2000
|
+
yield { type: "nodes", nodes };
|
|
2001
|
+
if (rows.length < options.batchSize) return;
|
|
2002
|
+
after = requireDefined(rows.at(-1)).id;
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
async function* exportEdgeChunks(backend, graphId, kind, options) {
|
|
2006
|
+
let after;
|
|
2007
|
+
for (; ; ) {
|
|
2008
|
+
const rows = await backend.findEdgesByKind({
|
|
2009
|
+
graphId,
|
|
2010
|
+
kind,
|
|
2011
|
+
excludeDeleted: !options.includeDeleted,
|
|
2012
|
+
orderBy: "id",
|
|
2013
|
+
limit: options.batchSize,
|
|
2014
|
+
...after === void 0 ? {} : { after }
|
|
2015
|
+
});
|
|
2016
|
+
if (rows.length === 0) return;
|
|
2017
|
+
const edges = rows.map((row) => {
|
|
2018
|
+
const edge = {
|
|
2019
|
+
kind: row.kind,
|
|
2020
|
+
id: row.id,
|
|
2021
|
+
from: {
|
|
2022
|
+
kind: row.from_kind,
|
|
2023
|
+
id: row.from_id
|
|
2024
|
+
},
|
|
2025
|
+
to: {
|
|
2026
|
+
kind: row.to_kind,
|
|
2027
|
+
id: row.to_id
|
|
2028
|
+
},
|
|
2029
|
+
properties: rowPropsToObject(row.props)
|
|
2030
|
+
};
|
|
2031
|
+
if (options.includeTemporal) {
|
|
2032
|
+
const validFrom = (
|
|
2033
|
+
// eslint-disable-next-line unicorn/no-null
|
|
2034
|
+
row.valid_from ?? null
|
|
2035
|
+
);
|
|
2036
|
+
edge.validFrom = validFrom;
|
|
2037
|
+
if (row.valid_to) {
|
|
2038
|
+
edge.validTo = row.valid_to;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
if (options.includeMeta) {
|
|
2042
|
+
edge.meta = {
|
|
2043
|
+
createdAt: row.created_at,
|
|
2044
|
+
updatedAt: row.updated_at
|
|
2045
|
+
};
|
|
2046
|
+
}
|
|
2047
|
+
return edge;
|
|
2048
|
+
});
|
|
2049
|
+
yield { type: "edges", edges };
|
|
2050
|
+
if (rows.length < options.batchSize) return;
|
|
2051
|
+
after = requireDefined(rows.at(-1)).id;
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
export { ConflictStrategySchema, ExportOptionsSchema, ExportStreamOptionsSchema, FORMAT_VERSION, GraphDataHeaderSchema, GraphDataSchema, GraphDataSourceSchema, GraphInterchangeChunkSchema, IdentityInterchangeModeSchema, ImportErrorSchema, ImportOptionsSchema, ImportResultSchema, InterchangeEdgeSchema, InterchangeIdentityAssertionSchema, InterchangeIdentitySchema, InterchangeNodeSchema, UnknownPropertyStrategySchema, exportGraph, exportGraphStream, exportStreamBackend, importGraph, importGraphStream, serializedStreamRefusal, withImportStreamLease };
|
|
2056
|
+
//# sourceMappingURL=chunk-WQZLUGVR.js.map
|
|
2057
|
+
//# sourceMappingURL=chunk-WQZLUGVR.js.map
|