@noy-db/hub 0.8.0 → 0.9.0-pre.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.md +8 -8
- package/codemods/0.4.0-pre.json +1 -1
- package/codemods/0.8.0-pre.json +1 -1
- package/dist/api-6EFD43KN.js +18 -0
- package/dist/as/index.js +33 -23
- package/dist/as/index.js.map +1 -1
- package/dist/at/index.js +8 -8
- package/dist/attestation/index.js +18 -18
- package/dist/{backup-YE2VQ26S.js → backup-RJBFQSHE.js} +92 -31
- package/dist/backup-RJBFQSHE.js.map +1 -0
- package/dist/blobs/index.js +12 -12
- package/dist/broker/index.js +19 -11
- package/dist/broker/index.js.map +1 -1
- package/dist/by/index.js +2 -2
- package/dist/capsule/contract.d.ts +1 -1
- package/dist/capsule/contract.js +6 -6
- package/dist/capsule/enclave-aes/broker/proof.d.ts +12 -3
- package/dist/capsule/enclave-aes/crypto.d.ts +18 -0
- package/dist/capsule/enclave-aes/index.d.ts +3 -3
- package/dist/capsule/enclave-aes/index.js +46 -14
- package/dist/capsule/enclave-aes/index.js.map +1 -1
- package/dist/capsule/index.d.ts +2 -2
- package/dist/capsule/plumbing/record-codec.d.ts +7 -0
- package/dist/{capsule-VOLX3QRQ.js → capsule-6LJAVQNM.js} +14 -8
- package/dist/cargo/index.js +35 -35
- package/dist/{chunk-GZT3NQRA.js → chunk-25FXU6WS.js} +8 -2
- package/dist/chunk-25FXU6WS.js.map +1 -0
- package/dist/{chunk-OYW3I5M4.js → chunk-2DOCDOPL.js} +6 -6
- package/dist/chunk-2DX4JCRI.js +1 -0
- package/dist/{chunk-FLPCETP6.js → chunk-2LBS62AC.js} +2 -2
- package/dist/{chunk-WXE4W2U6.js → chunk-2ST3WA3C.js} +4 -4
- package/dist/{chunk-DQESF6RP.js → chunk-3G3GOGZZ.js} +50 -40
- package/dist/chunk-3G3GOGZZ.js.map +1 -0
- package/dist/{chunk-O2IMQSNA.js → chunk-3YBIVLTC.js} +2 -2
- package/dist/{chunk-LGNDFRYC.js → chunk-4CFHL5RY.js} +1 -1
- package/dist/chunk-4CFHL5RY.js.map +1 -0
- package/dist/{chunk-DIZLQVAA.js → chunk-4CHM2TRS.js} +2 -2
- package/dist/{chunk-V7ADQF5O.js → chunk-4CUJLEOA.js} +4 -4
- package/dist/{chunk-3EWS67C7.js → chunk-4TG2ITRW.js} +2 -2
- package/dist/{chunk-HS554XAF.js → chunk-5HQFROZS.js} +2 -2
- package/dist/{chunk-IUBMZPHM.js → chunk-5ZVE3N5C.js} +5 -6
- package/dist/chunk-5ZVE3N5C.js.map +1 -0
- package/dist/{chunk-PP3K67AJ.js → chunk-654RE5YC.js} +3 -3
- package/dist/{chunk-FOEDQATO.js → chunk-66GD7XXU.js} +2 -2
- package/dist/{chunk-BNWA3WG5.js → chunk-72FQ46VW.js} +3 -3
- package/dist/{chunk-EYG2CY2X.js → chunk-7JPA3LIH.js} +11 -11
- package/dist/chunk-7JPA3LIH.js.map +1 -0
- package/dist/{chunk-JQYKPKG6.js → chunk-7K42TSCY.js} +4 -4
- package/dist/{chunk-BPWYQ25R.js → chunk-7Q6IU3R7.js} +4 -4
- package/dist/{chunk-BPWYQ25R.js.map → chunk-7Q6IU3R7.js.map} +1 -1
- package/dist/{chunk-T7IR4N2S.js → chunk-7UHLO4ZN.js} +37 -2
- package/dist/chunk-7UHLO4ZN.js.map +1 -0
- package/dist/{chunk-UEFOQH74.js → chunk-ADCXTW7L.js} +2 -2
- package/dist/{chunk-MBMTND6D.js → chunk-AGGPT4UE.js} +2 -2
- package/dist/{chunk-RB4DOPGZ.js → chunk-AKIEBFIJ.js} +10 -10
- package/dist/{chunk-VHBKYN2J.js → chunk-B3B6HV5S.js} +2 -2
- package/dist/{chunk-JWWYRR4Q.js → chunk-B6EWMGFX.js} +2 -2
- package/dist/{chunk-H7JBY4P7.js → chunk-B7Y2BRQA.js} +14 -14
- package/dist/chunk-B7Y2BRQA.js.map +1 -0
- package/dist/{chunk-4CIQDWRB.js → chunk-BFJE2GPP.js} +14 -8
- package/dist/{chunk-4CIQDWRB.js.map → chunk-BFJE2GPP.js.map} +1 -1
- package/dist/{chunk-WPOYWFSE.js → chunk-BJRKXNFP.js} +7 -7
- package/dist/{chunk-H427H63K.js → chunk-BPQ5CRJF.js} +2 -2
- package/dist/{chunk-VFHCKG5M.js → chunk-BVRJYMFZ.js} +3 -3
- package/dist/{chunk-244QBK7Z.js → chunk-C4XV3PSJ.js} +4 -4
- package/dist/{chunk-244QBK7Z.js.map → chunk-C4XV3PSJ.js.map} +1 -1
- package/dist/{chunk-SMV2HRDU.js → chunk-DFC4WTJW.js} +2 -2
- package/dist/{chunk-5A4345TG.js → chunk-DMIEKD24.js} +3 -3
- package/dist/{chunk-TKVUE4JF.js → chunk-DQAXA2GK.js} +9 -9
- package/dist/{chunk-AL6ML4LA.js → chunk-DWCFGF2K.js} +5 -5
- package/dist/{chunk-Y3LPTBG7.js → chunk-E2C6FFSI.js} +6 -9
- package/dist/chunk-E2C6FFSI.js.map +1 -0
- package/dist/{chunk-LNSVGRHO.js → chunk-EQ6C2ZPT.js} +17 -51
- package/dist/chunk-EQ6C2ZPT.js.map +1 -0
- package/dist/chunk-EZOFTI4K.js +60 -0
- package/dist/chunk-EZOFTI4K.js.map +1 -0
- package/dist/{chunk-PD64727P.js → chunk-FMINS2B6.js} +2 -2
- package/dist/chunk-FMINS2B6.js.map +1 -0
- package/dist/{chunk-QBLT6POL.js → chunk-FPF5HRBE.js} +2 -2
- package/dist/{chunk-UA372JHH.js → chunk-FZVCZEVI.js} +3 -3
- package/dist/chunk-FZVCZEVI.js.map +1 -0
- package/dist/{chunk-FFWI7UH7.js → chunk-GAGT3YZN.js} +1 -1
- package/dist/chunk-GAGT3YZN.js.map +1 -0
- package/dist/{chunk-5WWDGFOS.js → chunk-GIQJGXV7.js} +2 -2
- package/dist/{chunk-V3RWOOSO.js → chunk-GYZICGRU.js} +4 -4
- package/dist/chunk-GYZICGRU.js.map +1 -0
- package/dist/{chunk-BVCUTXE3.js → chunk-H334G6KU.js} +2 -2
- package/dist/{chunk-AEQGFRU4.js → chunk-HE7EAHGS.js} +3 -3
- package/dist/chunk-HKB4HZDU.js +74 -0
- package/dist/chunk-HKB4HZDU.js.map +1 -0
- package/dist/{chunk-4V3Q35WG.js → chunk-ICF3TIGY.js} +3 -3
- package/dist/{chunk-3L2NBKDR.js → chunk-IF2M43G4.js} +2 -2
- package/dist/{chunk-37EPTBBR.js → chunk-IKPQ2GGB.js} +46 -4
- package/dist/chunk-IKPQ2GGB.js.map +1 -0
- package/dist/{chunk-DHHGL42E.js → chunk-IKS2CPAG.js} +27 -11
- package/dist/chunk-IKS2CPAG.js.map +1 -0
- package/dist/{chunk-3A5DUZ56.js → chunk-IMIQAGR7.js} +6 -6
- package/dist/{chunk-3A5DUZ56.js.map → chunk-IMIQAGR7.js.map} +1 -1
- package/dist/{chunk-A4XMIZT3.js → chunk-IQAJ4OR6.js} +41 -6
- package/dist/chunk-IQAJ4OR6.js.map +1 -0
- package/dist/{chunk-IUT3GWP3.js → chunk-IUD7WU67.js} +17 -93
- package/dist/chunk-IUD7WU67.js.map +1 -0
- package/dist/{chunk-AUKKUPVB.js → chunk-IVLRPRXM.js} +2 -2
- package/dist/{chunk-GUZET4SG.js → chunk-JAQHWHUN.js} +4 -4
- package/dist/chunk-JAQHWHUN.js.map +1 -0
- package/dist/{chunk-Y2RKVFKR.js → chunk-K4JSTGXT.js} +2 -2
- package/dist/{chunk-EJLV62NI.js → chunk-KN4LUMH6.js} +2 -2
- package/dist/{chunk-23D6M6VM.js → chunk-KZA3DITX.js} +4 -4
- package/dist/{chunk-E2N2T5ZO.js → chunk-L2VM64HK.js} +2 -2
- package/dist/chunk-L4UL4IAR.js +55 -0
- package/dist/chunk-L4UL4IAR.js.map +1 -0
- package/dist/{chunk-UIYESRD6.js → chunk-LUCQY3OM.js} +10 -10
- package/dist/{chunk-UIYESRD6.js.map → chunk-LUCQY3OM.js.map} +1 -1
- package/dist/{chunk-O46CHQT7.js → chunk-MAQBYBUX.js} +4 -4
- package/dist/{chunk-EZOAKKTB.js → chunk-MBPZMZVB.js} +2 -2
- package/dist/{chunk-XQRFSKYM.js → chunk-MCNNZBG7.js} +2 -2
- package/dist/{chunk-YRXFFWG4.js → chunk-MLVPMDOZ.js} +2 -2
- package/dist/{chunk-5FE75J4S.js → chunk-MMCN7TLK.js} +5 -5
- package/dist/{chunk-MXOQ5JCG.js → chunk-MRCR7ZMC.js} +686 -202
- package/dist/chunk-MRCR7ZMC.js.map +1 -0
- package/dist/{chunk-SYNY3VB5.js → chunk-NCTN2FW4.js} +2 -2
- package/dist/{chunk-4T3SE27M.js → chunk-ND6S2DKC.js} +2 -2
- package/dist/{chunk-3XJQBBZD.js → chunk-NTVKHQ3L.js} +2 -2
- package/dist/{chunk-RHVMZSUU.js → chunk-NV4OESHC.js} +1 -1
- package/dist/chunk-NV4OESHC.js.map +1 -0
- package/dist/chunk-NXQEOC3X.js +40 -0
- package/dist/chunk-NXQEOC3X.js.map +1 -0
- package/dist/{chunk-Q4UHJ5SN.js → chunk-O2VQ3K36.js} +2 -2
- package/dist/{chunk-HW7CMKUG.js → chunk-OFD2HD6F.js} +1 -1
- package/dist/{chunk-HW7CMKUG.js.map → chunk-OFD2HD6F.js.map} +1 -1
- package/dist/{chunk-2GHDLTPE.js → chunk-OGKMRIIJ.js} +2 -2
- package/dist/{chunk-N7EVQD5S.js → chunk-ONZ3B5EW.js} +4 -4
- package/dist/{chunk-VKI2EB3N.js → chunk-OOPSMQX5.js} +3 -3
- package/dist/{chunk-VMV5SBE4.js → chunk-OYEBSWLE.js} +5 -5
- package/dist/{chunk-JDEYF3PJ.js → chunk-P6FATPXQ.js} +2 -2
- package/dist/chunk-P6FATPXQ.js.map +1 -0
- package/dist/{chunk-VVOLDVIR.js → chunk-PLTIJDQN.js} +2 -2
- package/dist/{chunk-OKSTSITU.js → chunk-PR7ILCIH.js} +3 -3
- package/dist/{chunk-7R2Q7CPL.js → chunk-QE3TLZ6Z.js} +7 -7
- package/dist/{chunk-G2MVBDFP.js → chunk-QFW4MYGN.js} +39 -9
- package/dist/chunk-QFW4MYGN.js.map +1 -0
- package/dist/{chunk-6QVKPHBF.js → chunk-QHG7GQKM.js} +2 -2
- package/dist/chunk-QJNAHPVT.js +91 -0
- package/dist/chunk-QJNAHPVT.js.map +1 -0
- package/dist/{chunk-LIOVX5TJ.js → chunk-QT5ITL25.js} +3 -3
- package/dist/{chunk-IMQKWR46.js → chunk-R2ZULFXU.js} +4 -5
- package/dist/chunk-R2ZULFXU.js.map +1 -0
- package/dist/{chunk-Q2XRFWOI.js → chunk-RRTQAQ3N.js} +2 -2
- package/dist/{chunk-H23XHRTT.js → chunk-RUE6TCES.js} +809 -101
- package/dist/chunk-RUE6TCES.js.map +1 -0
- package/dist/chunk-RWFQVNXR.js +24 -0
- package/dist/chunk-RWFQVNXR.js.map +1 -0
- package/dist/{chunk-2PVXF5CS.js → chunk-SALY3LIJ.js} +3 -3
- package/dist/{chunk-JOSPH3E5.js → chunk-SBAR5JQ6.js} +2 -2
- package/dist/{chunk-N7UDP5VU.js → chunk-SCH2F7R2.js} +7 -7
- package/dist/{chunk-MXKI4ABF.js → chunk-SOOAW6HK.js} +3 -3
- package/dist/{chunk-VQDZYB2B.js → chunk-T4VTRKCY.js} +3 -3
- package/dist/{chunk-VQDZYB2B.js.map → chunk-T4VTRKCY.js.map} +1 -1
- package/dist/{chunk-TEHHROZL.js → chunk-T5EXREMW.js} +5 -5
- package/dist/{chunk-M5B2W6YY.js → chunk-TDJVQTXD.js} +3 -3
- package/dist/{chunk-QMWY2FDS.js → chunk-THQ3BHLM.js} +2 -2
- package/dist/{chunk-VAORSCSU.js → chunk-TLIFNRU5.js} +2 -2
- package/dist/chunk-TLIFNRU5.js.map +1 -0
- package/dist/{chunk-FVH5QYY2.js → chunk-TRHUNPXP.js} +2 -2
- package/dist/{chunk-EGVJ7TM4.js → chunk-UL6MTRMJ.js} +8 -9
- package/dist/chunk-UL6MTRMJ.js.map +1 -0
- package/dist/chunk-UNL6YGL5.js +169 -0
- package/dist/chunk-UNL6YGL5.js.map +1 -0
- package/dist/{chunk-747G6CT3.js → chunk-UORWAG7K.js} +2 -2
- package/dist/{chunk-XSPFDUDG.js → chunk-V7AKGAXY.js} +314 -77
- package/dist/chunk-V7AKGAXY.js.map +1 -0
- package/dist/{chunk-TZZBXPZK.js → chunk-VLQ6KT7F.js} +2 -2
- package/dist/{chunk-NCO25JZO.js → chunk-VP37VUKQ.js} +4 -4
- package/dist/{chunk-52MGURXO.js → chunk-WBUHHMLN.js} +2 -2
- package/dist/{chunk-EZGV6F47.js → chunk-WRLWEYHY.js} +7 -1
- package/dist/chunk-WRLWEYHY.js.map +1 -0
- package/dist/{chunk-2MG3SW7Q.js → chunk-X6Z2SIRF.js} +2 -2
- package/dist/{chunk-U2FZNEWJ.js → chunk-XLKWF3L4.js} +8 -6
- package/dist/{chunk-U2FZNEWJ.js.map → chunk-XLKWF3L4.js.map} +1 -1
- package/dist/{chunk-5UMK7GRI.js → chunk-XPNJH7GN.js} +5 -5
- package/dist/{chunk-KZXDLBY6.js → chunk-XPW7T2GN.js} +5 -5
- package/dist/{chunk-SV2PM2OY.js → chunk-XX4R7TUB.js} +1 -1
- package/dist/chunk-XX4R7TUB.js.map +1 -0
- package/dist/{chunk-NBQ3KK6X.js → chunk-Y55XHMQB.js} +4 -4
- package/dist/chunk-Y55XHMQB.js.map +1 -0
- package/dist/{chunk-5KC7QYEC.js → chunk-YH4OUOSI.js} +33 -7
- package/dist/chunk-YH4OUOSI.js.map +1 -0
- package/dist/{chunk-FFJFE4T7.js → chunk-YJGKOSQD.js} +2 -2
- package/dist/{chunk-2AFX33YA.js → chunk-Z3STG43F.js} +3 -3
- package/dist/chunk-Z3STG43F.js.map +1 -0
- package/dist/{chunk-OIEO4WDL.js → chunk-ZD7WFMV2.js} +2 -2
- package/dist/chunk-ZD7WFMV2.js.map +1 -0
- package/dist/{chunk-42ALPZ2B.js → chunk-ZF6JECDT.js} +4 -4
- package/dist/{chunk-42ALPZ2B.js.map → chunk-ZF6JECDT.js.map} +1 -1
- package/dist/{chunk-4JPMZTJY.js → chunk-ZF7KNSLU.js} +2 -2
- package/dist/{chunk-2ODVB6LZ.js → chunk-ZGKODRGB.js} +2 -2
- package/dist/{chunk-JIBUPZHA.js → chunk-ZNEKQFGF.js} +4 -4
- package/dist/{chunk-DLD5LGDS.js → chunk-ZUTUNVFD.js} +3 -3
- package/dist/{chunk-JSMTXZR3.js → chunk-ZWMOHANW.js} +8 -6
- package/dist/chunk-ZWMOHANW.js.map +1 -0
- package/dist/{chunk-MILI22XQ.js → chunk-ZZZ4VCY2.js} +10 -10
- package/dist/chunk-ZZZ4VCY2.js.map +1 -0
- package/dist/classified/index.js +5 -5
- package/dist/{classified-marker-SIYKUU5L.js → classified-marker-WKP33U2R.js} +3 -3
- package/dist/collection-facade-COSGZKLC.js +41 -0
- package/dist/{computed-FAD7SED4.js → computed-WN6V53ZV.js} +3 -3
- package/dist/consent/index.js +8 -8
- package/dist/cover/index.js +8 -8
- package/dist/coverage/index.js +1 -1
- package/dist/custody/index.js +12 -11
- package/dist/{dead-filter-4F3LWYKH.js → dead-filter-43WL3Y5H.js} +10 -10
- package/dist/debug/index.js +7 -7
- package/dist/delegation-5UWT4JGT.js +26 -0
- package/dist/derivations/index.js +24 -23
- package/dist/derive-IL5JNLEE.js +20 -0
- package/dist/directory/index.js +9 -9
- package/dist/{dispatch-JDZNGEB3.js → dispatch-LOQQD5JK.js} +20 -20
- package/dist/{dispatch-H4HV32AM.js → dispatch-SHKTG57L.js} +5 -5
- package/dist/executor-6PQ7WAGV.js +35 -0
- package/dist/executor-6ZPQA6HS.js +8 -0
- package/dist/executor-OXRA64TY.js +8 -0
- package/dist/export-accessible-XG3FKJWW.js +22 -0
- package/dist/extract-partition-33747NY5.js +35 -0
- package/dist/{fanout-sidecar-L7NHPIOC.js → fanout-sidecar-NEB6IYV7.js} +8 -8
- package/dist/find-VW543G3J.js +10 -0
- package/dist/forget/index.js +25 -8
- package/dist/forget/index.js.map +1 -1
- package/dist/guards/index.js +4 -4
- package/dist/history/index.js +15 -13
- package/dist/history/index.js.map +1 -1
- package/dist/i18n/index.js +13 -13
- package/dist/index.d.ts +9 -6
- package/dist/index.js +126 -110
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +6 -6
- package/dist/introspection/index.js +15 -11
- package/dist/issue-4ZPSY44M.js +19 -0
- package/dist/kernel/cache/index.d.ts +1 -1
- package/dist/kernel/collection.d.ts +86 -6
- package/dist/kernel/constants.d.ts +7 -0
- package/dist/kernel/errors.d.ts +42 -4
- package/dist/kernel/export-scope.d.ts +40 -0
- package/dist/kernel/merge-authority.d.ts +5 -1
- package/dist/kernel/noydb.d.ts +118 -21
- package/dist/kernel/put-many-atomic.d.ts +1 -1
- package/dist/kernel/query/live/live.d.ts +2 -1
- package/dist/kernel/query/sort-key.d.ts +0 -2
- package/dist/kernel/rejection-seal.d.ts +13 -0
- package/dist/kernel/sync-wiring.d.ts +55 -0
- package/dist/kernel/tx-write-gate.d.ts +2 -4
- package/dist/kernel/types.d.ts +463 -14
- package/dist/kernel/validation.d.ts +0 -10
- package/dist/kernel/vault.d.ts +42 -4
- package/dist/kernel/via/reconcile.d.ts +1 -42
- package/dist/{ledger-5ZMGE44K.js → ledger-MZ25UFT2.js} +10 -10
- package/dist/liberate-OJIP2ZFX.js +25 -0
- package/dist/{link-set-MFXC24VK.js → link-set-4FRRAVTH.js} +10 -10
- package/dist/{managed-secret-MFTFT22X.js → managed-secret-SQWO2EUR.js} +9 -9
- package/dist/materialized-views/index.js +26 -26
- package/dist/{migrate-cek-DZRFFOHW.js → migrate-cek-JWEM4RP6.js} +2 -2
- package/dist/money/index.js +5 -5
- package/dist/noydb-U62OVKXB.js +100 -0
- package/dist/on/index.js +25 -0
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.js +11 -11
- package/dist/pod/index.js +71 -67
- package/dist/{pod-handle-A3LET7NO.js → pod-handle-R6Y4MILX.js} +8 -8
- package/dist/policy/index.js +9 -9
- package/dist/port/as/active.d.ts +4 -0
- package/dist/port/as/index.d.ts +1 -0
- package/dist/port/by/index.d.ts +14 -5
- package/dist/port/on/index.d.ts +26 -0
- package/dist/port/to/index.d.ts +1 -1
- package/dist/port/with/admission-authority.d.ts +34 -0
- package/dist/port/with/broker-strategy.d.ts +24 -0
- package/dist/port/with/coverage-strategy.d.ts +17 -1
- package/dist/port/with/formats-strategy.d.ts +34 -1
- package/dist/port/with/lookup-strategy.d.ts +85 -4
- package/dist/port/with/rejection-courier.d.ts +37 -0
- package/dist/port/with/restore-host.d.ts +33 -0
- package/dist/port/with/service-bus.d.ts +20 -1
- package/dist/port/with/team-strategy.d.ts +2 -2
- package/dist/port/with/write-hooks.d.ts +2 -0
- package/dist/portability/index.js +8 -8
- package/dist/{post-register-7ND27CF2.js → post-register-GRVQHBOH.js} +6 -6
- package/dist/query/all/index.js +16 -16
- package/dist/query/index.js +8 -8
- package/dist/query/live/index.js +7 -7
- package/dist/query/reduce/index.js +10 -10
- package/dist/query/relate/index.js +10 -10
- package/dist/reduce/index.js +8 -8
- package/dist/register-YJ5YLQ36.js +30 -0
- package/dist/registry-ICOHBQFD.js +8 -0
- package/dist/registry-MJ7MWCSW.js +35 -0
- package/dist/registry-MVEKKGWL.js +23 -0
- package/dist/registry-XP744FAP.js +8 -0
- package/dist/request-withdrawal-LSU4G6W3.js +28 -0
- package/dist/restore-to-DVBHAQAA.js +66 -0
- package/dist/restore-to-DVBHAQAA.js.map +1 -0
- package/dist/{reveal-B2TLP4JQ.js → reveal-AIHOIK2N.js} +7 -7
- package/dist/revoke-QUT7WWG3.js +24 -0
- package/dist/satellites/index.js +1 -1
- package/dist/schema-update/index.js +2 -2
- package/dist/sealed-record/index.js +11 -11
- package/dist/search/index.js +4 -4
- package/dist/{seed-XNSE4U3S.js → seed-GFLA4FCK.js} +136 -27
- package/dist/seed-GFLA4FCK.js.map +1 -0
- package/dist/sequence/index.js +8 -8
- package/dist/session/index.js +8 -8
- package/dist/shadow/index.js +2 -2
- package/dist/signer-NYR4I3FO.js +23 -0
- package/dist/snapshots/index.js +9 -9
- package/dist/{stale-6GCFMOOR.js → stale-PIJPAL5T.js} +9 -9
- package/dist/storage-VH6SPXHC.js +21 -0
- package/dist/{store-coordination-provider-C4BPT5XF.js → store-coordination-provider-2KSHCOPU.js} +9 -9
- package/dist/sync/index.js +19 -13
- package/dist/sync/index.js.map +1 -1
- package/dist/team/index.js +17 -15
- package/dist/tiers/index.js +10 -10
- package/dist/to/index.js +2 -2
- package/dist/transactions/index.js +3 -3
- package/dist/util/index.js +1 -1
- package/dist/vault-head/index.js +8 -8
- package/dist/{verify-UKALW7C2.js → verify-PCFS4PNS.js} +7 -7
- package/dist/via/classified/presets.d.ts +29 -0
- package/dist/via/classified/resolve.d.ts +7 -0
- package/dist/via/geo/binding.d.ts +0 -4
- package/dist/via/lookup/index.d.ts +32 -0
- package/dist/via/money/binding.d.ts +0 -9
- package/dist/walk-OZCN6LJS.js +20 -0
- package/dist/with-audit/attestation/strategy.d.ts +2 -1
- package/dist/with-audit/attestation/vault-facade.d.ts +7 -0
- package/dist/with-audit/forget/completeness.d.ts +66 -0
- package/dist/with-audit/forget/index.d.ts +2 -0
- package/dist/with-audit/forget/strategy.d.ts +65 -0
- package/dist/with-audit/guards/types.d.ts +2 -0
- package/dist/with-audit/periods/periods.d.ts +0 -9
- package/dist/with-audit/periods/reserved-collections.d.ts +7 -0
- package/dist/with-audit/portability/request-withdrawal.d.ts +0 -1
- package/dist/with-audit/portability/withdraw-accessible.d.ts +0 -1
- package/dist/with-audit/tiers/index.d.ts +6 -6
- package/dist/with-audit/tiers/strategy.d.ts +19 -0
- package/dist/with-cargo/extract-partition.d.ts +0 -46
- package/dist/with-commit/history/history.d.ts +16 -0
- package/dist/with-commit/history/restore-to.d.ts +40 -0
- package/dist/with-commit/numbering/index.d.ts +0 -2
- package/dist/with-commit/tx/transaction.d.ts +2 -24
- package/dist/with-commit/vault-head/strategy.d.ts +7 -0
- package/dist/with-fork/snapshots/noydb-facade.d.ts +7 -0
- package/dist/with-formula/derivations/trigger-match.d.ts +8 -5
- package/dist/with-formula/materialized-views/emit-memo.d.ts +0 -2
- package/dist/with-lookup/embeddings/index.d.ts +1 -2
- package/dist/with-lookup/indexing/collection-facade.d.ts +1 -15
- package/dist/with-lookup/indexing/eager-indexes.d.ts +2 -1
- package/dist/with-lookup/search/collection-facade.d.ts +0 -12
- package/dist/with-party/broker/index.d.ts +2 -2
- package/dist/with-party/broker/seed.d.ts +29 -10
- package/dist/with-party/custody/strategy.d.ts +2 -2
- package/dist/with-party/policy/noydb-facade.d.ts +1 -31
- package/dist/with-party/team/delegation.d.ts +93 -7
- package/dist/with-party/team/echo-secret.d.ts +0 -2
- package/dist/with-party/team/index.d.ts +2 -0
- package/dist/with-party/team/keyring.d.ts +133 -24
- package/dist/with-party/team/magic-link-grant.d.ts +11 -1
- package/dist/with-party/team/noydb-facade.d.ts +19 -2
- package/dist/with-party/team/reserved-secret-collections.d.ts +10 -8
- package/dist/with-party/team/roster-tag.d.ts +42 -1
- package/dist/with-party/team/tiers.d.ts +36 -5
- package/dist/with-pod/backup.d.ts +9 -17
- package/dist/with-shape/blobs/blob-set.d.ts +23 -0
- package/dist/with-shape/blobs/legacy-sweep.d.ts +16 -0
- package/dist/with-shape/introspection/describe.d.ts +1 -2
- package/dist/with-shape/introspection/index.d.ts +1 -0
- package/dist/with-shape/introspection/types.d.ts +2 -1
- package/dist/with-shape/schema-update/fence.d.ts +7 -0
- package/dist/with-sync/active.d.ts +28 -1
- package/dist/with-sync/engine.d.ts +88 -2
- package/dist/with-sync/index.d.ts +1 -0
- package/dist/with-sync/reserved-mirror.d.ts +112 -0
- package/dist/with-sync/strategy.d.ts +9 -0
- package/dist/with-sync/tab-write-relay.d.ts +0 -10
- package/dist/withdraw-accessible-MTFP7OIB.js +25 -0
- package/dist/withdraw-accessible-MTFP7OIB.js.map +1 -0
- package/package.json +3 -3
- package/dist/api-OS747DS4.js +0 -18
- package/dist/backup-YE2VQ26S.js.map +0 -1
- package/dist/chunk-2AFX33YA.js.map +0 -1
- package/dist/chunk-2RH777HH.js +0 -126
- package/dist/chunk-2RH777HH.js.map +0 -1
- package/dist/chunk-37EPTBBR.js.map +0 -1
- package/dist/chunk-5KC7QYEC.js.map +0 -1
- package/dist/chunk-7XMHMVTS.js +0 -20
- package/dist/chunk-7XMHMVTS.js.map +0 -1
- package/dist/chunk-A4XMIZT3.js.map +0 -1
- package/dist/chunk-DHHGL42E.js.map +0 -1
- package/dist/chunk-DQESF6RP.js.map +0 -1
- package/dist/chunk-EGVJ7TM4.js.map +0 -1
- package/dist/chunk-EYG2CY2X.js.map +0 -1
- package/dist/chunk-EZGV6F47.js.map +0 -1
- package/dist/chunk-FFWI7UH7.js.map +0 -1
- package/dist/chunk-G2MVBDFP.js.map +0 -1
- package/dist/chunk-GUZET4SG.js.map +0 -1
- package/dist/chunk-GZT3NQRA.js.map +0 -1
- package/dist/chunk-H23XHRTT.js.map +0 -1
- package/dist/chunk-H7JBY4P7.js.map +0 -1
- package/dist/chunk-IMQKWR46.js.map +0 -1
- package/dist/chunk-IUBMZPHM.js.map +0 -1
- package/dist/chunk-IUT3GWP3.js.map +0 -1
- package/dist/chunk-JDEYF3PJ.js.map +0 -1
- package/dist/chunk-JSMTXZR3.js.map +0 -1
- package/dist/chunk-LGNDFRYC.js.map +0 -1
- package/dist/chunk-LNSVGRHO.js.map +0 -1
- package/dist/chunk-MILI22XQ.js.map +0 -1
- package/dist/chunk-MXOQ5JCG.js.map +0 -1
- package/dist/chunk-NBQ3KK6X.js.map +0 -1
- package/dist/chunk-OIEO4WDL.js.map +0 -1
- package/dist/chunk-PD64727P.js.map +0 -1
- package/dist/chunk-RHVMZSUU.js.map +0 -1
- package/dist/chunk-SV2PM2OY.js.map +0 -1
- package/dist/chunk-T7IR4N2S.js.map +0 -1
- package/dist/chunk-UA372JHH.js.map +0 -1
- package/dist/chunk-UBSFPGVJ.js +0 -1
- package/dist/chunk-V3RWOOSO.js.map +0 -1
- package/dist/chunk-VAORSCSU.js.map +0 -1
- package/dist/chunk-XSPFDUDG.js.map +0 -1
- package/dist/chunk-Y3LPTBG7.js.map +0 -1
- package/dist/collection-facade-CS7CA2XO.js +0 -47
- package/dist/delegation-NANZDG4N.js +0 -102
- package/dist/delegation-NANZDG4N.js.map +0 -1
- package/dist/derive-XLOJVXPU.js +0 -20
- package/dist/executor-FMPVN6DT.js +0 -35
- package/dist/executor-LBBBRTN5.js +0 -8
- package/dist/executor-OSRZST2U.js +0 -8
- package/dist/export-accessible-RAOJAAU5.js +0 -22
- package/dist/extract-partition-JD5CUSTG.js +0 -37
- package/dist/find-JRN4RNQ5.js +0 -10
- package/dist/issue-YIVBZUTH.js +0 -19
- package/dist/liberate-WZSZM2W6.js +0 -24
- package/dist/noydb-ITIGNKUF.js +0 -96
- package/dist/register-DT5HZ2LZ.js +0 -30
- package/dist/registry-BHRZGZHF.js +0 -8
- package/dist/registry-ET6ZFFWA.js +0 -34
- package/dist/registry-HXFGLGTG.js +0 -8
- package/dist/registry-THCPDEOD.js +0 -23
- package/dist/request-withdrawal-AV2MJJKY.js +0 -30
- package/dist/revoke-6DWIEOCV.js +0 -24
- package/dist/seed-XNSE4U3S.js.map +0 -1
- package/dist/signer-MHGW3OIL.js +0 -23
- package/dist/storage-BYN5LO6Z.js +0 -21
- package/dist/walk-4VPK7AS4.js +0 -20
- package/dist/withdraw-accessible-HL7GACUE.js +0 -27
- /package/dist/{api-OS747DS4.js.map → api-6EFD43KN.js.map} +0 -0
- /package/dist/{capsule-VOLX3QRQ.js.map → capsule-6LJAVQNM.js.map} +0 -0
- /package/dist/{chunk-OYW3I5M4.js.map → chunk-2DOCDOPL.js.map} +0 -0
- /package/dist/{chunk-UBSFPGVJ.js.map → chunk-2DX4JCRI.js.map} +0 -0
- /package/dist/{chunk-FLPCETP6.js.map → chunk-2LBS62AC.js.map} +0 -0
- /package/dist/{chunk-WXE4W2U6.js.map → chunk-2ST3WA3C.js.map} +0 -0
- /package/dist/{chunk-O2IMQSNA.js.map → chunk-3YBIVLTC.js.map} +0 -0
- /package/dist/{chunk-DIZLQVAA.js.map → chunk-4CHM2TRS.js.map} +0 -0
- /package/dist/{chunk-V7ADQF5O.js.map → chunk-4CUJLEOA.js.map} +0 -0
- /package/dist/{chunk-3EWS67C7.js.map → chunk-4TG2ITRW.js.map} +0 -0
- /package/dist/{chunk-HS554XAF.js.map → chunk-5HQFROZS.js.map} +0 -0
- /package/dist/{chunk-PP3K67AJ.js.map → chunk-654RE5YC.js.map} +0 -0
- /package/dist/{chunk-FOEDQATO.js.map → chunk-66GD7XXU.js.map} +0 -0
- /package/dist/{chunk-BNWA3WG5.js.map → chunk-72FQ46VW.js.map} +0 -0
- /package/dist/{chunk-JQYKPKG6.js.map → chunk-7K42TSCY.js.map} +0 -0
- /package/dist/{chunk-UEFOQH74.js.map → chunk-ADCXTW7L.js.map} +0 -0
- /package/dist/{chunk-MBMTND6D.js.map → chunk-AGGPT4UE.js.map} +0 -0
- /package/dist/{chunk-RB4DOPGZ.js.map → chunk-AKIEBFIJ.js.map} +0 -0
- /package/dist/{chunk-VHBKYN2J.js.map → chunk-B3B6HV5S.js.map} +0 -0
- /package/dist/{chunk-JWWYRR4Q.js.map → chunk-B6EWMGFX.js.map} +0 -0
- /package/dist/{chunk-WPOYWFSE.js.map → chunk-BJRKXNFP.js.map} +0 -0
- /package/dist/{chunk-H427H63K.js.map → chunk-BPQ5CRJF.js.map} +0 -0
- /package/dist/{chunk-VFHCKG5M.js.map → chunk-BVRJYMFZ.js.map} +0 -0
- /package/dist/{chunk-SMV2HRDU.js.map → chunk-DFC4WTJW.js.map} +0 -0
- /package/dist/{chunk-5A4345TG.js.map → chunk-DMIEKD24.js.map} +0 -0
- /package/dist/{chunk-TKVUE4JF.js.map → chunk-DQAXA2GK.js.map} +0 -0
- /package/dist/{chunk-AL6ML4LA.js.map → chunk-DWCFGF2K.js.map} +0 -0
- /package/dist/{chunk-QBLT6POL.js.map → chunk-FPF5HRBE.js.map} +0 -0
- /package/dist/{chunk-5WWDGFOS.js.map → chunk-GIQJGXV7.js.map} +0 -0
- /package/dist/{chunk-BVCUTXE3.js.map → chunk-H334G6KU.js.map} +0 -0
- /package/dist/{chunk-AEQGFRU4.js.map → chunk-HE7EAHGS.js.map} +0 -0
- /package/dist/{chunk-4V3Q35WG.js.map → chunk-ICF3TIGY.js.map} +0 -0
- /package/dist/{chunk-3L2NBKDR.js.map → chunk-IF2M43G4.js.map} +0 -0
- /package/dist/{chunk-AUKKUPVB.js.map → chunk-IVLRPRXM.js.map} +0 -0
- /package/dist/{chunk-Y2RKVFKR.js.map → chunk-K4JSTGXT.js.map} +0 -0
- /package/dist/{chunk-EJLV62NI.js.map → chunk-KN4LUMH6.js.map} +0 -0
- /package/dist/{chunk-23D6M6VM.js.map → chunk-KZA3DITX.js.map} +0 -0
- /package/dist/{chunk-E2N2T5ZO.js.map → chunk-L2VM64HK.js.map} +0 -0
- /package/dist/{chunk-O46CHQT7.js.map → chunk-MAQBYBUX.js.map} +0 -0
- /package/dist/{chunk-EZOAKKTB.js.map → chunk-MBPZMZVB.js.map} +0 -0
- /package/dist/{chunk-XQRFSKYM.js.map → chunk-MCNNZBG7.js.map} +0 -0
- /package/dist/{chunk-YRXFFWG4.js.map → chunk-MLVPMDOZ.js.map} +0 -0
- /package/dist/{chunk-5FE75J4S.js.map → chunk-MMCN7TLK.js.map} +0 -0
- /package/dist/{chunk-SYNY3VB5.js.map → chunk-NCTN2FW4.js.map} +0 -0
- /package/dist/{chunk-4T3SE27M.js.map → chunk-ND6S2DKC.js.map} +0 -0
- /package/dist/{chunk-3XJQBBZD.js.map → chunk-NTVKHQ3L.js.map} +0 -0
- /package/dist/{chunk-Q4UHJ5SN.js.map → chunk-O2VQ3K36.js.map} +0 -0
- /package/dist/{chunk-2GHDLTPE.js.map → chunk-OGKMRIIJ.js.map} +0 -0
- /package/dist/{chunk-N7EVQD5S.js.map → chunk-ONZ3B5EW.js.map} +0 -0
- /package/dist/{chunk-VKI2EB3N.js.map → chunk-OOPSMQX5.js.map} +0 -0
- /package/dist/{chunk-VMV5SBE4.js.map → chunk-OYEBSWLE.js.map} +0 -0
- /package/dist/{chunk-VVOLDVIR.js.map → chunk-PLTIJDQN.js.map} +0 -0
- /package/dist/{chunk-OKSTSITU.js.map → chunk-PR7ILCIH.js.map} +0 -0
- /package/dist/{chunk-7R2Q7CPL.js.map → chunk-QE3TLZ6Z.js.map} +0 -0
- /package/dist/{chunk-6QVKPHBF.js.map → chunk-QHG7GQKM.js.map} +0 -0
- /package/dist/{chunk-LIOVX5TJ.js.map → chunk-QT5ITL25.js.map} +0 -0
- /package/dist/{chunk-Q2XRFWOI.js.map → chunk-RRTQAQ3N.js.map} +0 -0
- /package/dist/{chunk-2PVXF5CS.js.map → chunk-SALY3LIJ.js.map} +0 -0
- /package/dist/{chunk-JOSPH3E5.js.map → chunk-SBAR5JQ6.js.map} +0 -0
- /package/dist/{chunk-N7UDP5VU.js.map → chunk-SCH2F7R2.js.map} +0 -0
- /package/dist/{chunk-MXKI4ABF.js.map → chunk-SOOAW6HK.js.map} +0 -0
- /package/dist/{chunk-TEHHROZL.js.map → chunk-T5EXREMW.js.map} +0 -0
- /package/dist/{chunk-M5B2W6YY.js.map → chunk-TDJVQTXD.js.map} +0 -0
- /package/dist/{chunk-QMWY2FDS.js.map → chunk-THQ3BHLM.js.map} +0 -0
- /package/dist/{chunk-FVH5QYY2.js.map → chunk-TRHUNPXP.js.map} +0 -0
- /package/dist/{chunk-747G6CT3.js.map → chunk-UORWAG7K.js.map} +0 -0
- /package/dist/{chunk-TZZBXPZK.js.map → chunk-VLQ6KT7F.js.map} +0 -0
- /package/dist/{chunk-NCO25JZO.js.map → chunk-VP37VUKQ.js.map} +0 -0
- /package/dist/{chunk-52MGURXO.js.map → chunk-WBUHHMLN.js.map} +0 -0
- /package/dist/{chunk-2MG3SW7Q.js.map → chunk-X6Z2SIRF.js.map} +0 -0
- /package/dist/{chunk-5UMK7GRI.js.map → chunk-XPNJH7GN.js.map} +0 -0
- /package/dist/{chunk-KZXDLBY6.js.map → chunk-XPW7T2GN.js.map} +0 -0
- /package/dist/{chunk-FFJFE4T7.js.map → chunk-YJGKOSQD.js.map} +0 -0
- /package/dist/{chunk-4JPMZTJY.js.map → chunk-ZF7KNSLU.js.map} +0 -0
- /package/dist/{chunk-2ODVB6LZ.js.map → chunk-ZGKODRGB.js.map} +0 -0
- /package/dist/{chunk-JIBUPZHA.js.map → chunk-ZNEKQFGF.js.map} +0 -0
- /package/dist/{chunk-DLD5LGDS.js.map → chunk-ZUTUNVFD.js.map} +0 -0
- /package/dist/{classified-marker-SIYKUU5L.js.map → classified-marker-WKP33U2R.js.map} +0 -0
- /package/dist/{collection-facade-CS7CA2XO.js.map → collection-facade-COSGZKLC.js.map} +0 -0
- /package/dist/{computed-FAD7SED4.js.map → computed-WN6V53ZV.js.map} +0 -0
- /package/dist/{dead-filter-4F3LWYKH.js.map → dead-filter-43WL3Y5H.js.map} +0 -0
- /package/dist/{derive-XLOJVXPU.js.map → delegation-5UWT4JGT.js.map} +0 -0
- /package/dist/{executor-FMPVN6DT.js.map → derive-IL5JNLEE.js.map} +0 -0
- /package/dist/{dispatch-JDZNGEB3.js.map → dispatch-LOQQD5JK.js.map} +0 -0
- /package/dist/{dispatch-H4HV32AM.js.map → dispatch-SHKTG57L.js.map} +0 -0
- /package/dist/{executor-LBBBRTN5.js.map → executor-6PQ7WAGV.js.map} +0 -0
- /package/dist/{executor-OSRZST2U.js.map → executor-6ZPQA6HS.js.map} +0 -0
- /package/dist/{export-accessible-RAOJAAU5.js.map → executor-OXRA64TY.js.map} +0 -0
- /package/dist/{extract-partition-JD5CUSTG.js.map → export-accessible-XG3FKJWW.js.map} +0 -0
- /package/dist/{find-JRN4RNQ5.js.map → extract-partition-33747NY5.js.map} +0 -0
- /package/dist/{fanout-sidecar-L7NHPIOC.js.map → fanout-sidecar-NEB6IYV7.js.map} +0 -0
- /package/dist/{issue-YIVBZUTH.js.map → find-VW543G3J.js.map} +0 -0
- /package/dist/{ledger-5ZMGE44K.js.map → issue-4ZPSY44M.js.map} +0 -0
- /package/dist/{liberate-WZSZM2W6.js.map → ledger-MZ25UFT2.js.map} +0 -0
- /package/dist/{link-set-MFXC24VK.js.map → liberate-OJIP2ZFX.js.map} +0 -0
- /package/dist/{managed-secret-MFTFT22X.js.map → link-set-4FRRAVTH.js.map} +0 -0
- /package/dist/{noydb-ITIGNKUF.js.map → managed-secret-SQWO2EUR.js.map} +0 -0
- /package/dist/{migrate-cek-DZRFFOHW.js.map → migrate-cek-JWEM4RP6.js.map} +0 -0
- /package/dist/{register-DT5HZ2LZ.js.map → noydb-U62OVKXB.js.map} +0 -0
- /package/dist/{pod-handle-A3LET7NO.js.map → pod-handle-R6Y4MILX.js.map} +0 -0
- /package/dist/{post-register-7ND27CF2.js.map → post-register-GRVQHBOH.js.map} +0 -0
- /package/dist/{registry-BHRZGZHF.js.map → register-YJ5YLQ36.js.map} +0 -0
- /package/dist/{registry-ET6ZFFWA.js.map → registry-ICOHBQFD.js.map} +0 -0
- /package/dist/{registry-HXFGLGTG.js.map → registry-MJ7MWCSW.js.map} +0 -0
- /package/dist/{registry-THCPDEOD.js.map → registry-MVEKKGWL.js.map} +0 -0
- /package/dist/{request-withdrawal-AV2MJJKY.js.map → registry-XP744FAP.js.map} +0 -0
- /package/dist/{revoke-6DWIEOCV.js.map → request-withdrawal-LSU4G6W3.js.map} +0 -0
- /package/dist/{reveal-B2TLP4JQ.js.map → reveal-AIHOIK2N.js.map} +0 -0
- /package/dist/{signer-MHGW3OIL.js.map → revoke-QUT7WWG3.js.map} +0 -0
- /package/dist/{stale-6GCFMOOR.js.map → signer-NYR4I3FO.js.map} +0 -0
- /package/dist/{storage-BYN5LO6Z.js.map → stale-PIJPAL5T.js.map} +0 -0
- /package/dist/{walk-4VPK7AS4.js.map → storage-VH6SPXHC.js.map} +0 -0
- /package/dist/{store-coordination-provider-C4BPT5XF.js.map → store-coordination-provider-2KSHCOPU.js.map} +0 -0
- /package/dist/{verify-UKALW7C2.js.map → verify-PCFS4PNS.js.map} +0 -0
- /package/dist/{withdraw-accessible-HL7GACUE.js.map → walk-OZCN6LJS.js.map} +0 -0
|
@@ -1,31 +1,38 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
REJECTIONS_COLLECTION,
|
|
3
|
+
SyncScheduler,
|
|
4
|
+
rejectionKey
|
|
5
|
+
} from "./chunk-7UHLO4ZN.js";
|
|
4
6
|
import {
|
|
5
7
|
PERIODS_COLLECTION,
|
|
6
8
|
PERIOD_ARCHIVES_COLLECTION,
|
|
7
9
|
PERIOD_FREEZES_COLLECTION,
|
|
8
10
|
PERIOD_TARGET_PURGES_COLLECTION,
|
|
9
11
|
periodExclusiveUpperBound
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FPF5HRBE.js";
|
|
11
13
|
import {
|
|
14
|
+
parseKeyringEnvelope
|
|
15
|
+
} from "./chunk-V7AKGAXY.js";
|
|
16
|
+
import {
|
|
17
|
+
buildDeleteMarker,
|
|
12
18
|
buildRecordEnvelope,
|
|
13
19
|
decrypt,
|
|
14
20
|
derivePresenceKey,
|
|
15
21
|
derivePresenceTagKey,
|
|
16
22
|
encrypt,
|
|
23
|
+
envelopeBodyForHash,
|
|
17
24
|
envelopeBodySize,
|
|
18
25
|
hmacSignHex,
|
|
19
26
|
isDeleteMarker,
|
|
20
27
|
isTombstoneShape
|
|
21
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-WRLWEYHY.js";
|
|
22
29
|
import {
|
|
23
30
|
NOYDB_SYNC_VERSION
|
|
24
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-XX4R7TUB.js";
|
|
25
32
|
import {
|
|
26
33
|
ValidationError,
|
|
27
34
|
isConflictError
|
|
28
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-IKPQ2GGB.js";
|
|
29
36
|
|
|
30
37
|
// src/with-sync/presence.ts
|
|
31
38
|
var PresenceHandle = class {
|
|
@@ -273,6 +280,126 @@ var PresenceHandle = class {
|
|
|
273
280
|
}
|
|
274
281
|
};
|
|
275
282
|
|
|
283
|
+
// src/with-sync/reserved-mirror.ts
|
|
284
|
+
var KEYRING_COLLECTION = "_keyring";
|
|
285
|
+
function keyringEpoch(envelope) {
|
|
286
|
+
if (!envelope) return void 0;
|
|
287
|
+
try {
|
|
288
|
+
const epoch = parseKeyringEnvelope(envelope).roster_epoch;
|
|
289
|
+
return typeof epoch === "number" ? epoch : void 0;
|
|
290
|
+
} catch {
|
|
291
|
+
return void 0;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function keyringIdentity(envelope) {
|
|
295
|
+
const file = parseKeyringEnvelope(envelope);
|
|
296
|
+
return `${file.roster_epoch ?? ""}:${file.roster_tag.data}:${file.display_name}`;
|
|
297
|
+
}
|
|
298
|
+
function keyringSupersedes(candidate, current) {
|
|
299
|
+
if (!current) return true;
|
|
300
|
+
const c = keyringEpoch(candidate);
|
|
301
|
+
const k = keyringEpoch(current);
|
|
302
|
+
if (c === void 0) return false;
|
|
303
|
+
if (k === void 0) return true;
|
|
304
|
+
return c > k;
|
|
305
|
+
}
|
|
306
|
+
function versionSupersedes(candidate, current) {
|
|
307
|
+
if (!current) return true;
|
|
308
|
+
return candidate._v > current._v;
|
|
309
|
+
}
|
|
310
|
+
var RESERVED_REPLICATION = [
|
|
311
|
+
{ collection: KEYRING_COLLECTION, supersedes: keyringSupersedes, propagateDeletes: true },
|
|
312
|
+
// core#91 — the broker seeds. `_broker/<brokerId>` is sealed under the
|
|
313
|
+
// admin `_broker` DEK (owner/admin only hold it), `_broker_member/<userId>`
|
|
314
|
+
// under that grantee's own DEK: both are ciphertext to the mover and to every
|
|
315
|
+
// other device, and both are exactly what a fresh device needs to mint its
|
|
316
|
+
// first credential. Revocation is a delete that rides the dirty log.
|
|
317
|
+
// core#97 — SECOND, right behind the roster: on a fresh device every target
|
|
318
|
+
// request before the seed has landed cannot be served by the device's own
|
|
319
|
+
// mint, so the seed travels before anything else a first pull carries.
|
|
320
|
+
{ collection: "_broker_member", supersedes: versionSupersedes, propagateDeletes: true },
|
|
321
|
+
{ collection: "_broker", supersedes: versionSupersedes, propagateDeletes: true },
|
|
322
|
+
{ collection: "_meta", idPrefix: "invite-audit-", supersedes: versionSupersedes, propagateDeletes: false },
|
|
323
|
+
{ collection: "_users", supersedes: versionSupersedes, propagateDeletes: true },
|
|
324
|
+
{ collection: "_delegations", supersedes: versionSupersedes, propagateDeletes: true },
|
|
325
|
+
// core#107 — the arbiter's admission refusals, so the WRITER learns its
|
|
326
|
+
// offline record was rejected instead of the verdict staying on the device
|
|
327
|
+
// that made it. Sealed under the refused record's OWN collection DEK, so
|
|
328
|
+
// this mover still reads nothing (`_v` is on the envelope) and a member
|
|
329
|
+
// without that collection receives ciphertext they quietly skip.
|
|
330
|
+
// `propagateDeletes: true` — clearing a refusal is how it stops being shown.
|
|
331
|
+
{ collection: REJECTIONS_COLLECTION, supersedes: versionSupersedes, propagateDeletes: true }
|
|
332
|
+
];
|
|
333
|
+
function inScope(rule, id) {
|
|
334
|
+
return rule.idPrefix === void 0 || id.startsWith(rule.idPrefix);
|
|
335
|
+
}
|
|
336
|
+
async function mirrorRule(rule, from, to, vault, fromIds) {
|
|
337
|
+
const copied = [];
|
|
338
|
+
for (const id of fromIds ?? await from.list(vault, rule.collection)) {
|
|
339
|
+
if (!inScope(rule, id)) continue;
|
|
340
|
+
const candidate = await from.get(vault, rule.collection, id);
|
|
341
|
+
if (!candidate) continue;
|
|
342
|
+
const current = await to.get(vault, rule.collection, id);
|
|
343
|
+
if (!rule.supersedes(candidate, current)) continue;
|
|
344
|
+
await to.put(vault, rule.collection, id, candidate);
|
|
345
|
+
copied.push(id);
|
|
346
|
+
}
|
|
347
|
+
return copied;
|
|
348
|
+
}
|
|
349
|
+
async function pushReserved(local, remote, vault) {
|
|
350
|
+
let copied = 0;
|
|
351
|
+
let keyringsCopied = [];
|
|
352
|
+
const staleKeyrings = [];
|
|
353
|
+
for (const rule of RESERVED_REPLICATION) {
|
|
354
|
+
const ids = await mirrorRule(rule, local, remote, vault);
|
|
355
|
+
copied += ids.length;
|
|
356
|
+
if (rule.collection === KEYRING_COLLECTION) {
|
|
357
|
+
keyringsCopied = ids;
|
|
358
|
+
for (const id of await local.list(vault, KEYRING_COLLECTION)) {
|
|
359
|
+
if (ids.includes(id)) continue;
|
|
360
|
+
const [mine, theirs] = await Promise.all([local.get(vault, KEYRING_COLLECTION, id), remote.get(vault, KEYRING_COLLECTION, id)]);
|
|
361
|
+
if (mine && theirs && keyringIdentity(mine) !== keyringIdentity(theirs)) staleKeyrings.push({ id, local: mine, remote: theirs });
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return { copied, deleted: 0, keyringsCopied, ...staleKeyrings.length > 0 && { staleKeyrings } };
|
|
366
|
+
}
|
|
367
|
+
async function pullKeyringFile(remote, local, vault, userId) {
|
|
368
|
+
const rule = RESERVED_REPLICATION[0];
|
|
369
|
+
const ids = await mirrorRule(rule, remote, local, vault, [userId]);
|
|
370
|
+
return ids.length > 0;
|
|
371
|
+
}
|
|
372
|
+
async function pullReserved(remote, local, vault, protectedIds) {
|
|
373
|
+
const localKeyrings = await local.list(vault, KEYRING_COLLECTION);
|
|
374
|
+
if (localKeyrings.length === 0) return { copied: 0, deleted: 0, keyringsCopied: [] };
|
|
375
|
+
let copied = 0;
|
|
376
|
+
let deleted = 0;
|
|
377
|
+
let keyringsCopied = [];
|
|
378
|
+
let remoteInitialised = false;
|
|
379
|
+
for (const rule of RESERVED_REPLICATION) {
|
|
380
|
+
const remoteIds = (await remote.list(vault, rule.collection)).filter((id) => inScope(rule, id));
|
|
381
|
+
if (rule.collection === KEYRING_COLLECTION) remoteInitialised = remoteIds.length > 0;
|
|
382
|
+
const ids = await mirrorRule(rule, remote, local, vault, remoteIds);
|
|
383
|
+
copied += ids.length;
|
|
384
|
+
if (rule.collection === KEYRING_COLLECTION) keyringsCopied = ids;
|
|
385
|
+
if (!rule.propagateDeletes || !remoteInitialised) continue;
|
|
386
|
+
const remoteSet = new Set(remoteIds);
|
|
387
|
+
const protectedSet = protectedIds.get(rule.collection);
|
|
388
|
+
const localIds = rule.collection === KEYRING_COLLECTION ? localKeyrings : await local.list(vault, rule.collection);
|
|
389
|
+
for (const id of localIds) {
|
|
390
|
+
if (!inScope(rule, id) || remoteSet.has(id) || protectedSet?.has(id)) continue;
|
|
391
|
+
await local.delete(vault, rule.collection, id);
|
|
392
|
+
deleted++;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return { copied, deleted, keyringsCopied };
|
|
396
|
+
}
|
|
397
|
+
async function bootstrapKeyrings(local, peer, vault) {
|
|
398
|
+
if (!peer) return;
|
|
399
|
+
if ((await local.list(vault, KEYRING_COLLECTION)).length > 0) return;
|
|
400
|
+
await mirrorRule(RESERVED_REPLICATION[0], peer, local, vault);
|
|
401
|
+
}
|
|
402
|
+
|
|
276
403
|
// src/with-sync/period-scope.ts
|
|
277
404
|
var PERIOD_SUMMARY_COLLECTIONS = [
|
|
278
405
|
PERIODS_COLLECTION,
|
|
@@ -319,8 +446,22 @@ function buildPeriodScope(option, records) {
|
|
|
319
446
|
}
|
|
320
447
|
|
|
321
448
|
// src/with-sync/engine.ts
|
|
449
|
+
var REJECTED_COLLECTION = "_sync_rejected";
|
|
450
|
+
function parseParked(env) {
|
|
451
|
+
return JSON.parse(envelopeBodyForHash(env));
|
|
452
|
+
}
|
|
322
453
|
var SyncEngine = class {
|
|
323
454
|
mergeAuthority;
|
|
455
|
+
/** core#74 — the vault's admission gate for incoming records; wired at open like the other vault seams. */
|
|
456
|
+
admission;
|
|
457
|
+
#applied = [];
|
|
458
|
+
#rejected = [];
|
|
459
|
+
/** core#108 — this device's own records, refused by its own gates on the way OUT. */
|
|
460
|
+
#pushRejected = [];
|
|
461
|
+
/** core#107 — the vault's sealing capability for replicated refusals; wired at open like `admission`. */
|
|
462
|
+
courier;
|
|
463
|
+
/** core#107 — this device's refusals replicate. Configuration, not an authority proof — see `withSync`. */
|
|
464
|
+
arbiter;
|
|
324
465
|
local;
|
|
325
466
|
remote;
|
|
326
467
|
strategy;
|
|
@@ -336,6 +477,8 @@ var SyncEngine = class {
|
|
|
336
477
|
/** #1036 — last SUCCESSFUL push/pull. Never advanced by a failed attempt. */
|
|
337
478
|
lastPush = null;
|
|
338
479
|
lastPull = null;
|
|
480
|
+
/** core#72 — the target's restore epoch this device adopted; null before any. */
|
|
481
|
+
epoch = null;
|
|
339
482
|
/**
|
|
340
483
|
* #1036 — the failure that ended the most recent attempt, cleared by the next
|
|
341
484
|
* success. Live state only: deliberately NOT persisted in `_sync/meta`, since a
|
|
@@ -403,11 +546,216 @@ var SyncEngine = class {
|
|
|
403
546
|
setPeriodPullSource(source) {
|
|
404
547
|
this.periodPullSource = source;
|
|
405
548
|
}
|
|
549
|
+
/** core#81 — the collections this keyring names; the walk set for a paged pull. Wired by the vault at open. */
|
|
550
|
+
collectionNames;
|
|
551
|
+
/** Wire the paged-pull walk set (core#81). Same injection pattern as `setCacheInvalidator`. */
|
|
552
|
+
setCollectionNames(fn) {
|
|
553
|
+
this.collectionNames = fn;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* core#81 — one chunk per page of one collection, for `pull({ paged: true })`.
|
|
557
|
+
* `listPage` when the store offers it (200 per page), else `list` + `get` in
|
|
558
|
+
* batches of the same size. Never more than one page in memory.
|
|
559
|
+
*/
|
|
560
|
+
async *pagedChunks(names) {
|
|
561
|
+
const PAGE = 200;
|
|
562
|
+
const remote = this.remote;
|
|
563
|
+
for (const coll of names) {
|
|
564
|
+
if (typeof remote.listPage === "function") {
|
|
565
|
+
let cursor;
|
|
566
|
+
do {
|
|
567
|
+
const page = await remote.listPage(this.vault, coll, cursor, PAGE);
|
|
568
|
+
const records = {};
|
|
569
|
+
for (const { id, envelope } of page.items) records[id] = envelope;
|
|
570
|
+
if (page.items.length > 0) yield [coll, records];
|
|
571
|
+
cursor = page.nextCursor ?? void 0;
|
|
572
|
+
} while (cursor !== void 0);
|
|
573
|
+
} else {
|
|
574
|
+
const ids = await remote.list(this.vault, coll);
|
|
575
|
+
for (let i = 0; i < ids.length; i += PAGE) {
|
|
576
|
+
const records = {};
|
|
577
|
+
for (const id of ids.slice(i, i + PAGE)) {
|
|
578
|
+
const env = await remote.get(this.vault, coll, id);
|
|
579
|
+
if (env) records[id] = env;
|
|
580
|
+
}
|
|
581
|
+
if (Object.keys(records).length > 0) yield [coll, records];
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
/** core#82 — when a pull replaces the caller's OWN keyring file, the vault reloads it in place. */
|
|
587
|
+
rosterReload;
|
|
588
|
+
/** Wire the roster-reload seam (core#82). Same injection pattern as `setCacheInvalidator`. */
|
|
589
|
+
/** core#107 — wire the vault's rejection sealing capability. */
|
|
590
|
+
setRejectionCourier(c) {
|
|
591
|
+
this.courier = c;
|
|
592
|
+
}
|
|
593
|
+
/** core#74 — wire the vault's admission gate. */
|
|
594
|
+
setAdmission(a) {
|
|
595
|
+
this.admission = a;
|
|
596
|
+
}
|
|
597
|
+
/** core#71 — after `vault.load()`: a new base. Drops the dirty log, the watermarks and the adopted epoch. */
|
|
598
|
+
async resetAfterRestore() {
|
|
599
|
+
await this.ensureLoaded();
|
|
600
|
+
this.dirty = [];
|
|
601
|
+
this.lastPush = null;
|
|
602
|
+
this.lastPull = null;
|
|
603
|
+
this.epoch = null;
|
|
604
|
+
await this.persistMeta();
|
|
605
|
+
}
|
|
606
|
+
/** core#72 — the target's epoch record, or null when the target was never replaced. */
|
|
607
|
+
async #remoteEpoch() {
|
|
608
|
+
const env = await this.remote.get(this.vault, "_sync", "epoch");
|
|
609
|
+
return env ? JSON.parse(envelopeBodyForHash(env)) : null;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* core#72 — make the LOCAL vault authoritative on this target. Every local
|
|
613
|
+
* record goes up unconditionally, re-sealed above the target's version where
|
|
614
|
+
* the target moved on (so every peer's copy is superseded); every id the
|
|
615
|
+
* target holds that the local vault lacks gets a delete marker at
|
|
616
|
+
* `remote._v + 1` (so every peer removes it); the reserved collections are
|
|
617
|
+
* mirrored; then a new epoch is written at `_sync/epoch`. The dirty log is
|
|
618
|
+
* cleared: the target IS the local state now.
|
|
619
|
+
*/
|
|
620
|
+
async replaceRemote() {
|
|
621
|
+
await this.ensureLoaded();
|
|
622
|
+
const actor = this.rosterReload?.userId ?? "sync";
|
|
623
|
+
const [localSnapshot, remoteSnapshot] = await Promise.all([this.local.loadAll(this.vault), this.remote.loadAll(this.vault)]);
|
|
624
|
+
let replaced = 0;
|
|
625
|
+
let tombstoned = 0;
|
|
626
|
+
for (const [collection, records] of Object.entries(localSnapshot)) {
|
|
627
|
+
if (collection === REJECTED_COLLECTION) continue;
|
|
628
|
+
for (const [id, envelope] of Object.entries(records)) {
|
|
629
|
+
const current = remoteSnapshot[collection]?.[id];
|
|
630
|
+
let out = envelope;
|
|
631
|
+
if (current && current._v >= envelope._v) {
|
|
632
|
+
out = this.mergeAuthority ? await this.mergeAuthority.advance(collection, id, envelope, current._v + 1) : { ...envelope, _v: current._v + 1 };
|
|
633
|
+
await this.local.put(this.vault, collection, id, out);
|
|
634
|
+
}
|
|
635
|
+
await this.remote.put(this.vault, collection, id, out);
|
|
636
|
+
replaced++;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
for (const [collection, records] of Object.entries(remoteSnapshot)) {
|
|
640
|
+
if (collection === REJECTED_COLLECTION) continue;
|
|
641
|
+
for (const [id, envelope] of Object.entries(records)) {
|
|
642
|
+
if (localSnapshot[collection]?.[id] !== void 0) continue;
|
|
643
|
+
if (isTombstoneShape(envelope) || isDeleteMarker(envelope)) continue;
|
|
644
|
+
const marker = buildDeleteMarker({ collection, id }, envelope._v + 1, actor);
|
|
645
|
+
await this.remote.put(this.vault, collection, id, marker);
|
|
646
|
+
await this.local.put(this.vault, collection, id, marker);
|
|
647
|
+
tombstoned++;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const mirrored = await pushReserved(this.local, this.remote, this.vault);
|
|
651
|
+
const previous = await this.#remoteEpoch();
|
|
652
|
+
const epoch = (previous?.epoch ?? 0) + 1;
|
|
653
|
+
const record = { epoch, at: (/* @__PURE__ */ new Date()).toISOString(), by: actor, replaced, tombstoned };
|
|
654
|
+
await this.remote.put(this.vault, "_sync", "epoch", buildRecordEnvelope({ collection: "_sync", id: "epoch", version: epoch }, { iv: "", data: JSON.stringify(record) }));
|
|
655
|
+
this.dirty = [];
|
|
656
|
+
this.epoch = epoch;
|
|
657
|
+
this.lastPush = (/* @__PURE__ */ new Date()).toISOString();
|
|
658
|
+
await this.persistMeta();
|
|
659
|
+
const result = { epoch, replaced, tombstoned, reserved: mirrored.copied };
|
|
660
|
+
this.emitter.emit("sync:replace", { vault: this.vault, ...result });
|
|
661
|
+
return result;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* core#72 — at the start of a pull: has the target been replaced since this
|
|
665
|
+
* device last synced? If so, park every unpushed local edit (same fate as an
|
|
666
|
+
* admission refusal, reason `restore-epoch`), drop the dirty log, adopt the
|
|
667
|
+
* epoch. The pull that follows then adopts the target: the restorer lifted
|
|
668
|
+
* its records above every peer's version and tombstoned what it removed.
|
|
669
|
+
*/
|
|
670
|
+
async #crossEpoch() {
|
|
671
|
+
const remote = await this.#remoteEpoch();
|
|
672
|
+
if (!remote || remote.epoch === this.epoch) return { epoch: remote?.epoch ?? 0, resynced: false, parked: 0 };
|
|
673
|
+
const from = this.epoch;
|
|
674
|
+
let parked = 0;
|
|
675
|
+
for (const entry of this.dirty) {
|
|
676
|
+
if (entry.action === "delete") continue;
|
|
677
|
+
const env = await this.local.get(this.vault, entry.collection, entry.id);
|
|
678
|
+
if (!env) continue;
|
|
679
|
+
const rejection = {
|
|
680
|
+
vault: this.vault,
|
|
681
|
+
collection: entry.collection,
|
|
682
|
+
id: entry.id,
|
|
683
|
+
version: env._v,
|
|
684
|
+
reason: `restore-epoch: the target was replaced (epoch ${remote.epoch}) after this edit; readmit to keep it locally, then put again to push`,
|
|
685
|
+
...env._by !== void 0 ? { by: env._by } : {},
|
|
686
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
687
|
+
};
|
|
688
|
+
const parkedRecord = { rejection, envelope: env };
|
|
689
|
+
await this.local.put(
|
|
690
|
+
this.vault,
|
|
691
|
+
REJECTED_COLLECTION,
|
|
692
|
+
`${entry.collection}::${entry.id}`,
|
|
693
|
+
buildRecordEnvelope({ collection: REJECTED_COLLECTION, id: `${entry.collection}::${entry.id}`, version: 1 }, { iv: "", data: JSON.stringify(parkedRecord) })
|
|
694
|
+
);
|
|
695
|
+
this.#rejected.push(rejection);
|
|
696
|
+
this.emitter.emit("sync:rejected", rejection);
|
|
697
|
+
await this.local.delete(this.vault, entry.collection, entry.id);
|
|
698
|
+
await this.cacheInvalidator?.(entry.collection, entry.id, "delete");
|
|
699
|
+
parked++;
|
|
700
|
+
}
|
|
701
|
+
this.dirty = [];
|
|
702
|
+
this.epoch = remote.epoch;
|
|
703
|
+
this.emitter.emit("sync:epoch", { vault: this.vault, from, to: remote.epoch, parked });
|
|
704
|
+
return { epoch: remote.epoch, resynced: true, parked };
|
|
705
|
+
}
|
|
706
|
+
/** core#74 — the parked refusals on this device, and their two fates. */
|
|
707
|
+
rejected() {
|
|
708
|
+
const key = (collection, id) => `${collection}::${id}`;
|
|
709
|
+
return {
|
|
710
|
+
list: async () => {
|
|
711
|
+
const out = [];
|
|
712
|
+
for (const id of await this.local.list(this.vault, REJECTED_COLLECTION)) {
|
|
713
|
+
const env = await this.local.get(this.vault, REJECTED_COLLECTION, id);
|
|
714
|
+
if (!env) continue;
|
|
715
|
+
out.push(parseParked(env).rejection);
|
|
716
|
+
}
|
|
717
|
+
return out;
|
|
718
|
+
},
|
|
719
|
+
readmit: async (collection, id) => {
|
|
720
|
+
const env = await this.local.get(this.vault, REJECTED_COLLECTION, key(collection, id));
|
|
721
|
+
if (!env) throw new ValidationError(`sync: no rejected record parked for "${collection}/${id}".`);
|
|
722
|
+
await this.local.put(this.vault, collection, id, parseParked(env).envelope);
|
|
723
|
+
await this.local.delete(this.vault, REJECTED_COLLECTION, key(collection, id));
|
|
724
|
+
await this.cacheInvalidator?.(collection, id, "put");
|
|
725
|
+
},
|
|
726
|
+
discard: async (collection, id) => {
|
|
727
|
+
await this.local.delete(this.vault, REJECTED_COLLECTION, key(collection, id));
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
setRosterReload(seam) {
|
|
732
|
+
this.rosterReload = seam;
|
|
733
|
+
}
|
|
734
|
+
/** core#81 — the running sync's latest progress sample; `null` when idle. Read by `status()`. */
|
|
735
|
+
inFlight = null;
|
|
736
|
+
progressLastEmit = 0;
|
|
737
|
+
progressLastRecords = 0;
|
|
738
|
+
/** Emit `sync:progress` no more than every 25 records or ~250 ms; `force` for the final sample. */
|
|
739
|
+
progress(sample, force = false) {
|
|
740
|
+
this.inFlight = sample;
|
|
741
|
+
const now = Date.now();
|
|
742
|
+
if (!force && sample.records - this.progressLastRecords < 25 && now - this.progressLastEmit < 250) return;
|
|
743
|
+
this.progressLastEmit = now;
|
|
744
|
+
this.progressLastRecords = sample.records;
|
|
745
|
+
this.emitter.emit("sync:progress", sample);
|
|
746
|
+
}
|
|
747
|
+
progressDone() {
|
|
748
|
+
this.inFlight = null;
|
|
749
|
+
this.progressLastEmit = 0;
|
|
750
|
+
this.progressLastRecords = 0;
|
|
751
|
+
}
|
|
406
752
|
/** #807: KPI accumulator `applyRemote` feeds during a period-scoped `pull()` — pointed at the
|
|
407
753
|
* active phase's counters (summaries → records) and cleared before pull returns. Approximate
|
|
408
754
|
* by design: a push interleaved mid-pull on the same engine would attribute its converge
|
|
409
755
|
* applies to the open phase; the counters are a download-budget KPI, not an audit source. */
|
|
410
756
|
pullByteSink = null;
|
|
757
|
+
/** core#81 — ciphertext bytes applied by the running pull, for `sync:progress`. */
|
|
758
|
+
pullBytes = 0;
|
|
411
759
|
constructor(opts) {
|
|
412
760
|
this.local = opts.local;
|
|
413
761
|
this.remote = opts.remote;
|
|
@@ -418,6 +766,7 @@ var SyncEngine = class {
|
|
|
418
766
|
this.label = opts.label;
|
|
419
767
|
this.policy = opts.syncPolicy;
|
|
420
768
|
this.mergeAuthority = opts.mergeAuthority;
|
|
769
|
+
this.arbiter = opts.arbiter === true;
|
|
421
770
|
const policy = opts.syncPolicy;
|
|
422
771
|
if (policy && (policy.push.mode !== "manual" || policy.pull.mode !== "manual")) {
|
|
423
772
|
this.scheduler = new SyncScheduler(policy, {
|
|
@@ -510,112 +859,305 @@ var SyncEngine = class {
|
|
|
510
859
|
this.dirty = this.dirty.filter((d) => !(d.collection === collection && d.id === id));
|
|
511
860
|
if (this.dirty.length !== before) await this.persistMeta();
|
|
512
861
|
}
|
|
862
|
+
/**
|
|
863
|
+
* core#74/#108 — park a refused envelope under `_sync_rejected` and announce
|
|
864
|
+
* it. One helper for both fates: a record that ARRIVED and was turned away,
|
|
865
|
+
* and (core#108) this device's own record refused on the way out. The
|
|
866
|
+
* parking key is `collection::id`, so re-refusing the same record overwrites
|
|
867
|
+
* rather than accumulating.
|
|
868
|
+
*/
|
|
869
|
+
async #park(collection, id, envelope, reason, origin) {
|
|
870
|
+
const rejection = {
|
|
871
|
+
vault: this.vault,
|
|
872
|
+
collection,
|
|
873
|
+
id,
|
|
874
|
+
reason,
|
|
875
|
+
version: envelope._v,
|
|
876
|
+
...envelope._by !== void 0 ? { by: envelope._by } : {},
|
|
877
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
878
|
+
...envelope._ts !== void 0 ? { recordAt: envelope._ts } : {},
|
|
879
|
+
origin
|
|
880
|
+
};
|
|
881
|
+
await this.#parkBuilt(collection, id, envelope, rejection);
|
|
882
|
+
if (origin === "sync-apply") await this.#publishRejection(collection, id, rejection);
|
|
883
|
+
return rejection;
|
|
884
|
+
}
|
|
885
|
+
/** Store half of `#park`, also used for a refusal that arrived already made (core#107). */
|
|
886
|
+
async #parkBuilt(collection, id, envelope, rejection) {
|
|
887
|
+
const parked = { rejection, envelope };
|
|
888
|
+
await this.local.put(
|
|
889
|
+
this.vault,
|
|
890
|
+
REJECTED_COLLECTION,
|
|
891
|
+
`${collection}::${id}`,
|
|
892
|
+
buildRecordEnvelope({ collection: REJECTED_COLLECTION, id: `${collection}::${id}`, version: 1 }, { iv: "", data: JSON.stringify(parked) })
|
|
893
|
+
);
|
|
894
|
+
this.emitter.emit("sync:rejected", rejection);
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* core#107 — turn the arbiter's replicated refusals into THIS device's
|
|
898
|
+
* notification. Runs after the reserved phase has brought `_sync_rejections`.
|
|
899
|
+
*
|
|
900
|
+
* Three filters, each load-bearing:
|
|
901
|
+
* - `courier.open` returns `null` for a member who holds no DEK for that
|
|
902
|
+
* collection. They received the ciphertext like everyone else and skip it.
|
|
903
|
+
* - a refusal names a VERSION. If this device's copy has moved on, the
|
|
904
|
+
* refusal is about a record that no longer exists here and is ignored —
|
|
905
|
+
* otherwise a stale verdict would re-fire against an edit that may well
|
|
906
|
+
* pass now.
|
|
907
|
+
* - an existing parking at the same version means this was already
|
|
908
|
+
* reported; the parking record IS the dedupe, so no extra state is kept
|
|
909
|
+
* and it survives a restart.
|
|
910
|
+
*
|
|
911
|
+
* ⛔ REPORT ONLY (ruled): the local record is never deleted, tombstoned or
|
|
912
|
+
* hidden. The refusal is information; what to do about it is the writer's.
|
|
913
|
+
*/
|
|
914
|
+
async #scanReplicatedRejections() {
|
|
915
|
+
if (!this.courier) return 0;
|
|
916
|
+
let keys;
|
|
917
|
+
try {
|
|
918
|
+
keys = await this.local.list(this.vault, REJECTIONS_COLLECTION);
|
|
919
|
+
} catch {
|
|
920
|
+
return 0;
|
|
921
|
+
}
|
|
922
|
+
let seen = 0;
|
|
923
|
+
for (const parkKey of keys) {
|
|
924
|
+
const sep = parkKey.indexOf("::");
|
|
925
|
+
if (sep < 0) continue;
|
|
926
|
+
const collection = parkKey.slice(0, sep), id = parkKey.slice(sep + 2);
|
|
927
|
+
const env = await this.local.get(this.vault, REJECTIONS_COLLECTION, parkKey);
|
|
928
|
+
if (!env) continue;
|
|
929
|
+
const rejection = await this.courier.open(collection, id, env);
|
|
930
|
+
if (!rejection) continue;
|
|
931
|
+
const mine = await this.local.get(this.vault, collection, id);
|
|
932
|
+
if (!mine || mine._v !== rejection.version) continue;
|
|
933
|
+
const existing = await this.local.get(this.vault, REJECTED_COLLECTION, parkKey);
|
|
934
|
+
if (existing && parseParked(existing).rejection.version === rejection.version) continue;
|
|
935
|
+
const replicated = { ...rejection, origin: "arbiter" };
|
|
936
|
+
await this.#parkBuilt(collection, id, mine, replicated);
|
|
937
|
+
this.#rejected.push(replicated);
|
|
938
|
+
seen++;
|
|
939
|
+
}
|
|
940
|
+
return seen;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* core#107 — the ARBITER's refusals replicate, so the writer finds out.
|
|
944
|
+
*
|
|
945
|
+
* Only `sync-apply` refusals: a `push-recheck` refusal (core#108) never left
|
|
946
|
+
* this device, so there is no other device to tell, and a `restore-epoch`
|
|
947
|
+
* parking is this device adopting a new base rather than a judgement about
|
|
948
|
+
* the record.
|
|
949
|
+
*
|
|
950
|
+
* ⛔ Best-effort by construction: a failure to publish must not fail the
|
|
951
|
+
* pull that produced it. The local parking, the `PullResult.rejected` row
|
|
952
|
+
* and the event have already happened and are this device's real answer;
|
|
953
|
+
* replication is the courtesy on top. So a courier that cannot seal (no DEK
|
|
954
|
+
* for that collection) or a store that refuses the write leaves the refusal
|
|
955
|
+
* exactly as it was before core#107.
|
|
956
|
+
*/
|
|
957
|
+
async #publishRejection(collection, id, rejection) {
|
|
958
|
+
if (!this.arbiter || !this.courier) return;
|
|
959
|
+
try {
|
|
960
|
+
const key = rejectionKey(collection, id);
|
|
961
|
+
const current = await this.local.get(this.vault, REJECTIONS_COLLECTION, key);
|
|
962
|
+
const envelope = await this.courier.seal(collection, id, rejection, (current?._v ?? 0) + 1);
|
|
963
|
+
if (envelope) await this.local.put(this.vault, REJECTIONS_COLLECTION, key, envelope);
|
|
964
|
+
} catch {
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* core#108 — re-run this device's OWN gates against a dirty record before it
|
|
969
|
+
* is pushed. A record written offline may no longer pass the rules this
|
|
970
|
+
* device now holds (a period closed while it was away); today it pushes and
|
|
971
|
+
* every OTHER device refuses it at admission, so the writer is the last to
|
|
972
|
+
* know. Judging it here means the writer's own device catches it the moment
|
|
973
|
+
* it reconnects, before anyone else sees the record.
|
|
974
|
+
*
|
|
975
|
+
* Returns `true` when the entry was WITHHELD. The entry stays dirty and the
|
|
976
|
+
* local copy is untouched, so nothing is lost and a later push carries it
|
|
977
|
+
* unchanged once the rule passes again — which is why a refusal here needs
|
|
978
|
+
* no `readmit()` and no dirty-log surgery.
|
|
979
|
+
*
|
|
980
|
+
* ⛔ `rekey` is never re-checked: core#100 rewrites the SAME content under a
|
|
981
|
+
* new key, so judging it would refuse a record for its content having been
|
|
982
|
+
* legal at write time — the same wrong answer core#74 fixed with
|
|
983
|
+
* `admit: false` for a device's own re-versioned records.
|
|
984
|
+
*/
|
|
985
|
+
async #withheldByRecheck(entry, envelope) {
|
|
986
|
+
if (!this.admission || entry.action === "rekey") return false;
|
|
987
|
+
if (isTombstoneShape(envelope) || isDeleteMarker(envelope)) return false;
|
|
988
|
+
const verdict = await this.admission.admit(entry.collection, entry.id, envelope, "push-recheck");
|
|
989
|
+
if (verdict.admitted) return false;
|
|
990
|
+
this.#pushRejected.push(await this.#park(entry.collection, entry.id, envelope, verdict.reason, "push-recheck"));
|
|
991
|
+
return true;
|
|
992
|
+
}
|
|
993
|
+
/** One dirty entry of `push()`: the CAS put, the tombstone assertion, the delete, and every conflict branch. Extracted so `push({ concurrency })` can run entries in a bounded pool (core#93). */
|
|
994
|
+
async #pushOne(i, entry, acc) {
|
|
995
|
+
try {
|
|
996
|
+
if (entry.action === "delete") {
|
|
997
|
+
await this.remote.delete(this.vault, entry.collection, entry.id);
|
|
998
|
+
acc.completed.push(i);
|
|
999
|
+
acc.pushed++;
|
|
1000
|
+
} else {
|
|
1001
|
+
const envelope = await this.local.get(this.vault, entry.collection, entry.id);
|
|
1002
|
+
if (!envelope) {
|
|
1003
|
+
acc.completed.push(i);
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
if (isTombstoneShape(envelope)) {
|
|
1007
|
+
await this.remote.put(this.vault, entry.collection, entry.id, envelope);
|
|
1008
|
+
acc.completed.push(i);
|
|
1009
|
+
acc.pushed++;
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
if (await this.#withheldByRecheck(entry, envelope)) return;
|
|
1013
|
+
try {
|
|
1014
|
+
await this.remote.put(
|
|
1015
|
+
this.vault,
|
|
1016
|
+
entry.collection,
|
|
1017
|
+
entry.id,
|
|
1018
|
+
envelope,
|
|
1019
|
+
// core#100 — a `rekey` re-encrypted the record in place: same `_v`,
|
|
1020
|
+
// new bytes; the target still at that version takes it.
|
|
1021
|
+
entry.action === "rekey" ? entry.version : entry.version - 1
|
|
1022
|
+
);
|
|
1023
|
+
acc.completed.push(i);
|
|
1024
|
+
acc.pushed++;
|
|
1025
|
+
acc.bytes += envelopeBodySize(envelope);
|
|
1026
|
+
} catch (err) {
|
|
1027
|
+
if (isConflictError(err)) {
|
|
1028
|
+
const remoteEnvelope = await this.remote.get(this.vault, entry.collection, entry.id);
|
|
1029
|
+
if (remoteEnvelope) {
|
|
1030
|
+
if (remoteEnvelope._v === envelope._v && envelopeBodyForHash(remoteEnvelope) === envelopeBodyForHash(envelope)) {
|
|
1031
|
+
acc.completed.push(i);
|
|
1032
|
+
} else if (isTombstoneShape(remoteEnvelope)) {
|
|
1033
|
+
await this.applyRemote(entry.collection, entry.id, remoteEnvelope);
|
|
1034
|
+
acc.erasures.push(this.reportErasure(entry.collection, entry.id, remoteEnvelope, envelope, "push"));
|
|
1035
|
+
acc.completed.push(i);
|
|
1036
|
+
} else if (remoteEnvelope._v === envelope._v && isDeleteMarker(remoteEnvelope) !== isDeleteMarker(envelope) && !this.conflictResolvers.get(entry.collection)) {
|
|
1037
|
+
if (isDeleteMarker(remoteEnvelope)) {
|
|
1038
|
+
await this.applyRemote(entry.collection, entry.id, remoteEnvelope);
|
|
1039
|
+
acc.completed.push(i);
|
|
1040
|
+
} else {
|
|
1041
|
+
await this.remote.put(this.vault, entry.collection, entry.id, envelope);
|
|
1042
|
+
acc.completed.push(i);
|
|
1043
|
+
acc.pushed++;
|
|
1044
|
+
}
|
|
1045
|
+
} else {
|
|
1046
|
+
const { handled, conflict } = await this.handleConflict(
|
|
1047
|
+
entry.collection,
|
|
1048
|
+
entry.id,
|
|
1049
|
+
envelope,
|
|
1050
|
+
remoteEnvelope,
|
|
1051
|
+
"push"
|
|
1052
|
+
);
|
|
1053
|
+
acc.conflicts.push(conflict);
|
|
1054
|
+
if (handled === "local") {
|
|
1055
|
+
const winner = await this.advancePastRemote(conflict.local, entry.collection, entry.id, remoteEnvelope);
|
|
1056
|
+
await this.remote.put(this.vault, entry.collection, entry.id, winner);
|
|
1057
|
+
if (winner !== conflict.local) await this.applyRemote(entry.collection, entry.id, winner, { admit: false });
|
|
1058
|
+
acc.completed.push(i);
|
|
1059
|
+
acc.pushed++;
|
|
1060
|
+
} else if (handled === "remote") {
|
|
1061
|
+
await this.applyRemote(entry.collection, entry.id, conflict.remote);
|
|
1062
|
+
acc.completed.push(i);
|
|
1063
|
+
} else if (handled === "merged" && conflict.local !== envelope) {
|
|
1064
|
+
const merged = conflict.local;
|
|
1065
|
+
await this.remote.put(this.vault, entry.collection, entry.id, merged);
|
|
1066
|
+
await this.applyRemote(entry.collection, entry.id, merged);
|
|
1067
|
+
acc.completed.push(i);
|
|
1068
|
+
acc.pushed++;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
} else {
|
|
1073
|
+
throw err;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
} catch (err) {
|
|
1078
|
+
acc.errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* core#92 — mark every user-collection record in the local store dirty at its
|
|
1083
|
+
* current version, so the next push sends it (CAS against `_v - 1`, so a
|
|
1084
|
+
* remote that already holds a newer copy resolves through the conflict path
|
|
1085
|
+
* instead of being overwritten). Records already dirty keep their entry.
|
|
1086
|
+
*/
|
|
1087
|
+
async markAllDirty() {
|
|
1088
|
+
await this.ensureLoaded();
|
|
1089
|
+
const snapshot = await this.local.loadAll(this.vault);
|
|
1090
|
+
for (const [collection, records] of Object.entries(snapshot)) {
|
|
1091
|
+
if (collection === REJECTED_COLLECTION) continue;
|
|
1092
|
+
for (const [id, envelope] of Object.entries(records)) {
|
|
1093
|
+
if (this.dirty.some((d) => d.collection === collection && d.id === id)) continue;
|
|
1094
|
+
this.dirty.push({ vault: this.vault, collection, id, action: "put", version: envelope._v, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
await this.persistMeta();
|
|
1098
|
+
}
|
|
513
1099
|
/** Push dirty records to remote adapter. Accepts optional `PushOptions` for partial sync. */
|
|
514
1100
|
async push(options) {
|
|
515
1101
|
await this.ensureLoaded();
|
|
1102
|
+
if (options?.full) await this.markAllDirty();
|
|
516
1103
|
this.graphBatchController?.begin();
|
|
517
|
-
let
|
|
1104
|
+
let reserved = 0;
|
|
1105
|
+
this.#pushRejected = [];
|
|
518
1106
|
const conflicts = [];
|
|
519
1107
|
const erasures = [];
|
|
520
1108
|
const errors = [];
|
|
521
1109
|
const completed = [];
|
|
522
1110
|
const expanded = options?.collections ? this.pairExpander?.(options.collections) ?? options.collections : null;
|
|
523
1111
|
const filter = expanded ? /* @__PURE__ */ new Set([...expanded, ...this.reservedDictExpander?.(expanded) ?? []]) : null;
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
1112
|
+
const pushTotal = filter ? this.dirty.filter((d) => filter.has(d.collection)).length : this.dirty.length;
|
|
1113
|
+
const acc = { pushed: 0, bytes: 0, completed, conflicts, erasures, errors };
|
|
1114
|
+
const eligible = [...this.dirty.keys()].filter((i) => !filter || filter.has(this.dirty[i].collection));
|
|
1115
|
+
const width = Math.max(1, Math.floor(options?.concurrency ?? 1));
|
|
1116
|
+
let next = 0;
|
|
1117
|
+
const worker = async () => {
|
|
1118
|
+
while (next < eligible.length) {
|
|
1119
|
+
const i = eligible[next++];
|
|
1120
|
+
this.progress({ direction: "push", phase: "records", records: acc.pushed, bytes: acc.bytes, total: { records: pushTotal } });
|
|
1121
|
+
await this.#pushOne(i, this.dirty[i], acc);
|
|
528
1122
|
}
|
|
1123
|
+
};
|
|
1124
|
+
await Promise.all(Array.from({ length: Math.min(width, eligible.length) }, () => worker()));
|
|
1125
|
+
const pushed = acc.pushed;
|
|
1126
|
+
const pushBytes = acc.bytes;
|
|
1127
|
+
for (const i of completed.sort((a, b) => b - a)) {
|
|
1128
|
+
this.dirty.splice(i, 1);
|
|
1129
|
+
}
|
|
1130
|
+
if (!filter) {
|
|
1131
|
+
this.progress({ direction: "push", phase: "reserved", records: pushed, bytes: pushBytes, total: { records: pushTotal } });
|
|
529
1132
|
try {
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
completed.push(i);
|
|
543
|
-
pushed++;
|
|
544
|
-
continue;
|
|
545
|
-
}
|
|
546
|
-
try {
|
|
547
|
-
await this.remote.put(
|
|
548
|
-
this.vault,
|
|
549
|
-
entry.collection,
|
|
550
|
-
entry.id,
|
|
551
|
-
envelope,
|
|
552
|
-
entry.version - 1
|
|
553
|
-
);
|
|
554
|
-
completed.push(i);
|
|
555
|
-
pushed++;
|
|
556
|
-
} catch (err) {
|
|
557
|
-
if (isConflictError(err)) {
|
|
558
|
-
const remoteEnvelope = await this.remote.get(this.vault, entry.collection, entry.id);
|
|
559
|
-
if (remoteEnvelope) {
|
|
560
|
-
if (isTombstoneShape(remoteEnvelope)) {
|
|
561
|
-
await this.applyRemote(entry.collection, entry.id, remoteEnvelope);
|
|
562
|
-
erasures.push(this.reportErasure(entry.collection, entry.id, remoteEnvelope, envelope, "push"));
|
|
563
|
-
completed.push(i);
|
|
564
|
-
} else if (remoteEnvelope._v === envelope._v && isDeleteMarker(remoteEnvelope) !== isDeleteMarker(envelope) && !this.conflictResolvers.get(entry.collection)) {
|
|
565
|
-
if (isDeleteMarker(remoteEnvelope)) {
|
|
566
|
-
await this.applyRemote(entry.collection, entry.id, remoteEnvelope);
|
|
567
|
-
completed.push(i);
|
|
568
|
-
} else {
|
|
569
|
-
await this.remote.put(this.vault, entry.collection, entry.id, envelope);
|
|
570
|
-
completed.push(i);
|
|
571
|
-
pushed++;
|
|
572
|
-
}
|
|
573
|
-
} else {
|
|
574
|
-
const { handled, conflict } = await this.handleConflict(
|
|
575
|
-
entry.collection,
|
|
576
|
-
entry.id,
|
|
577
|
-
envelope,
|
|
578
|
-
remoteEnvelope,
|
|
579
|
-
"push"
|
|
580
|
-
);
|
|
581
|
-
conflicts.push(conflict);
|
|
582
|
-
if (handled === "local") {
|
|
583
|
-
const winner = await this.advancePastRemote(conflict.local, entry.collection, entry.id, remoteEnvelope);
|
|
584
|
-
await this.remote.put(this.vault, entry.collection, entry.id, winner);
|
|
585
|
-
if (winner !== conflict.local) await this.applyRemote(entry.collection, entry.id, winner);
|
|
586
|
-
completed.push(i);
|
|
587
|
-
pushed++;
|
|
588
|
-
} else if (handled === "remote") {
|
|
589
|
-
await this.applyRemote(entry.collection, entry.id, conflict.remote);
|
|
590
|
-
completed.push(i);
|
|
591
|
-
} else if (handled === "merged" && conflict.local !== envelope) {
|
|
592
|
-
const merged = conflict.local;
|
|
593
|
-
await this.remote.put(this.vault, entry.collection, entry.id, merged);
|
|
594
|
-
await this.applyRemote(entry.collection, entry.id, merged);
|
|
595
|
-
completed.push(i);
|
|
596
|
-
pushed++;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
} else {
|
|
601
|
-
throw err;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
1133
|
+
const mirrored = await pushReserved(this.local, this.remote, this.vault);
|
|
1134
|
+
reserved = mirrored.copied;
|
|
1135
|
+
for (const stale of mirrored.staleKeyrings ?? []) {
|
|
1136
|
+
conflicts.push({
|
|
1137
|
+
vault: this.vault,
|
|
1138
|
+
collection: "_keyring",
|
|
1139
|
+
id: stale.id,
|
|
1140
|
+
local: stale.local,
|
|
1141
|
+
remote: stale.remote,
|
|
1142
|
+
localVersion: stale.local._v,
|
|
1143
|
+
remoteVersion: stale.remote._v
|
|
1144
|
+
});
|
|
604
1145
|
}
|
|
605
1146
|
} catch (err) {
|
|
606
1147
|
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
607
1148
|
}
|
|
608
1149
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
1150
|
+
this.progress({ direction: "push", phase: "records", records: pushed, bytes: pushBytes, total: { records: pushTotal } }, true);
|
|
1151
|
+
this.progressDone();
|
|
612
1152
|
this.recordOutcome("push", errors);
|
|
613
1153
|
try {
|
|
614
1154
|
await this.persistMeta();
|
|
615
1155
|
} finally {
|
|
616
1156
|
await this.graphBatchController?.flush();
|
|
617
1157
|
}
|
|
618
|
-
const
|
|
1158
|
+
const pushRejected = this.#pushRejected;
|
|
1159
|
+
this.#pushRejected = [];
|
|
1160
|
+
const result = { pushed, conflicts, errors, erasures, ...reserved > 0 && { reserved }, ...pushRejected.length > 0 && { rejected: pushRejected } };
|
|
619
1161
|
this.emitter.emit("sync:push", result);
|
|
620
1162
|
return result;
|
|
621
1163
|
}
|
|
@@ -623,9 +1165,43 @@ var SyncEngine = class {
|
|
|
623
1165
|
async pull(options) {
|
|
624
1166
|
await this.ensureLoaded();
|
|
625
1167
|
let pulled = 0;
|
|
1168
|
+
let reserved = 0;
|
|
1169
|
+
this.#applied = [];
|
|
1170
|
+
this.#rejected = [];
|
|
626
1171
|
const conflicts = [];
|
|
627
|
-
const erasures = [];
|
|
628
1172
|
const errors = [];
|
|
1173
|
+
let crossed = { epoch: 0, resynced: false, parked: 0 };
|
|
1174
|
+
const erasures = [];
|
|
1175
|
+
if (!options?.collections) {
|
|
1176
|
+
this.progress({ direction: "pull", phase: "reserved", records: 0, bytes: 0 });
|
|
1177
|
+
try {
|
|
1178
|
+
const protectedIds = /* @__PURE__ */ new Map();
|
|
1179
|
+
for (const d of this.dirty) {
|
|
1180
|
+
if (d.action === "delete") continue;
|
|
1181
|
+
let set = protectedIds.get(d.collection);
|
|
1182
|
+
if (!set) {
|
|
1183
|
+
set = /* @__PURE__ */ new Set();
|
|
1184
|
+
protectedIds.set(d.collection, set);
|
|
1185
|
+
}
|
|
1186
|
+
set.add(d.id);
|
|
1187
|
+
}
|
|
1188
|
+
const mirrored = await pullReserved(this.remote, this.local, this.vault, protectedIds);
|
|
1189
|
+
reserved = mirrored.copied + mirrored.deleted;
|
|
1190
|
+
if (this.rosterReload && mirrored.keyringsCopied.includes(this.rosterReload.userId)) await this.rosterReload.reload();
|
|
1191
|
+
} catch (err) {
|
|
1192
|
+
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
1193
|
+
}
|
|
1194
|
+
try {
|
|
1195
|
+
await this.#scanReplicatedRejections();
|
|
1196
|
+
} catch (err) {
|
|
1197
|
+
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
try {
|
|
1201
|
+
crossed = await this.#crossEpoch();
|
|
1202
|
+
} catch (err) {
|
|
1203
|
+
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
1204
|
+
}
|
|
629
1205
|
let periodScope = null;
|
|
630
1206
|
let phases = null;
|
|
631
1207
|
if (options?.periods !== void 0) {
|
|
@@ -646,7 +1222,7 @@ var SyncEngine = class {
|
|
|
646
1222
|
const remoteEnvelope = await this.remote.get(this.vault, collName, id);
|
|
647
1223
|
if (!remoteEnvelope) continue;
|
|
648
1224
|
const localEnvelope = await this.local.get(this.vault, collName, id);
|
|
649
|
-
if (!localEnvelope || remoteEnvelope
|
|
1225
|
+
if (!localEnvelope || this.#remoteSupersedes(collName, id, remoteEnvelope, localEnvelope)) {
|
|
650
1226
|
await this.applyRemote(collName, id, remoteEnvelope);
|
|
651
1227
|
pulled++;
|
|
652
1228
|
}
|
|
@@ -671,12 +1247,35 @@ var SyncEngine = class {
|
|
|
671
1247
|
const expanded = options?.collections ? this.pairExpander?.(options.collections) ?? options.collections : null;
|
|
672
1248
|
const filter = expanded ? /* @__PURE__ */ new Set([...expanded, ...this.reservedDictExpander?.(expanded) ?? []]) : null;
|
|
673
1249
|
try {
|
|
674
|
-
|
|
675
|
-
|
|
1250
|
+
let pullTotal = 0;
|
|
1251
|
+
let chunks;
|
|
1252
|
+
if (options?.paged) {
|
|
1253
|
+
const known = new Set(this.collectionNames?.() ?? []);
|
|
1254
|
+
for (const name of filter ?? []) if (!name.startsWith("_")) known.add(name);
|
|
1255
|
+
const names = [...known].filter((n) => !n.startsWith("_") && (!filter || filter.has(n)));
|
|
1256
|
+
for (const name of names) pullTotal += (await this.remote.list(this.vault, name)).length;
|
|
1257
|
+
chunks = this.pagedChunks(names);
|
|
1258
|
+
} else {
|
|
1259
|
+
const remoteSnapshot = await this.remote.loadAll(this.vault);
|
|
1260
|
+
for (const [collName, records] of Object.entries(remoteSnapshot)) {
|
|
1261
|
+
if (filter && !filter.has(collName)) continue;
|
|
1262
|
+
pullTotal += Object.keys(records).length;
|
|
1263
|
+
}
|
|
1264
|
+
chunks = Object.entries(remoteSnapshot);
|
|
1265
|
+
}
|
|
1266
|
+
const pullSample = () => ({
|
|
1267
|
+
direction: "pull",
|
|
1268
|
+
phase: "records",
|
|
1269
|
+
records: pulled,
|
|
1270
|
+
bytes: this.pullBytes,
|
|
1271
|
+
total: { records: pullTotal }
|
|
1272
|
+
});
|
|
1273
|
+
for await (const [collName, records] of chunks) {
|
|
676
1274
|
if (filter && !filter.has(collName)) {
|
|
677
1275
|
continue;
|
|
678
1276
|
}
|
|
679
1277
|
for (const [id, remoteEnvelope] of Object.entries(records)) {
|
|
1278
|
+
this.progress(pullSample());
|
|
680
1279
|
if (options?.modifiedSince && remoteEnvelope._ts <= options.modifiedSince && !isTombstoneShape(remoteEnvelope) && !isDeleteMarker(remoteEnvelope)) {
|
|
681
1280
|
continue;
|
|
682
1281
|
}
|
|
@@ -718,7 +1317,7 @@ var SyncEngine = class {
|
|
|
718
1317
|
this.dirty = this.dirty.filter((d) => !(d.collection === collName && d.id === id));
|
|
719
1318
|
pulled++;
|
|
720
1319
|
}
|
|
721
|
-
} else if (remoteEnvelope
|
|
1320
|
+
} else if (this.#remoteSupersedes(collName, id, remoteEnvelope, localEnvelope)) {
|
|
722
1321
|
const isDirty = this.dirty.some((d) => d.collection === collName && d.id === id);
|
|
723
1322
|
if (isDirty) {
|
|
724
1323
|
const { handled, conflict } = await this.handleConflict(
|
|
@@ -768,7 +1367,7 @@ var SyncEngine = class {
|
|
|
768
1367
|
this.dirty = this.dirty.filter((d) => !(d.collection === collName && d.id === id));
|
|
769
1368
|
pulled++;
|
|
770
1369
|
}
|
|
771
|
-
} else if (remoteEnvelope
|
|
1370
|
+
} else if (this.#remoteSupersedes(collName, id, remoteEnvelope, localEnvelope)) {
|
|
772
1371
|
await this.applyRemote(collName, id, remoteEnvelope);
|
|
773
1372
|
this.dirty = this.dirty.filter((d) => !(d.collection === collName && d.id === id));
|
|
774
1373
|
pulled++;
|
|
@@ -781,6 +1380,9 @@ var SyncEngine = class {
|
|
|
781
1380
|
} catch (err) {
|
|
782
1381
|
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
783
1382
|
}
|
|
1383
|
+
this.progress({ direction: "pull", phase: "records", records: pulled, bytes: this.pullBytes, total: { records: pulled } }, true);
|
|
1384
|
+
this.progressDone();
|
|
1385
|
+
this.pullBytes = 0;
|
|
784
1386
|
this.recordOutcome("pull", errors);
|
|
785
1387
|
try {
|
|
786
1388
|
await this.persistMeta();
|
|
@@ -788,7 +1390,22 @@ var SyncEngine = class {
|
|
|
788
1390
|
this.pullByteSink = null;
|
|
789
1391
|
await this.graphBatchController?.flush();
|
|
790
1392
|
}
|
|
791
|
-
|
|
1393
|
+
pulled = Math.max(0, pulled - this.#rejected.length);
|
|
1394
|
+
const applied = this.#applied, rejected = this.#rejected;
|
|
1395
|
+
this.#applied = [];
|
|
1396
|
+
this.#rejected = [];
|
|
1397
|
+
const result = {
|
|
1398
|
+
pulled,
|
|
1399
|
+
conflicts,
|
|
1400
|
+
errors,
|
|
1401
|
+
erasures,
|
|
1402
|
+
...reserved > 0 && { reserved },
|
|
1403
|
+
...phases !== null ? { phases } : {},
|
|
1404
|
+
...applied.length > 0 && { applied },
|
|
1405
|
+
...rejected.length > 0 && { rejected },
|
|
1406
|
+
...crossed.epoch > 0 && { epoch: crossed.epoch },
|
|
1407
|
+
...crossed.resynced && { resynced: true }
|
|
1408
|
+
};
|
|
792
1409
|
this.emitter.emit("sync:pull", result);
|
|
793
1410
|
return result;
|
|
794
1411
|
}
|
|
@@ -806,6 +1423,7 @@ var SyncEngine = class {
|
|
|
806
1423
|
async pushFiltered(predicate) {
|
|
807
1424
|
await this.ensureLoaded();
|
|
808
1425
|
let pushed = 0;
|
|
1426
|
+
this.#pushRejected = [];
|
|
809
1427
|
const conflicts = [];
|
|
810
1428
|
const erasures = [];
|
|
811
1429
|
const errors = [];
|
|
@@ -830,6 +1448,7 @@ var SyncEngine = class {
|
|
|
830
1448
|
pushed++;
|
|
831
1449
|
continue;
|
|
832
1450
|
}
|
|
1451
|
+
if (await this.#withheldByRecheck(entry, envelope)) continue;
|
|
833
1452
|
try {
|
|
834
1453
|
await this.remote.put(
|
|
835
1454
|
this.vault,
|
|
@@ -869,7 +1488,7 @@ var SyncEngine = class {
|
|
|
869
1488
|
if (handled === "local") {
|
|
870
1489
|
const winner = await this.advancePastRemote(conflict.local, entry.collection, entry.id, remoteEnvelope);
|
|
871
1490
|
await this.remote.put(this.vault, entry.collection, entry.id, winner);
|
|
872
|
-
if (winner !== conflict.local) await this.applyRemote(entry.collection, entry.id, winner);
|
|
1491
|
+
if (winner !== conflict.local) await this.applyRemote(entry.collection, entry.id, winner, { admit: false });
|
|
873
1492
|
completed.push(i);
|
|
874
1493
|
pushed++;
|
|
875
1494
|
} else if (handled === "remote") {
|
|
@@ -898,7 +1517,9 @@ var SyncEngine = class {
|
|
|
898
1517
|
}
|
|
899
1518
|
this.recordOutcome("push", errors);
|
|
900
1519
|
await this.persistMeta();
|
|
901
|
-
const
|
|
1520
|
+
const pushRejected = this.#pushRejected;
|
|
1521
|
+
this.#pushRejected = [];
|
|
1522
|
+
const result = { pushed, conflicts, errors, erasures, ...pushRejected.length > 0 && { rejected: pushRejected } };
|
|
902
1523
|
this.emitter.emit("sync:push", result);
|
|
903
1524
|
return result;
|
|
904
1525
|
}
|
|
@@ -924,12 +1545,67 @@ var SyncEngine = class {
|
|
|
924
1545
|
else this.lastPull = at;
|
|
925
1546
|
}
|
|
926
1547
|
/** Get current sync status. */
|
|
1548
|
+
/**
|
|
1549
|
+
* core#82 — re-align a CORRUPTED local from the target. An ordinary pull
|
|
1550
|
+
* cannot repair a damaged local envelope: it keeps its `_v`, so it wins
|
|
1551
|
+
* (or ties) against the target's healthy copy. Here every local
|
|
1552
|
+
* user-collection envelope is verified with the same `MergeAuthority`
|
|
1553
|
+
* check pull runs on remote envelopes; one that fails is replaced by the
|
|
1554
|
+
* target's copy when THAT verifies, and the dirty log is dropped (a dirty
|
|
1555
|
+
* entry over a damaged record would push the damage). Records at a tier
|
|
1556
|
+
* the caller holds no key for pass unverified, as in pull — this repairs
|
|
1557
|
+
* what the caller can read. Requires a `MergeAuthority` (a vault-attached
|
|
1558
|
+
* engine on an encrypted vault).
|
|
1559
|
+
*/
|
|
1560
|
+
/**
|
|
1561
|
+
* core#96 (pilot-1, finding B) — bring one member's keyring file down from
|
|
1562
|
+
* this target if the target's copy supersedes ours. Called by the kernel
|
|
1563
|
+
* before an authority edit; reloads the caller's own keyring in place when
|
|
1564
|
+
* it is the one refreshed (core#82).
|
|
1565
|
+
*/
|
|
1566
|
+
async refreshKeyring(userId) {
|
|
1567
|
+
await this.ensureLoaded();
|
|
1568
|
+
const copied = await pullKeyringFile(this.remote, this.local, this.vault, userId);
|
|
1569
|
+
if (copied && this.rosterReload && userId === this.rosterReload.userId) await this.rosterReload.reload();
|
|
1570
|
+
return copied;
|
|
1571
|
+
}
|
|
1572
|
+
async realign() {
|
|
1573
|
+
await this.ensureLoaded();
|
|
1574
|
+
if (!this.mergeAuthority) throw new ValidationError("realign: requires a vault-attached engine on an encrypted vault (no MergeAuthority).");
|
|
1575
|
+
const errors = [];
|
|
1576
|
+
let checked = 0;
|
|
1577
|
+
let replaced = 0;
|
|
1578
|
+
let unrecoverable = 0;
|
|
1579
|
+
const local = await this.local.loadAll(this.vault);
|
|
1580
|
+
for (const [collection, records] of Object.entries(local)) {
|
|
1581
|
+
for (const [id, envelope] of Object.entries(records)) {
|
|
1582
|
+
checked++;
|
|
1583
|
+
if (isTombstoneShape(envelope) || isDeleteMarker(envelope)) continue;
|
|
1584
|
+
if (await this.mergeAuthority.verify(collection, id, envelope)) continue;
|
|
1585
|
+
const remote = await this.remote.get(this.vault, collection, id);
|
|
1586
|
+
if (remote && await this.mergeAuthority.verify(collection, id, remote)) {
|
|
1587
|
+
await this.local.put(this.vault, collection, id, remote);
|
|
1588
|
+
await this.cacheInvalidator?.(collection, id, "put");
|
|
1589
|
+
replaced++;
|
|
1590
|
+
} else {
|
|
1591
|
+
unrecoverable++;
|
|
1592
|
+
errors.push(new ValidationError(`realign: "${collection}/${id}" fails to authenticate locally and the target holds no healthy copy.`));
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
this.dirty = [];
|
|
1597
|
+
await this.persistMeta();
|
|
1598
|
+
return { checked, replaced, unrecoverable, errors };
|
|
1599
|
+
}
|
|
927
1600
|
status() {
|
|
928
1601
|
const scheduled = this.scheduler?.status;
|
|
929
1602
|
return {
|
|
930
1603
|
dirty: this.dirty.length,
|
|
931
1604
|
lastPush: this.lastPush,
|
|
932
1605
|
lastPull: this.lastPull,
|
|
1606
|
+
...this.epoch !== null ? { epoch: this.epoch } : {},
|
|
1607
|
+
// core#72
|
|
1608
|
+
...this.inFlight ? { inFlight: this.inFlight } : {},
|
|
933
1609
|
online: this.isOnline,
|
|
934
1610
|
...this.lastError ? { lastError: this.lastError } : {},
|
|
935
1611
|
...scheduled && scheduled.readiness.size > 0 ? { readiness: scheduled.readiness, phase: scheduled.phase } : {}
|
|
@@ -1012,13 +1688,42 @@ var SyncEngine = class {
|
|
|
1012
1688
|
if (!this.mergeAuthority) return { ...winner, _v: toVersion };
|
|
1013
1689
|
return this.mergeAuthority.advance(collection, id, winner, toVersion);
|
|
1014
1690
|
}
|
|
1015
|
-
|
|
1691
|
+
/**
|
|
1692
|
+
* core#100 — does the remote copy supersede ours? A higher `_v`, as always;
|
|
1693
|
+
* or the SAME `_v` with different bytes while we hold no pending write for
|
|
1694
|
+
* it: a DEK rotation re-encrypts records in place, and a replica that kept
|
|
1695
|
+
* the old ciphertext would read `TamperedError` under the delivered key.
|
|
1696
|
+
* `applyRemote` still authenticates it (`MergeAuthority`) before it lands.
|
|
1697
|
+
*/
|
|
1698
|
+
#remoteSupersedes(collection, id, remote, local) {
|
|
1699
|
+
if (remote._v > local._v) return true;
|
|
1700
|
+
if (remote._v !== local._v) return false;
|
|
1701
|
+
if (this.dirty.some((d) => d.collection === collection && d.id === id)) return false;
|
|
1702
|
+
return envelopeBodyForHash(remote) !== envelopeBodyForHash(local);
|
|
1703
|
+
}
|
|
1704
|
+
async applyRemote(collection, id, envelope, opts) {
|
|
1016
1705
|
if (this.mergeAuthority && !await this.mergeAuthority.verify(collection, id, envelope)) {
|
|
1017
1706
|
throw new ValidationError(
|
|
1018
1707
|
`sync: refusing remote envelope for "${collection}/${id}" \u2014 it does not authenticate at the identity and version it claims. The local copy is unchanged.`
|
|
1019
1708
|
);
|
|
1020
1709
|
}
|
|
1710
|
+
const isErasure = isTombstoneShape(envelope) || isDeleteMarker(envelope);
|
|
1711
|
+
if (this.admission && !isErasure && opts?.admit !== false) {
|
|
1712
|
+
const verdict = await this.admission.admit(collection, id, envelope);
|
|
1713
|
+
if (!verdict.admitted) {
|
|
1714
|
+
this.#rejected.push(await this.#park(collection, id, envelope, verdict.reason, "sync-apply"));
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
const prior = await this.local.get(this.vault, collection, id);
|
|
1021
1719
|
await this.local.put(this.vault, collection, id, envelope);
|
|
1720
|
+
this.#applied.push({
|
|
1721
|
+
collection,
|
|
1722
|
+
id,
|
|
1723
|
+
action: isErasure ? "delete" : "put",
|
|
1724
|
+
...prior && prior._v < envelope._v ? { replaced: { version: prior._v, ...prior._by !== void 0 ? { by: prior._by } : {} } } : {}
|
|
1725
|
+
});
|
|
1726
|
+
this.pullBytes += envelopeBodySize(envelope);
|
|
1022
1727
|
if (this.pullByteSink !== null) {
|
|
1023
1728
|
this.pullByteSink.records++;
|
|
1024
1729
|
this.pullByteSink.bytes += envelopeBodySize(envelope);
|
|
@@ -1117,6 +1822,7 @@ var SyncEngine = class {
|
|
|
1117
1822
|
this.dirty = [...meta.dirty];
|
|
1118
1823
|
this.lastPush = meta.last_push;
|
|
1119
1824
|
this.lastPull = meta.last_pull;
|
|
1825
|
+
this.epoch = meta.epoch ?? null;
|
|
1120
1826
|
}
|
|
1121
1827
|
this.loaded = true;
|
|
1122
1828
|
}
|
|
@@ -1125,7 +1831,8 @@ var SyncEngine = class {
|
|
|
1125
1831
|
_noydb_sync: NOYDB_SYNC_VERSION,
|
|
1126
1832
|
last_push: this.lastPush,
|
|
1127
1833
|
last_pull: this.lastPull,
|
|
1128
|
-
dirty: this.dirty
|
|
1834
|
+
dirty: this.dirty,
|
|
1835
|
+
...this.epoch !== null ? { epoch: this.epoch } : {}
|
|
1129
1836
|
};
|
|
1130
1837
|
const envelope = buildRecordEnvelope(
|
|
1131
1838
|
{ collection: "_sync", id: "meta", version: 1 },
|
|
@@ -1195,7 +1902,8 @@ var SyncTransaction = class {
|
|
|
1195
1902
|
|
|
1196
1903
|
export {
|
|
1197
1904
|
PresenceHandle,
|
|
1905
|
+
bootstrapKeyrings,
|
|
1198
1906
|
SyncEngine,
|
|
1199
1907
|
SyncTransaction
|
|
1200
1908
|
};
|
|
1201
|
-
//# sourceMappingURL=chunk-
|
|
1909
|
+
//# sourceMappingURL=chunk-RUE6TCES.js.map
|