@noy-db/hub 0.4.0-pre.0 → 0.4.0-pre.2
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/aggregate/index.js +4 -4
- package/dist/api-UPAT2O3M.js +20 -0
- package/dist/as/index.js +12 -12
- package/dist/attestation/index.js +18 -18
- package/dist/{backup-YPUEKIJC.js → backup-JL2GOTYQ.js} +11 -11
- package/dist/blobs/index.js +27 -15
- package/dist/blobs/index.js.map +1 -1
- package/dist/broker/index.js +12 -12
- package/dist/by/index.js +2 -2
- package/dist/cargo/index.js +51 -22
- package/dist/{chunk-QDPZWTN2.js → chunk-25G7U6RD.js} +2 -2
- package/dist/{chunk-WKGF2TJR.js → chunk-25ZDB25Z.js} +2 -2
- package/dist/{chunk-5WZXVULX.js → chunk-2ITLQZV5.js} +2 -2
- package/dist/{chunk-WNGGJWF7.js → chunk-2JZ763FV.js} +2 -2
- package/dist/{chunk-U6JMEH2P.js → chunk-2KL6NDD4.js} +90 -3
- package/dist/chunk-2KL6NDD4.js.map +1 -0
- package/dist/{chunk-RJ6K6K5M.js → chunk-2RMDVWGB.js} +2 -2
- package/dist/{chunk-VAIS3SOM.js → chunk-2U7Y7BNJ.js} +2 -2
- package/dist/{chunk-WZSHL6DQ.js → chunk-2VUISNGP.js} +5 -5
- package/dist/chunk-2YELV6TV.js +1 -0
- package/dist/{chunk-ANATMTOA.js → chunk-3O62PVDW.js} +3 -3
- package/dist/{chunk-VKCADNMR.js → chunk-4JYYIFHW.js} +12 -12
- package/dist/{chunk-6HY2X62D.js → chunk-5BB3N4KU.js} +2 -2
- package/dist/{chunk-VCTXU4AT.js → chunk-5BHNQNBI.js} +1 -1
- package/dist/chunk-5BHNQNBI.js.map +1 -0
- package/dist/{chunk-6NQMXTYC.js → chunk-5OZCCLNC.js} +2 -2
- package/dist/{chunk-HOONYZY2.js → chunk-5P5AAXYA.js} +2 -2
- package/dist/{chunk-KS6SAVCZ.js → chunk-5SZ24DL5.js} +6 -6
- package/dist/{chunk-NCCBLYEB.js → chunk-7B4GPCX7.js} +5 -5
- package/dist/{chunk-SV6EAJEP.js → chunk-A64XXIIB.js} +4 -4
- package/dist/{chunk-WOM2HTML.js → chunk-AFBJHD55.js} +5 -5
- package/dist/{chunk-RFI7GGFO.js → chunk-BCOKZC4B.js} +2 -2
- package/dist/{chunk-XUW3PXHU.js → chunk-BVZGNMVM.js} +4 -16
- package/dist/{chunk-XUW3PXHU.js.map → chunk-BVZGNMVM.js.map} +1 -1
- package/dist/{chunk-MT62YSPZ.js → chunk-BZEPJJOK.js} +4 -4
- package/dist/{chunk-OCVM6KDH.js → chunk-BZK6JEX4.js} +5 -5
- package/dist/{chunk-H2KSZMIX.js → chunk-CCT4WZI6.js} +5 -5
- package/dist/{chunk-VEALDPHW.js → chunk-CGPO3ZYB.js} +2 -2
- package/dist/{chunk-S3BFKGET.js → chunk-DEQ2JTBR.js} +3 -3
- package/dist/{chunk-3ELCWU56.js → chunk-E2R5NRZD.js} +2 -2
- package/dist/{chunk-BOKIKJJB.js → chunk-EDC6MLXV.js} +2 -2
- package/dist/chunk-F4COUZLL.js +24 -0
- package/dist/chunk-F4COUZLL.js.map +1 -0
- package/dist/{chunk-JRBNCUNT.js → chunk-FLHOTGOO.js} +3 -3
- package/dist/{chunk-6XE5TMPR.js → chunk-GBEXTC6Z.js} +3 -3
- package/dist/{chunk-YZQOGE2L.js → chunk-GS3Q2KOZ.js} +2 -2
- package/dist/{chunk-M75ZZHSR.js → chunk-GYB2QK5H.js} +2 -2
- package/dist/{chunk-EO43ANYJ.js → chunk-GZ2GKOF6.js} +3 -3
- package/dist/{chunk-VCBHAE4R.js → chunk-H4SMS4O2.js} +2 -2
- package/dist/{chunk-LVGTA624.js → chunk-HGVFJL63.js} +4 -4
- package/dist/{chunk-554KLAAN.js → chunk-HVZT5T22.js} +5 -5
- package/dist/{chunk-CJ5KKU6X.js → chunk-IGAIOGCN.js} +4 -4
- package/dist/{chunk-W2OG6JOL.js → chunk-IGM563N5.js} +18 -1
- package/dist/chunk-IGM563N5.js.map +1 -0
- package/dist/{chunk-GWKAVKHI.js → chunk-ITI3SJRB.js} +2 -2
- package/dist/{chunk-CZTLE22N.js → chunk-IU22SQGU.js} +7 -7
- package/dist/{chunk-RTEK4C4I.js → chunk-IUIVDVXM.js} +2 -2
- package/dist/{chunk-Z4PD65VQ.js → chunk-IZX3WZBF.js} +4 -4
- package/dist/{chunk-RDXW3OBQ.js → chunk-J3VW4J4E.js} +2 -2
- package/dist/{chunk-IK43HLDQ.js → chunk-J6U4BOKP.js} +11 -11
- package/dist/{chunk-XJMBYYQK.js → chunk-JA5B3SLH.js} +2 -2
- package/dist/{chunk-IAO54J4R.js → chunk-JA5O7UEZ.js} +6 -6
- package/dist/{chunk-6HPG2YNX.js → chunk-JKWNG3TM.js} +2 -2
- package/dist/{chunk-WQIYTNJV.js → chunk-JLHKBAFY.js} +3 -3
- package/dist/{chunk-ISMV2QVG.js → chunk-KMONEHDJ.js} +2 -2
- package/dist/{chunk-XTHCN77N.js → chunk-KR5EGWI4.js} +7 -19
- package/dist/chunk-KR5EGWI4.js.map +1 -0
- package/dist/{chunk-JBBSYJHP.js → chunk-KVHWEPT2.js} +2 -2
- package/dist/{chunk-2PCAVW2I.js → chunk-L4OA7G5T.js} +2 -2
- package/dist/{chunk-IY23KFNZ.js → chunk-LLVW4V7R.js} +3 -3
- package/dist/{chunk-N6WF5PKC.js → chunk-M25ZIEB2.js} +2 -2
- package/dist/{chunk-FXZDAV62.js → chunk-MEKAMERX.js} +5 -7
- package/dist/{chunk-FXZDAV62.js.map → chunk-MEKAMERX.js.map} +1 -1
- package/dist/{chunk-UC2ETWF3.js → chunk-MSO2C3KG.js} +2 -2
- package/dist/{chunk-ONTBCHCP.js → chunk-N4FZ6ITM.js} +2 -2
- package/dist/{chunk-DRWBEDYO.js → chunk-NM4LVUXH.js} +2 -2
- package/dist/{chunk-DSBQODMZ.js → chunk-NOT7XA7Q.js} +5 -5
- package/dist/{chunk-RA5VTXXG.js → chunk-OGFPMCSP.js} +2 -2
- package/dist/{chunk-WHXXI3W5.js → chunk-OGY4ELVE.js} +3 -3
- package/dist/{chunk-T36RFODW.js → chunk-OOTTXUNV.js} +2 -2
- package/dist/{chunk-A5BDDEXK.js → chunk-OTU5ZI3U.js} +4 -4
- package/dist/{chunk-THVIPWRF.js → chunk-OVFCAS7U.js} +9 -9
- package/dist/{chunk-IFJTPAQQ.js → chunk-OWE4BNTM.js} +4 -4
- package/dist/{chunk-W4TCUGQR.js → chunk-PEY6ZTCF.js} +12 -8
- package/dist/{chunk-W4TCUGQR.js.map → chunk-PEY6ZTCF.js.map} +1 -1
- package/dist/{chunk-UDSR4X4L.js → chunk-PWZKD4HW.js} +74 -10
- package/dist/chunk-PWZKD4HW.js.map +1 -0
- package/dist/{chunk-HQBM64EE.js → chunk-QAFWRGVD.js} +127 -8
- package/dist/chunk-QAFWRGVD.js.map +1 -0
- package/dist/{chunk-ZXEZCY6F.js → chunk-QCUH2G77.js} +6 -6
- package/dist/{chunk-XDIAFPUM.js → chunk-QDNVHA2F.js} +2 -2
- package/dist/{chunk-FH52K7EG.js → chunk-QRHEAZ2Q.js} +2 -6
- package/dist/{chunk-FH52K7EG.js.map → chunk-QRHEAZ2Q.js.map} +1 -1
- package/dist/{chunk-A4FXFAKF.js → chunk-QXPOEAKR.js} +2 -2
- package/dist/{chunk-EWPOHMN2.js → chunk-RGL53NXZ.js} +3 -3
- package/dist/{chunk-TAGR72IR.js → chunk-RI3OKC3Y.js} +4 -4
- package/dist/{chunk-5PFOMHTB.js → chunk-S3OYXPSE.js} +9 -9
- package/dist/{chunk-JVBO4SZL.js → chunk-STR7WBOB.js} +2 -2
- package/dist/{chunk-DDHM2TLX.js → chunk-T7S4V52L.js} +4 -4
- package/dist/{chunk-WG4QBZDN.js → chunk-TYBXP6WE.js} +7 -7
- package/dist/{chunk-T3GY65MA.js → chunk-UEO5CHOP.js} +3 -3
- package/dist/{chunk-RNG4VRP4.js → chunk-V3YB7RGR.js} +3 -3
- package/dist/{chunk-R3BLCFD2.js → chunk-V7RGFJ3Z.js} +6 -6
- package/dist/{chunk-SMIRGMS7.js → chunk-VEX6G3YL.js} +2 -2
- package/dist/{chunk-PK6JIDQ4.js → chunk-VZELV7ZJ.js} +116 -119
- package/dist/chunk-VZELV7ZJ.js.map +1 -0
- package/dist/{chunk-GULTLPFB.js → chunk-XGVDLQJE.js} +3 -3
- package/dist/{chunk-JFCV5QIK.js → chunk-XULHFSHO.js} +5 -5
- package/dist/{chunk-S6Q37VGE.js → chunk-XW7NWO4A.js} +2 -2
- package/dist/{chunk-YCH5GBN6.js → chunk-Y5E6CH6S.js} +6 -6
- package/dist/{chunk-GDG5VVYG.js → chunk-YG2EG6CB.js} +4 -4
- package/dist/{chunk-UHNGNKLV.js → chunk-YGBIBOCR.js} +307 -17
- package/dist/chunk-YGBIBOCR.js.map +1 -0
- package/dist/{chunk-YTGDPCPW.js → chunk-YHPUDB7Q.js} +2 -2
- package/dist/{chunk-VRBTNCM4.js → chunk-ZJGZDOQO.js} +6 -6
- package/dist/classified/index.js +5 -5
- package/dist/{classified-marker-QWMASIT7.js → classified-marker-7S5IEP6Q.js} +3 -3
- package/dist/collection-facade-ENLTJZBT.js +44 -0
- package/dist/{computed-DSMPXG2Y.js → computed-WZNKRLCY.js} +3 -3
- package/dist/consent/index.js +9 -9
- package/dist/cover-B5MY43CK.js +36 -0
- package/dist/{dead-filter-66VGI6JV.js → dead-filter-KRRD57MR.js} +2 -2
- package/dist/delegation-UT76AQIG.js +25 -0
- package/dist/derivations/index.js +12 -12
- package/dist/derive-SGRCCKVP.js +21 -0
- package/dist/{enclave-TKPN2G2U.js → enclave-YFKA3GJT.js} +11 -9
- package/dist/executor-TEZYRDN3.js +9 -0
- package/dist/executor-UF7LTX6O.js +26 -0
- package/dist/executor-WQQRU6DO.js +9 -0
- package/dist/export-accessible-HXMKW5LJ.js +23 -0
- package/dist/extract-partition-QGQ2R42J.js +38 -0
- package/dist/{fanout-sidecar-B5LVUMQX.js → fanout-sidecar-HJV3A7PM.js} +9 -9
- package/dist/find-VV6PBIHK.js +11 -0
- package/dist/forget/index.js +9 -9
- package/dist/guards/index.js +3 -3
- package/dist/history/index.js +12 -12
- package/dist/i18n/index.js +14 -14
- package/dist/index.d.ts +1 -4
- package/dist/index.js +151 -168
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +2 -2
- package/dist/issue-7JJ4Z7E2.js +19 -0
- package/dist/kernel/enclave/index.d.ts +1 -1
- package/dist/kernel/enclave/record-keys/envelope-body.d.ts +8 -0
- package/dist/kernel/errors.d.ts +19 -0
- package/dist/kernel/noydb.d.ts +0 -6
- package/dist/kernel/types.d.ts +61 -17
- package/dist/kernel/vault.d.ts +0 -4
- package/dist/{ledger-PTK7XZYT.js → ledger-SW6QQOAV.js} +11 -11
- package/dist/liberate-W5MDWYOV.js +24 -0
- package/dist/{link-set-SLJHDNRB.js → link-set-GNGGCFI4.js} +11 -11
- package/dist/materialized-views/index.js +19 -19
- package/dist/noydb-U63S3Z7F.js +70 -0
- package/dist/on/index.js +9 -9
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.js +19 -17
- package/dist/pod/index.js +27 -12
- package/dist/{policy-YDLCOX4P.js → policy-EAXSJNOT.js} +5 -5
- package/dist/portability/index.js +8 -8
- package/dist/{post-register-2YHSADFI.js → post-register-A7HCFBP5.js} +6 -6
- package/dist/query/index.js +6 -6
- package/dist/register-YBMRLZLO.js +23 -0
- package/dist/registry-3LXKNXWI.js +9 -0
- package/dist/registry-4V4YQCXJ.js +19 -0
- package/dist/registry-ZULMLCJK.js +18 -0
- package/dist/request-withdrawal-SEVUEUDG.js +31 -0
- package/dist/{reveal-HWO4YMTJ.js → reveal-PB3YG544.js} +6 -6
- package/dist/revoke-5QULL5GT.js +24 -0
- package/dist/satellites/index.js +1 -1
- package/dist/sealed-record/index.js +12 -12
- package/dist/{seed-3ZMF5EQA.js → seed-6GTDHL4E.js} +11 -11
- package/dist/session/index.js +9 -9
- package/dist/shadow/index.js +2 -2
- package/dist/share-link/index.d.ts +19 -0
- package/dist/share-link/index.js +201 -0
- package/dist/share-link/index.js.map +1 -0
- package/dist/share-link/share-link.d.ts +123 -0
- package/dist/signer-ZMH35FT4.js +25 -0
- package/dist/snapshots/index.js +10 -10
- package/dist/{stale-VZL4OGUB.js → stale-NB5BGRVU.js} +10 -10
- package/dist/storage-QOZKQMID.js +23 -0
- package/dist/{store-coordination-provider-FKX3F6TQ.js → store-coordination-provider-3AELCZ4Y.js} +3 -3
- package/dist/sync/index.js +10 -9
- package/dist/sync/index.js.map +1 -1
- package/dist/team/index.js +29 -28
- package/dist/tiers/index.js +10 -10
- package/dist/to/index.js +1 -1
- package/dist/tx/index.js +3 -3
- package/dist/util/index.js +1 -1
- package/dist/{verify-26Y5GNMK.js → verify-MXN66IIH.js} +5 -5
- package/dist/via/blob/active.d.ts +27 -1
- package/dist/via/blob/index.d.ts +4 -0
- package/dist/{walk-IPLXE36W.js → walk-H4YE52S2.js} +11 -11
- package/dist/with/index.js +11 -11
- package/dist/with-audit/periods/periods.d.ts +1 -16
- package/dist/with-audit/periods/vault-facade.d.ts +6 -0
- package/dist/with-audit/periods/window.d.ts +16 -0
- package/dist/{legacy/kernel.d.ts → with-cargo/floor.d.ts} +1 -1
- package/dist/with-cargo/index.d.ts +12 -1
- package/dist/with-party/directory/cover/index.d.ts +0 -5
- package/dist/with-party/directory/cover/schema.d.ts +0 -6
- package/dist/with-party/directory/cover/storage.d.ts +0 -8
- package/dist/with-party/directory/cover/types.d.ts +0 -21
- package/dist/with-party/team/sync-period-scope.d.ts +78 -0
- package/dist/with-party/team/sync.d.ts +13 -0
- package/dist/with-pod/bundle.d.ts +0 -2
- package/dist/with-pod/index.d.ts +2 -1
- package/dist/with-shape/blobs/blob-compaction.d.ts +43 -3
- package/dist/with-shape/blobs/blob-pinning.d.ts +112 -0
- package/dist/with-shape/blobs/blob-set.d.ts +85 -1
- package/dist/withdraw-accessible-HPOVNG2G.js +28 -0
- package/package.json +7 -7
- package/dist/api-3A3S4SHH.js +0 -20
- package/dist/bundle/index.js +0 -153
- package/dist/bundle/index.js.map +0 -1
- package/dist/chunk-CHFZDSE4.js +0 -1
- package/dist/chunk-HQBM64EE.js.map +0 -1
- package/dist/chunk-PK6JIDQ4.js.map +0 -1
- package/dist/chunk-TIGPQDYP.js +0 -14
- package/dist/chunk-TIGPQDYP.js.map +0 -1
- package/dist/chunk-U6JMEH2P.js.map +0 -1
- package/dist/chunk-UDSR4X4L.js.map +0 -1
- package/dist/chunk-UHNGNKLV.js.map +0 -1
- package/dist/chunk-VCTXU4AT.js.map +0 -1
- package/dist/chunk-W2OG6JOL.js.map +0 -1
- package/dist/chunk-XTHCN77N.js.map +0 -1
- package/dist/collection-facade-KMBWNCYP.js +0 -44
- package/dist/cover-QJRXINN5.js +0 -52
- package/dist/delegation-SU6CV3W7.js +0 -25
- package/dist/derive-6RWU4EOA.js +0 -21
- package/dist/executor-KGNLJD6M.js +0 -9
- package/dist/executor-UX4FASYZ.js +0 -26
- package/dist/executor-XGIS6CR7.js +0 -9
- package/dist/export-accessible-LKW3QV22.js +0 -23
- package/dist/extract-partition-UP2ARO75.js +0 -38
- package/dist/find-YS6XWB46.js +0 -11
- package/dist/issue-3ACYX7LO.js +0 -19
- package/dist/legacy/bundle.d.ts +0 -30
- package/dist/liberate-MQQXBUCY.js +0 -24
- package/dist/noydb-ANIW5FLE.js +0 -69
- package/dist/register-PAM3ASRZ.js +0 -23
- package/dist/registry-4S2U2TR4.js +0 -19
- package/dist/registry-NPOWJHC4.js +0 -9
- package/dist/registry-VZ3CCI2R.js +0 -18
- package/dist/request-withdrawal-DI3H5WUI.js +0 -31
- package/dist/revoke-R5CZSVUT.js +0 -24
- package/dist/signer-MBVHW4WE.js +0 -25
- package/dist/storage-3MEJUENZ.js +0 -23
- package/dist/withdraw-accessible-TZDATQVR.js +0 -28
- /package/dist/{api-3A3S4SHH.js.map → api-UPAT2O3M.js.map} +0 -0
- /package/dist/{backup-YPUEKIJC.js.map → backup-JL2GOTYQ.js.map} +0 -0
- /package/dist/{chunk-QDPZWTN2.js.map → chunk-25G7U6RD.js.map} +0 -0
- /package/dist/{chunk-WKGF2TJR.js.map → chunk-25ZDB25Z.js.map} +0 -0
- /package/dist/{chunk-5WZXVULX.js.map → chunk-2ITLQZV5.js.map} +0 -0
- /package/dist/{chunk-WNGGJWF7.js.map → chunk-2JZ763FV.js.map} +0 -0
- /package/dist/{chunk-RJ6K6K5M.js.map → chunk-2RMDVWGB.js.map} +0 -0
- /package/dist/{chunk-VAIS3SOM.js.map → chunk-2U7Y7BNJ.js.map} +0 -0
- /package/dist/{chunk-WZSHL6DQ.js.map → chunk-2VUISNGP.js.map} +0 -0
- /package/dist/{chunk-CHFZDSE4.js.map → chunk-2YELV6TV.js.map} +0 -0
- /package/dist/{chunk-ANATMTOA.js.map → chunk-3O62PVDW.js.map} +0 -0
- /package/dist/{chunk-VKCADNMR.js.map → chunk-4JYYIFHW.js.map} +0 -0
- /package/dist/{chunk-6HY2X62D.js.map → chunk-5BB3N4KU.js.map} +0 -0
- /package/dist/{chunk-6NQMXTYC.js.map → chunk-5OZCCLNC.js.map} +0 -0
- /package/dist/{chunk-HOONYZY2.js.map → chunk-5P5AAXYA.js.map} +0 -0
- /package/dist/{chunk-KS6SAVCZ.js.map → chunk-5SZ24DL5.js.map} +0 -0
- /package/dist/{chunk-NCCBLYEB.js.map → chunk-7B4GPCX7.js.map} +0 -0
- /package/dist/{chunk-SV6EAJEP.js.map → chunk-A64XXIIB.js.map} +0 -0
- /package/dist/{chunk-WOM2HTML.js.map → chunk-AFBJHD55.js.map} +0 -0
- /package/dist/{chunk-RFI7GGFO.js.map → chunk-BCOKZC4B.js.map} +0 -0
- /package/dist/{chunk-MT62YSPZ.js.map → chunk-BZEPJJOK.js.map} +0 -0
- /package/dist/{chunk-OCVM6KDH.js.map → chunk-BZK6JEX4.js.map} +0 -0
- /package/dist/{chunk-H2KSZMIX.js.map → chunk-CCT4WZI6.js.map} +0 -0
- /package/dist/{chunk-VEALDPHW.js.map → chunk-CGPO3ZYB.js.map} +0 -0
- /package/dist/{chunk-S3BFKGET.js.map → chunk-DEQ2JTBR.js.map} +0 -0
- /package/dist/{chunk-3ELCWU56.js.map → chunk-E2R5NRZD.js.map} +0 -0
- /package/dist/{chunk-BOKIKJJB.js.map → chunk-EDC6MLXV.js.map} +0 -0
- /package/dist/{chunk-JRBNCUNT.js.map → chunk-FLHOTGOO.js.map} +0 -0
- /package/dist/{chunk-6XE5TMPR.js.map → chunk-GBEXTC6Z.js.map} +0 -0
- /package/dist/{chunk-YZQOGE2L.js.map → chunk-GS3Q2KOZ.js.map} +0 -0
- /package/dist/{chunk-M75ZZHSR.js.map → chunk-GYB2QK5H.js.map} +0 -0
- /package/dist/{chunk-EO43ANYJ.js.map → chunk-GZ2GKOF6.js.map} +0 -0
- /package/dist/{chunk-VCBHAE4R.js.map → chunk-H4SMS4O2.js.map} +0 -0
- /package/dist/{chunk-LVGTA624.js.map → chunk-HGVFJL63.js.map} +0 -0
- /package/dist/{chunk-554KLAAN.js.map → chunk-HVZT5T22.js.map} +0 -0
- /package/dist/{chunk-CJ5KKU6X.js.map → chunk-IGAIOGCN.js.map} +0 -0
- /package/dist/{chunk-GWKAVKHI.js.map → chunk-ITI3SJRB.js.map} +0 -0
- /package/dist/{chunk-CZTLE22N.js.map → chunk-IU22SQGU.js.map} +0 -0
- /package/dist/{chunk-RTEK4C4I.js.map → chunk-IUIVDVXM.js.map} +0 -0
- /package/dist/{chunk-Z4PD65VQ.js.map → chunk-IZX3WZBF.js.map} +0 -0
- /package/dist/{chunk-RDXW3OBQ.js.map → chunk-J3VW4J4E.js.map} +0 -0
- /package/dist/{chunk-IK43HLDQ.js.map → chunk-J6U4BOKP.js.map} +0 -0
- /package/dist/{chunk-XJMBYYQK.js.map → chunk-JA5B3SLH.js.map} +0 -0
- /package/dist/{chunk-IAO54J4R.js.map → chunk-JA5O7UEZ.js.map} +0 -0
- /package/dist/{chunk-6HPG2YNX.js.map → chunk-JKWNG3TM.js.map} +0 -0
- /package/dist/{chunk-WQIYTNJV.js.map → chunk-JLHKBAFY.js.map} +0 -0
- /package/dist/{chunk-ISMV2QVG.js.map → chunk-KMONEHDJ.js.map} +0 -0
- /package/dist/{chunk-JBBSYJHP.js.map → chunk-KVHWEPT2.js.map} +0 -0
- /package/dist/{chunk-2PCAVW2I.js.map → chunk-L4OA7G5T.js.map} +0 -0
- /package/dist/{chunk-IY23KFNZ.js.map → chunk-LLVW4V7R.js.map} +0 -0
- /package/dist/{chunk-N6WF5PKC.js.map → chunk-M25ZIEB2.js.map} +0 -0
- /package/dist/{chunk-UC2ETWF3.js.map → chunk-MSO2C3KG.js.map} +0 -0
- /package/dist/{chunk-ONTBCHCP.js.map → chunk-N4FZ6ITM.js.map} +0 -0
- /package/dist/{chunk-DRWBEDYO.js.map → chunk-NM4LVUXH.js.map} +0 -0
- /package/dist/{chunk-DSBQODMZ.js.map → chunk-NOT7XA7Q.js.map} +0 -0
- /package/dist/{chunk-RA5VTXXG.js.map → chunk-OGFPMCSP.js.map} +0 -0
- /package/dist/{chunk-WHXXI3W5.js.map → chunk-OGY4ELVE.js.map} +0 -0
- /package/dist/{chunk-T36RFODW.js.map → chunk-OOTTXUNV.js.map} +0 -0
- /package/dist/{chunk-A5BDDEXK.js.map → chunk-OTU5ZI3U.js.map} +0 -0
- /package/dist/{chunk-THVIPWRF.js.map → chunk-OVFCAS7U.js.map} +0 -0
- /package/dist/{chunk-IFJTPAQQ.js.map → chunk-OWE4BNTM.js.map} +0 -0
- /package/dist/{chunk-ZXEZCY6F.js.map → chunk-QCUH2G77.js.map} +0 -0
- /package/dist/{chunk-XDIAFPUM.js.map → chunk-QDNVHA2F.js.map} +0 -0
- /package/dist/{chunk-A4FXFAKF.js.map → chunk-QXPOEAKR.js.map} +0 -0
- /package/dist/{chunk-EWPOHMN2.js.map → chunk-RGL53NXZ.js.map} +0 -0
- /package/dist/{chunk-TAGR72IR.js.map → chunk-RI3OKC3Y.js.map} +0 -0
- /package/dist/{chunk-5PFOMHTB.js.map → chunk-S3OYXPSE.js.map} +0 -0
- /package/dist/{chunk-JVBO4SZL.js.map → chunk-STR7WBOB.js.map} +0 -0
- /package/dist/{chunk-DDHM2TLX.js.map → chunk-T7S4V52L.js.map} +0 -0
- /package/dist/{chunk-WG4QBZDN.js.map → chunk-TYBXP6WE.js.map} +0 -0
- /package/dist/{chunk-T3GY65MA.js.map → chunk-UEO5CHOP.js.map} +0 -0
- /package/dist/{chunk-RNG4VRP4.js.map → chunk-V3YB7RGR.js.map} +0 -0
- /package/dist/{chunk-R3BLCFD2.js.map → chunk-V7RGFJ3Z.js.map} +0 -0
- /package/dist/{chunk-SMIRGMS7.js.map → chunk-VEX6G3YL.js.map} +0 -0
- /package/dist/{chunk-GULTLPFB.js.map → chunk-XGVDLQJE.js.map} +0 -0
- /package/dist/{chunk-JFCV5QIK.js.map → chunk-XULHFSHO.js.map} +0 -0
- /package/dist/{chunk-S6Q37VGE.js.map → chunk-XW7NWO4A.js.map} +0 -0
- /package/dist/{chunk-YCH5GBN6.js.map → chunk-Y5E6CH6S.js.map} +0 -0
- /package/dist/{chunk-GDG5VVYG.js.map → chunk-YG2EG6CB.js.map} +0 -0
- /package/dist/{chunk-YTGDPCPW.js.map → chunk-YHPUDB7Q.js.map} +0 -0
- /package/dist/{chunk-VRBTNCM4.js.map → chunk-ZJGZDOQO.js.map} +0 -0
- /package/dist/{classified-marker-QWMASIT7.js.map → classified-marker-7S5IEP6Q.js.map} +0 -0
- /package/dist/{collection-facade-KMBWNCYP.js.map → collection-facade-ENLTJZBT.js.map} +0 -0
- /package/dist/{computed-DSMPXG2Y.js.map → computed-WZNKRLCY.js.map} +0 -0
- /package/dist/{cover-QJRXINN5.js.map → cover-B5MY43CK.js.map} +0 -0
- /package/dist/{dead-filter-66VGI6JV.js.map → dead-filter-KRRD57MR.js.map} +0 -0
- /package/dist/{delegation-SU6CV3W7.js.map → delegation-UT76AQIG.js.map} +0 -0
- /package/dist/{derive-6RWU4EOA.js.map → derive-SGRCCKVP.js.map} +0 -0
- /package/dist/{enclave-TKPN2G2U.js.map → enclave-YFKA3GJT.js.map} +0 -0
- /package/dist/{executor-KGNLJD6M.js.map → executor-TEZYRDN3.js.map} +0 -0
- /package/dist/{executor-UX4FASYZ.js.map → executor-UF7LTX6O.js.map} +0 -0
- /package/dist/{executor-XGIS6CR7.js.map → executor-WQQRU6DO.js.map} +0 -0
- /package/dist/{export-accessible-LKW3QV22.js.map → export-accessible-HXMKW5LJ.js.map} +0 -0
- /package/dist/{extract-partition-UP2ARO75.js.map → extract-partition-QGQ2R42J.js.map} +0 -0
- /package/dist/{fanout-sidecar-B5LVUMQX.js.map → fanout-sidecar-HJV3A7PM.js.map} +0 -0
- /package/dist/{find-YS6XWB46.js.map → find-VV6PBIHK.js.map} +0 -0
- /package/dist/{issue-3ACYX7LO.js.map → issue-7JJ4Z7E2.js.map} +0 -0
- /package/dist/{ledger-PTK7XZYT.js.map → ledger-SW6QQOAV.js.map} +0 -0
- /package/dist/{liberate-MQQXBUCY.js.map → liberate-W5MDWYOV.js.map} +0 -0
- /package/dist/{link-set-SLJHDNRB.js.map → link-set-GNGGCFI4.js.map} +0 -0
- /package/dist/{noydb-ANIW5FLE.js.map → noydb-U63S3Z7F.js.map} +0 -0
- /package/dist/{policy-YDLCOX4P.js.map → policy-EAXSJNOT.js.map} +0 -0
- /package/dist/{post-register-2YHSADFI.js.map → post-register-A7HCFBP5.js.map} +0 -0
- /package/dist/{register-PAM3ASRZ.js.map → register-YBMRLZLO.js.map} +0 -0
- /package/dist/{registry-4S2U2TR4.js.map → registry-3LXKNXWI.js.map} +0 -0
- /package/dist/{registry-NPOWJHC4.js.map → registry-4V4YQCXJ.js.map} +0 -0
- /package/dist/{registry-VZ3CCI2R.js.map → registry-ZULMLCJK.js.map} +0 -0
- /package/dist/{request-withdrawal-DI3H5WUI.js.map → request-withdrawal-SEVUEUDG.js.map} +0 -0
- /package/dist/{reveal-HWO4YMTJ.js.map → reveal-PB3YG544.js.map} +0 -0
- /package/dist/{revoke-R5CZSVUT.js.map → revoke-5QULL5GT.js.map} +0 -0
- /package/dist/{seed-3ZMF5EQA.js.map → seed-6GTDHL4E.js.map} +0 -0
- /package/dist/{signer-MBVHW4WE.js.map → signer-ZMH35FT4.js.map} +0 -0
- /package/dist/{stale-VZL4OGUB.js.map → stale-NB5BGRVU.js.map} +0 -0
- /package/dist/{storage-3MEJUENZ.js.map → storage-QOZKQMID.js.map} +0 -0
- /package/dist/{store-coordination-provider-FKX3F6TQ.js.map → store-coordination-provider-3AELCZ4Y.js.map} +0 -0
- /package/dist/{verify-26Y5GNMK.js.map → verify-MXN66IIH.js.map} +0 -0
- /package/dist/{walk-IPLXE36W.js.map → walk-H4YE52S2.js.map} +0 -0
- /package/dist/{withdraw-accessible-TZDATQVR.js.map → withdraw-accessible-HPOVNG2G.js.map} +0 -0
|
@@ -1,32 +1,94 @@
|
|
|
1
|
+
import {
|
|
2
|
+
walkClosure
|
|
3
|
+
} from "./chunk-OVFCAS7U.js";
|
|
1
4
|
import {
|
|
2
5
|
resolveManagedSecret
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-BCOKZC4B.js";
|
|
4
7
|
import {
|
|
5
8
|
parseExtractedPartitionBody,
|
|
6
9
|
readPod,
|
|
7
10
|
readPodHeader
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-MEKAMERX.js";
|
|
9
12
|
import {
|
|
10
13
|
createOwnerKeyring
|
|
11
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-HVZT5T22.js";
|
|
12
15
|
import {
|
|
13
16
|
LedgerStore
|
|
14
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-ZJGZDOQO.js";
|
|
15
18
|
import {
|
|
16
19
|
LEDGER_COLLECTION
|
|
17
20
|
} from "./chunk-I7FD46FF.js";
|
|
18
21
|
import {
|
|
19
22
|
openEnvelopeJson
|
|
20
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-PEY6ZTCF.js";
|
|
21
24
|
import {
|
|
22
25
|
base64ToBuffer,
|
|
23
26
|
wrapKey
|
|
24
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-5P5AAXYA.js";
|
|
25
28
|
import {
|
|
26
29
|
AdoptionStateError,
|
|
27
30
|
TransferSealError,
|
|
28
31
|
ValidationError
|
|
29
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-IGM563N5.js";
|
|
33
|
+
|
|
34
|
+
// src/with-cargo/active.ts
|
|
35
|
+
function withCargo() {
|
|
36
|
+
return {
|
|
37
|
+
async extractPartition(vault, opts) {
|
|
38
|
+
const { extractPartitionCore } = await import("./extract-partition-QGQ2R42J.js");
|
|
39
|
+
return extractPartitionCore(vault, opts);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/with-cargo/describe-extraction.ts
|
|
45
|
+
async function describeExtraction(vault, opts) {
|
|
46
|
+
const { closure, graph, danglingRefs } = await walkClosure(vault, opts);
|
|
47
|
+
const { name: vaultName, adapter } = vault._introspectState();
|
|
48
|
+
const encoder = new TextEncoder();
|
|
49
|
+
const byCollection = [];
|
|
50
|
+
const inaccessible = [];
|
|
51
|
+
let totalBytes = 0;
|
|
52
|
+
let totalRecords = 0;
|
|
53
|
+
for (const [collectionName, ids] of closure) {
|
|
54
|
+
let bytes = 0;
|
|
55
|
+
let oldestTs;
|
|
56
|
+
let newestTs;
|
|
57
|
+
let recordCount = 0;
|
|
58
|
+
for (const id of ids) {
|
|
59
|
+
const env = await adapter.get(vaultName, collectionName, id);
|
|
60
|
+
if (!env) {
|
|
61
|
+
inaccessible.push({ collection: collectionName, id });
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
recordCount++;
|
|
65
|
+
bytes += encoder.encode(JSON.stringify(env)).length;
|
|
66
|
+
const ts = env._ts;
|
|
67
|
+
if (oldestTs === void 0 || ts < oldestTs) oldestTs = ts;
|
|
68
|
+
if (newestTs === void 0 || ts > newestTs) newestTs = ts;
|
|
69
|
+
}
|
|
70
|
+
byCollection.push({
|
|
71
|
+
name: collectionName,
|
|
72
|
+
recordCount,
|
|
73
|
+
bytes,
|
|
74
|
+
// Spread conditionally — exactOptionalPropertyTypes forbids an
|
|
75
|
+
// explicit `undefined` on an optional property.
|
|
76
|
+
...oldestTs !== void 0 ? { oldestTs } : {},
|
|
77
|
+
...newestTs !== void 0 ? { newestTs } : {}
|
|
78
|
+
});
|
|
79
|
+
totalBytes += bytes;
|
|
80
|
+
totalRecords += recordCount;
|
|
81
|
+
}
|
|
82
|
+
byCollection.sort((a, b) => a.name.localeCompare(b.name));
|
|
83
|
+
return Object.freeze({
|
|
84
|
+
totalRecords,
|
|
85
|
+
totalBytes,
|
|
86
|
+
byCollection,
|
|
87
|
+
graph,
|
|
88
|
+
inaccessible,
|
|
89
|
+
danglingRefs
|
|
90
|
+
});
|
|
91
|
+
}
|
|
30
92
|
|
|
31
93
|
// src/with-cargo/adopt-partition.ts
|
|
32
94
|
async function unsealDeks(seal, transferKey) {
|
|
@@ -179,7 +241,7 @@ async function createOwnerOnAdoptedPartition(store, vaultName, opts) {
|
|
|
179
241
|
}
|
|
180
242
|
}
|
|
181
243
|
if (isManaged(opts)) {
|
|
182
|
-
const { createNoydb } = await import("./noydb-
|
|
244
|
+
const { createNoydb } = await import("./noydb-U63S3Z7F.js");
|
|
183
245
|
const db = await createNoydb({
|
|
184
246
|
store,
|
|
185
247
|
user: userId,
|
|
@@ -230,6 +292,8 @@ export {
|
|
|
230
292
|
unsealDeks,
|
|
231
293
|
adoptPartition,
|
|
232
294
|
createOwnerOnAdoptedPartition,
|
|
233
|
-
decryptExtractedPartition
|
|
295
|
+
decryptExtractedPartition,
|
|
296
|
+
withCargo,
|
|
297
|
+
describeExtraction
|
|
234
298
|
};
|
|
235
|
-
//# sourceMappingURL=chunk-
|
|
299
|
+
//# sourceMappingURL=chunk-PWZKD4HW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-cargo/active.ts","../src/with-cargo/describe-extraction.ts","../src/with-cargo/adopt-partition.ts","../src/with-cargo/decrypt-partition.ts"],"sourcesContent":["/**\n * Enable the cargo (partition extraction) capability.\n * Pass to `createNoydb({ cargoStrategy: withCargo() })` to make the source-side\n * `extractPartition(vault, ...)` free function live. The extraction crypto\n * engine is dynamically imported here, so it stays out of the floor bundle\n * until opted in. The recipient-side `adoptPartition` /\n * `decryptExtractedPartition` free functions — and `diffVault` (shared\n * import/merge infra) — are ungated and unaffected.\n */\nimport type { CargoStrategy } from './strategy.js'\n\nexport function withCargo(): CargoStrategy {\n return {\n async extractPartition(vault, opts) {\n const { extractPartitionCore } = await import('./extract-partition.js')\n return extractPartitionCore(vault, opts)\n },\n }\n}\n","/**\n * Partition-extraction dry-run. Read-only preview of what an\n * `extractPartition` would move: record counts, byte totals, and the\n * timestamp span per collection — computed from raw encrypted\n * envelopes WITHOUT decrypting them. Writes nothing, mutates nothing.\n *\n * @module\n */\nimport type { Vault } from '../kernel/vault.js'\nimport { walkClosure, type WalkClosureOptions, type DanglingRefNotice } from './walk-closure.js'\n\nexport interface ExtractionPreview {\n readonly totalRecords: number\n /** Sum of serialized encrypted-envelope sizes (bytes). */\n readonly totalBytes: number\n readonly byCollection: ReadonlyArray<{\n readonly name: string\n readonly recordCount: number\n readonly bytes: number\n /** Earliest envelope `_ts` in this collection (lexicographic). */\n readonly oldestTs?: string\n readonly newestTs?: string\n }>\n readonly graph: { readonly depth: number; readonly cyclesDetected: boolean }\n /** Records the walk reached but whose envelope couldn't be read. */\n readonly inaccessible: ReadonlyArray<{ readonly collection: string; readonly id: string }>\n /**\n * #772: outbound FK edges whose referenced parent was excluded from the\n * closure (missing, or tier-elevated and therefore invisible) — mirrors\n * `ExtractPartitionResult.danglingRefs` (#759) so a preview/extract pair\n * gives the SAME signal. A parent excluded this way never appears in\n * `byCollection` at all; this is the only preview-time indicator.\n */\n readonly danglingRefs: ReadonlyArray<DanglingRefNotice>\n}\n\nexport async function describeExtraction(\n vault: Vault,\n opts: WalkClosureOptions,\n): Promise<ExtractionPreview> {\n const { closure, graph, danglingRefs } = await walkClosure(vault, opts)\n\n const { name: vaultName, adapter } = vault._introspectState()\n const encoder = new TextEncoder()\n\n const byCollection: Array<{\n name: string; recordCount: number; bytes: number; oldestTs?: string; newestTs?: string\n }> = []\n const inaccessible: Array<{ collection: string; id: string }> = []\n let totalBytes = 0\n let totalRecords = 0\n\n for (const [collectionName, ids] of closure) {\n let bytes = 0\n let oldestTs: string | undefined\n let newestTs: string | undefined\n let recordCount = 0\n\n for (const id of ids) {\n const env = await adapter.get(vaultName, collectionName, id)\n if (!env) {\n // Walk reached it (via decrypted list) but the raw store read\n // returned nothing — surface rather than miscount.\n inaccessible.push({ collection: collectionName, id })\n continue\n }\n recordCount++\n bytes += encoder.encode(JSON.stringify(env)).length\n const ts = env._ts\n if (oldestTs === undefined || ts < oldestTs) oldestTs = ts\n if (newestTs === undefined || ts > newestTs) newestTs = ts\n }\n\n byCollection.push({\n name: collectionName,\n recordCount,\n bytes,\n // Spread conditionally — exactOptionalPropertyTypes forbids an\n // explicit `undefined` on an optional property.\n ...(oldestTs !== undefined ? { oldestTs } : {}),\n ...(newestTs !== undefined ? { newestTs } : {}),\n })\n totalBytes += bytes\n totalRecords += recordCount\n }\n\n byCollection.sort((a, b) => a.name.localeCompare(b.name))\n\n return Object.freeze({\n totalRecords,\n totalBytes,\n byCollection,\n graph,\n inaccessible,\n danglingRefs,\n })\n}\n","/**\n * Partition adoption. Recipient side: verify an extracted bundle,\n * validate the transfer key, import the re-keyed collections into a\n * destination store, and record an `_meta/adoption` marker. The bundle\n * stays UNOWNED after adoption — `createOwnerOnAdoptedPartition`\n * mints the owner; the transfer seal is then destroyed.\n *\n * @module\n */\nimport { base64ToBuffer, wrapKey, type EnclaveKey } from '../kernel/enclave/index.js'\nimport { TransferSealError, AdoptionStateError, ValidationError } from '../kernel/errors.js'\nimport type { NoydbStore, VaultSnapshot, KeyringFile } from '../kernel/types.js'\nimport { createOwnerKeyring } from '../with-party/team/keyring.js'\nimport { resolveManagedSecret } from '../with-party/team/managed-passphrase.js'\nimport type { SealingKeyProvider } from '../with-party/team/managed-passphrase.js'\nimport type { ShamirRecoveryProvider } from '../with-party/team/shamir-recovery-provider.js'\nimport type { RecoveryEnrollmentInput } from '../with-party/team/rotate-recover.js'\nimport { LedgerStore } from '../with-commit/history/ledger/store.js'\nimport { LEDGER_COLLECTION } from '../with-commit/history/ledger/constants.js'\nimport type { TransferSealPayload } from '../with-pod/bundle.js'\nimport { readPodHeader, readPod, parseExtractedPartitionBody } from '../with-pod/bundle.js'\n\n/**\n * Reverse of `sealDeks`. Imports the transfer key, decrypts the\n * sealed `{ collection: base64(rawDEK) }` map (layout iv(12)‖ct‖tag), and\n * re-imports each DEK as an AES-GCM key. Throws `TransferSealError` on a\n * wrong key (AES-GCM auth-tag failure) or malformed payload.\n */\nexport async function unsealDeks(\n seal: TransferSealPayload,\n transferKey: Uint8Array,\n): Promise<Map<string, EnclaveKey>> {\n if (transferKey.byteLength !== 32) {\n throw new TransferSealError(\n `transfer key must be 32 bytes, got ${transferKey.byteLength}.`,\n )\n }\n const key = await crypto.subtle.importKey('raw', transferKey as BufferSource, 'AES-GCM', false, ['decrypt'])\n const raw = base64ToBuffer(seal.payload)\n let plaintext: ArrayBuffer\n try {\n plaintext = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: raw.slice(0, 12) as BufferSource },\n key,\n raw.slice(12) as BufferSource,\n )\n } catch {\n throw new TransferSealError(\n 'transfer seal could not be opened — wrong transfer key (AES-GCM authentication failed).',\n )\n }\n let dekMap: Record<string, string>\n try {\n dekMap = JSON.parse(new TextDecoder().decode(plaintext)) as Record<string, string>\n } catch {\n throw new TransferSealError('transfer seal payload is not valid JSON after decryption.')\n }\n const deks = new Map<string, EnclaveKey>()\n for (const [collection, b64] of Object.entries(dekMap)) {\n // Extractable: the recipient must be able to re-wrap these under their\n // own KEK (AES-KW) at owner-creation. Matches generateDEK.\n const dek = await crypto.subtle.importKey('raw', base64ToBuffer(b64) as BufferSource, 'AES-GCM', true, ['encrypt', 'decrypt'])\n deks.set(collection, dek)\n }\n return deks\n}\n\nexport interface AdoptPartitionOptions {\n readonly transferKey: Uint8Array\n readonly destinationStore: NoydbStore\n readonly vaultName: string\n}\n\nexport interface AdoptPartitionResult {\n readonly vaultName: string\n readonly needsOwner: true\n readonly sealId: string\n}\n\nexport async function adoptPartition(\n bundleBytes: Uint8Array,\n opts: AdoptPartitionOptions,\n): Promise<AdoptPartitionResult> {\n const { transferKey, destinationStore, vaultName } = opts\n\n const header = readPodHeader(bundleBytes)\n if (header.bundleKind !== 'extracted-partition' || header.transferSeal === undefined) {\n throw new ValidationError(\n 'adoptPartition requires an extracted-partition bundle with a transfer seal. '\n + 'For ordinary backups use readPod + vault.load.',\n )\n }\n\n const { dumpJson } = await readPod(bundleBytes)\n const { dump, seal } = parseExtractedPartitionBody(dumpJson)\n\n // Validate the transfer key by unsealing in memory; throws\n // TransferSealError on mismatch. DEKs are discarded here — they stay\n // sealed at rest (in _meta/adoption) until owner-creation wraps them under the\n // recipient's KEK.\n await unsealDeks(seal, transferKey)\n\n // Single-occupancy per vaultName: an `_meta/adoption` marker already present\n // means this slot holds a partition (adopted-and-unowned, or already owned).\n // saveAll below would overwrite its data and replace the marker, stranding the\n // prior adoption's transfer seal. Refuse regardless of sealId — re-adopting the\n // SAME bundle is a redundant call, and adopting a DIFFERENT bundle here would\n // clobber the existing partition. Either way, pick a fresh vaultName.\n const existing = await destinationStore.get(vaultName, '_meta', 'adoption')\n if (existing) {\n const prior = JSON.parse(existing._data) as { sealId?: string }\n if (prior.sealId === seal.sealId) {\n throw new AdoptionStateError(\n `partition (sealId ${seal.sealId}) is already adopted into vault \"${vaultName}\".`,\n )\n }\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already holds an adopted partition (sealId ${prior.sealId}); `\n + `adopting a different partition (sealId ${seal.sealId}) here would overwrite it. `\n + `Adopt into a fresh vaultName instead.`,\n )\n }\n\n // The marker-only check above misses a worse case: a vaultName already in use\n // by an ORDINARY vault (createNoydb + openVault) carries no `_meta/adoption`,\n // yet `saveAll` below is destructive on SQL adapters (`DELETE FROM ... WHERE\n // vault = ?` followed by upsert) and would wipe the legitimate keyring +\n // data. Refuse adoption into ANY occupied slot — a fresh vaultName is the\n // documented precondition.\n const existingKeyring = await destinationStore.list(vaultName, '_keyring')\n if (existingKeyring.length > 0) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already holds a keyring (an unrelated owner exists at this slot); `\n + `adoptPartition requires a fresh vaultName to avoid destructive saveAll on SQL adapters.`,\n )\n }\n\n const backup = JSON.parse(dump) as { collections: VaultSnapshot; _internal?: VaultSnapshot }\n await destinationStore.saveAll(vaultName, backup.collections)\n\n // Import carried internal collections (e.g. _schemas from carrySchemas).\n // saveAll only writes data collections; _internal is written per-record.\n if (backup._internal) {\n for (const [collection, records] of Object.entries(backup._internal)) {\n for (const [id, envelope] of Object.entries(records)) {\n await destinationStore.put(vaultName, collection, id, envelope)\n }\n }\n }\n\n const adoptedAt = new Date().toISOString()\n const adoption = { sealId: seal.sealId, adoptedAt, needsOwner: true as const, transferSeal: seal }\n await destinationStore.put(vaultName, '_meta', 'adoption', {\n _noydb: 1, _v: 1, _ts: adoptedAt, _iv: '', _data: JSON.stringify(adoption),\n })\n\n return { vaultName, needsOwner: true, sealId: seal.sealId }\n}\n\nexport interface CreateOwnerResult {\n readonly vaultName: string\n readonly userId: string\n}\n\n/** Standard-mode owner: recipient supplies the passphrase. */\nexport interface CreateOwnerStandardOptions {\n readonly userId: string\n readonly passphrase: string\n readonly transferKey: Uint8Array\n}\n\n/**\n * Managed-mode owner: the passphrase is minted + sealed under\n * a `SealingKeyProvider` (e.g. an `at-*` OS keychain) so the partition\n * auto-unlocks on the recipient's device. Managed mode mandates a strong\n * (Shamir) recovery profile at creation, which needs the\n * `shamirRecovery` provider injected.\n */\nexport interface CreateOwnerManagedOptions {\n readonly userId: string\n readonly passphraseMode: 'managed'\n readonly sealingKey: SealingKeyProvider\n readonly recovery: ReadonlyArray<RecoveryEnrollmentInput>\n readonly shamirRecovery: ShamirRecoveryProvider\n readonly transferKey: Uint8Array\n}\n\nexport type CreateOwnerOptions = CreateOwnerStandardOptions | CreateOwnerManagedOptions\n\nfunction isManaged(o: CreateOwnerOptions): o is CreateOwnerManagedOptions {\n return 'passphraseMode' in o && o.passphraseMode === 'managed'\n}\n\n/**\n * Mint the first owner keyring on an adopted-but-unowned partition,\n * then destroy the transfer seal.\n *\n * Standard mode: the recipient supplies a passphrase. Managed mode: the\n * passphrase is minted + sealed under a `SealingKeyProvider` and a strong\n * (Shamir) recovery profile is enrolled — orchestrated via the existing\n * `openVaultAndEnrollRecovery` ceremony.\n *\n * Either way, reuses `createOwnerKeyring` to derive the KEK + write the base\n * keyring, then wraps the partition's DEKs (recovered from the seal) under that\n * KEK and re-persists the merged keyring file.\n *\n * Idempotent under retry: the seal is destroyed LAST (Stage D), after the\n * keyring (Stage A), the ledger transition (Stage B), and — in managed mode —\n * strong-recovery enrollment (Stage C). A failure in the fallible enrollment\n * step leaves the seal intact, and re-running with the same `userId` +\n * `transferKey` resumes from the first incomplete stage. (Multi-profile recovery\n * arrays may re-enroll an already-enrolled profile on retry; managed mode's\n * mandated single Shamir profile does not.)\n */\nexport async function createOwnerOnAdoptedPartition(\n store: NoydbStore,\n vaultName: string,\n opts: CreateOwnerOptions,\n): Promise<CreateOwnerResult> {\n const { userId, transferKey } = opts\n\n // Managed mode requires a strong (Shamir) recovery profile, validated BEFORE\n // any disk write — same gate as createNoydb.\n if (isManaged(opts) && !opts.recovery.some((r) => r.profile === 'shamir')) {\n throw new AdoptionStateError(\n 'managed-mode adoption requires at least one strong (shamir) recovery profile in '\n + '`recovery` — paper alone is not strong when there is no user passphrase to fall back on.',\n )\n }\n\n // 1. Verify adopted-unowned state.\n const adoptionEnv = await store.get(vaultName, '_meta', 'adoption')\n if (!adoptionEnv) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" is not an adopted partition (no _meta/adoption). `\n + `createOwnerOnAdoptedPartition only applies to vaults created via adoptPartition.`,\n )\n }\n const adoption = JSON.parse(adoptionEnv._data) as {\n sealId: string; adoptedAt: string; needsOwner?: boolean\n consumedAt?: string; transferSeal?: TransferSealPayload\n }\n if (adoption.consumedAt !== undefined || adoption.transferSeal === undefined) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already has an owner (transfer seal consumed at ${adoption.consumedAt}).`,\n )\n }\n\n // 2. Recover the partition DEKs from the seal (throws on wrong key) BEFORE\n // writing any keyring, so a bad transfer key leaves no trace. Always\n // validated, including when resuming a partial prior call.\n const partitionDeks = await unsealDeks(adoption.transferSeal, transferKey)\n\n // The ceremony below is split into stages so a failure in the fallible\n // managed-enrollment step (network/provider outage) leaves the call RETRYABLE\n // — the seal is destroyed only once everything durable is in place. Each stage\n // detects its own prior completion rather than relying on a single resume bit.\n\n // A keyring present for a DIFFERENT user (with the seal still unconsumed) is a\n // genuine second-owner attempt — refuse it. A same-user keyring is a resumed\n // partial call and is handled by the stage checks below.\n const existingKeyring = await store.get(vaultName, '_keyring', userId)\n const otherOwners = (await store.list(vaultName, '_keyring')).filter((u) => u !== userId)\n if (otherOwners.length > 0) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already has a keyring for a different owner; cannot create owner \"${userId}\".`,\n )\n }\n\n // Stage A — mint the owner keyring + merge the partition DEKs. Considered done\n // only when the keyring already holds every partition DEK. createOwnerKeyring\n // overwrites (fresh KEK + fresh _users DEK), so re-running is safe ONLY while\n // no recovery has been enrolled yet — guaranteed here because enrollment\n // (Stage C) runs strictly after Stage A completes.\n const partitionCollections = [...partitionDeks.keys()]\n const priorDeks = existingKeyring ? (JSON.parse(existingKeyring._data) as KeyringFile).deks : {}\n const ownerMinted = existingKeyring !== null && partitionCollections.every((c) => c in priorDeks)\n if (!ownerMinted) {\n // Resolve the owner passphrase. Managed mode mints a random passphrase, seals\n // it under the provider, and persists _meta/sealed-passphrase (so the\n // partition auto-unlocks on the recipient's device); standard mode uses the\n // caller's passphrase. Idempotent under retry — resolveManagedSecret's reopen\n // arm reuses an already-sealed passphrase.\n const passphrase = isManaged(opts)\n ? await resolveManagedSecret(store, vaultName, opts.sealingKey)\n : opts.passphrase\n\n // Mint the owner keyring (KEK + _users DEK + canary, written to disk).\n const unlocked = await createOwnerKeyring(store, vaultName, userId, passphrase)\n\n // Merge the partition DEKs (wrapped under the new KEK) into the keyring.\n const env = await store.get(vaultName, '_keyring', userId)\n if (!env) throw new AdoptionStateError(`keyring write for \"${userId}\" did not persist`)\n const keyringFile = JSON.parse(env._data) as KeyringFile\n const kek = unlocked.kek\n if (!kek) throw new AdoptionStateError(`owner keyring for \"${userId}\" has no KEK to wrap partition DEKs under`)\n const mergedDeks: Record<string, string> = { ...keyringFile.deks }\n for (const [collection, dek] of partitionDeks) {\n mergedDeks[collection] = await wrapKey(dek, kek)\n }\n const mergedFile: KeyringFile = { ...keyringFile, deks: mergedDeks }\n await store.put(vaultName, '_keyring', userId, { ...env, _data: JSON.stringify(mergedFile) })\n }\n\n // Stage B — record the ownership transition on the carried\n // audit chain (carryLedger sealed the _ledger DEK). No-op without that DEK.\n // Idempotent: appended only if the closing `transfer-seal-consumed` entry is\n // absent, so a retry does not duplicate the pair.\n const ledgerDek = partitionDeks.get(LEDGER_COLLECTION)\n if (ledgerDek) {\n const ledger = new LedgerStore({\n adapter: store,\n vault: vaultName,\n encrypted: true,\n getDEK: async () => ledgerDek,\n actor: userId,\n })\n const creationReason = `creation-of-new-owner:${userId}`\n const consumedReason = `transfer-seal-consumed:${adoption.sealId}`\n // Gate each append on its own presence — a crash or store error strictly\n // between the two adjacent puts would otherwise re-append the first one\n // on retry. The pair is the audit record, not a single transaction.\n const recordedReasons = new Set((await ledger.loadAllEntries()).map((e) => e.reason))\n if (!recordedReasons.has(creationReason)) {\n await ledger.append({ op: 'lifecycle', collection: '', id: '', version: 0, actor: '', payloadHash: '', reason: creationReason })\n }\n if (!recordedReasons.has(consumedReason)) {\n await ledger.append({ op: 'lifecycle', collection: '', id: '', version: 0, actor: '', payloadHash: '', reason: consumedReason })\n }\n }\n\n // Stage C — Managed mode: enroll the mandatory strong recovery\n // by orchestrating the existing public ceremony. The partition is\n // now a managed-mode vault on disk (sealed passphrase + keyring), so we\n // open it as a normal client and let openVaultAndEnrollRecovery do the\n // gate-bypass + enroll + re-assert. Dynamic import keeps the Noydb class\n // out of the @noy-db/hub/cargo static graph. Runs BEFORE seal destruction\n // so a failure here leaves the seal intact and the call retryable.\n if (isManaged(opts)) {\n const { createNoydb } = await import('../kernel/noydb.js')\n const db = await createNoydb({\n store,\n user: userId,\n passphraseMode: 'managed',\n sealingKey: opts.sealingKey,\n shamirRecovery: opts.shamirRecovery,\n })\n await db.openVaultAndEnrollRecovery(vaultName, { recovery: opts.recovery })\n }\n\n // Stage D — Destroy the transfer seal LAST — the commit point. Everything\n // above is either idempotent or resumable, so the seal is only consumed\n // once the owner keyring (and, in managed mode, strong recovery) is\n // durably in place. Retain sealId + consumedAt for audit.\n const consumed = { sealId: adoption.sealId, adoptedAt: adoption.adoptedAt, consumedAt: new Date().toISOString() }\n await store.put(vaultName, '_meta', 'adoption', { ...adoptionEnv, _data: JSON.stringify(consumed) })\n\n return { vaultName, userId }\n}\n","/**\n * Read-side counterpart to `extractPartition`: decrypt an\n * extracted-partition bundle's records to plaintext using its transfer\n * key, WITHOUT adopting it into a vault. Used by an outward\n * orchestration layer's reconcile/merge and field-authority flows to\n * compare incoming records against a receiver. The transfer key validates the bundle\n * (wrong key throws).\n * @module\n */\nimport type { EncryptedEnvelope } from '../kernel/types.js'\nimport { openEnvelopeJson } from '../kernel/enclave/index.js'\nimport { readPodHeader, readPod, parseExtractedPartitionBody } from '../with-pod/bundle.js'\nimport { unsealDeks } from './adopt-partition.js'\n\n/** One decrypted record from an extracted-partition compartment. */\nexport interface DecryptedRecord {\n readonly id: string\n readonly record: Record<string, unknown>\n /** Source envelope write timestamp (ISO) — for last-write-wins merges. */\n readonly ts: string\n /** Source envelope version. */\n readonly version: number\n /** Provenance source id (FR-5). Present only when the source collection had provenance:true and a source was supplied on put. */\n readonly source?: string\n /** ISO-8601 timestamp the provenance source was recorded (FR-5). */\n readonly sourceTs?: string\n}\n\n/**\n * Decrypt every record of an extracted-partition bundle to plaintext,\n * grouped by collection. Throws if the bundle isn't an\n * extracted-partition or the transfer key is wrong.\n */\nexport async function decryptExtractedPartition(\n bundleBytes: Uint8Array,\n transferKey: Uint8Array,\n): Promise<Record<string, DecryptedRecord[]>> {\n const header = readPodHeader(bundleBytes)\n if (header.bundleKind !== 'extracted-partition' || header.transferSeal === undefined) {\n throw new Error('decryptExtractedPartition: bundle is not an extracted-partition.')\n }\n const { dumpJson } = await readPod(bundleBytes)\n const { dump, seal } = parseExtractedPartitionBody(dumpJson)\n const deks = await unsealDeks(seal, transferKey) // throws TransferSealError on wrong key\n const backup = JSON.parse(dump) as { collections: Record<string, Record<string, EncryptedEnvelope>> }\n const out: Record<string, DecryptedRecord[]> = {}\n for (const [collection, byId] of Object.entries(backup.collections)) {\n const dek = deks.get(collection)\n if (dek === undefined) continue // no DEK sealed for this collection — skip\n const recs: DecryptedRecord[] = []\n for (const [id, env] of Object.entries(byId)) {\n const plaintext = await openEnvelopeJson(env, dek)\n const body = JSON.parse(plaintext) as Record<string, unknown>\n recs.push({\n id,\n record: { ...body, id },\n ts: env._ts,\n version: env._v,\n ...(env._source !== undefined ? { source: env._source } : {}),\n ...(env._sourceTs !== undefined ? { sourceTs: env._sourceTs } : {}),\n })\n }\n out[collection] = recs\n }\n return out\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SyncScheduler
|
|
3
3
|
} from "./chunk-VQNJ7UZL.js";
|
|
4
|
+
import {
|
|
5
|
+
PERIODS_COLLECTION,
|
|
6
|
+
PERIOD_ARCHIVES_COLLECTION,
|
|
7
|
+
PERIOD_FREEZES_COLLECTION,
|
|
8
|
+
PERIOD_TARGET_PURGES_COLLECTION,
|
|
9
|
+
periodExclusiveUpperBound
|
|
10
|
+
} from "./chunk-F4COUZLL.js";
|
|
11
|
+
import {
|
|
12
|
+
envelopeBodySize
|
|
13
|
+
} from "./chunk-PEY6ZTCF.js";
|
|
4
14
|
import {
|
|
5
15
|
isDeleteMarker,
|
|
6
16
|
isTombstoneShape
|
|
7
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-QXPOEAKR.js";
|
|
8
18
|
import {
|
|
9
19
|
bufferToBase64,
|
|
10
20
|
decrypt,
|
|
11
21
|
derivePresenceKey,
|
|
12
22
|
encrypt,
|
|
13
23
|
generateIV
|
|
14
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-5P5AAXYA.js";
|
|
15
25
|
import {
|
|
16
26
|
NOYDB_SYNC_VERSION
|
|
17
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-5BHNQNBI.js";
|
|
18
28
|
import {
|
|
19
|
-
ConflictError
|
|
20
|
-
|
|
29
|
+
ConflictError,
|
|
30
|
+
ValidationError
|
|
31
|
+
} from "./chunk-IGM563N5.js";
|
|
21
32
|
|
|
22
33
|
// src/with-party/team/presence.ts
|
|
23
34
|
var PresenceHandle = class {
|
|
@@ -226,6 +237,51 @@ var PresenceHandle = class {
|
|
|
226
237
|
}
|
|
227
238
|
};
|
|
228
239
|
|
|
240
|
+
// src/with-party/team/sync-period-scope.ts
|
|
241
|
+
var PERIOD_SUMMARY_COLLECTIONS = [
|
|
242
|
+
PERIODS_COLLECTION,
|
|
243
|
+
PERIOD_FREEZES_COLLECTION,
|
|
244
|
+
PERIOD_ARCHIVES_COLLECTION,
|
|
245
|
+
PERIOD_TARGET_PURGES_COLLECTION
|
|
246
|
+
];
|
|
247
|
+
var SHAPE_MSG = "pull: `periods` must be an array of closed-period names, or `{ current: true }`.";
|
|
248
|
+
function validatePeriodsOption(periods) {
|
|
249
|
+
if (Array.isArray(periods)) {
|
|
250
|
+
for (const p of periods) {
|
|
251
|
+
if (typeof p !== "string" || p.length === 0) throw new ValidationError(SHAPE_MSG);
|
|
252
|
+
}
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (typeof periods === "object" && periods !== null && periods.current === true && Object.keys(periods).length === 1) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
throw new ValidationError(SHAPE_MSG);
|
|
259
|
+
}
|
|
260
|
+
function buildPeriodScope(option, records) {
|
|
261
|
+
const closed = records.filter((r) => r.kind === "closed").map((r) => ({ name: r.name, bound: periodExclusiveUpperBound(r.endDate) })).sort((a, b) => a.bound.localeCompare(b.bound));
|
|
262
|
+
let windows;
|
|
263
|
+
if (Array.isArray(option)) {
|
|
264
|
+
windows = option.map((name) => {
|
|
265
|
+
const idx = closed.findIndex((c) => c.name === name);
|
|
266
|
+
if (idx < 0) {
|
|
267
|
+
const other = records.find((r) => r.name === name);
|
|
268
|
+
throw new ValidationError(
|
|
269
|
+
other ? `pull: period "${name}" is "${other.kind}" \u2014 only closed periods have a backfill window; use \`{ current: true }\` for the open period.` : `pull: no closed period named "${name}" in this vault's _periods index.`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
return { lower: idx > 0 ? closed[idx - 1].bound : null, upper: closed[idx].bound };
|
|
273
|
+
});
|
|
274
|
+
} else {
|
|
275
|
+
windows = [{ lower: closed.length > 0 ? closed[closed.length - 1].bound : null, upper: null }];
|
|
276
|
+
}
|
|
277
|
+
return (envelope) => {
|
|
278
|
+
if (isTombstoneShape(envelope) || isDeleteMarker(envelope)) return true;
|
|
279
|
+
return windows.some(
|
|
280
|
+
(w) => (w.lower === null || envelope._ts >= w.lower) && (w.upper === null || envelope._ts < w.upper)
|
|
281
|
+
);
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
229
285
|
// src/with-party/team/sync.ts
|
|
230
286
|
var SyncEngine = class {
|
|
231
287
|
local;
|
|
@@ -288,6 +344,20 @@ var SyncEngine = class {
|
|
|
288
344
|
setReservedDictExpander(expander) {
|
|
289
345
|
this.reservedDictExpander = expander;
|
|
290
346
|
}
|
|
347
|
+
/** #807: vault-injected source of decrypted period records — resolves the `_ts` windows a
|
|
348
|
+
* `pull({ periods })` scopes to. Re-read AFTER the summaries phase, so a fresh device's
|
|
349
|
+
* freshly pulled `_periods` index is what the windows are computed from. `undefined` on a
|
|
350
|
+
* standalone engine that was never vault-attached (period-scoped pull then throws). */
|
|
351
|
+
periodPullSource;
|
|
352
|
+
/** Wire the period-window source (#807). Same injection pattern as `setReservedLookupSource`. */
|
|
353
|
+
setPeriodPullSource(source) {
|
|
354
|
+
this.periodPullSource = source;
|
|
355
|
+
}
|
|
356
|
+
/** #807: KPI accumulator `applyRemote` feeds during a period-scoped `pull()` — pointed at the
|
|
357
|
+
* active phase's counters (summaries → records) and cleared before pull returns. Approximate
|
|
358
|
+
* by design: a push interleaved mid-pull on the same engine would attribute its converge
|
|
359
|
+
* applies to the open phase; the counters are a download-budget KPI, not an audit source. */
|
|
360
|
+
pullByteSink = null;
|
|
291
361
|
constructor(opts) {
|
|
292
362
|
this.local = opts.local;
|
|
293
363
|
this.remote = opts.remote;
|
|
@@ -490,11 +560,52 @@ var SyncEngine = class {
|
|
|
490
560
|
/** Pull remote records to local adapter. Accepts optional `PullOptions` for partial sync. */
|
|
491
561
|
async pull(options) {
|
|
492
562
|
await this.ensureLoaded();
|
|
493
|
-
this.graphBatchController?.begin();
|
|
494
563
|
let pulled = 0;
|
|
495
564
|
const conflicts = [];
|
|
496
565
|
const erasures = [];
|
|
497
566
|
const errors = [];
|
|
567
|
+
let periodScope = null;
|
|
568
|
+
let phases = null;
|
|
569
|
+
if (options?.periods !== void 0) {
|
|
570
|
+
validatePeriodsOption(options.periods);
|
|
571
|
+
if (!this.periodPullSource) {
|
|
572
|
+
throw new ValidationError(
|
|
573
|
+
"period-scoped pull requires a vault-attached sync engine (open the vault via createNoydb + openVault)."
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
phases = { summaries: { records: 0, bytes: 0 }, records: { records: 0, bytes: 0 } };
|
|
577
|
+
let remotePeriodCount = 0;
|
|
578
|
+
this.pullByteSink = phases.summaries;
|
|
579
|
+
try {
|
|
580
|
+
for (const collName of PERIOD_SUMMARY_COLLECTIONS) {
|
|
581
|
+
for (const id of await this.remote.list(this.vault, collName)) {
|
|
582
|
+
if (collName === PERIODS_COLLECTION) remotePeriodCount++;
|
|
583
|
+
try {
|
|
584
|
+
const remoteEnvelope = await this.remote.get(this.vault, collName, id);
|
|
585
|
+
if (!remoteEnvelope) continue;
|
|
586
|
+
const localEnvelope = await this.local.get(this.vault, collName, id);
|
|
587
|
+
if (!localEnvelope || remoteEnvelope._v > localEnvelope._v) {
|
|
588
|
+
await this.applyRemote(collName, id, remoteEnvelope);
|
|
589
|
+
pulled++;
|
|
590
|
+
}
|
|
591
|
+
} catch (err) {
|
|
592
|
+
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
} finally {
|
|
597
|
+
this.pullByteSink = null;
|
|
598
|
+
}
|
|
599
|
+
const periodRecords = await this.periodPullSource.periods();
|
|
600
|
+
if (remotePeriodCount > 0 && periodRecords.length === 0) {
|
|
601
|
+
throw new ValidationError(
|
|
602
|
+
"period-scoped pull: the vault holds _periods records but none are readable \u2014 enable the periods service (`periodsStrategy: withPeriods()`) so pull can resolve the period windows."
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
periodScope = buildPeriodScope(options.periods, periodRecords);
|
|
606
|
+
}
|
|
607
|
+
this.graphBatchController?.begin();
|
|
608
|
+
if (phases) this.pullByteSink = phases.records;
|
|
498
609
|
const expanded = options?.collections ? this.pairExpander?.(options.collections) ?? options.collections : null;
|
|
499
610
|
const filter = expanded ? /* @__PURE__ */ new Set([...expanded, ...this.reservedDictExpander?.(expanded) ?? []]) : null;
|
|
500
611
|
try {
|
|
@@ -507,6 +618,9 @@ var SyncEngine = class {
|
|
|
507
618
|
if (options?.modifiedSince && remoteEnvelope._ts <= options.modifiedSince && !isTombstoneShape(remoteEnvelope) && !isDeleteMarker(remoteEnvelope)) {
|
|
508
619
|
continue;
|
|
509
620
|
}
|
|
621
|
+
if (periodScope !== null && !periodScope(remoteEnvelope)) {
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
510
624
|
try {
|
|
511
625
|
const localEnvelope = await this.local.get(this.vault, collName, id);
|
|
512
626
|
const remoteIsTombstone = isTombstoneShape(remoteEnvelope);
|
|
@@ -609,9 +723,10 @@ var SyncEngine = class {
|
|
|
609
723
|
try {
|
|
610
724
|
await this.persistMeta();
|
|
611
725
|
} finally {
|
|
726
|
+
this.pullByteSink = null;
|
|
612
727
|
await this.graphBatchController?.flush();
|
|
613
728
|
}
|
|
614
|
-
const result = { pulled, conflicts, errors, erasures };
|
|
729
|
+
const result = { pulled, conflicts, errors, erasures, ...phases !== null ? { phases } : {} };
|
|
615
730
|
this.emitter.emit("sync:pull", result);
|
|
616
731
|
return result;
|
|
617
732
|
}
|
|
@@ -776,6 +891,10 @@ var SyncEngine = class {
|
|
|
776
891
|
* (sync delete ≠ forget: freshness only, no shred/residue channel on the receiving side). */
|
|
777
892
|
async applyRemote(collection, id, envelope) {
|
|
778
893
|
await this.local.put(this.vault, collection, id, envelope);
|
|
894
|
+
if (this.pullByteSink !== null) {
|
|
895
|
+
this.pullByteSink.records++;
|
|
896
|
+
this.pullByteSink.bytes += envelopeBodySize(envelope);
|
|
897
|
+
}
|
|
779
898
|
const action = isTombstoneShape(envelope) || isDeleteMarker(envelope) ? "delete" : "put";
|
|
780
899
|
await this.cacheInvalidator?.(collection, id, action);
|
|
781
900
|
}
|
|
@@ -954,4 +1073,4 @@ export {
|
|
|
954
1073
|
SyncEngine,
|
|
955
1074
|
SyncTransaction
|
|
956
1075
|
};
|
|
957
|
-
//# sourceMappingURL=chunk-
|
|
1076
|
+
//# sourceMappingURL=chunk-QAFWRGVD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-party/team/presence.ts","../src/with-party/team/sync-period-scope.ts","../src/with-party/team/sync.ts","../src/with-party/team/sync-transaction.ts"],"sourcesContent":["/**\n * Presence handle — real-time awareness of who is viewing/editing a collection.\n * encrypted ephemeral channel keyed by collection DEK via HKDF.\n *\n * The presence key is derived from the collection DEK so:\n * - The adapter never learns user identities from presence payloads.\n * - Presence rotates automatically when the DEK rotates (revoked users\n * can no longer participate after a DEK rotation).\n *\n * Two transport strategies:\n * 1. **Pub/sub** (real-time): used when the adapter implements\n * `presencePublish` and `presenceSubscribe`.\n * 2. **Storage-poll** (fallback): presence records are written to a\n * reserved `_presence_<collection>` collection on the sync adapter\n * (if available) or local adapter, and polled periodically.\n */\n\nimport type { NoydbStore, PresencePeer } from '../../kernel/types.js'\nimport { encrypt, decrypt, generateIV, bufferToBase64, derivePresenceKey, type EnclaveKey } from '../../kernel/enclave/index.js'\n\n/** Options for constructing a PresenceHandle. @internal */\nexport interface PresenceHandleOpts {\n /** Local adapter for storage-poll fallback. */\n adapter: NoydbStore\n /** Remote (sync) adapter — preferred for broadcasting presence if available. */\n syncAdapter?: NoydbStore\n /** Vault name — used as part of the channel and storage key. */\n vault: string\n /** Collection name — used as HKDF `info` and channel suffix. */\n collectionName: string\n /** Calling user's ID, embedded unencrypted in storage records. */\n userId: string\n /** Whether encryption is active. When false, presence payloads are stored as JSON. */\n encrypted: boolean\n /** Callback that resolves the collection DEK (used to derive the presence key). */\n getDEK: (collectionName: string) => Promise<EnclaveKey>\n /** How long (ms) before a peer's presence is considered stale. Default: 30_000. */\n staleMs?: number\n /** Poll interval (ms) for the storage-poll fallback. Default: 5_000. */\n pollIntervalMs?: number\n}\n\n/**\n * Internal storage envelope for the storage-poll fallback.\n * Written to `_presence_<collection>` as `{ userId, lastSeen, iv, data }`.\n */\ninterface StoragePresenceRecord {\n userId: string\n lastSeen: string\n iv: string // base64 AES-GCM IV (empty when not encrypted)\n data: string // base64 ciphertext or JSON string when not encrypted\n}\n\n/** Presence handle for a single collection. */\nexport class PresenceHandle<P> {\n private readonly adapter: NoydbStore\n private readonly syncAdapter: NoydbStore | undefined\n private readonly vault: string\n private readonly collectionName: string\n private readonly userId: string\n private readonly encrypted: boolean\n private readonly getDEK: (collectionName: string) => Promise<EnclaveKey>\n private readonly staleMs: number\n private readonly pollIntervalMs: number\n private readonly channel: string\n private readonly storageCollection: string\n\n private presenceKey: EnclaveKey | null = null\n private subscribers: Array<(peers: PresencePeer<P>[]) => void> = []\n private unsubscribePubSub: (() => void) | null = null\n private pollTimer: ReturnType<typeof setInterval> | null = null\n private stopped = false\n\n constructor(opts: PresenceHandleOpts) {\n this.adapter = opts.adapter\n this.syncAdapter = opts.syncAdapter\n this.vault = opts.vault\n this.collectionName = opts.collectionName\n this.userId = opts.userId\n this.encrypted = opts.encrypted\n this.getDEK = opts.getDEK\n this.staleMs = opts.staleMs ?? 30_000\n this.pollIntervalMs = opts.pollIntervalMs ?? 5_000\n // Channel used by pub/sub adapters — vault-scoped so two collections\n // in the same vault don't bleed into each other's presence channels.\n this.channel = `${opts.vault}:${opts.collectionName}:presence`\n // Reserved collection name for the storage-poll fallback.\n this.storageCollection = `_presence_${opts.collectionName}`\n }\n\n /**\n * Announce yourself (or update your cursor/status).\n * Encrypts `payload` with the presence key and publishes it.\n */\n async update(payload: P): Promise<void> {\n if (this.stopped) return\n\n const key = await this.getPresenceKey()\n const now = new Date().toISOString()\n const plaintext = JSON.stringify({ userId: this.userId, lastSeen: now, payload })\n let encryptedPayload: string\n\n if (this.encrypted && key) {\n const iv = generateIV()\n const ivB64 = bufferToBase64(iv)\n const { data } = await encrypt(plaintext, key)\n encryptedPayload = JSON.stringify({ iv: ivB64, data })\n } else {\n encryptedPayload = plaintext\n }\n\n // Pub/sub path — publish to any adapter that supports it\n const pubAdapter = this.getPubSubAdapter()\n if (pubAdapter?.presencePublish) {\n await pubAdapter.presencePublish(this.channel, encryptedPayload)\n }\n\n // Storage-poll path — write a record to the storage adapter\n await this.writeStorageRecord(payload, now)\n }\n\n /**\n * Subscribe to presence updates. The callback receives a filtered, decrypted\n * list of all currently-active peers (excluding yourself, excluding stale).\n *\n * Returns an unsubscribe function. Also call `stop()` to release all resources.\n */\n subscribe(cb: (peers: PresencePeer<P>[]) => void): () => void {\n if (this.stopped) return () => {}\n\n this.subscribers.push(cb)\n\n // Start pub/sub listener on first subscriber\n if (this.subscribers.length === 1) {\n this.startListening()\n }\n\n return () => {\n this.subscribers = this.subscribers.filter(s => s !== cb)\n if (this.subscribers.length === 0) this.stopListening()\n }\n }\n\n /** Stop all listening and clear resources. */\n stop(): void {\n this.stopped = true\n this.stopListening()\n this.subscribers = []\n }\n\n // ─── Private ────────────────────────────────────────────────────────\n\n private async getPresenceKey(): Promise<EnclaveKey | null> {\n if (!this.encrypted) return null\n if (!this.presenceKey) {\n try {\n const dek = await this.getDEK(this.collectionName)\n this.presenceKey = await derivePresenceKey(dek, this.collectionName)\n } catch {\n // no-op — presence degrades gracefully if crypto fails\n }\n }\n return this.presenceKey\n }\n\n private getPubSubAdapter(): NoydbStore | undefined {\n // Prefer the sync adapter (it broadcasts to other devices)\n if (this.syncAdapter?.presencePublish) return this.syncAdapter\n if (this.adapter.presencePublish) return this.adapter\n return undefined\n }\n\n private startListening(): void {\n const pubAdapter = this.getPubSubAdapter()\n\n if (pubAdapter?.presenceSubscribe) {\n // Real-time pub/sub path\n this.unsubscribePubSub = pubAdapter.presenceSubscribe(\n this.channel,\n (encryptedPayload) => { void this.handlePubSubMessage(encryptedPayload) },\n )\n } else {\n // Storage-poll fallback\n this.pollTimer = setInterval(\n () => { void this.pollStoragePresence() },\n this.pollIntervalMs,\n )\n // Kick off an immediate poll\n void this.pollStoragePresence()\n }\n }\n\n private stopListening(): void {\n if (this.unsubscribePubSub) {\n this.unsubscribePubSub()\n this.unsubscribePubSub = null\n }\n if (this.pollTimer) {\n clearInterval(this.pollTimer)\n this.pollTimer = null\n }\n }\n\n private async handlePubSubMessage(encryptedPayload: string): Promise<void> {\n try {\n const peer = await this.decryptPresencePayload(encryptedPayload)\n if (!peer || peer.userId === this.userId) return\n\n const cutoff = new Date(Date.now() - this.staleMs).toISOString()\n if (peer.lastSeen < cutoff) return\n\n // Deliver only this new peer to subscribers; a full snapshot poll follows\n // on next interval. For pub/sub, we could maintain a map of active peers,\n // but for simplicity: emit a snapshot read from storage.\n await this.pollStoragePresence()\n } catch {\n // Decrypt failure — stale key or tampered payload, ignore\n }\n }\n\n private async decryptPresencePayload(\n encryptedPayload: string,\n ): Promise<{ userId: string; lastSeen: string; payload: P } | null> {\n const key = await this.getPresenceKey()\n\n if (!this.encrypted || !key) {\n return JSON.parse(encryptedPayload) as { userId: string; lastSeen: string; payload: P }\n }\n\n const { iv: ivB64, data } = JSON.parse(encryptedPayload) as { iv: string; data: string }\n const plaintext = await decrypt(ivB64, data, key)\n return JSON.parse(plaintext) as { userId: string; lastSeen: string; payload: P }\n }\n\n private async writeStorageRecord(payload: P, now: string): Promise<void> {\n const key = await this.getPresenceKey()\n const plaintext = JSON.stringify(payload)\n let iv = ''\n let data: string\n\n if (this.encrypted && key) {\n const ivBytes = generateIV()\n iv = bufferToBase64(ivBytes)\n const result = await encrypt(plaintext, key)\n data = result.data\n } else {\n data = plaintext\n }\n\n const record: StoragePresenceRecord = { userId: this.userId, lastSeen: now, iv, data }\n const json = JSON.stringify(record)\n\n // Use the sync adapter if available (so other devices can read it);\n // fall back to local adapter.\n const storeAdapter = this.syncAdapter ?? this.adapter\n const envelope = {\n _noydb: 1 as const,\n _v: 1,\n _ts: now,\n _iv: '',\n _data: json,\n }\n try {\n await storeAdapter.put(\n this.vault,\n this.storageCollection,\n this.userId,\n envelope,\n )\n } catch {\n // Presence write failure is non-fatal — the user is still present locally\n }\n }\n\n private async pollStoragePresence(): Promise<void> {\n if (this.stopped || this.subscribers.length === 0) return\n\n try {\n const storeAdapter = this.syncAdapter ?? this.adapter\n const ids = await storeAdapter.list(this.vault, this.storageCollection)\n const cutoff = new Date(Date.now() - this.staleMs).toISOString()\n const peers: PresencePeer<P>[] = []\n\n for (const id of ids) {\n if (id === this.userId) continue // skip ourselves\n const envelope = await storeAdapter.get(this.vault, this.storageCollection, id)\n if (!envelope) continue\n\n const record = JSON.parse(envelope._data) as StoragePresenceRecord\n if (record.lastSeen < cutoff) continue\n\n let peerPayload: P\n if (this.encrypted && this.presenceKey && record.iv) {\n const plaintext = await decrypt(record.iv, record.data, this.presenceKey)\n peerPayload = JSON.parse(plaintext) as P\n } else {\n peerPayload = JSON.parse(record.data) as P\n }\n\n peers.push({ userId: record.userId, payload: peerPayload, lastSeen: record.lastSeen })\n }\n\n for (const cb of this.subscribers) {\n cb(peers)\n }\n } catch {\n // Poll failure is non-fatal\n }\n }\n}\n","/**\n * #807 — period-scoped pull support for the `SyncEngine` (thin-client\n * bootstrap: first sync pulls the current period + the period summaries;\n * historical periods backfill on demand via `pull({ periods: ['<name>'] })`).\n *\n * ## Membership law\n *\n * The engine only ever sees ciphertext envelopes, so period membership at\n * the sync tier is by write-time `_ts` against the closed periods' windows\n * (`periodExclusiveUpperBound`) — the SAME store-tier law freeze (#604) and\n * archive (#613) use, NOT the business-date (`dateField`) seal the write\n * guard applies inside the trust boundary. A late-booked record (in-period\n * business date, current write-time) therefore syncs with the current\n * period — mirroring how it archives at the NEXT period's archive.\n *\n * Closed periods partition the timeline by their exclusive upper bounds:\n * the earliest closed period's window is unbounded below (a close seals\n * everything at-or-before its `endDate`), each later one starts at its\n * predecessor's bound, and the CURRENT window is everything at-or-after\n * the latest closed bound (everything, when no period was ever closed).\n *\n * ## What a period filter can never drop\n *\n * - **Period summaries** (`_periods` + freeze/archive/target-purge\n * companions) — the navigation index; always pulled in full, exempt from\n * every filter (`collections`, `periods`, `modifiedSince`).\n * - **Delete markers and tombstones** — an erasure or delete must never be\n * skipped by partial sync (the #589/#590 law, mirroring the\n * `modifiedSince` exemption). This is what makes a device that never\n * pulled period P converge instead of resurrecting P's deleted records\n * when it later backfills.\n * - **Reserved lookups** (`_dict_*`/`_lookup_*`) — vocabulary/config rows\n * are not period-partitioned data; their explicit pull loop is exempt.\n *\n * Push is NEVER period-filtered — `PushOptions` has no `periods` member at\n * all; a thin client's writes always flow up in full.\n */\nimport type { EncryptedEnvelope } from '../../kernel/types.js'\nimport { ValidationError } from '../../kernel/errors.js'\nimport { isTombstoneShape, isDeleteMarker } from '../../kernel/enclave/index.js'\nimport {\n PERIODS_COLLECTION,\n PERIOD_FREEZES_COLLECTION,\n PERIOD_ARCHIVES_COLLECTION,\n PERIOD_TARGET_PURGES_COLLECTION,\n periodExclusiveUpperBound,\n} from '../../with-audit/periods/window.js'\n\nexport { PERIODS_COLLECTION }\n\n/**\n * The reserved period-metadata collections a period-scoped pull always\n * fetches first (phase 1, \"summaries\"): the hash-chained `_periods` index\n * plus its freeze/archive/target-purge companions.\n */\nexport const PERIOD_SUMMARY_COLLECTIONS: readonly string[] = [\n PERIODS_COLLECTION,\n PERIOD_FREEZES_COLLECTION,\n PERIOD_ARCHIVES_COLLECTION,\n PERIOD_TARGET_PURGES_COLLECTION,\n]\n\n/** The subset of a decrypted `PeriodRecord` the window computation needs. */\nexport interface PeriodSyncRecord {\n readonly name: string\n readonly kind: 'closed' | 'opened'\n readonly endDate: string\n}\n\n/**\n * Vault-injected source of decrypted period records, wired via\n * `SyncEngine.setPeriodPullSource` (same injection pattern as\n * `setReservedLookupSource`). MUST re-read from the local store on every\n * call — the engine invokes it right after the summaries phase applied\n * freshly pulled `_periods` envelopes.\n */\nexport interface PeriodPullSource {\n periods(): Promise<readonly PeriodSyncRecord[]>\n}\n\nconst SHAPE_MSG =\n 'pull: `periods` must be an array of closed-period names, or `{ current: true }`.'\n\n/** Validate the raw `PullOptions.periods` value (runtime shape only). */\nexport function validatePeriodsOption(\n periods: unknown,\n): asserts periods is readonly string[] | { readonly current: true } {\n if (Array.isArray(periods)) {\n for (const p of periods) {\n if (typeof p !== 'string' || p.length === 0) throw new ValidationError(SHAPE_MSG)\n }\n return\n }\n if (\n typeof periods === 'object' && periods !== null &&\n (periods as { current?: unknown }).current === true &&\n Object.keys(periods).length === 1\n ) {\n return\n }\n throw new ValidationError(SHAPE_MSG)\n}\n\n/**\n * Resolve the selected `_ts` windows and return the pull-scope predicate:\n * `true` = the envelope is in scope for this pull. Tombstones and delete\n * markers are unconditionally in scope (see module doc).\n *\n * Throws `ValidationError` for a name that is not a closed period in the\n * (freshly synced) `_periods` index — a typo'd backfill must fail loudly,\n * never silently pull nothing.\n */\nexport function buildPeriodScope(\n option: readonly string[] | { readonly current: true },\n records: readonly PeriodSyncRecord[],\n): (envelope: EncryptedEnvelope) => boolean {\n const closed = records\n .filter((r) => r.kind === 'closed')\n .map((r) => ({ name: r.name, bound: periodExclusiveUpperBound(r.endDate) }))\n .sort((a, b) => a.bound.localeCompare(b.bound))\n\n let windows: ReadonlyArray<{ lower: string | null; upper: string | null }>\n if (Array.isArray(option)) {\n windows = (option as readonly string[]).map((name) => {\n const idx = closed.findIndex((c) => c.name === name)\n if (idx < 0) {\n const other = records.find((r) => r.name === name)\n throw new ValidationError(\n other\n ? `pull: period \"${name}\" is \"${other.kind}\" — only closed periods have a backfill window; use \\`{ current: true }\\` for the open period.`\n : `pull: no closed period named \"${name}\" in this vault's _periods index.`,\n )\n }\n return { lower: idx > 0 ? closed[idx - 1]!.bound : null, upper: closed[idx]!.bound }\n })\n } else {\n // { current: true } — everything at-or-after the latest closed bound\n // (everything, when no period was ever closed: with no closed periods\n // there is nothing historical to exclude, so nothing is silently dropped).\n windows = [{ lower: closed.length > 0 ? closed[closed.length - 1]!.bound : null, upper: null }]\n }\n\n return (envelope: EncryptedEnvelope): boolean => {\n if (isTombstoneShape(envelope) || isDeleteMarker(envelope)) return true\n return windows.some(\n (w) =>\n (w.lower === null || envelope._ts >= w.lower) &&\n (w.upper === null || envelope._ts < w.upper),\n )\n }\n}\n","import type {\n NoydbStore,\n DirtyEntry,\n Conflict,\n ConflictStrategy,\n CollectionConflictResolver,\n PushOptions,\n PullOptions,\n PushResult,\n PullResult,\n SyncStatus,\n EncryptedEnvelope,\n SyncMetadata,\n SyncTargetRole,\n ErasureEnforcement,\n} from '../../kernel/types.js'\nimport { NOYDB_SYNC_VERSION } from '../../kernel/types.js'\nimport { ConflictError, ValidationError } from '../../kernel/errors.js'\nimport {\n PERIOD_SUMMARY_COLLECTIONS,\n PERIODS_COLLECTION,\n buildPeriodScope,\n validatePeriodsOption,\n type PeriodPullSource,\n} from './sync-period-scope.js'\nimport type { NoydbEventEmitter } from '../../kernel/events.js'\nimport type { SyncPolicy } from '../../kernel/sync-policy.js'\nimport { SyncScheduler } from '../../kernel/sync-policy.js'\nimport { isTombstoneShape, isDeleteMarker, envelopeBodySize } from '../../kernel/enclave/index.js'\n\n/** #650 Task 4 (#647) — the declared reserved-lookup (`_dict_*`/`_lookup_*`) collection-name\n * registry a `SyncEngine` enumerates on pull. Explicit, not a blanket underscore-glob — other\n * `_`-prefixed namespaces keep their `loadAll`-skip semantics untouched. */\nexport interface ReservedLookupSource {\n /** Declared reserved-lookup collection names to enumerate on pull. */\n collections(): readonly string[]\n}\n\n/** Sync engine: dirty tracking, push, pull, conflict resolution, scheduling. */\nexport class SyncEngine {\n private readonly local: NoydbStore\n private readonly remote: NoydbStore\n private readonly strategy: ConflictStrategy\n private readonly emitter: NoydbEventEmitter\n private readonly vault: string\n readonly role: SyncTargetRole\n readonly label: string | undefined\n\n private dirty: DirtyEntry[] = []\n private lastPush: string | null = null\n private lastPull: string | null = null\n private loaded = false\n private autoSyncInterval: ReturnType<typeof setInterval> | null = null\n private isOnline = true\n\n /** Sync scheduler. Manages push/pull timing. */\n readonly scheduler: SyncScheduler | null\n\n /** Per-collection conflict resolvers registered by Collection instances. */\n private readonly conflictResolvers = new Map<string, CollectionConflictResolver>()\n\n /**\n * Expands a collection-name filter to include its satellite pair partner(s)\n * (spec #591 convergence rule 5b) — wired from vault satellite declaration\n * via `setPairExpander`. `undefined` when no satellite has ever been\n * declared for this vault: identity behavior, zero change for non-satellite\n * users.\n */\n private pairExpander?: (names: readonly string[]) => readonly string[]\n\n /** #598: refreshes Collection in-memory views after a sync-applied local write. Wired by the\n * vault at open. `action` (#640): `'delete'` for a pulled tombstone/delete-marker, `'put'`\n * otherwise — classified at `applyRemote`, the one choke point that holds the envelope. */\n private cacheInvalidator?: (collection: string, id: string, action: 'put' | 'delete') => Promise<void>\n\n /** Wire the Collection-cache invalidation hook (#598). Same injection pattern as `setPairExpander`. */\n setCacheInvalidator(fn: (collection: string, id: string, action: 'put' | 'delete') => Promise<void>): void {\n this.cacheInvalidator = fn\n }\n\n /** #638 Task 4: opens/flushes the vault's graph-dispatch touch batch around a whole `pull()`/\n * `push()` call, so N `applyRemote`d records feeding the same derivation/rollup/MV target\n * recompute once. Wired by the vault at open, mirroring `cacheInvalidator`. */\n private graphBatchController?: { begin(): void; flush(): Promise<void> }\n\n /** Wire the graph-dispatch batch controller (#638 Task 4). Same injection pattern as `setCacheInvalidator`. */\n setGraphBatchController(controller: { begin(): void; flush(): Promise<void> }): void {\n this.graphBatchController = controller\n }\n\n /** #650 Task 4 (#647): declared reserved-lookup collections `pull()` enumerates explicitly (the\n * store's `list()` does not skip `_`-prefixed names, unlike `loadAll()`). Wired by the vault at\n * open, same injection pattern as `setCacheInvalidator`/`setGraphBatchController`. */\n private reservedLookup?: ReservedLookupSource\n\n /** Wire the reserved-lookup source (#650 Task 4). */\n setReservedLookupSource(source: ReservedLookupSource): void {\n this.reservedLookup = source\n }\n\n /** #653: expands a (pair-expanded) collection-name filter to include the reserved `_dict_*`\n * collections those names depend on via their lookup fields — mirrors `pairExpander` so a\n * partial `push`/`pull({collections:[...]})` never drops the dictionary a named collection's\n * labels/membership rely on. Wired unconditionally from the vault's `dictKeyFieldRegistry` via\n * `setReservedDictExpander` (returns `[]` when the vault has no dict-backed collections); only\n * `undefined` on a standalone engine that was never vault-attached. */\n private reservedDictExpander?: (names: readonly string[]) => readonly string[]\n\n /** Wire the reserved-dict expansion function (#653). Same injection pattern as `setPairExpander`. */\n setReservedDictExpander(expander: (names: readonly string[]) => readonly string[]): void {\n this.reservedDictExpander = expander\n }\n\n /** #807: vault-injected source of decrypted period records — resolves the `_ts` windows a\n * `pull({ periods })` scopes to. Re-read AFTER the summaries phase, so a fresh device's\n * freshly pulled `_periods` index is what the windows are computed from. `undefined` on a\n * standalone engine that was never vault-attached (period-scoped pull then throws). */\n private periodPullSource?: PeriodPullSource\n\n /** Wire the period-window source (#807). Same injection pattern as `setReservedLookupSource`. */\n setPeriodPullSource(source: PeriodPullSource): void {\n this.periodPullSource = source\n }\n\n /** #807: KPI accumulator `applyRemote` feeds during a period-scoped `pull()` — pointed at the\n * active phase's counters (summaries → records) and cleared before pull returns. Approximate\n * by design: a push interleaved mid-pull on the same engine would attribute its converge\n * applies to the open phase; the counters are a download-budget KPI, not an audit source. */\n private pullByteSink: { records: number; bytes: number } | null = null\n\n constructor(opts: {\n local: NoydbStore\n remote: NoydbStore\n vault: string\n strategy: ConflictStrategy\n emitter: NoydbEventEmitter\n syncPolicy?: SyncPolicy\n role?: SyncTargetRole\n label?: string\n }) {\n this.local = opts.local\n this.remote = opts.remote\n this.vault = opts.vault\n this.strategy = opts.strategy\n this.emitter = opts.emitter\n this.role = opts.role ?? 'sync-peer'\n this.label = opts.label\n\n // Create scheduler if a policy is provided\n const policy = opts.syncPolicy\n if (policy && policy.push.mode !== 'manual') {\n this.scheduler = new SyncScheduler(policy, {\n push: () => this.push().then(() => {}),\n pull: () => this.pull().then(() => {}),\n getDirtyCount: () => this.dirty.length,\n })\n } else {\n this.scheduler = null\n }\n }\n\n /** Start the sync scheduler. Called after vault is fully opened. */\n startScheduler(): void {\n this.scheduler?.start()\n }\n\n /** Stop the sync scheduler. Called on close. */\n stopScheduler(): void {\n this.scheduler?.stop()\n }\n\n /**\n * Register a per-collection conflict resolver.\n * Called by Collection when `conflictPolicy` is set.\n *\n * Pair-coupled (#591 rule 5b): registering for one satellite-pair member\n * registers the same resolver for both — the pair converges under one\n * resolution policy. Expands with whatever the pair-expander knows *at this\n * call*; resolvers registered before their pair existed are covered by\n * `remirrorPairResolvers` at pair-registration time.\n */\n registerConflictResolver(collection: string, resolver: CollectionConflictResolver): void {\n for (const n of this.pairExpander?.([collection]) ?? [collection]) this.conflictResolvers.set(n, resolver)\n }\n\n /**\n * Wire a satellite pair-expansion function (vault registration, #591 Task 11).\n * The closure re-reads the live `SatelliteRegistry` on every call, so pairs\n * declared after this is set are still picked up by `push`/`pull` filters.\n */\n setPairExpander(expander: (names: readonly string[]) => readonly string[]): void {\n this.pairExpander = expander\n }\n\n /**\n * Retroactively mirror per-collection conflict resolvers across a newly\n * registered satellite pair (#591 rule 5b) — covers the NORMAL declaration\n * order, where the base's `conflictPolicy` resolver was registered before\n * the satellite was declared (so the call-time expansion in\n * `registerConflictResolver` saw no pair yet). Tie-break: when BOTH members\n * already carry (different) resolvers, the FIRST name in `names` wins —\n * callers pass `[base, satellite]`, so the base's resolver is canonical.\n * Idempotent; a later explicit registration on either member still\n * overwrites both (last-wins, via the call-time expansion above).\n */\n remirrorPairResolvers(names: readonly string[]): void {\n const canonical = names.map(n => this.conflictResolvers.get(n)).find(r => r !== undefined)\n if (!canonical) return\n for (const n of names) this.conflictResolvers.set(n, canonical)\n }\n\n /** Record a local change for later push. */\n async trackChange(collection: string, id: string, action: 'put' | 'delete', version: number): Promise<void> {\n await this.ensureLoaded()\n\n // Deduplicate: if same collection+id already in dirty, update it\n const idx = this.dirty.findIndex(d => d.collection === collection && d.id === id)\n const entry: DirtyEntry = {\n vault: this.vault,\n collection,\n id,\n action,\n version,\n timestamp: new Date().toISOString(),\n }\n\n if (idx >= 0) {\n this.dirty[idx] = entry\n } else {\n this.dirty.push(entry)\n }\n\n await this.persistMeta()\n\n // Notify scheduler of the write (triggers on-change or debounce)\n this.scheduler?.notifyChange()\n }\n\n /** Remove a dirty entry (satellite fan-out revert cleanup — spec #591). */\n async removeDirty(collection: string, id: string): Promise<void> {\n const before = this.dirty.length\n this.dirty = this.dirty.filter(d => !(d.collection === collection && d.id === id))\n if (this.dirty.length !== before) await this.persistMeta()\n }\n\n /** Push dirty records to remote adapter. Accepts optional `PushOptions` for partial sync. */\n async push(options?: PushOptions): Promise<PushResult> {\n await this.ensureLoaded()\n this.graphBatchController?.begin() // #638 Task 4\n\n let pushed = 0\n const conflicts: Conflict[] = []\n const erasures: ErasureEnforcement[] = []\n const errors: Error[] = []\n const completed: number[] = []\n\n // Partial sync: expand the filter to cover satellite pair partners (#591 rule 5b), then the\n // reserved `_dict_*` collections those (expanded) names depend on (#653) — a locally-dirty\n // dict edit pushes alongside push({collections:['orders']}), symmetric with pull below.\n const expanded = options?.collections ? (this.pairExpander?.(options.collections) ?? options.collections) : null\n const filter = expanded ? new Set([...expanded, ...(this.reservedDictExpander?.(expanded) ?? [])]) : null\n\n for (let i = 0; i < this.dirty.length; i++) {\n const entry = this.dirty[i]!\n\n // Partial sync: skip collections not in the filter\n if (filter && !filter.has(entry.collection)) {\n continue\n }\n\n try {\n if (entry.action === 'delete') {\n await this.remote.delete(this.vault, entry.collection, entry.id)\n completed.push(i)\n pushed++\n } else {\n const envelope = await this.local.get(this.vault, entry.collection, entry.id)\n if (!envelope) {\n // Record was deleted locally after being marked dirty\n completed.push(i)\n continue\n }\n\n if (isTombstoneShape(envelope)) {\n // #590: a tombstone push is an erasure assertion — unconditional,\n // no CAS, no conflict resolution. Erasure always wins.\n await this.remote.put(this.vault, entry.collection, entry.id, envelope)\n completed.push(i)\n pushed++\n continue\n }\n\n try {\n await this.remote.put(\n this.vault,\n entry.collection,\n entry.id,\n envelope,\n entry.version - 1,\n )\n completed.push(i)\n pushed++\n } catch (err) {\n if (err instanceof ConflictError) {\n const remoteEnvelope = await this.remote.get(this.vault, entry.collection, entry.id)\n if (remoteEnvelope) {\n if (isTombstoneShape(remoteEnvelope)) {\n // #590: remote already shredded this record — enforce locally,\n // never resolve. Resolvers must not overrule an erasure.\n await this.applyRemote(entry.collection, entry.id, remoteEnvelope)\n erasures.push(this.reportErasure(entry.collection, entry.id, remoteEnvelope, envelope, 'push'))\n completed.push(i)\n } else if (\n remoteEnvelope._v === envelope._v &&\n isDeleteMarker(remoteEnvelope) !== isDeleteMarker(envelope) &&\n !this.conflictResolvers.get(entry.collection)\n ) {\n // #589: a same-_v delete-vs-edit tie on the push channel. handleConflict's db-level\n // 'version' default would resolve it to local-wins; the tie rule consults ONLY the\n // per-collection resolver, else delete-wins. (When a per-collection resolver IS set,\n // fall through to handleConflict, which already honors it — incl. the merged case.)\n if (isDeleteMarker(remoteEnvelope)) {\n // remote already deleted → converge locally, drop our (edit) push\n await this.applyRemote(entry.collection, entry.id, remoteEnvelope)\n completed.push(i)\n } else {\n // our local is the marker → force the delete onto the remote (unconditional put)\n await this.remote.put(this.vault, entry.collection, entry.id, envelope)\n completed.push(i)\n pushed++\n }\n } else {\n const { handled, conflict } = await this.handleConflict(\n entry.collection,\n entry.id,\n envelope,\n remoteEnvelope,\n 'push',\n )\n conflicts.push(conflict)\n if (handled === 'local') {\n await this.remote.put(this.vault, entry.collection, entry.id, conflict.local)\n completed.push(i)\n pushed++\n } else if (handled === 'remote') {\n await this.applyRemote(entry.collection, entry.id, conflict.remote)\n completed.push(i)\n } else if (handled === 'merged' && conflict.local !== envelope) {\n // Merged envelope is stored in conflict.local (the winner)\n const merged = conflict.local\n await this.remote.put(this.vault, entry.collection, entry.id, merged)\n await this.applyRemote(entry.collection, entry.id, merged)\n completed.push(i)\n pushed++\n }\n // handled === 'deferred': leave in dirty log\n }\n }\n } else {\n throw err\n }\n }\n }\n } catch (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)))\n }\n }\n\n // Remove completed entries from dirty log (reverse order to preserve indices)\n for (const i of completed.sort((a, b) => b - a)) {\n this.dirty.splice(i, 1)\n }\n\n this.lastPush = new Date().toISOString()\n try {\n await this.persistMeta()\n } finally {\n // #644 item 1: flush (which clears `_graphBatch` unconditionally, vault.ts:1316-1320) must\n // run even if `persistMeta()` throws — else a throw here leaves the batch open, corrupting\n // the NEXT pull()/push() call's wave with THIS call's stale touches.\n await this.graphBatchController?.flush() // #638 Task 4\n }\n\n const result: PushResult = { pushed, conflicts, errors, erasures }\n this.emitter.emit('sync:push', result)\n return result\n }\n\n /** Pull remote records to local adapter. Accepts optional `PullOptions` for partial sync. */\n async pull(options?: PullOptions): Promise<PullResult> {\n await this.ensureLoaded()\n\n let pulled = 0\n const conflicts: Conflict[] = []\n const erasures: ErasureEnforcement[] = []\n const errors: Error[] = []\n\n // ── #807 period-scoped pull: validate the option, sync the period summaries\n // (`_periods` + companions — the navigation index, ALWAYS pulled in full,\n // exempt from every filter), then resolve the selected `_ts` windows from the\n // freshly synced index. All BEFORE the graph batch opens, so an invalid\n // option throws without leaving a batch dangling; summary applies route\n // through `applyRemote` like any pull (reserved names have no Collection\n // instance, so the invalidator/graph seams are no-ops for them). ──\n let periodScope: ((envelope: EncryptedEnvelope) => boolean) | null = null\n let phases: { summaries: { records: number; bytes: number }; records: { records: number; bytes: number } } | null = null\n if (options?.periods !== undefined) {\n validatePeriodsOption(options.periods)\n if (!this.periodPullSource) {\n throw new ValidationError(\n 'period-scoped pull requires a vault-attached sync engine (open the vault via createNoydb + openVault).',\n )\n }\n phases = { summaries: { records: 0, bytes: 0 }, records: { records: 0, bytes: 0 } }\n let remotePeriodCount = 0\n this.pullByteSink = phases.summaries\n try {\n for (const collName of PERIOD_SUMMARY_COLLECTIONS) {\n for (const id of await this.remote.list(this.vault, collName)) {\n if (collName === PERIODS_COLLECTION) remotePeriodCount++\n try {\n const remoteEnvelope = await this.remote.get(this.vault, collName, id)\n if (!remoteEnvelope) continue\n const localEnvelope = await this.local.get(this.vault, collName, id)\n if (!localEnvelope || remoteEnvelope._v > localEnvelope._v) {\n await this.applyRemote(collName, id, remoteEnvelope)\n pulled++\n }\n } catch (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)))\n }\n }\n }\n } finally {\n this.pullByteSink = null\n }\n const periodRecords = await this.periodPullSource.periods()\n if (remotePeriodCount > 0 && periodRecords.length === 0) {\n throw new ValidationError(\n 'period-scoped pull: the vault holds _periods records but none are readable — enable the ' +\n 'periods service (`periodsStrategy: withPeriods()`) so pull can resolve the period windows.',\n )\n }\n periodScope = buildPeriodScope(options.periods, periodRecords)\n }\n\n this.graphBatchController?.begin() // #638 Task 4\n if (phases) this.pullByteSink = phases.records\n\n // Partial sync: expand the filter to cover satellite pair partners (#591 rule 5b), then the\n // reserved `_dict_*` collections those (expanded) names depend on (#653) — adopters never\n // name `_dict_*` in a `collections` filter, so a named collection's lookup-field dependency\n // must be pulled alongside it or its labels/membership go stale.\n const expanded = options?.collections ? (this.pairExpander?.(options.collections) ?? options.collections) : null\n const filter = expanded ? new Set([...expanded, ...(this.reservedDictExpander?.(expanded) ?? [])]) : null\n\n try {\n const remoteSnapshot = await this.remote.loadAll(this.vault)\n\n for (const [collName, records] of Object.entries(remoteSnapshot)) {\n // Partial sync: skip collections not in the filter\n if (filter && !filter.has(collName)) {\n continue\n }\n\n for (const [id, remoteEnvelope] of Object.entries(records)) {\n // Partial sync: modifiedSince filter — arriving tombstones are exempt (#590):\n // an erasure must never be skipped by partial sync.\n if (\n options?.modifiedSince &&\n remoteEnvelope._ts <= options.modifiedSince &&\n !isTombstoneShape(remoteEnvelope) &&\n !isDeleteMarker(remoteEnvelope)\n ) {\n continue\n }\n\n // #807: period scope — a record whose `_ts` falls outside every selected\n // window is skipped. Tombstones and delete markers are ALWAYS in scope\n // (built into the predicate): an erasure or delete must never be skipped\n // by partial sync, mirroring the modifiedSince exemption above — that is\n // what lets a device that never pulled a period backfill it later without\n // resurrecting its deleted records.\n if (periodScope !== null && !periodScope(remoteEnvelope)) {\n continue\n }\n\n try {\n const localEnvelope = await this.local.get(this.vault, collName, id)\n const remoteIsTombstone = isTombstoneShape(remoteEnvelope)\n\n if (!localEnvelope) {\n // New record from remote (tombstones included — durable erasure evidence)\n await this.applyRemote(collName, id, remoteEnvelope)\n pulled++\n } else if (isTombstoneShape(localEnvelope)) {\n if (remoteIsTombstone) {\n // Both shredded — keep the higher version counter\n if (remoteEnvelope._v > localEnvelope._v) await this.applyRemote(collName, id, remoteEnvelope)\n } else {\n // Terminal rule (#590): a tombstone is never overwritten by a live\n // envelope, regardless of _v. Re-assert the shred outward instead.\n erasures.push(await this.reassertTombstone(collName, id, localEnvelope, remoteEnvelope))\n }\n } else if (remoteIsTombstone) {\n // Terminal rule (#590): an arriving tombstone wins over any local live\n // envelope — even a newer, dirty one. Resolvers are never consulted.\n const wasDirty = this.dirty.some(d => d.collection === collName && d.id === id)\n await this.applyRemote(collName, id, remoteEnvelope)\n this.dirty = this.dirty.filter(d => !(d.collection === collName && d.id === id))\n pulled++\n if (wasDirty) erasures.push(this.reportErasure(collName, id, remoteEnvelope, localEnvelope, 'pull'))\n } else if (\n remoteEnvelope._v === localEnvelope._v &&\n isDeleteMarker(remoteEnvelope) !== isDeleteMarker(localEnvelope)\n ) {\n // #589: true concurrent delete-vs-edit at the SAME version. Version order\n // can't break the tie. A per-collection resolver decides if one is set;\n // otherwise DELETE wins (the db-level 'version' default is deliberately NOT\n // consulted — it would resolve a tie to local-wins).\n const resolver = this.conflictResolvers.get(collName)\n if (resolver) {\n const winner = await resolver(id, localEnvelope, remoteEnvelope)\n if (winner !== localEnvelope && winner !== null) {\n // #589 (review): a novel/merged winner (neither side verbatim) must also be\n // pushed to remote — mirrors handleConflict's 'merged' handling — so both\n // sides converge instead of local applying it while remote keeps its old copy.\n if (winner !== remoteEnvelope) {\n await this.remote.put(this.vault, collName, id, winner)\n }\n await this.applyRemote(collName, id, winner)\n this.dirty = this.dirty.filter(d => !(d.collection === collName && d.id === id))\n pulled++\n }\n // winner === localEnvelope or null → keep local (its dirty entry, if any, pushes out)\n } else if (isDeleteMarker(remoteEnvelope)) {\n // no resolver → delete wins; the incoming marker is the delete\n await this.applyRemote(collName, id, remoteEnvelope)\n this.dirty = this.dirty.filter(d => !(d.collection === collName && d.id === id))\n pulled++\n }\n // no resolver and LOCAL is the marker → keep local marker; its dirty 'put' pushes it outward\n } else if (remoteEnvelope._v > localEnvelope._v) {\n // Remote is newer — check if we have a dirty entry for this\n const isDirty = this.dirty.some(d => d.collection === collName && d.id === id)\n if (isDirty) {\n // Both changed — conflict\n const { handled, conflict } = await this.handleConflict(\n collName,\n id,\n localEnvelope,\n remoteEnvelope,\n 'pull',\n )\n conflicts.push(conflict)\n if (handled === 'remote') {\n await this.applyRemote(collName, id, conflict.remote)\n this.dirty = this.dirty.filter(d => !(d.collection === collName && d.id === id))\n pulled++\n } else if (handled === 'merged' && conflict.local !== localEnvelope) {\n const merged = conflict.local\n await this.applyRemote(collName, id, merged)\n this.dirty = this.dirty.filter(d => !(d.collection === collName && d.id === id))\n pulled++\n }\n // 'local' or 'deferred': push handles it\n } else {\n // Remote is newer, no local changes — update\n await this.applyRemote(collName, id, remoteEnvelope)\n pulled++\n }\n }\n // Same version or local is newer — skip (push will handle)\n } catch (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)))\n }\n }\n }\n\n // #650 Task 4 (#647) — reserved lookup collections (`_dict_*`/`_lookup_*`) are invisible to\n // `remote.loadAll()` above (the store contract skips `_`-prefixed names) but DO sync via\n // this EXPLICIT, declared registry — not a blanket underscore-glob; other `_` namespaces\n // keep their `loadAll`-skip semantics untouched. Applied through the SAME `applyRemote` path,\n // still inside this try block, BEFORE `persistMeta`/`flush` below — so a pulled vocabulary\n // row lands before the wave (flushed at the end of `pull()`) recomputes any dependent.\n //\n // #647 fix wave 1: `LookupHandle.delete()`/`rename()` now write a version-ordered\n // delete-marker row (mirroring #589) instead of a raw adapter delete, so a removed key is\n // a normal row here too — reachable via `remote.list()` like any other id, applied by\n // version like any other write. The only reserved-tier-specific rule is the same-version\n // tie-break below: reserved lookups have no per-collection conflict-resolver concept\n // (dictionaries are admin-edited, not multi-actor-negotiated), so a converging delete\n // unconditionally wins over a same-version live edit.\n //\n // #807: the period scope deliberately does NOT apply here — vocabularies/config rows are\n // not period-partitioned data (a thin client's lookups must be complete regardless of\n // which periods it pulled), the same always-pull rule as the `_periods` summaries phase.\n for (const collName of this.reservedLookup?.collections() ?? []) {\n if (filter && !filter.has(collName)) continue\n for (const id of await this.remote.list(this.vault, collName)) {\n try {\n const remoteEnvelope = await this.remote.get(this.vault, collName, id)\n if (!remoteEnvelope) continue\n\n // Partial sync: modifiedSince filter — a delete marker is exempt, mirroring the\n // main loop's tombstone/marker exemption above: a deletion must never be silently\n // skipped by a time-window partial pull.\n if (\n options?.modifiedSince &&\n remoteEnvelope._ts <= options.modifiedSince &&\n !isDeleteMarker(remoteEnvelope)\n ) {\n continue\n }\n\n const localEnvelope = await this.local.get(this.vault, collName, id)\n if (!localEnvelope) {\n await this.applyRemote(collName, id, remoteEnvelope)\n pulled++\n } else if (\n remoteEnvelope._v === localEnvelope._v &&\n isDeleteMarker(remoteEnvelope) !== isDeleteMarker(localEnvelope)\n ) {\n // Same-version delete-vs-edit tie (#589's rule, reserved-tier default: no\n // resolver concept here, so delete always wins).\n if (isDeleteMarker(remoteEnvelope)) {\n await this.applyRemote(collName, id, remoteEnvelope)\n this.dirty = this.dirty.filter(d => !(d.collection === collName && d.id === id))\n pulled++\n }\n // else: local already holds the marker (a local delete not yet observed\n // remotely) — keep it; push re-asserts it.\n } else if (remoteEnvelope._v > localEnvelope._v) {\n await this.applyRemote(collName, id, remoteEnvelope)\n // Drop any now-superseded local dirty entry so a subsequent push doesn't\n // redundantly re-fight a CAS conflict over content pull just overwrote.\n this.dirty = this.dirty.filter(d => !(d.collection === collName && d.id === id))\n pulled++\n }\n } catch (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)))\n }\n }\n }\n } catch (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)))\n }\n\n this.lastPull = new Date().toISOString()\n try {\n await this.persistMeta()\n } finally {\n this.pullByteSink = null // #807: stop attributing applies to this pull's KPI counters\n // #644 item 1: flush must run even if `persistMeta()` throws (see push()'s identical guard).\n await this.graphBatchController?.flush() // #638 Task 4\n }\n\n const result: PullResult = { pulled, conflicts, errors, erasures, ...(phases !== null ? { phases } : {}) }\n this.emitter.emit('sync:pull', result)\n return result\n }\n\n /** Bidirectional sync: pull then push. */\n async sync(options?: { push?: PushOptions; pull?: PullOptions }): Promise<{ pull: PullResult; push: PushResult }> {\n const pullResult = await this.pull(options?.pull)\n const pushResult = await this.push(options?.push)\n return { pull: pullResult, push: pushResult }\n }\n\n /**\n * Push a specific subset of dirty entries (for sync transactions, ).\n * Entries are matched by collection+id from the dirty log; matched entries\n * are removed from the dirty log on success.\n */\n async pushFiltered(predicate: (entry: DirtyEntry) => boolean): Promise<PushResult> {\n await this.ensureLoaded()\n\n let pushed = 0\n const conflicts: Conflict[] = []\n const erasures: ErasureEnforcement[] = []\n const errors: Error[] = []\n const completed: number[] = []\n\n for (let i = 0; i < this.dirty.length; i++) {\n const entry = this.dirty[i]!\n if (!predicate(entry)) continue\n\n try {\n if (entry.action === 'delete') {\n await this.remote.delete(this.vault, entry.collection, entry.id)\n completed.push(i)\n pushed++\n } else {\n const envelope = await this.local.get(this.vault, entry.collection, entry.id)\n if (!envelope) {\n completed.push(i)\n continue\n }\n\n if (isTombstoneShape(envelope)) {\n // #590: a tombstone push is an erasure assertion — unconditional,\n // no CAS, no conflict resolution. Erasure always wins.\n await this.remote.put(this.vault, entry.collection, entry.id, envelope)\n completed.push(i)\n pushed++\n continue\n }\n\n try {\n await this.remote.put(\n this.vault,\n entry.collection,\n entry.id,\n envelope,\n entry.version - 1,\n )\n completed.push(i)\n pushed++\n } catch (err) {\n if (err instanceof ConflictError) {\n const remoteEnvelope = await this.remote.get(this.vault, entry.collection, entry.id)\n if (remoteEnvelope) {\n if (isTombstoneShape(remoteEnvelope)) {\n // #590: remote already shredded this record — enforce locally,\n // never resolve. Resolvers must not overrule an erasure.\n await this.applyRemote(entry.collection, entry.id, remoteEnvelope)\n erasures.push(this.reportErasure(entry.collection, entry.id, remoteEnvelope, envelope, 'push'))\n completed.push(i)\n } else if (\n remoteEnvelope._v === envelope._v &&\n isDeleteMarker(remoteEnvelope) !== isDeleteMarker(envelope) &&\n !this.conflictResolvers.get(entry.collection)\n ) {\n // #589: a same-_v delete-vs-edit tie on the push channel. handleConflict's db-level\n // 'version' default would resolve it to local-wins; the tie rule consults ONLY the\n // per-collection resolver, else delete-wins. (When a per-collection resolver IS set,\n // fall through to handleConflict, which already honors it — incl. the merged case.)\n if (isDeleteMarker(remoteEnvelope)) {\n // remote already deleted → converge locally, drop our (edit) push\n await this.applyRemote(entry.collection, entry.id, remoteEnvelope)\n completed.push(i)\n } else {\n // our local is the marker → force the delete onto the remote (unconditional put)\n await this.remote.put(this.vault, entry.collection, entry.id, envelope)\n completed.push(i)\n pushed++\n }\n } else {\n const { handled, conflict } = await this.handleConflict(\n entry.collection,\n entry.id,\n envelope,\n remoteEnvelope,\n 'push',\n )\n conflicts.push(conflict)\n if (handled === 'local') {\n await this.remote.put(this.vault, entry.collection, entry.id, conflict.local)\n completed.push(i)\n pushed++\n } else if (handled === 'remote') {\n await this.applyRemote(entry.collection, entry.id, conflict.remote)\n completed.push(i)\n } else if (handled === 'merged' && conflict.local !== envelope) {\n const merged = conflict.local\n await this.remote.put(this.vault, entry.collection, entry.id, merged)\n await this.applyRemote(entry.collection, entry.id, merged)\n completed.push(i)\n pushed++\n }\n }\n }\n } else {\n throw err\n }\n }\n }\n } catch (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)))\n }\n }\n\n for (const i of completed.sort((a, b) => b - a)) {\n this.dirty.splice(i, 1)\n }\n\n this.lastPush = new Date().toISOString()\n await this.persistMeta()\n\n const result: PushResult = { pushed, conflicts, errors, erasures }\n this.emitter.emit('sync:push', result)\n return result\n }\n\n /** Get current sync status. */\n status(): SyncStatus {\n return {\n dirty: this.dirty.length,\n lastPush: this.lastPush,\n lastPull: this.lastPull,\n online: this.isOnline,\n }\n }\n\n // ─── Auto-Sync ───────────────────────────────────────────────────\n\n /** Start auto-sync: listen for online/offline events, optional periodic sync. */\n startAutoSync(intervalMs?: number): void {\n // Online/offline detection\n if (typeof globalThis.addEventListener === 'function') {\n globalThis.addEventListener('online', this.handleOnline)\n globalThis.addEventListener('offline', this.handleOffline)\n }\n\n // Periodic sync\n if (intervalMs && intervalMs > 0) {\n this.autoSyncInterval = setInterval(() => {\n if (this.isOnline) {\n void this.sync()\n }\n }, intervalMs)\n }\n }\n\n /** Stop auto-sync and scheduler. */\n stopAutoSync(): void {\n this.stopScheduler()\n if (typeof globalThis.removeEventListener === 'function') {\n globalThis.removeEventListener('online', this.handleOnline)\n globalThis.removeEventListener('offline', this.handleOffline)\n }\n if (this.autoSyncInterval) {\n clearInterval(this.autoSyncInterval)\n this.autoSyncInterval = null\n }\n }\n\n private handleOnline = (): void => {\n this.isOnline = true\n this.emitter.emit('sync:online', undefined as never)\n void this.sync()\n }\n\n private handleOffline = (): void => {\n this.isOnline = false\n this.emitter.emit('sync:offline', undefined as never)\n }\n\n /** Apply an envelope to the local store and refresh in-memory views (#598). `action` (#640):\n * classified HERE — the one choke point that still holds the envelope — so the\n * cacheInvalidator seam (and, downstream, the dispatch wave) can tell a pulled delete from an\n * ordinary put. `isTombstoneShape` covers a forgotten-elsewhere record arriving as a shred;\n * `isDeleteMarker` covers an ordinary (#589) delete — both route to the SAME 'delete' action\n * (sync delete ≠ forget: freshness only, no shred/residue channel on the receiving side). */\n private async applyRemote(collection: string, id: string, envelope: EncryptedEnvelope): Promise<void> {\n await this.local.put(this.vault, collection, id, envelope)\n if (this.pullByteSink !== null) {\n // #807: KPI — one applied envelope; bytes ≈ ciphertext payload size.\n this.pullByteSink.records++\n this.pullByteSink.bytes += envelopeBodySize(envelope)\n }\n const action: 'put' | 'delete' = isTombstoneShape(envelope) || isDeleteMarker(envelope) ? 'delete' : 'put'\n await this.cacheInvalidator?.(collection, id, action)\n }\n\n /** Record + emit a tombstone enforcement (#590). */\n private reportErasure(\n collection: string, id: string,\n tombstone: EncryptedEnvelope, suppressed: EncryptedEnvelope,\n direction: 'pull' | 'push',\n ): ErasureEnforcement {\n const enforcement: ErasureEnforcement = { vault: this.vault, collection, id, tombstone, suppressed, direction }\n this.emitter.emit('sync:erasure', enforcement)\n return enforcement\n }\n\n /**\n * Re-assert a local tombstone over a live remote envelope (#590): the shred\n * wins in both directions, regardless of `_v`. Bumps the tombstone to the\n * suppressed envelope's `_v` when higher so per-key version counters stay\n * monotonic on every store; `_by` (the shredding actor) is preserved.\n */\n private async reassertTombstone(\n collection: string, id: string,\n tombstone: EncryptedEnvelope, suppressedRemote: EncryptedEnvelope,\n ): Promise<ErasureEnforcement> {\n let winner = tombstone\n if (suppressedRemote._v > tombstone._v) {\n winner = { ...tombstone, _v: suppressedRemote._v, _ts: new Date().toISOString() }\n await this.applyRemote(collection, id, winner)\n }\n await this.remote.put(this.vault, collection, id, winner)\n // The re-assert already delivered the tombstone remotely — drop any dirty\n // entry so the following push doesn't redundantly re-put it.\n this.dirty = this.dirty.filter(d => !(d.collection === collection && d.id === id))\n return this.reportErasure(collection, id, winner, suppressedRemote, 'pull')\n }\n\n /**\n * Resolve a conflict, checking per-collection resolvers first,\n * then falling back to the db-level `ConflictStrategy`.\n *\n * Returns the resolved `Conflict` object (possibly with `resolve` set for\n * manual mode) and a `handled` discriminant:\n * - `'local'` — keep the local envelope; push it to remote.\n * - `'remote'` — keep the remote envelope; update local.\n * - `'merged'` — a custom merge fn produced a new envelope stored as `conflict.local`.\n * - `'deferred'` — manual mode, resolve was not called synchronously.\n */\n private async handleConflict(\n collection: string,\n id: string,\n local: EncryptedEnvelope,\n remote: EncryptedEnvelope,\n _phase: 'push' | 'pull',\n ): Promise<{ handled: 'local' | 'remote' | 'merged' | 'deferred'; conflict: Conflict }> {\n const resolver = this.conflictResolvers.get(collection)\n\n if (resolver) {\n // Per-collection resolver is responsible for emitting sync:conflict\n // (manual policy emits with a resolve callback; LWW/FWW/custom are silent).\n const winner = await resolver(id, local, remote)\n const base: Conflict = {\n vault: this.vault,\n collection,\n id,\n local,\n remote,\n localVersion: local._v,\n remoteVersion: remote._v,\n }\n if (winner === null) return { handled: 'deferred', conflict: base }\n if (winner === local) return { handled: 'local', conflict: base }\n if (winner === remote) return { handled: 'remote', conflict: base }\n // Custom merge fn produced a new envelope — store as conflict.local for the caller\n return {\n handled: 'merged',\n conflict: { ...base, local: winner, localVersion: winner._v },\n }\n }\n\n // Fall back to db-level strategy — emit once\n const baseConflict: Conflict = {\n vault: this.vault,\n collection,\n id,\n local,\n remote,\n localVersion: local._v,\n remoteVersion: remote._v,\n }\n this.emitter.emit('sync:conflict', baseConflict)\n const side = this.legacyResolve(baseConflict)\n return { handled: side, conflict: baseConflict }\n }\n\n /** DB-level ConflictStrategy resolution (legacy, kept for backward compat). */\n private legacyResolve(conflict: Conflict): 'local' | 'remote' {\n if (typeof this.strategy === 'function') {\n return this.strategy(conflict)\n }\n switch (this.strategy) {\n case 'local-wins': return 'local'\n case 'remote-wins': return 'remote'\n case 'version':\n default:\n return conflict.localVersion >= conflict.remoteVersion ? 'local' : 'remote'\n }\n }\n\n // ─── Persistence ─────────────────────────────────────────────────\n\n private async ensureLoaded(): Promise<void> {\n if (this.loaded) return\n\n const envelope = await this.local.get(this.vault, '_sync', 'meta')\n if (envelope) {\n const meta = JSON.parse(envelope._data) as SyncMetadata\n this.dirty = [...meta.dirty]\n this.lastPush = meta.last_push\n this.lastPull = meta.last_pull\n }\n\n this.loaded = true\n }\n\n private async persistMeta(): Promise<void> {\n const meta: SyncMetadata = {\n _noydb_sync: NOYDB_SYNC_VERSION,\n last_push: this.lastPush,\n last_pull: this.lastPull,\n dirty: this.dirty,\n }\n\n const envelope: EncryptedEnvelope = {\n _noydb: 1,\n _v: 1,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: JSON.stringify(meta),\n }\n\n await this.local.put(this.vault, '_sync', 'meta', envelope)\n }\n}\n","import type { SyncTransactionResult } from '../../kernel/types.js'\nimport type { SyncEngine } from './sync.js'\nimport type { Vault } from '../../kernel/vault.js'\n\ninterface TxOp {\n readonly type: 'put' | 'delete'\n readonly collection: string\n readonly id: string\n readonly record?: unknown\n}\n\n/**\n * Sync transaction.\n *\n * Stages local writes and then pushes only those records to remote in a\n * single batch. If any record conflicts during the push, the result\n * carries `status: 'conflict'` — no automatic rollback is performed;\n * the caller handles conflict resolution.\n *\n * Obtain via `db.transaction(compartmentName)`.\n */\nexport class SyncTransaction {\n private readonly comp: Vault\n private readonly engine: SyncEngine\n private readonly ops: TxOp[] = []\n\n /** @internal — constructed by `Noydb.transaction()` */\n constructor(comp: Vault, engine: SyncEngine) {\n this.comp = comp\n this.engine = engine\n }\n\n /** Stage a record write. Does not write to any adapter until `commit()`. */\n put(collection: string, id: string, record: unknown): this {\n this.ops.push({ type: 'put', collection, id, record })\n return this\n }\n\n /** Stage a record delete. Does not write to any adapter until `commit()`. */\n delete(collection: string, id: string): this {\n this.ops.push({ type: 'delete', collection, id })\n return this\n }\n\n /**\n * Commit the transaction.\n *\n * Phase 1 — writes all staged operations to the local adapter via the\n * collection layer (encryption + dirty-log tracking).\n *\n * Phase 2 — pushes only the records that were written in this\n * transaction to the remote adapter. Existing dirty entries from\n * outside this transaction are not affected.\n *\n * If any record conflicts during the push, `status` is `'conflict'`\n * and `conflicts` lists the affected records. No automatic rollback is\n * performed.\n */\n async commit(): Promise<SyncTransactionResult> {\n // Phase 1: write all staged ops to local via collection layer\n for (const op of this.ops) {\n if (op.type === 'put') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await (this.comp.collection<any>(op.collection)).put(op.id, op.record as any)\n } else {\n await this.comp.collection(op.collection).delete(op.id)\n }\n }\n\n // Phase 2: push only the records from this transaction\n const opSet = new Set<string>()\n for (const op of this.ops) {\n opSet.add(`${op.collection}::${op.id}`)\n }\n\n const pushResult = await this.engine.pushFiltered(\n (entry) => opSet.has(`${entry.collection}::${entry.id}`),\n )\n\n return {\n status: pushResult.conflicts.length > 0 ? 'conflict' : 'committed',\n pushed: pushResult.pushed,\n conflicts: pushResult.conflicts,\n ...(pushResult.erasures?.length ? { erasures: pushResult.erasures } : {}),\n }\n }\n}\n"],"mappings":"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","names":[]}
|