@noy-db/hub 0.2.0-pre.4 → 0.2.0-pre.5
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.cjs.map +1 -1
- package/dist/aggregate/index.js +4 -4
- package/dist/attestation/index.cjs.map +1 -1
- package/dist/attestation/index.d.cts +4 -4
- package/dist/attestation/index.d.ts +4 -4
- package/dist/attestation/index.js +6 -6
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +5 -5
- package/dist/blobs/index.d.ts +5 -5
- package/dist/blobs/index.js +5 -5
- package/dist/bundle/index.cjs +443 -338
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +17 -17
- package/dist/bundle/index.d.ts +17 -17
- package/dist/bundle/index.js +10 -10
- package/dist/bundle/index.js.map +1 -1
- package/dist/{chunk-YL2DR3HY.js → chunk-25WFLKOH.js} +2 -2
- package/dist/chunk-25WFLKOH.js.map +1 -0
- package/dist/{chunk-EMEX37ZN.js → chunk-2GMRNNI3.js} +3 -3
- package/dist/chunk-2GMRNNI3.js.map +1 -0
- package/dist/{chunk-NGSPBLLE.js → chunk-34XGYMQT.js} +3 -3
- package/dist/chunk-34XGYMQT.js.map +1 -0
- package/dist/{chunk-FXQYZNOW.js → chunk-5OVIFUQE.js} +1 -1
- package/dist/chunk-5OVIFUQE.js.map +1 -0
- package/dist/{chunk-P6256WTJ.js → chunk-5QPF2MJ5.js} +3 -3
- package/dist/chunk-5QPF2MJ5.js.map +1 -0
- package/dist/{chunk-5ZGZ6HIZ.js → chunk-5VMTAX4Y.js} +2 -2
- package/dist/{chunk-74JEQFMT.js → chunk-6A4AMQ2H.js} +5 -5
- package/dist/chunk-6A4AMQ2H.js.map +1 -0
- package/dist/{chunk-YDLAFP36.js → chunk-6HJ2ZALB.js} +1 -1
- package/dist/chunk-6HJ2ZALB.js.map +1 -0
- package/dist/{chunk-GDTCGIPX.js → chunk-7TX7HN42.js} +2 -2
- package/dist/chunk-7TX7HN42.js.map +1 -0
- package/dist/{chunk-EPK6A3WJ.js → chunk-A3JMGXPG.js} +2 -2
- package/dist/chunk-A3JMGXPG.js.map +1 -0
- package/dist/{chunk-75QDHSE4.js → chunk-A4JNVBPF.js} +5 -5
- package/dist/{chunk-IS5HWQO7.js → chunk-ARZAHCCF.js} +3 -3
- package/dist/{chunk-T6HQMVML.js → chunk-BT7544RM.js} +399 -301
- package/dist/chunk-BT7544RM.js.map +1 -0
- package/dist/{chunk-4OQWR46B.js → chunk-CCC25PA7.js} +5 -5
- package/dist/{chunk-NSLTPGEN.js → chunk-CGJFCT3X.js} +2 -2
- package/dist/{chunk-YK72A4IT.js → chunk-CKH247ZR.js} +4 -4
- package/dist/{chunk-HGZ7DC5H.js → chunk-DFCINPB5.js} +2 -2
- package/dist/chunk-DFCINPB5.js.map +1 -0
- package/dist/{chunk-4X2S7PBF.js → chunk-E225X5CQ.js} +3 -3
- package/dist/chunk-E225X5CQ.js.map +1 -0
- package/dist/{chunk-5YHWBPOT.js → chunk-ED3E3OLO.js} +2 -2
- package/dist/{chunk-UOF74WQY.js → chunk-EKTOYEZ3.js} +2 -2
- package/dist/{chunk-SAVQ6E2O.js → chunk-G26QAQNI.js} +2 -2
- package/dist/{chunk-YMYK7US4.js → chunk-HIELMTUK.js} +2 -2
- package/dist/{chunk-MRIBLZL3.js → chunk-ICH4AIGL.js} +1 -1
- package/dist/chunk-ICH4AIGL.js.map +1 -0
- package/dist/{chunk-LOL725S4.js → chunk-JSYTGEX4.js} +3 -3
- package/dist/{chunk-FBMXWVGP.js → chunk-KGFV72WK.js} +5 -5
- package/dist/{chunk-GVXBHCZ2.js → chunk-LJO6Q3X6.js} +5 -5
- package/dist/chunk-LJO6Q3X6.js.map +1 -0
- package/dist/{chunk-ZC2AAE6J.js → chunk-LWFQYT4N.js} +2 -2
- package/dist/chunk-LWFQYT4N.js.map +1 -0
- package/dist/{chunk-K5PVGKE4.js → chunk-MDIC4FAU.js} +2 -2
- package/dist/{chunk-A6SWRXUQ.js → chunk-NONMIU6C.js} +2 -2
- package/dist/{chunk-ZUMGGHRB.js → chunk-OPD3PZOG.js} +4 -4
- package/dist/{chunk-LS3JLEIB.js → chunk-PS5G6A3Y.js} +4 -4
- package/dist/{chunk-KYKMKLJ6.js → chunk-PX3MJ6RB.js} +3 -3
- package/dist/{chunk-FCDO7UAO.js → chunk-R4LTCI6O.js} +2 -2
- package/dist/{chunk-BFI3RS42.js → chunk-R7JTYCRX.js} +2 -2
- package/dist/chunk-R7JTYCRX.js.map +1 -0
- package/dist/{chunk-WRLHNG6H.js → chunk-RIHZBSWJ.js} +4 -4
- package/dist/chunk-RIHZBSWJ.js.map +1 -0
- package/dist/{chunk-UVPGJXVO.js → chunk-SGSHQ4PH.js} +5 -5
- package/dist/{chunk-TLFUDXVV.js → chunk-T6MTNGBM.js} +5 -5
- package/dist/chunk-T6MTNGBM.js.map +1 -0
- package/dist/{chunk-6S3LLAQ5.js → chunk-TNBIWSQ7.js} +2 -2
- package/dist/{chunk-GD3BGKAR.js → chunk-UGVDIOY7.js} +2 -2
- package/dist/{chunk-FS7A4XNF.js → chunk-WEA4TDTJ.js} +3 -3
- package/dist/{chunk-4UBOTYP5.js → chunk-XDW37COG.js} +5 -5
- package/dist/chunk-XDW37COG.js.map +1 -0
- package/dist/{chunk-QAU5HM6Q.js → chunk-XVJFFGTG.js} +3 -3
- package/dist/{chunk-2EYC3WDT.js → chunk-Y3P5DEMZ.js} +6 -6
- package/dist/chunk-Y3P5DEMZ.js.map +1 -0
- package/dist/{chunk-G7PAZ3TD.js → chunk-YEHUEUNP.js} +4 -4
- package/dist/chunk-YEHUEUNP.js.map +1 -0
- package/dist/{chunk-2XLVPKXG.js → chunk-YJ46RFCD.js} +2 -2
- package/dist/{chunk-KMI2NBBF.js → chunk-YZ6JETII.js} +6 -6
- package/dist/{chunk-NCO2JGKK.js → chunk-Z6FNBOTC.js} +1 -1
- package/dist/chunk-Z6FNBOTC.js.map +1 -0
- package/dist/{chunk-GAUBWHAF.js → chunk-ZQMYB56Z.js} +4 -4
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +5 -5
- package/dist/consent/index.d.ts +5 -5
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-H2Y3DDFW.js → crypto-5UDZZL26.js} +3 -3
- package/dist/{delegation-QSC7G5QC.js → delegation-42LO4WFO.js} +5 -5
- package/dist/derivations/index.cjs +1 -1
- package/dist/derivations/index.cjs.map +1 -1
- package/dist/derivations/index.d.cts +8 -8
- package/dist/derivations/index.d.ts +8 -8
- package/dist/derivations/index.js +4 -4
- package/dist/{dev-unlock-Cf2B7Kih.d.ts → dev-unlock--ahUTrhc.d.ts} +1 -1
- package/dist/{dev-unlock-De3mjQWv.d.cts → dev-unlock-BIwt2V3p.d.cts} +1 -1
- package/dist/executor-AWCHQ2KN.js +8 -0
- package/dist/executor-RWICJI7J.js +11 -0
- package/dist/executor-SOLEQVUB.js +8 -0
- package/dist/{fanout-sidecar-NRBWSLRK.js → fanout-sidecar-EVICRM46.js} +2 -2
- package/dist/fanout-sidecar-EVICRM46.js.map +1 -0
- package/dist/guards/index.cjs +1 -1
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +6 -6
- package/dist/guards/index.d.ts +6 -6
- package/dist/guards/index.js +4 -4
- package/dist/{hash-vBCB0-Ps.d.cts → hash-BQVrGV-t.d.cts} +1 -1
- package/dist/{hash-gVn_uKhp.d.ts → hash-CJEFQxSD.d.ts} +1 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +6 -6
- package/dist/history/index.d.ts +6 -6
- package/dist/history/index.js +6 -6
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +5 -5
- package/dist/i18n/index.d.ts +5 -5
- package/dist/i18n/index.js +7 -7
- package/dist/{index-DVkvrgpm.d.cts → index-5I0MZ0jQ.d.cts} +12 -12
- package/dist/{index-BF1B2HB9.d.ts → index-fIPPh5dg.d.ts} +12 -12
- package/dist/index.cjs +362 -264
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -22
- package/dist/index.d.ts +20 -22
- package/dist/index.js +45 -45
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs +1 -1
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.d.cts +3 -3
- package/dist/indexing/index.d.ts +3 -3
- package/dist/indexing/index.js +4 -4
- package/dist/issue-IODMTPME.js +12 -0
- package/dist/{lazy-builder-Rpd-V3jP.d.ts → lazy-builder-D1MyR1qH.d.ts} +2 -2
- package/dist/{lazy-builder-C-rPfWG0.d.cts → lazy-builder-DXlSCNCJ.d.cts} +2 -2
- package/dist/{ledger-WOEJUYTP.js → ledger-UX4QIHWI.js} +6 -6
- package/dist/materialized-views/index.cjs.map +1 -1
- package/dist/materialized-views/index.d.cts +18 -18
- package/dist/materialized-views/index.d.ts +18 -18
- package/dist/materialized-views/index.js +7 -7
- package/dist/noydb-6TADQIYH.js +34 -0
- package/dist/overlay-views/index.cjs +1 -1
- package/dist/overlay-views/index.cjs.map +1 -1
- package/dist/overlay-views/index.d.cts +8 -8
- package/dist/overlay-views/index.d.ts +8 -8
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +5 -5
- package/dist/periods/index.d.ts +5 -5
- package/dist/periods/index.js +6 -6
- package/dist/{predicate-Dnu81tsS.d.cts → predicate-B0IKeBXx.d.cts} +1 -1
- package/dist/{predicate-Dnu81tsS.d.ts → predicate-B0IKeBXx.d.ts} +1 -1
- package/dist/{public-envelope-OHQ5UZFM.js → public-envelope-YKHKP74C.js} +4 -4
- package/dist/query/index.cjs +2 -2
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +2 -2
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +6 -6
- package/dist/registry-446I2NMN.js +8 -0
- package/dist/{registry-CDHASH73.js → registry-4NEW7LQY.js} +3 -3
- package/dist/registry-524KJZG4.js +8 -0
- package/dist/registry-DKEXOJVO.js +7 -0
- package/dist/{revoke-7JOVLZFD.js → revoke-R5NIQ74J.js} +6 -6
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +6 -6
- package/dist/session/index.d.ts +6 -6
- package/dist/session/index.js +3 -3
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +5 -5
- package/dist/shadow/index.d.ts +5 -5
- package/dist/shadow/index.js +2 -2
- package/dist/{signer-M4K5HBLD.js → signer-WGDJNWSU.js} +5 -5
- package/dist/{stale-PAGCS4K5.js → stale-74WGLVZ2.js} +2 -2
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +5 -5
- package/dist/store/index.d.ts +5 -5
- package/dist/store/index.js +2 -2
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +4 -4
- package/dist/sync/index.d.ts +4 -4
- package/dist/sync/index.js +4 -4
- package/dist/team/index.cjs +1 -1
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +5 -5
- package/dist/team/index.d.ts +5 -5
- package/dist/team/index.js +8 -8
- package/dist/tx/index.cjs +2 -2
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +5 -5
- package/dist/tx/index.d.ts +5 -5
- package/dist/tx/index.js +3 -3
- package/dist/tx/index.js.map +1 -1
- package/dist/{types-D9eB0Rvh.d.ts → types-BV4AZKmx.d.ts} +340 -302
- package/dist/{types-CSLcfytP.d.cts → types-BeKi0hCx.d.cts} +340 -302
- package/dist/{ulid-CiM2OAeM.d.ts → ulid-CQc0eBxE.d.ts} +19 -19
- package/dist/{ulid-CG2YvAbg.d.cts → ulid-Cvljl7ZZ.d.cts} +19 -19
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.js +1 -1
- package/dist/{with-derivation-Bzpj6UTv.d.ts → with-derivation-BWcwmevt.d.ts} +1 -1
- package/dist/{with-derivation-DWajFh4K.d.cts → with-derivation-BkOBDhsu.d.cts} +1 -1
- package/dist/{with-guard-DF_Ul3DT.d.cts → with-guard-BD4Hyu8s.d.cts} +1 -1
- package/dist/{with-guard-DR7U-l4v.d.ts → with-guard-Du54s3Ti.d.ts} +1 -1
- package/dist/{with-materialized-view-qtoJ3xKJ.d.ts → with-materialized-view-B5W4wFAC.d.ts} +2 -2
- package/dist/{with-materialized-view-_piodoIz.d.cts → with-materialized-view-BCPPZdjC.d.cts} +2 -2
- package/dist/{with-overlayed-view-DFaRfgMr.d.ts → with-overlayed-view-B8RrlLsG.d.cts} +2 -2
- package/dist/{with-overlayed-view-DwzCKxn2.d.cts → with-overlayed-view-Cw-h9p9N.d.ts} +2 -2
- package/package.json +3 -3
- package/dist/chunk-2EYC3WDT.js.map +0 -1
- package/dist/chunk-4UBOTYP5.js.map +0 -1
- package/dist/chunk-4X2S7PBF.js.map +0 -1
- package/dist/chunk-74JEQFMT.js.map +0 -1
- package/dist/chunk-BFI3RS42.js.map +0 -1
- package/dist/chunk-EMEX37ZN.js.map +0 -1
- package/dist/chunk-EPK6A3WJ.js.map +0 -1
- package/dist/chunk-FXQYZNOW.js.map +0 -1
- package/dist/chunk-G7PAZ3TD.js.map +0 -1
- package/dist/chunk-GDTCGIPX.js.map +0 -1
- package/dist/chunk-GVXBHCZ2.js.map +0 -1
- package/dist/chunk-HGZ7DC5H.js.map +0 -1
- package/dist/chunk-MRIBLZL3.js.map +0 -1
- package/dist/chunk-NCO2JGKK.js.map +0 -1
- package/dist/chunk-NGSPBLLE.js.map +0 -1
- package/dist/chunk-P6256WTJ.js.map +0 -1
- package/dist/chunk-T6HQMVML.js.map +0 -1
- package/dist/chunk-TLFUDXVV.js.map +0 -1
- package/dist/chunk-WRLHNG6H.js.map +0 -1
- package/dist/chunk-YDLAFP36.js.map +0 -1
- package/dist/chunk-YL2DR3HY.js.map +0 -1
- package/dist/chunk-ZC2AAE6J.js.map +0 -1
- package/dist/executor-BZKFZVRC.js +0 -8
- package/dist/executor-GFZFDQXV.js +0 -8
- package/dist/executor-KT2IOZVP.js +0 -11
- package/dist/fanout-sidecar-NRBWSLRK.js.map +0 -1
- package/dist/issue-BAJ7ZB4S.js +0 -12
- package/dist/noydb-XNQSKXGO.js +0 -34
- package/dist/registry-2IEARCGT.js +0 -7
- package/dist/registry-EMGLZGR6.js +0 -8
- package/dist/registry-NQALYR77.js +0 -8
- /package/dist/{chunk-5ZGZ6HIZ.js.map → chunk-5VMTAX4Y.js.map} +0 -0
- /package/dist/{chunk-75QDHSE4.js.map → chunk-A4JNVBPF.js.map} +0 -0
- /package/dist/{chunk-IS5HWQO7.js.map → chunk-ARZAHCCF.js.map} +0 -0
- /package/dist/{chunk-4OQWR46B.js.map → chunk-CCC25PA7.js.map} +0 -0
- /package/dist/{chunk-NSLTPGEN.js.map → chunk-CGJFCT3X.js.map} +0 -0
- /package/dist/{chunk-YK72A4IT.js.map → chunk-CKH247ZR.js.map} +0 -0
- /package/dist/{chunk-5YHWBPOT.js.map → chunk-ED3E3OLO.js.map} +0 -0
- /package/dist/{chunk-UOF74WQY.js.map → chunk-EKTOYEZ3.js.map} +0 -0
- /package/dist/{chunk-SAVQ6E2O.js.map → chunk-G26QAQNI.js.map} +0 -0
- /package/dist/{chunk-YMYK7US4.js.map → chunk-HIELMTUK.js.map} +0 -0
- /package/dist/{chunk-LOL725S4.js.map → chunk-JSYTGEX4.js.map} +0 -0
- /package/dist/{chunk-FBMXWVGP.js.map → chunk-KGFV72WK.js.map} +0 -0
- /package/dist/{chunk-K5PVGKE4.js.map → chunk-MDIC4FAU.js.map} +0 -0
- /package/dist/{chunk-A6SWRXUQ.js.map → chunk-NONMIU6C.js.map} +0 -0
- /package/dist/{chunk-ZUMGGHRB.js.map → chunk-OPD3PZOG.js.map} +0 -0
- /package/dist/{chunk-LS3JLEIB.js.map → chunk-PS5G6A3Y.js.map} +0 -0
- /package/dist/{chunk-KYKMKLJ6.js.map → chunk-PX3MJ6RB.js.map} +0 -0
- /package/dist/{chunk-FCDO7UAO.js.map → chunk-R4LTCI6O.js.map} +0 -0
- /package/dist/{chunk-UVPGJXVO.js.map → chunk-SGSHQ4PH.js.map} +0 -0
- /package/dist/{chunk-6S3LLAQ5.js.map → chunk-TNBIWSQ7.js.map} +0 -0
- /package/dist/{chunk-GD3BGKAR.js.map → chunk-UGVDIOY7.js.map} +0 -0
- /package/dist/{chunk-FS7A4XNF.js.map → chunk-WEA4TDTJ.js.map} +0 -0
- /package/dist/{chunk-QAU5HM6Q.js.map → chunk-XVJFFGTG.js.map} +0 -0
- /package/dist/{chunk-2XLVPKXG.js.map → chunk-YJ46RFCD.js.map} +0 -0
- /package/dist/{chunk-KMI2NBBF.js.map → chunk-YZ6JETII.js.map} +0 -0
- /package/dist/{chunk-GAUBWHAF.js.map → chunk-ZQMYB56Z.js.map} +0 -0
- /package/dist/{crypto-H2Y3DDFW.js.map → crypto-5UDZZL26.js.map} +0 -0
- /package/dist/{delegation-QSC7G5QC.js.map → delegation-42LO4WFO.js.map} +0 -0
- /package/dist/{executor-BZKFZVRC.js.map → executor-AWCHQ2KN.js.map} +0 -0
- /package/dist/{executor-GFZFDQXV.js.map → executor-RWICJI7J.js.map} +0 -0
- /package/dist/{executor-KT2IOZVP.js.map → executor-SOLEQVUB.js.map} +0 -0
- /package/dist/{issue-BAJ7ZB4S.js.map → issue-IODMTPME.js.map} +0 -0
- /package/dist/{ledger-WOEJUYTP.js.map → ledger-UX4QIHWI.js.map} +0 -0
- /package/dist/{noydb-XNQSKXGO.js.map → noydb-6TADQIYH.js.map} +0 -0
- /package/dist/{public-envelope-OHQ5UZFM.js.map → public-envelope-YKHKP74C.js.map} +0 -0
- /package/dist/{registry-2IEARCGT.js.map → registry-446I2NMN.js.map} +0 -0
- /package/dist/{registry-CDHASH73.js.map → registry-4NEW7LQY.js.map} +0 -0
- /package/dist/{registry-EMGLZGR6.js.map → registry-524KJZG4.js.map} +0 -0
- /package/dist/{registry-NQALYR77.js.map → registry-DKEXOJVO.js.map} +0 -0
- /package/dist/{revoke-7JOVLZFD.js.map → revoke-R5NIQ74J.js.map} +0 -0
- /package/dist/{signer-M4K5HBLD.js.map → signer-WGDJNWSU.js.map} +0 -0
- /package/dist/{stale-PAGCS4K5.js.map → stale-74WGLVZ2.js.map} +0 -0
package/dist/bundle/index.cjs
CHANGED
|
@@ -1724,7 +1724,7 @@ async function changeSecret(adapter, vault, keyring, newPassphrase, passphraseOp
|
|
|
1724
1724
|
// Tier-2 slots are NOT preserved through `changeSecret` —
|
|
1725
1725
|
// each slot wraps the OLD KEK, so the new keyring has no
|
|
1726
1726
|
// authenticator slots until the user re-enrolls. The higher-level
|
|
1727
|
-
// `db.rotatePassphrase()`
|
|
1727
|
+
// `db.rotatePassphrase()` preserves slots by rewrapping the
|
|
1728
1728
|
// KEK reference, not the KEK itself.
|
|
1729
1729
|
authenticators: [],
|
|
1730
1730
|
...keyring.policy !== void 0 && { policy: keyring.policy }
|
|
@@ -4173,7 +4173,7 @@ var init_builder = __esm({
|
|
|
4173
4173
|
/**
|
|
4174
4174
|
* @internal — clone this Query with a declared-predicate map
|
|
4175
4175
|
* attached. Used by the materialized-view registry to enable
|
|
4176
|
-
* `.wherePredicate(name, ctx?)` for the MV's query callback
|
|
4176
|
+
* `.wherePredicate(name, ctx?)` for the MV's query callback.
|
|
4177
4177
|
* Consumers don't call this directly.
|
|
4178
4178
|
*/
|
|
4179
4179
|
_withPredicates(predicates) {
|
|
@@ -4186,7 +4186,7 @@ var init_builder = __esm({
|
|
|
4186
4186
|
);
|
|
4187
4187
|
}
|
|
4188
4188
|
/**
|
|
4189
|
-
* Filter by a registered deterministic predicate
|
|
4189
|
+
* Filter by a registered deterministic predicate. Requires
|
|
4190
4190
|
* the Query to have been augmented with a predicates map (typically
|
|
4191
4191
|
* via the materialized-view registry — bare Queries constructed
|
|
4192
4192
|
* outside an MV throw on `.wherePredicate()`).
|
|
@@ -5604,7 +5604,7 @@ var init_transaction = __esm({
|
|
|
5604
5604
|
init_errors();
|
|
5605
5605
|
init_ulid();
|
|
5606
5606
|
TxContext = class {
|
|
5607
|
-
/** Stable id for this transaction; shared by all writes it performs
|
|
5607
|
+
/** Stable id for this transaction; shared by all writes it performs. */
|
|
5608
5608
|
txId = generateULID();
|
|
5609
5609
|
/** @internal */
|
|
5610
5610
|
_ops = [];
|
|
@@ -5614,7 +5614,7 @@ var init_transaction = __esm({
|
|
|
5614
5614
|
* restore prior state via `revertExecuted`. Side-effect writes (e.g.
|
|
5615
5615
|
* recursive derivation outputs fired inside `Collection.put`) are
|
|
5616
5616
|
* appended here in execution order so they roll back alongside the
|
|
5617
|
-
* main staged ops
|
|
5617
|
+
* main staged ops.
|
|
5618
5618
|
*/
|
|
5619
5619
|
_executed = [];
|
|
5620
5620
|
/** @internal */
|
|
@@ -5914,7 +5914,7 @@ async function resolveStaleOnRead(accessor, outputCollection, id) {
|
|
|
5914
5914
|
}
|
|
5915
5915
|
if (out.kind === "array") {
|
|
5916
5916
|
console.warn(
|
|
5917
|
-
`[derivation] unexpected array-shape output "${key}" in lazy resolve path; array-shape derivations require lifecycle: "eager"
|
|
5917
|
+
`[derivation] unexpected array-shape output "${key}" in lazy resolve path; array-shape derivations require lifecycle: "eager".`
|
|
5918
5918
|
);
|
|
5919
5919
|
continue;
|
|
5920
5920
|
}
|
|
@@ -6436,81 +6436,6 @@ var init_stale2 = __esm({
|
|
|
6436
6436
|
}
|
|
6437
6437
|
});
|
|
6438
6438
|
|
|
6439
|
-
// src/guards/executor.ts
|
|
6440
|
-
var executor_exports3 = {};
|
|
6441
|
-
__export(executor_exports3, {
|
|
6442
|
-
GuardExecutor: () => GuardExecutor
|
|
6443
|
-
});
|
|
6444
|
-
function deepEqual(a, b) {
|
|
6445
|
-
if (a === b) return true;
|
|
6446
|
-
if (a === null || b === null) return a === b;
|
|
6447
|
-
if (typeof a !== typeof b) return false;
|
|
6448
|
-
if (typeof a !== "object") return a === b;
|
|
6449
|
-
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
6450
|
-
if (Array.isArray(a)) {
|
|
6451
|
-
const aa = a;
|
|
6452
|
-
const bb = b;
|
|
6453
|
-
if (aa.length !== bb.length) return false;
|
|
6454
|
-
for (let i = 0; i < aa.length; i++) if (!deepEqual(aa[i], bb[i])) return false;
|
|
6455
|
-
return true;
|
|
6456
|
-
}
|
|
6457
|
-
const ao = a;
|
|
6458
|
-
const bo = b;
|
|
6459
|
-
const ak = Object.keys(ao);
|
|
6460
|
-
const bk = Object.keys(bo);
|
|
6461
|
-
if (ak.length !== bk.length) return false;
|
|
6462
|
-
for (const k of ak) {
|
|
6463
|
-
if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;
|
|
6464
|
-
if (!deepEqual(ao[k], bo[k])) return false;
|
|
6465
|
-
}
|
|
6466
|
-
return true;
|
|
6467
|
-
}
|
|
6468
|
-
var GuardExecutor;
|
|
6469
|
-
var init_executor3 = __esm({
|
|
6470
|
-
"src/guards/executor.ts"() {
|
|
6471
|
-
"use strict";
|
|
6472
|
-
init_errors();
|
|
6473
|
-
GuardExecutor = {
|
|
6474
|
-
/**
|
|
6475
|
-
* Compare existing vs incoming for each `frozenFields.fields` entry
|
|
6476
|
-
* when `frozenFields.when(existing)` is true. Throws
|
|
6477
|
-
* `FieldFrozenError` listing every changed frozen field.
|
|
6478
|
-
*/
|
|
6479
|
-
async checkFrozenFields(guard, id, existing, incoming) {
|
|
6480
|
-
const ff = guard.frozenFields;
|
|
6481
|
-
if (!ff) return;
|
|
6482
|
-
if (existing === null) return;
|
|
6483
|
-
if (!ff.when(existing)) return;
|
|
6484
|
-
const changed = [];
|
|
6485
|
-
for (const f of ff.fields) {
|
|
6486
|
-
if (existing[f] !== incoming[f]) {
|
|
6487
|
-
if (!deepEqual(existing[f], incoming[f])) changed.push(String(f));
|
|
6488
|
-
}
|
|
6489
|
-
}
|
|
6490
|
-
if (changed.length > 0) {
|
|
6491
|
-
throw new FieldFrozenError(guard.collection, id, changed);
|
|
6492
|
-
}
|
|
6493
|
-
},
|
|
6494
|
-
/**
|
|
6495
|
-
* Run a single guard's invariant over its slice of the change-set.
|
|
6496
|
-
* Any throw is converted to `InvariantError` unless it already is one.
|
|
6497
|
-
*/
|
|
6498
|
-
async runInvariant(guard, changes, ctx) {
|
|
6499
|
-
const amendment = guard.amendment;
|
|
6500
|
-
if (!amendment) return;
|
|
6501
|
-
try {
|
|
6502
|
-
await amendment.invariant(changes, ctx);
|
|
6503
|
-
} catch (err) {
|
|
6504
|
-
if (err instanceof InvariantError) throw err;
|
|
6505
|
-
throw new InvariantError(
|
|
6506
|
-
err instanceof Error ? err.message : `invariant violated: ${String(err)}`
|
|
6507
|
-
);
|
|
6508
|
-
}
|
|
6509
|
-
}
|
|
6510
|
-
};
|
|
6511
|
-
}
|
|
6512
|
-
});
|
|
6513
|
-
|
|
6514
6439
|
// src/derivations/fanout-sidecar.ts
|
|
6515
6440
|
var fanout_sidecar_exports = {};
|
|
6516
6441
|
__export(fanout_sidecar_exports, {
|
|
@@ -6648,6 +6573,7 @@ var init_collection = __esm({
|
|
|
6648
6573
|
schemaUpdateGate;
|
|
6649
6574
|
schemaFence;
|
|
6650
6575
|
writeHooks;
|
|
6576
|
+
subsystemBus;
|
|
6651
6577
|
activeTxId;
|
|
6652
6578
|
getDEK;
|
|
6653
6579
|
onDirty;
|
|
@@ -6836,42 +6762,14 @@ var init_collection = __esm({
|
|
|
6836
6762
|
syncAdapter;
|
|
6837
6763
|
/** — consent-audit hook, no-op when no scope is active. */
|
|
6838
6764
|
onAccess;
|
|
6839
|
-
/**
|
|
6840
|
-
* accounting-period write guard. Called BEFORE any
|
|
6841
|
-
* adapter write with:
|
|
6842
|
-
* - `existing` — the prior envelope's `_ts` and decrypted record
|
|
6843
|
-
* (or `null` if no prior envelope exists)
|
|
6844
|
-
* - `incoming` — the record being written (or `null` for delete)
|
|
6845
|
-
*
|
|
6846
|
-
* Throws `PeriodClosedError` if either side falls inside a closed
|
|
6847
|
-
* period. Installed by Vault; no-op when no period has been closed.
|
|
6848
|
-
* Async so the Vault can lazy-load the period list from the
|
|
6849
|
-
* adapter on first use.
|
|
6850
|
-
*/
|
|
6851
|
-
periodGuard;
|
|
6852
|
-
/**
|
|
6853
|
-
* Optional back-reference to the owning vault's guard registry + a
|
|
6854
|
-
* read-only vault facade. When present, `Collection.put` and
|
|
6855
|
-
* `Collection.delete` consult the registry for guards declared
|
|
6856
|
-
* against this collection and run their `check` + `frozenFields`
|
|
6857
|
-
* before the adapter write. Absent in unit tests that construct
|
|
6858
|
-
* a Collection directly; production code always sets it via
|
|
6859
|
-
* `Vault.collection()`.
|
|
6860
|
-
*
|
|
6861
|
-
* Typed structurally rather than as `Vault` to avoid a circular
|
|
6862
|
-
* import (mirrors the `refEnforcer` / `joinResolver` pattern).
|
|
6863
|
-
*/
|
|
6864
|
-
guardSource;
|
|
6865
6765
|
/**
|
|
6866
6766
|
* Vault-internal hook for derivation dispatch. When set,
|
|
6867
6767
|
* `Collection.put` consults the registry after the source-write
|
|
6868
6768
|
* commits and writes derived outputs through `getCollection(name).put`.
|
|
6869
|
-
* Same structural-interface pattern as `guardSource` to avoid a
|
|
6870
|
-
* circular Vault import.
|
|
6871
6769
|
*/
|
|
6872
6770
|
derivationSource;
|
|
6873
6771
|
/**
|
|
6874
|
-
* Vault-internal hook for materialized-view dispatch
|
|
6772
|
+
* Vault-internal hook for materialized-view dispatch.
|
|
6875
6773
|
* Parallel to `derivationSource` — when set, `Collection.put` fires
|
|
6876
6774
|
* `MaterializedViewRegistry.onSourceWrite` after the source-write
|
|
6877
6775
|
* commits + after `dispatchDerivations` has run.
|
|
@@ -6927,6 +6825,7 @@ var init_collection = __esm({
|
|
|
6927
6825
|
this.schemaUpdateGate = opts.schemaUpdateGate;
|
|
6928
6826
|
this.schemaFence = opts.schemaFence;
|
|
6929
6827
|
this.writeHooks = opts.writeHooks;
|
|
6828
|
+
this.subsystemBus = opts.subsystemBus;
|
|
6930
6829
|
this.activeTxId = opts.activeTxId;
|
|
6931
6830
|
this.blobStrategy = opts.blobStrategy ?? NO_BLOBS;
|
|
6932
6831
|
this.aggregateStrategy = opts.aggregateStrategy ?? NO_AGGREGATE;
|
|
@@ -6951,8 +6850,6 @@ var init_collection = __esm({
|
|
|
6951
6850
|
this.crdtMode = opts.crdt;
|
|
6952
6851
|
this.syncAdapter = opts.syncAdapter;
|
|
6953
6852
|
this.onAccess = opts.onAccess;
|
|
6954
|
-
this.periodGuard = opts.periodGuard;
|
|
6955
|
-
this.guardSource = opts.guardSource;
|
|
6956
6853
|
this.derivationSource = opts.derivationSource;
|
|
6957
6854
|
this.materializedViewSource = opts.materializedViewSource;
|
|
6958
6855
|
this.tiers = opts.tiers && opts.tiers.length > 0 ? new Set(opts.tiers) : null;
|
|
@@ -7154,21 +7051,23 @@ var init_collection = __esm({
|
|
|
7154
7051
|
}
|
|
7155
7052
|
/**
|
|
7156
7053
|
* Create or update a record. Runs inside the hub's write-queue tracker
|
|
7157
|
-
*
|
|
7054
|
+
* so `hub.writeQueue.pending` reflects this write.
|
|
7158
7055
|
*
|
|
7159
7056
|
* @param id Record identifier.
|
|
7160
7057
|
* @param record The record body (validated by the collection's schema
|
|
7161
7058
|
* if one was attached at `vault.collection(...)` time).
|
|
7162
7059
|
* @param options Optional metadata for audit + import workflows.
|
|
7163
7060
|
* `reason` is stamped onto the resulting ledger entry
|
|
7164
|
-
*
|
|
7061
|
+
* so audit consumers can filter via
|
|
7165
7062
|
* `entries.filter(e => e.reason?.startsWith('import:'))`.
|
|
7166
7063
|
*/
|
|
7167
7064
|
async put(id, record, options) {
|
|
7168
7065
|
await this.schemaUpdateGate?.assertWritable();
|
|
7169
7066
|
await this.schemaFence?.assertWritable(this.name);
|
|
7067
|
+
const hooksActive = this.#hooksActive();
|
|
7068
|
+
const busAfterPut = (this.subsystemBus?.hasHandlers("afterPut") ?? false) && !(this.subsystemBus?.dispatching ?? false);
|
|
7170
7069
|
let event;
|
|
7171
|
-
if (
|
|
7070
|
+
if (hooksActive || busAfterPut) {
|
|
7172
7071
|
const prior = await this.#priorForHook(id);
|
|
7173
7072
|
event = {
|
|
7174
7073
|
op: prior.record === null ? "create" : "update",
|
|
@@ -7183,23 +7082,26 @@ var init_collection = __esm({
|
|
|
7183
7082
|
baseVersion: prior.version,
|
|
7184
7083
|
version: prior.version + 1
|
|
7185
7084
|
};
|
|
7186
|
-
await this.writeHooks.runBefore(event);
|
|
7085
|
+
if (hooksActive) await this.writeHooks.runBefore(event);
|
|
7187
7086
|
}
|
|
7188
7087
|
if (this.writeQueue) await this.writeQueue.track(() => this.putInternal(id, record, options));
|
|
7189
7088
|
else await this.putInternal(id, record, options);
|
|
7190
|
-
if (event)
|
|
7089
|
+
if (event) {
|
|
7090
|
+
if (hooksActive) await this.writeHooks.runAfter(event);
|
|
7091
|
+
if (busAfterPut) await this.subsystemBus.dispatch("afterPut", event);
|
|
7092
|
+
}
|
|
7191
7093
|
}
|
|
7192
|
-
/** @internal
|
|
7094
|
+
/** @internal — true when hooks should fire for this write (handlers exist, not re-entrant). */
|
|
7193
7095
|
#hooksActive() {
|
|
7194
7096
|
return this.writeHooks !== void 0 && this.writeHooks.hasHandlers && !this.writeHooks.suppressed;
|
|
7195
7097
|
}
|
|
7196
7098
|
/**
|
|
7197
|
-
* @internal
|
|
7099
|
+
* @internal — resolve the prior record for a hook's `before` and
|
|
7198
7100
|
* its version. Critically, this uses the SAME basis `putInternal` writes from
|
|
7199
7101
|
* (the in-memory cache in eager mode; lru-then-adapter in lazy) — NOT a fresh
|
|
7200
7102
|
* store read — so `baseVersion`/`version` match the version actually written.
|
|
7201
7103
|
* A separate store read would diverge once another tab has advanced the shared
|
|
7202
|
-
* store past this tab's cache, breaking
|
|
7104
|
+
* store past this tab's cache, breaking cross-tab conflict detection.
|
|
7203
7105
|
*/
|
|
7204
7106
|
async #priorForHook(id) {
|
|
7205
7107
|
if (this.lazy && this.lru) {
|
|
@@ -7221,52 +7123,28 @@ var init_collection = __esm({
|
|
|
7221
7123
|
if (!hasWritePermission(this.keyring, this.name)) {
|
|
7222
7124
|
throw new ReadOnlyError();
|
|
7223
7125
|
}
|
|
7224
|
-
if (this.
|
|
7225
|
-
const registry = this.guardSource.registry();
|
|
7226
|
-
const guards = registry.guardsFor(this.name);
|
|
7227
|
-
if (guards.length > 0) {
|
|
7228
|
-
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
7229
|
-
let existingRecord = null;
|
|
7230
|
-
if (existingEnv) {
|
|
7231
|
-
try {
|
|
7232
|
-
existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
|
|
7233
|
-
} catch {
|
|
7234
|
-
existingRecord = null;
|
|
7235
|
-
}
|
|
7236
|
-
}
|
|
7237
|
-
const incomingRecord = record;
|
|
7238
|
-
const ctx = {
|
|
7239
|
-
existing: existingRecord,
|
|
7240
|
-
vault: this.guardSource.readOnlyVault(),
|
|
7241
|
-
userId: this.keyring.userId,
|
|
7242
|
-
role: this.keyring.role
|
|
7243
|
-
};
|
|
7244
|
-
if (registry.isAmendmentActive()) {
|
|
7245
|
-
const vBefore = existingEnv?._v ?? 0;
|
|
7246
|
-
registry.collectChange(this.name, id, existingRecord, incomingRecord, vBefore, vBefore + 1);
|
|
7247
|
-
} else {
|
|
7248
|
-
await registry.runChecks(this.name, incomingRecord, ctx);
|
|
7249
|
-
const { GuardExecutor: GuardExecutor2 } = await Promise.resolve().then(() => (init_executor3(), executor_exports3));
|
|
7250
|
-
for (const g of guards) {
|
|
7251
|
-
await GuardExecutor2.checkFrozenFields(g, id, existingRecord, incomingRecord);
|
|
7252
|
-
}
|
|
7253
|
-
}
|
|
7254
|
-
}
|
|
7255
|
-
}
|
|
7256
|
-
if (this.periodGuard !== void 0) {
|
|
7126
|
+
if (this.subsystemBus?.hasGateHandlers("beforePut")) {
|
|
7257
7127
|
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
7258
|
-
let
|
|
7128
|
+
let existingRecord = null;
|
|
7259
7129
|
if (existingEnv) {
|
|
7260
7130
|
try {
|
|
7261
|
-
|
|
7131
|
+
existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
|
|
7262
7132
|
} catch {
|
|
7263
|
-
|
|
7133
|
+
existingRecord = null;
|
|
7264
7134
|
}
|
|
7265
7135
|
}
|
|
7266
|
-
await this.
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7136
|
+
await this.subsystemBus.dispatchGate("beforePut", {
|
|
7137
|
+
op: existingEnv ? "update" : "create",
|
|
7138
|
+
vault: this.vault,
|
|
7139
|
+
collection: this.name,
|
|
7140
|
+
docId: id,
|
|
7141
|
+
incoming: record,
|
|
7142
|
+
existing: existingRecord,
|
|
7143
|
+
existingVersion: existingEnv?._v ?? 0,
|
|
7144
|
+
existingTs: existingEnv?._ts,
|
|
7145
|
+
userId: this.keyring.userId,
|
|
7146
|
+
role: this.keyring.role
|
|
7147
|
+
});
|
|
7270
7148
|
}
|
|
7271
7149
|
if (this.schema !== void 0) {
|
|
7272
7150
|
record = await validateSchemaInput(this.schema, record, `put(${id})`);
|
|
@@ -7452,7 +7330,7 @@ var init_collection = __esm({
|
|
|
7452
7330
|
* Fire registered MV strategies whose dependency set includes this
|
|
7453
7331
|
* collection. Eager-mode MVs re-materialize inline via
|
|
7454
7332
|
* `MaterializedViewExecutor.refresh`; lazy / manual modes are
|
|
7455
|
-
* no-ops in the foundation
|
|
7333
|
+
* no-ops in the foundation; wired in the lazy-mode implementation.
|
|
7456
7334
|
*
|
|
7457
7335
|
* Skips entirely when the record being written is itself an
|
|
7458
7336
|
* MV-emitted row (carries `_materializedFrom`) — defensive guard
|
|
@@ -7596,13 +7474,15 @@ var init_collection = __esm({
|
|
|
7596
7474
|
}
|
|
7597
7475
|
/**
|
|
7598
7476
|
* Delete a record by ID. Runs inside the hub's write-queue tracker
|
|
7599
|
-
*
|
|
7477
|
+
* so `hub.writeQueue.pending` reflects this write.
|
|
7600
7478
|
*/
|
|
7601
7479
|
async delete(id) {
|
|
7602
7480
|
await this.schemaUpdateGate?.assertWritable();
|
|
7603
7481
|
await this.schemaFence?.assertWritable(this.name);
|
|
7482
|
+
const hooksActive = this.#hooksActive();
|
|
7483
|
+
const busAfterDelete = (this.subsystemBus?.hasHandlers("afterDelete") ?? false) && !(this.subsystemBus?.dispatching ?? false);
|
|
7604
7484
|
let event;
|
|
7605
|
-
if (
|
|
7485
|
+
if (hooksActive || busAfterDelete) {
|
|
7606
7486
|
const prior = await this.#priorForHook(id);
|
|
7607
7487
|
event = {
|
|
7608
7488
|
op: "delete",
|
|
@@ -7617,14 +7497,17 @@ var init_collection = __esm({
|
|
|
7617
7497
|
baseVersion: prior.version,
|
|
7618
7498
|
version: prior.version + 1
|
|
7619
7499
|
};
|
|
7620
|
-
await this.writeHooks.runBefore(event);
|
|
7500
|
+
if (hooksActive) await this.writeHooks.runBefore(event);
|
|
7621
7501
|
}
|
|
7622
7502
|
if (this.writeQueue) await this.writeQueue.track(() => this.deleteInternal(id));
|
|
7623
7503
|
else await this.deleteInternal(id);
|
|
7624
|
-
if (event)
|
|
7504
|
+
if (event) {
|
|
7505
|
+
if (hooksActive) await this.writeHooks.runAfter(event);
|
|
7506
|
+
if (busAfterDelete) await this.subsystemBus.dispatch("afterDelete", event);
|
|
7507
|
+
}
|
|
7625
7508
|
}
|
|
7626
7509
|
/**
|
|
7627
|
-
* @internal
|
|
7510
|
+
* @internal — bulk-rewrite every record through a cutover transform.
|
|
7628
7511
|
* Raw adapter path (bypasses the write gate + guards — the transform is
|
|
7629
7512
|
* trusted and runs only during the `migrating` phase). Bumps each
|
|
7630
7513
|
* record's `_v` and appends a ledger `op:'migration'` entry.
|
|
@@ -7663,8 +7546,7 @@ var init_collection = __esm({
|
|
|
7663
7546
|
}
|
|
7664
7547
|
/**
|
|
7665
7548
|
* @internal — system-internal delete that bypasses user-facing
|
|
7666
|
-
* delete hooks (`onDelete`,
|
|
7667
|
-
* enforcer). Used by derivation tombstones (#144) and MV refresh
|
|
7549
|
+
* delete hooks (`onDelete`, FK ref enforcer). Used by derivation tombstones and MV refresh
|
|
7668
7550
|
* (Dim 14 v2) — system housekeeping shouldn't trip user invariants
|
|
7669
7551
|
* registered against the output collection. The ledger entry and
|
|
7670
7552
|
* history snapshot still fire so backup integrity and time-travel
|
|
@@ -7676,7 +7558,7 @@ var init_collection = __esm({
|
|
|
7676
7558
|
*
|
|
7677
7559
|
* When a `txCtx` is supplied, the prior envelope is captured and
|
|
7678
7560
|
* pushed onto `txCtx._executed` BEFORE the delete fires — mirrors
|
|
7679
|
-
* the
|
|
7561
|
+
* the rollback hardening for puts. Callers outside a
|
|
7680
7562
|
* multi-record transaction pass `null` and skip the tracking.
|
|
7681
7563
|
*
|
|
7682
7564
|
* Amendment composition: if `_internalDelete` runs while a vault's
|
|
@@ -7719,58 +7601,27 @@ var init_collection = __esm({
|
|
|
7719
7601
|
if (!hasWritePermission(this.keyring, this.name)) {
|
|
7720
7602
|
throw new ReadOnlyError();
|
|
7721
7603
|
}
|
|
7722
|
-
if (this.
|
|
7723
|
-
const registry = this.guardSource.registry();
|
|
7724
|
-
const guards = registry.guardsFor(this.name);
|
|
7725
|
-
if (guards.length > 0) {
|
|
7726
|
-
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
7727
|
-
if (existingEnv) {
|
|
7728
|
-
let existingRecord = null;
|
|
7729
|
-
try {
|
|
7730
|
-
existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
|
|
7731
|
-
} catch {
|
|
7732
|
-
existingRecord = null;
|
|
7733
|
-
}
|
|
7734
|
-
if (registry.isAmendmentActive()) {
|
|
7735
|
-
const vBefore = existingEnv._v;
|
|
7736
|
-
registry.collectChange(
|
|
7737
|
-
this.name,
|
|
7738
|
-
id,
|
|
7739
|
-
existingRecord,
|
|
7740
|
-
null,
|
|
7741
|
-
vBefore,
|
|
7742
|
-
vBefore
|
|
7743
|
-
);
|
|
7744
|
-
} else if (!internal) {
|
|
7745
|
-
const ctx = {
|
|
7746
|
-
existing: existingRecord,
|
|
7747
|
-
vault: this.guardSource.readOnlyVault(),
|
|
7748
|
-
userId: this.keyring.userId,
|
|
7749
|
-
role: this.keyring.role
|
|
7750
|
-
};
|
|
7751
|
-
await registry.runOnDelete(
|
|
7752
|
-
this.name,
|
|
7753
|
-
existingRecord ?? {},
|
|
7754
|
-
ctx
|
|
7755
|
-
);
|
|
7756
|
-
}
|
|
7757
|
-
}
|
|
7758
|
-
}
|
|
7759
|
-
}
|
|
7760
|
-
if (!internal && this.periodGuard !== void 0) {
|
|
7604
|
+
if (this.subsystemBus?.hasGateHandlers("beforeDelete")) {
|
|
7761
7605
|
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
7762
|
-
let priorRecord = null;
|
|
7763
7606
|
if (existingEnv) {
|
|
7607
|
+
let existingRecord = null;
|
|
7764
7608
|
try {
|
|
7765
|
-
|
|
7609
|
+
existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
|
|
7766
7610
|
} catch {
|
|
7767
|
-
|
|
7611
|
+
existingRecord = null;
|
|
7768
7612
|
}
|
|
7613
|
+
await this.subsystemBus.dispatchGate("beforeDelete", {
|
|
7614
|
+
vault: this.vault,
|
|
7615
|
+
collection: this.name,
|
|
7616
|
+
docId: id,
|
|
7617
|
+
existing: existingRecord,
|
|
7618
|
+
existingVersion: existingEnv._v,
|
|
7619
|
+
existingTs: existingEnv._ts,
|
|
7620
|
+
internal,
|
|
7621
|
+
userId: this.keyring.userId,
|
|
7622
|
+
role: this.keyring.role
|
|
7623
|
+
});
|
|
7769
7624
|
}
|
|
7770
|
-
await this.periodGuard(
|
|
7771
|
-
existingEnv ? { ts: existingEnv._ts, record: priorRecord } : null,
|
|
7772
|
-
null
|
|
7773
|
-
);
|
|
7774
7625
|
}
|
|
7775
7626
|
if (!internal && this.refEnforcer !== void 0) {
|
|
7776
7627
|
await this.refEnforcer.enforceRefsOnDelete(this.name, id);
|
|
@@ -7831,7 +7682,7 @@ var init_collection = __esm({
|
|
|
7831
7682
|
}
|
|
7832
7683
|
/**
|
|
7833
7684
|
* Cascade deletes of array-shape derived rows when a source row is
|
|
7834
|
-
* deleted
|
|
7685
|
+
* deleted. Reads each registered strategy's fanout sidecar
|
|
7835
7686
|
* for this source id, deletes every listed derived row, then
|
|
7836
7687
|
* deletes the sidecar itself.
|
|
7837
7688
|
*
|
|
@@ -7870,8 +7721,8 @@ var init_collection = __esm({
|
|
|
7870
7721
|
}
|
|
7871
7722
|
}
|
|
7872
7723
|
/**
|
|
7873
|
-
* Mirror of {@link dispatchMaterializedViews} for the delete path
|
|
7874
|
-
*
|
|
7724
|
+
* Mirror of {@link dispatchMaterializedViews} for the delete path.
|
|
7725
|
+
* No record content is available (it's gone), so the
|
|
7875
7726
|
* `_materializedFrom` skip used by the put-side dispatch doesn't
|
|
7876
7727
|
* apply here — instead, the recursion guard is the `internal` gate
|
|
7877
7728
|
* at the `_doDelete` call site above.
|
|
@@ -8401,7 +8252,7 @@ var init_collection = __esm({
|
|
|
8401
8252
|
* .aggregate({ total: sum('amount'), n: count() })
|
|
8402
8253
|
* ```
|
|
8403
8254
|
*
|
|
8404
|
-
* **Lazy-MV gap
|
|
8255
|
+
* **Lazy-MV gap:** `scan()` is synchronous-build and does
|
|
8405
8256
|
* NOT trigger lazy materialized-view resolve-on-read. For lazy
|
|
8406
8257
|
* MVs, call `list()` (which DOES resolve) or `vault.refreshView(name)`
|
|
8407
8258
|
* before scanning. Same shape as the `query()` limitation.
|
|
@@ -8482,7 +8333,7 @@ var init_collection = __esm({
|
|
|
8482
8333
|
this.indexes?.upsert(id, record, previous ? previous.record : null);
|
|
8483
8334
|
}
|
|
8484
8335
|
/**
|
|
8485
|
-
*
|
|
8336
|
+
* Apply a peer tab's committed write to THIS tab's in-memory view:
|
|
8486
8337
|
* re-read the (already-persisted) envelope from the shared store + refresh
|
|
8487
8338
|
* cache/indexes, then emit a `change` event so reactive consumers re-render.
|
|
8488
8339
|
* Never writes to the store and never fires write hooks, so it cannot loop.
|
|
@@ -8491,7 +8342,7 @@ var init_collection = __esm({
|
|
|
8491
8342
|
await this._invalidateCacheEntry(id);
|
|
8492
8343
|
this.emitter.emit("change", { vault: this.vault, collection: this.name, id, action });
|
|
8493
8344
|
}
|
|
8494
|
-
/** @internal
|
|
8345
|
+
/** @internal — the current in-memory record without a store read (for conflict capture). */
|
|
8495
8346
|
_peekCached(id) {
|
|
8496
8347
|
const entry = this.lazy && this.lru ? this.lru.get(id) : this.cache.get(id);
|
|
8497
8348
|
return entry ? entry.record : null;
|
|
@@ -9511,7 +9362,7 @@ var init_virtual_collection = __esm({
|
|
|
9511
9362
|
// error pointing at the relevant issue — so consumers don't hit a
|
|
9512
9363
|
// cryptic `undefined is not a function` runtime crash.
|
|
9513
9364
|
//
|
|
9514
|
-
//
|
|
9365
|
+
// Throw-stubs so consumers get actionable errors rather than cryptic crashes.
|
|
9515
9366
|
/** @throws — chainable Query<T> over a virtual collection is deferred. */
|
|
9516
9367
|
query() {
|
|
9517
9368
|
throw new Error(
|
|
@@ -10087,7 +9938,7 @@ var init_api = __esm({
|
|
|
10087
9938
|
* the envelope on first call. Optimistic-concurrency safe — a stale
|
|
10088
9939
|
* `_v` (parallel writer on another device) throws `ConflictError`.
|
|
10089
9940
|
*
|
|
10090
|
-
* Patch semantics
|
|
9941
|
+
* Patch semantics:
|
|
10091
9942
|
* - `undefined` (or omitted key) — skip; existing value preserved
|
|
10092
9943
|
* - `null` — delete the field from the merged result
|
|
10093
9944
|
* - any other value — overwrite (deep-merge for plain objects,
|
|
@@ -10143,7 +9994,7 @@ var init_api = __esm({
|
|
|
10143
9994
|
this.fireChange(this.writerKeyringId, written);
|
|
10144
9995
|
return written;
|
|
10145
9996
|
}
|
|
10146
|
-
// ─── Visibility
|
|
9997
|
+
// ─── Visibility ──────────────────────────────────────────────────────
|
|
10147
9998
|
/**
|
|
10148
9999
|
* Read the current user's visibility flag from
|
|
10149
10000
|
* `_meta/visibility/<keyringId>`. Returns `{ hidden: false }` when no
|
|
@@ -11204,7 +11055,7 @@ var init_registry2 = __esm({
|
|
|
11204
11055
|
guardsFor(collection) {
|
|
11205
11056
|
return this._byCollection.get(collection) ?? [];
|
|
11206
11057
|
}
|
|
11207
|
-
/** Per-collection guard counts, for introspection
|
|
11058
|
+
/** Per-collection guard counts, for introspection. */
|
|
11208
11059
|
summary() {
|
|
11209
11060
|
return [...this._byCollection.entries()].map(([collection, guards]) => ({
|
|
11210
11061
|
collection,
|
|
@@ -11662,23 +11513,23 @@ var init_vault = __esm({
|
|
|
11662
11513
|
* `null` for vaults that never register any guard strategy. The
|
|
11663
11514
|
* runtime class is dynamic-imported on demand so consumers that
|
|
11664
11515
|
* never use guards don't pull `GuardRegistry`/`GuardExecutor` into
|
|
11665
|
-
* their bundle
|
|
11516
|
+
* their bundle.
|
|
11666
11517
|
*/
|
|
11667
11518
|
guardRegistry = null;
|
|
11668
11519
|
/**
|
|
11669
11520
|
* Per-vault derivation registry. Same lazy-load contract as
|
|
11670
11521
|
* `guardRegistry` — `null` until `_initDerivations()` runs with at
|
|
11671
|
-
* least one strategy handle.
|
|
11522
|
+
* least one strategy handle.
|
|
11672
11523
|
*/
|
|
11673
11524
|
derivationRegistry = null;
|
|
11674
11525
|
/**
|
|
11675
|
-
* Per-vault materialized-view registry
|
|
11526
|
+
* Per-vault materialized-view registry. Same lazy-load
|
|
11676
11527
|
* contract as `derivationRegistry` — `null` until
|
|
11677
11528
|
* `_initMaterializedViews()` runs with at least one MV handle.
|
|
11678
11529
|
*/
|
|
11679
11530
|
materializedViewRegistry = null;
|
|
11680
11531
|
/**
|
|
11681
|
-
* Per-vault overlay registry
|
|
11532
|
+
* Per-vault overlay registry. Same lazy-load contract as
|
|
11682
11533
|
* `materializedViewRegistry` — `null` until `_initOverlayedViews()`
|
|
11683
11534
|
* runs with at least one handle.
|
|
11684
11535
|
*/
|
|
@@ -11699,7 +11550,7 @@ var init_vault = __esm({
|
|
|
11699
11550
|
* target this vault session's keyringId. There is no method to write
|
|
11700
11551
|
* another principal's envelope (own-only write rule, structural).
|
|
11701
11552
|
* - Read-anyone: `get(keyringId)`, `list()` — read other principals'
|
|
11702
|
-
* envelopes, subject to the `view-team-profiles` policy gate
|
|
11553
|
+
* envelopes, subject to the `view-team-profiles` policy gate.
|
|
11703
11554
|
* - Reactive: `subscribe(id, cb)`, `live(id)` — fire on local writes.
|
|
11704
11555
|
*
|
|
11705
11556
|
* @see docs/superpowers/specs/2026-05-05-user-envelope-design.md
|
|
@@ -11719,12 +11570,12 @@ var init_vault = __esm({
|
|
|
11719
11570
|
*/
|
|
11720
11571
|
reloadKeyring;
|
|
11721
11572
|
collectionCache = /* @__PURE__ */ new Map();
|
|
11722
|
-
/**
|
|
11573
|
+
/** Vault-level schema cutover fence/controller. */
|
|
11723
11574
|
schemaFence;
|
|
11724
|
-
/**
|
|
11575
|
+
/** Per-client heartbeat/watcher; started lazily on cutover registration. */
|
|
11725
11576
|
#fenceWatcher;
|
|
11726
11577
|
#fenceCoordinationStarted = false;
|
|
11727
|
-
/**
|
|
11578
|
+
/** Per-collection registered schema-update strategy names. */
|
|
11728
11579
|
#schemaUpdateNames = /* @__PURE__ */ new Map();
|
|
11729
11580
|
/**
|
|
11730
11581
|
* per-collection `blobFields` retention/TTL config.
|
|
@@ -11798,8 +11649,7 @@ var init_vault = __esm({
|
|
|
11798
11649
|
* Cache of closed/opened accounting periods.
|
|
11799
11650
|
* Populated on first `closePeriod` / `openPeriod` / `listPeriods` /
|
|
11800
11651
|
* per-collection write call. Kept in memory as an ordered list (by
|
|
11801
|
-
* `closedAt`) so
|
|
11802
|
-
* each collection's put/delete path.
|
|
11652
|
+
* `closedAt`) so period checks run fast when the gate bus fires.
|
|
11803
11653
|
*
|
|
11804
11654
|
* Sentinel `null` means "not yet loaded" — the first consumer
|
|
11805
11655
|
* triggers a one-time `loadPeriods()` pass. Every subsequent
|
|
@@ -11994,6 +11844,7 @@ var init_vault = __esm({
|
|
|
11994
11844
|
emitter: this.emitter,
|
|
11995
11845
|
writeQueue: this.noydb._writeQueueTracker,
|
|
11996
11846
|
writeHooks: this.noydb._writeHooks,
|
|
11847
|
+
subsystemBus: this.noydb._subsystemBus,
|
|
11997
11848
|
activeTxId: () => this.noydb._activeTxContextOrNull?.txId ?? null,
|
|
11998
11849
|
schemaUpdateGate,
|
|
11999
11850
|
schemaFence: this.schemaFence,
|
|
@@ -12016,18 +11867,12 @@ var init_vault = __esm({
|
|
|
12016
11867
|
defaultLocale: this.locale,
|
|
12017
11868
|
onRegisterConflictResolver: this.onRegisterConflictResolver,
|
|
12018
11869
|
onAccess: (op, id) => this._logConsent(op, collectionName, id),
|
|
12019
|
-
|
|
12020
|
-
// Guard
|
|
12021
|
-
//
|
|
12022
|
-
//
|
|
12023
|
-
// `if (this.
|
|
12024
|
-
//
|
|
12025
|
-
...this.guardRegistry !== null ? {
|
|
12026
|
-
guardSource: {
|
|
12027
|
-
registry: () => this.guardRegistry,
|
|
12028
|
-
readOnlyVault: () => this._ensureReadOnlyFacade()
|
|
12029
|
-
}
|
|
12030
|
-
} : {},
|
|
11870
|
+
// Derivation source is only wired when the corresponding registry
|
|
11871
|
+
// has been initialised. Guard source was removed in Track A slice 3b
|
|
11872
|
+
// — guards now run via the gate bus in Noydb.#registerGuardGate.
|
|
11873
|
+
// Vaults without derivations skip this so `Collection.put`'s
|
|
11874
|
+
// `if (this.derivationSource)` branch no-ops without touching the
|
|
11875
|
+
// derivation subsystem code.
|
|
12031
11876
|
...this.derivationRegistry !== null ? {
|
|
12032
11877
|
derivationSource: {
|
|
12033
11878
|
registry: () => this.derivationRegistry,
|
|
@@ -12117,7 +11962,7 @@ var init_vault = __esm({
|
|
|
12117
11962
|
await Promise.allSettled(pending);
|
|
12118
11963
|
}
|
|
12119
11964
|
/**
|
|
12120
|
-
* Run a coordinated schema cutover
|
|
11965
|
+
* Run a coordinated schema cutover. Drains pending writes, waits
|
|
12121
11966
|
* for the active client set to quiesce (the ack-barrier), applies every
|
|
12122
11967
|
* pending collection transform in bulk, bumps the vault schema generation,
|
|
12123
11968
|
* and clears the fence. Returns the count of collections migrated.
|
|
@@ -12135,9 +11980,9 @@ var init_vault = __esm({
|
|
|
12135
11980
|
await coll._applyCutoverTransform(transform);
|
|
12136
11981
|
}
|
|
12137
11982
|
/**
|
|
12138
|
-
*
|
|
11983
|
+
* Refresh a loaded collection's view of one document from a peer
|
|
12139
11984
|
* tab's broadcast. No-op when the collection isn't loaded in this tab
|
|
12140
|
-
* (it will read fresh on next open). Mirrors
|
|
11985
|
+
* (it will read fresh on next open). Mirrors `#runCutoverTransform`'s guard.
|
|
12141
11986
|
*/
|
|
12142
11987
|
async _applyRemoteWrite(collectionName, docId, action) {
|
|
12143
11988
|
const coll = this.collectionCache.get(collectionName);
|
|
@@ -12145,9 +11990,9 @@ var init_vault = __esm({
|
|
|
12145
11990
|
await coll._applyRemoteChange(docId, action);
|
|
12146
11991
|
}
|
|
12147
11992
|
/**
|
|
12148
|
-
*
|
|
11993
|
+
* For a detected conflict: capture this tab's clobbered record,
|
|
12149
11994
|
* read the common ancestor from history, converge the cache to the store's
|
|
12150
|
-
* authoritative value (the
|
|
11995
|
+
* authoritative value (the re-read), and return all three for the
|
|
12151
11996
|
* WriteConflict payload. Returns null when the collection isn't loaded.
|
|
12152
11997
|
*/
|
|
12153
11998
|
async _captureAndConverge(collectionName, docId, action, baseV) {
|
|
@@ -12164,15 +12009,15 @@ var init_vault = __esm({
|
|
|
12164
12009
|
const remote = await coll.get(docId);
|
|
12165
12010
|
return { local, remote, base };
|
|
12166
12011
|
}
|
|
12167
|
-
/** Recover a stuck cutover fence
|
|
12012
|
+
/** Recover a stuck cutover fence — reset to normal without bumping. */
|
|
12168
12013
|
async abortSchemaCutover() {
|
|
12169
12014
|
await this.schemaFence.abort();
|
|
12170
12015
|
}
|
|
12171
|
-
/** Current schema-cutover fence state for this vault
|
|
12016
|
+
/** Current schema-cutover fence state for this vault. Thin live read. */
|
|
12172
12017
|
async schemaFenceState() {
|
|
12173
12018
|
return loadFence(this.adapter, this.name);
|
|
12174
12019
|
}
|
|
12175
|
-
/** @internal Start the per-client heartbeat + fence watcher once a cutover is registered
|
|
12020
|
+
/** @internal Start the per-client heartbeat + fence watcher once a cutover is registered. */
|
|
12176
12021
|
_ensureFenceCoordination() {
|
|
12177
12022
|
if (this.#fenceCoordinationStarted) return;
|
|
12178
12023
|
this.#fenceCoordinationStarted = true;
|
|
@@ -12845,7 +12690,7 @@ var init_vault = __esm({
|
|
|
12845
12690
|
* Dynamic-imports `GuardRegistry` + `ReadOnlyVaultFacade` and seeds
|
|
12846
12691
|
* the registry with the supplied strategy handles. No-op when the
|
|
12847
12692
|
* handles array is empty — keeps the guard subsystem out of the
|
|
12848
|
-
* floor bundle for consumers that don't use guards
|
|
12693
|
+
* floor bundle for consumers that don't use guards.
|
|
12849
12694
|
*
|
|
12850
12695
|
* The read-only facade is eagerly instantiated here so the sync
|
|
12851
12696
|
* accessor `_getReadOnlyFacade()` (called from the tx amendment
|
|
@@ -12863,10 +12708,9 @@ var init_vault = __esm({
|
|
|
12863
12708
|
this.readOnlyFacade = new ReadOnlyVaultFacade2(this);
|
|
12864
12709
|
}
|
|
12865
12710
|
/**
|
|
12866
|
-
* @internal —
|
|
12867
|
-
* vaults that never registered any guard
|
|
12868
|
-
* gate on null
|
|
12869
|
-
* `Collection` already do this transitively).
|
|
12711
|
+
* @internal — The gate handler in Noydb.#registerGuardGate calls into
|
|
12712
|
+
* this. Returns `null` for vaults that never registered any guard
|
|
12713
|
+
* strategy. Callers MUST gate on null.
|
|
12870
12714
|
*/
|
|
12871
12715
|
_getGuardRegistry() {
|
|
12872
12716
|
return this.guardRegistry;
|
|
@@ -12877,7 +12721,7 @@ var init_vault = __esm({
|
|
|
12877
12721
|
* derivation strategies (async because `strategyHash` computation
|
|
12878
12722
|
* goes through `crypto.subtle.digest`). No-op when the handles
|
|
12879
12723
|
* array is empty — keeps the derivation subsystem out of the floor
|
|
12880
|
-
* bundle for consumers that don't use derivations
|
|
12724
|
+
* bundle for consumers that don't use derivations. Throws
|
|
12881
12725
|
* `DerivationCycleError` if a cycle is detected after registration.
|
|
12882
12726
|
*/
|
|
12883
12727
|
async _initDerivations(handles) {
|
|
@@ -12909,7 +12753,7 @@ var init_vault = __esm({
|
|
|
12909
12753
|
* MV spec (which invokes its `query()` once for dependency
|
|
12910
12754
|
* analysis), then runs the unified cycle detection across the MV +
|
|
12911
12755
|
* derivation graphs. No-op when the handles array is empty — keeps
|
|
12912
|
-
* the MV subsystem out of the floor bundle (mirrors
|
|
12756
|
+
* the MV subsystem out of the floor bundle (mirrors the derivation lazy-import pattern).
|
|
12913
12757
|
* Throws `MaterializedViewCycleError` if a cycle is detected.
|
|
12914
12758
|
*/
|
|
12915
12759
|
async _initMaterializedViews(handles) {
|
|
@@ -12966,13 +12810,13 @@ var init_vault = __esm({
|
|
|
12966
12810
|
return this.overlayedViewRegistry;
|
|
12967
12811
|
}
|
|
12968
12812
|
/**
|
|
12969
|
-
* Manual re-materialize for a single registered MV
|
|
12813
|
+
* Manual re-materialize for a single registered MV. Useful
|
|
12970
12814
|
* for `refresh: 'manual'` MVs (whose consumer drives refreshes
|
|
12971
12815
|
* externally), for stale-bit recovery on vault re-open, and as the
|
|
12972
12816
|
* explicit bulk-recompute escape hatch after a strategy change.
|
|
12973
12817
|
*
|
|
12974
|
-
* Returns `{ written, deleted, failed }`. `deleted` is always 0
|
|
12975
|
-
*
|
|
12818
|
+
* Returns `{ written, deleted, failed }`. `deleted` is always 0
|
|
12819
|
+
* when tombstoning is not enabled.
|
|
12976
12820
|
*
|
|
12977
12821
|
* Throws if `name` is not a registered MV.
|
|
12978
12822
|
*/
|
|
@@ -13068,22 +12912,19 @@ var init_vault = __esm({
|
|
|
13068
12912
|
/**
|
|
13069
12913
|
* @internal — exposed for `runTransaction({ amendment: true })` so
|
|
13070
12914
|
* the amendment invariant runner can pass the SAME read-only vault
|
|
13071
|
-
* facade that the
|
|
13072
|
-
*
|
|
13073
|
-
* `
|
|
13074
|
-
*
|
|
13075
|
-
*
|
|
12915
|
+
* facade that the gate handler in Noydb.#registerGuardGate uses.
|
|
12916
|
+
* Eagerly instantiated by `_initGuards()` so this accessor stays
|
|
12917
|
+
* synchronous; returns `null` for vaults that never registered any
|
|
12918
|
+
* guard (amendments require at least one guard, so the caller should
|
|
12919
|
+
* never see null).
|
|
13076
12920
|
*/
|
|
13077
12921
|
_getReadOnlyFacade() {
|
|
13078
12922
|
return this.readOnlyFacade;
|
|
13079
12923
|
}
|
|
13080
12924
|
/**
|
|
13081
|
-
* Internal lazy-allocator for the read-only facade. Used
|
|
13082
|
-
*
|
|
13083
|
-
*
|
|
13084
|
-
* invocation (theoretically impossible — `Noydb.openVault` awaits
|
|
13085
|
-
* `_initGuards` before returning — but we keep the defensive lazy
|
|
13086
|
-
* path so the closure's contract stays "always returns a facade").
|
|
12925
|
+
* Internal lazy-allocator for the read-only facade. Used as a
|
|
12926
|
+
* defensive fallback; in practice `_initGuards()` eagerly
|
|
12927
|
+
* instantiates this, so the lazy path is a no-op.
|
|
13087
12928
|
*/
|
|
13088
12929
|
_ensureReadOnlyFacade() {
|
|
13089
12930
|
if (this.readOnlyFacade !== null) return this.readOnlyFacade;
|
|
@@ -13538,7 +13379,7 @@ var init_vault = __esm({
|
|
|
13538
13379
|
const all = await this._loadPeriodsCache();
|
|
13539
13380
|
return all.find((p) => p.name === name) ?? null;
|
|
13540
13381
|
}
|
|
13541
|
-
/** @internal —
|
|
13382
|
+
/** @internal — called by the gate bus before put/delete. */
|
|
13542
13383
|
async _assertTsWritable(existing, incoming) {
|
|
13543
13384
|
if (existing === null && incoming === null) return;
|
|
13544
13385
|
if (this.periodCache === null) {
|
|
@@ -13626,7 +13467,7 @@ var init_vault = __esm({
|
|
|
13626
13467
|
return dumpVaultSchema(this, opts);
|
|
13627
13468
|
}
|
|
13628
13469
|
/**
|
|
13629
|
-
* Lightweight read of the vault's registered schema
|
|
13470
|
+
* Lightweight read of the vault's registered schema: collections
|
|
13630
13471
|
* (+ doc counts), guards, materialized views, schema-update strategies,
|
|
13631
13472
|
* and the unlocked user's grants. Cheap — one `adapter.list` per
|
|
13632
13473
|
* collection, no decryption. For a full snapshot + stats use dumpSchema().
|
|
@@ -14433,6 +14274,116 @@ var init_write_hooks = __esm({
|
|
|
14433
14274
|
}
|
|
14434
14275
|
});
|
|
14435
14276
|
|
|
14277
|
+
// src/subsystem-bus.ts
|
|
14278
|
+
var SubsystemBus;
|
|
14279
|
+
var init_subsystem_bus = __esm({
|
|
14280
|
+
"src/subsystem-bus.ts"() {
|
|
14281
|
+
"use strict";
|
|
14282
|
+
SubsystemBus = class {
|
|
14283
|
+
#handlers = /* @__PURE__ */ new Map();
|
|
14284
|
+
#gateHandlers = /* @__PURE__ */ new Map();
|
|
14285
|
+
#depth = 0;
|
|
14286
|
+
/** Register a handler for an observe point. Returns an unsubscribe fn. */
|
|
14287
|
+
register(point, handler) {
|
|
14288
|
+
let arr = this.#handlers.get(point);
|
|
14289
|
+
if (!arr) {
|
|
14290
|
+
arr = [];
|
|
14291
|
+
this.#handlers.set(point, arr);
|
|
14292
|
+
}
|
|
14293
|
+
arr.push(handler);
|
|
14294
|
+
return () => {
|
|
14295
|
+
const a = this.#handlers.get(point);
|
|
14296
|
+
if (!a) return;
|
|
14297
|
+
const i = a.indexOf(handler);
|
|
14298
|
+
if (i >= 0) a.splice(i, 1);
|
|
14299
|
+
};
|
|
14300
|
+
}
|
|
14301
|
+
/** Cheap gate for the write path — true when any handler is registered for the point. */
|
|
14302
|
+
hasHandlers(point) {
|
|
14303
|
+
const a = this.#handlers.get(point);
|
|
14304
|
+
return a !== void 0 && a.length > 0;
|
|
14305
|
+
}
|
|
14306
|
+
/**
|
|
14307
|
+
* True while one or more dispatches are in flight. Backed by a depth counter
|
|
14308
|
+
* so that two concurrent async dispatches (`Promise.all([put('a'), put('b')])`
|
|
14309
|
+
* each captured `busAfterPut=true` at their respective put() tops while depth
|
|
14310
|
+
* was 0) both proceed independently — the counter stays > 0 until BOTH finish,
|
|
14311
|
+
* so any nested write attempted by a handler still sees `dispatching === true`
|
|
14312
|
+
* and is suppressed by the write-path gate in `collection.ts`
|
|
14313
|
+
* (`busAfterPut = hasHandlers('afterPut') && !dispatching`). Re-entrancy
|
|
14314
|
+
* suppression lives exclusively on that write-path gate; concurrent independent
|
|
14315
|
+
* dispatches must not drop each other's events.
|
|
14316
|
+
*/
|
|
14317
|
+
get dispatching() {
|
|
14318
|
+
return this.#depth > 0;
|
|
14319
|
+
}
|
|
14320
|
+
/**
|
|
14321
|
+
* Dispatch in registration order, awaited. Per-handler errors are warned, not
|
|
14322
|
+
* thrown — an observe handler must never abort a completed write. A
|
|
14323
|
+
* re-entrancy guard suppresses nested firing so a handler that itself writes
|
|
14324
|
+
* cannot loop (same rationale as WriteHookRegistry.#suppressed).
|
|
14325
|
+
*/
|
|
14326
|
+
async dispatch(point, event) {
|
|
14327
|
+
const a = this.#handlers.get(point);
|
|
14328
|
+
if (!a || a.length === 0) return;
|
|
14329
|
+
this.#depth++;
|
|
14330
|
+
try {
|
|
14331
|
+
for (const h of a.slice()) {
|
|
14332
|
+
try {
|
|
14333
|
+
await h(event);
|
|
14334
|
+
} catch (err) {
|
|
14335
|
+
console.warn(
|
|
14336
|
+
`[noy-db] subsystem observe handler failed at ${point}: ` + (err instanceof Error ? err.message : String(err))
|
|
14337
|
+
);
|
|
14338
|
+
}
|
|
14339
|
+
}
|
|
14340
|
+
} finally {
|
|
14341
|
+
this.#depth--;
|
|
14342
|
+
}
|
|
14343
|
+
}
|
|
14344
|
+
/** Register a write-gating handler. A throw from the handler ABORTS the write. Returns an unsubscribe fn. */
|
|
14345
|
+
registerGate(point, handler) {
|
|
14346
|
+
let arr = this.#gateHandlers.get(point);
|
|
14347
|
+
if (!arr) {
|
|
14348
|
+
arr = [];
|
|
14349
|
+
this.#gateHandlers.set(point, arr);
|
|
14350
|
+
}
|
|
14351
|
+
arr.push(handler);
|
|
14352
|
+
return () => {
|
|
14353
|
+
const a = this.#gateHandlers.get(point);
|
|
14354
|
+
if (!a) return;
|
|
14355
|
+
const i = a.indexOf(handler);
|
|
14356
|
+
if (i >= 0) a.splice(i, 1);
|
|
14357
|
+
};
|
|
14358
|
+
}
|
|
14359
|
+
/** Cheap gate for the write path — true when any gate handler is registered for the point. */
|
|
14360
|
+
hasGateHandlers(point) {
|
|
14361
|
+
const a = this.#gateHandlers.get(point);
|
|
14362
|
+
return a !== void 0 && a.length > 0;
|
|
14363
|
+
}
|
|
14364
|
+
/**
|
|
14365
|
+
* Run gate handlers in registration order, awaited. Unlike `dispatch`
|
|
14366
|
+
* (observe), a handler throw is NOT swallowed — it PROPAGATES, aborting the
|
|
14367
|
+
* write before it reaches the store. The first throw stops the remaining
|
|
14368
|
+
* handlers (fail-fast). This is the seam guards/periods migrate onto.
|
|
14369
|
+
*
|
|
14370
|
+
* Note: gate handlers are validators that read, not write. A gate handler
|
|
14371
|
+
* that writes back into the same collection would re-enter the write path
|
|
14372
|
+
* and re-dispatch this point; loop-suppression for that case is deferred to
|
|
14373
|
+
* the migration slice (contract: gate handlers must not perform writes that
|
|
14374
|
+
* re-trigger their own point).
|
|
14375
|
+
*/
|
|
14376
|
+
async dispatchGate(point, event) {
|
|
14377
|
+
const a = this.#gateHandlers.get(point);
|
|
14378
|
+
if (!a || a.length === 0) return;
|
|
14379
|
+
for (const h of a.slice()) {
|
|
14380
|
+
await h(event);
|
|
14381
|
+
}
|
|
14382
|
+
}
|
|
14383
|
+
};
|
|
14384
|
+
}
|
|
14385
|
+
});
|
|
14386
|
+
|
|
14436
14387
|
// src/tab-coordination.ts
|
|
14437
14388
|
function isPresenceMsg(x) {
|
|
14438
14389
|
if (x === null || typeof x !== "object") return false;
|
|
@@ -14935,9 +14886,9 @@ var init_presets = __esm({
|
|
|
14935
14886
|
minTier: 1,
|
|
14936
14887
|
enabled: true
|
|
14937
14888
|
},
|
|
14938
|
-
// rotate-recovery
|
|
14939
|
-
// when the user remembers their passphrase. PERSONAL
|
|
14940
|
-
//
|
|
14889
|
+
// rotate-recovery: deliberate paper-sheet regeneration
|
|
14890
|
+
// when the user remembers their passphrase. PERSONAL allows tier-1 —
|
|
14891
|
+
// knowing the passphrase is enough.
|
|
14941
14892
|
"rotate-recovery": { minTier: 1 },
|
|
14942
14893
|
"enroll-authenticator": { minTier: 1 },
|
|
14943
14894
|
"remove-authenticator": { minTier: 1 },
|
|
@@ -14971,7 +14922,7 @@ var init_presets = __esm({
|
|
|
14971
14922
|
minTier: 1,
|
|
14972
14923
|
enabled: false
|
|
14973
14924
|
},
|
|
14974
|
-
// ─── User envelope gates
|
|
14925
|
+
// ─── User envelope gates ──────────────────────────────────────────
|
|
14975
14926
|
// edit-own-profile: tier 3 floor — any active session can edit their
|
|
14976
14927
|
// own profile/preferences. Tightening to require a TOTP for
|
|
14977
14928
|
// profile changes is a one-line override.
|
|
@@ -14998,7 +14949,7 @@ var init_presets = __esm({
|
|
|
14998
14949
|
minTier: 1,
|
|
14999
14950
|
enabled: true
|
|
15000
14951
|
},
|
|
15001
|
-
// rotate-recovery
|
|
14952
|
+
// rotate-recovery: STRICT requires an off-device factor —
|
|
15002
14953
|
// rotating recovery is an off-site-trust event; a stolen unlocked
|
|
15003
14954
|
// laptop must not be able to silently mint a new sheet for the
|
|
15004
14955
|
// attacker. Matches the `peer-recover-user` STRICT default.
|
|
@@ -15071,7 +15022,7 @@ var init_presets = __esm({
|
|
|
15071
15022
|
minTier: 1,
|
|
15072
15023
|
enabled: false
|
|
15073
15024
|
},
|
|
15074
|
-
// ─── User envelope gates
|
|
15025
|
+
// ─── User envelope gates ──────────────────────────────────────────
|
|
15075
15026
|
// STRICT: profile edits require a TOTP/email-OTP factor (typical
|
|
15076
15027
|
// shared-workstation hardening — your name/avatar shouldn't change
|
|
15077
15028
|
// without a fresh second-factor proof).
|
|
@@ -15154,6 +15105,81 @@ var init_policy2 = __esm({
|
|
|
15154
15105
|
}
|
|
15155
15106
|
});
|
|
15156
15107
|
|
|
15108
|
+
// src/guards/executor.ts
|
|
15109
|
+
var executor_exports3 = {};
|
|
15110
|
+
__export(executor_exports3, {
|
|
15111
|
+
GuardExecutor: () => GuardExecutor
|
|
15112
|
+
});
|
|
15113
|
+
function deepEqual(a, b) {
|
|
15114
|
+
if (a === b) return true;
|
|
15115
|
+
if (a === null || b === null) return a === b;
|
|
15116
|
+
if (typeof a !== typeof b) return false;
|
|
15117
|
+
if (typeof a !== "object") return a === b;
|
|
15118
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
15119
|
+
if (Array.isArray(a)) {
|
|
15120
|
+
const aa = a;
|
|
15121
|
+
const bb = b;
|
|
15122
|
+
if (aa.length !== bb.length) return false;
|
|
15123
|
+
for (let i = 0; i < aa.length; i++) if (!deepEqual(aa[i], bb[i])) return false;
|
|
15124
|
+
return true;
|
|
15125
|
+
}
|
|
15126
|
+
const ao = a;
|
|
15127
|
+
const bo = b;
|
|
15128
|
+
const ak = Object.keys(ao);
|
|
15129
|
+
const bk = Object.keys(bo);
|
|
15130
|
+
if (ak.length !== bk.length) return false;
|
|
15131
|
+
for (const k of ak) {
|
|
15132
|
+
if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;
|
|
15133
|
+
if (!deepEqual(ao[k], bo[k])) return false;
|
|
15134
|
+
}
|
|
15135
|
+
return true;
|
|
15136
|
+
}
|
|
15137
|
+
var GuardExecutor;
|
|
15138
|
+
var init_executor3 = __esm({
|
|
15139
|
+
"src/guards/executor.ts"() {
|
|
15140
|
+
"use strict";
|
|
15141
|
+
init_errors();
|
|
15142
|
+
GuardExecutor = {
|
|
15143
|
+
/**
|
|
15144
|
+
* Compare existing vs incoming for each `frozenFields.fields` entry
|
|
15145
|
+
* when `frozenFields.when(existing)` is true. Throws
|
|
15146
|
+
* `FieldFrozenError` listing every changed frozen field.
|
|
15147
|
+
*/
|
|
15148
|
+
async checkFrozenFields(guard, id, existing, incoming) {
|
|
15149
|
+
const ff = guard.frozenFields;
|
|
15150
|
+
if (!ff) return;
|
|
15151
|
+
if (existing === null) return;
|
|
15152
|
+
if (!ff.when(existing)) return;
|
|
15153
|
+
const changed = [];
|
|
15154
|
+
for (const f of ff.fields) {
|
|
15155
|
+
if (existing[f] !== incoming[f]) {
|
|
15156
|
+
if (!deepEqual(existing[f], incoming[f])) changed.push(String(f));
|
|
15157
|
+
}
|
|
15158
|
+
}
|
|
15159
|
+
if (changed.length > 0) {
|
|
15160
|
+
throw new FieldFrozenError(guard.collection, id, changed);
|
|
15161
|
+
}
|
|
15162
|
+
},
|
|
15163
|
+
/**
|
|
15164
|
+
* Run a single guard's invariant over its slice of the change-set.
|
|
15165
|
+
* Any throw is converted to `InvariantError` unless it already is one.
|
|
15166
|
+
*/
|
|
15167
|
+
async runInvariant(guard, changes, ctx) {
|
|
15168
|
+
const amendment = guard.amendment;
|
|
15169
|
+
if (!amendment) return;
|
|
15170
|
+
try {
|
|
15171
|
+
await amendment.invariant(changes, ctx);
|
|
15172
|
+
} catch (err) {
|
|
15173
|
+
if (err instanceof InvariantError) throw err;
|
|
15174
|
+
throw new InvariantError(
|
|
15175
|
+
err instanceof Error ? err.message : `invariant violated: ${String(err)}`
|
|
15176
|
+
);
|
|
15177
|
+
}
|
|
15178
|
+
}
|
|
15179
|
+
};
|
|
15180
|
+
}
|
|
15181
|
+
});
|
|
15182
|
+
|
|
15157
15183
|
// src/noydb.ts
|
|
15158
15184
|
var noydb_exports = {};
|
|
15159
15185
|
__export(noydb_exports, {
|
|
@@ -15226,6 +15252,7 @@ var init_noydb = __esm({
|
|
|
15226
15252
|
init_events();
|
|
15227
15253
|
init_write_queue();
|
|
15228
15254
|
init_write_hooks();
|
|
15255
|
+
init_subsystem_bus();
|
|
15229
15256
|
init_tab_coordination();
|
|
15230
15257
|
init_tab_write_relay();
|
|
15231
15258
|
init_keyring();
|
|
@@ -15249,13 +15276,14 @@ var init_noydb = __esm({
|
|
|
15249
15276
|
emitter = new NoydbEventEmitter();
|
|
15250
15277
|
writeQueueTracker = new WriteQueueTracker();
|
|
15251
15278
|
writeHooks = new WriteHookRegistry();
|
|
15279
|
+
subsystemBus = new SubsystemBus();
|
|
15252
15280
|
clientId = generateULID();
|
|
15253
15281
|
vaultCache = /* @__PURE__ */ new Map();
|
|
15254
15282
|
keyringCache = /* @__PURE__ */ new Map();
|
|
15255
15283
|
syncEngines = /* @__PURE__ */ new Map();
|
|
15256
15284
|
/**
|
|
15257
15285
|
* Per-vault active session tier — defaults to `1` after a passphrase
|
|
15258
|
-
* unlock; tier-2 / tier-3 unlocks
|
|
15286
|
+
* unlock; tier-2 / tier-3 unlocks downgrade it. Used by
|
|
15259
15287
|
* {@link checkGate} to evaluate `gate.minTier`.
|
|
15260
15288
|
*/
|
|
15261
15289
|
activeTier = /* @__PURE__ */ new Map();
|
|
@@ -15265,14 +15293,14 @@ var init_noydb = __esm({
|
|
|
15265
15293
|
*/
|
|
15266
15294
|
policyCache = /* @__PURE__ */ new Map();
|
|
15267
15295
|
/**
|
|
15268
|
-
* One-shot bypass for the managed-mode strong-recovery check
|
|
15296
|
+
* One-shot bypass for the managed-mode strong-recovery check.
|
|
15269
15297
|
* Set true by {@link openVaultAndEnrollRecovery} for the duration of
|
|
15270
15298
|
* the bootstrap window so the keyring can be created before the
|
|
15271
15299
|
* strong recovery is enrolled. Always cleared (try/finally).
|
|
15272
15300
|
* @internal
|
|
15273
15301
|
*/
|
|
15274
15302
|
_skipNextManagedRecoveryCheck = false;
|
|
15275
|
-
/** Per-vault tier-3 (PIN / quick-resume) state
|
|
15303
|
+
/** Per-vault tier-3 (PIN / quick-resume) state. */
|
|
15276
15304
|
quickUnlock = new QuickUnlockStore();
|
|
15277
15305
|
/**
|
|
15278
15306
|
* Resolved public-envelope schema. Lazily computed once from
|
|
@@ -15282,9 +15310,9 @@ var init_noydb = __esm({
|
|
|
15282
15310
|
publicEnvelopeSchema;
|
|
15283
15311
|
closed = false;
|
|
15284
15312
|
sessionTimer = null;
|
|
15285
|
-
/** Same-device multi-tab coordinator
|
|
15313
|
+
/** Same-device multi-tab coordinator; created on `enableTabCoordination()`. */
|
|
15286
15314
|
tabCoordinator;
|
|
15287
|
-
/** Cross-tab write relay
|
|
15315
|
+
/** Cross-tab write relay; created on `enableTabCoordination()`. */
|
|
15288
15316
|
writeRelay;
|
|
15289
15317
|
/** Per-vault policy enforcers. */
|
|
15290
15318
|
policyEnforcers = /* @__PURE__ */ new Map();
|
|
@@ -15297,8 +15325,8 @@ var init_noydb = __esm({
|
|
|
15297
15325
|
* the same function's `finally` block. Side-effect writes triggered
|
|
15298
15326
|
* during a staged op's `Collection.put` (today: eager derivation
|
|
15299
15327
|
* outputs) register their pre-write envelope on `_executed` here so
|
|
15300
|
-
* a mid-batch failure rolls them back alongside the main staged ops
|
|
15301
|
-
*
|
|
15328
|
+
* a mid-batch failure rolls them back alongside the main staged ops.
|
|
15329
|
+
* `null` outside of Phase 2.
|
|
15302
15330
|
* @internal
|
|
15303
15331
|
*/
|
|
15304
15332
|
_activeTxContext = null;
|
|
@@ -15319,8 +15347,95 @@ var init_noydb = __esm({
|
|
|
15319
15347
|
if (options.sessionPolicy) {
|
|
15320
15348
|
this.sessionStrategy.validateSessionPolicy(options.sessionPolicy);
|
|
15321
15349
|
}
|
|
15350
|
+
this.#registerGuardGate();
|
|
15351
|
+
this.#registerPeriodGate();
|
|
15322
15352
|
this.resetSessionTimer();
|
|
15323
15353
|
}
|
|
15354
|
+
// Track A — guards migration. Registers record-lock / field-freeze / onDelete
|
|
15355
|
+
// / amendment-collect as gate-bus handlers (only when guards are opted in, so
|
|
15356
|
+
// the write path is zero-cost otherwise). Resolves the live vault's
|
|
15357
|
+
// GuardRegistry per dispatch. Registered BEFORE the period gate so guard
|
|
15358
|
+
// checks run first. The amendment branch is a side-effect (collectChange),
|
|
15359
|
+
// NOT a throw — and runs even for internal deletes (an amendment invariant
|
|
15360
|
+
// must see system housekeeping tombstones); onDelete/checks run only for
|
|
15361
|
+
// user (non-internal) operations.
|
|
15362
|
+
#registerGuardGate() {
|
|
15363
|
+
if (this.options.guardStrategies === void 0) return;
|
|
15364
|
+
this.subsystemBus.registerGate("beforePut", async (e) => {
|
|
15365
|
+
const v = this.vaultCache.get(e.vault);
|
|
15366
|
+
if (!v) return;
|
|
15367
|
+
const registry = v._getGuardRegistry();
|
|
15368
|
+
if (!registry) return;
|
|
15369
|
+
const guards = registry.guardsFor(e.collection);
|
|
15370
|
+
if (guards.length === 0) return;
|
|
15371
|
+
const existing = e.existing ?? null;
|
|
15372
|
+
const incoming = e.incoming;
|
|
15373
|
+
if (registry.isAmendmentActive()) {
|
|
15374
|
+
registry.collectChange(e.collection, e.docId, existing, incoming, e.existingVersion, e.existingVersion + 1);
|
|
15375
|
+
return;
|
|
15376
|
+
}
|
|
15377
|
+
const facade = v._getReadOnlyFacade();
|
|
15378
|
+
if (!facade) return;
|
|
15379
|
+
const ctx = { existing, vault: facade, userId: e.userId, role: e.role };
|
|
15380
|
+
await registry.runChecks(e.collection, incoming, ctx);
|
|
15381
|
+
const { GuardExecutor: GuardExecutor2 } = await Promise.resolve().then(() => (init_executor3(), executor_exports3));
|
|
15382
|
+
for (const g of guards) {
|
|
15383
|
+
await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming);
|
|
15384
|
+
}
|
|
15385
|
+
});
|
|
15386
|
+
this.subsystemBus.registerGate("beforeDelete", async (e) => {
|
|
15387
|
+
const v = this.vaultCache.get(e.vault);
|
|
15388
|
+
if (!v) return;
|
|
15389
|
+
const registry = v._getGuardRegistry();
|
|
15390
|
+
if (!registry) return;
|
|
15391
|
+
const guards = registry.guardsFor(e.collection);
|
|
15392
|
+
if (guards.length === 0) return;
|
|
15393
|
+
const existing = e.existing ?? null;
|
|
15394
|
+
if (registry.isAmendmentActive()) {
|
|
15395
|
+
registry.collectChange(e.collection, e.docId, existing, null, e.existingVersion, e.existingVersion);
|
|
15396
|
+
return;
|
|
15397
|
+
}
|
|
15398
|
+
if (e.internal) return;
|
|
15399
|
+
const facade = v._getReadOnlyFacade();
|
|
15400
|
+
if (!facade) return;
|
|
15401
|
+
const ctx = { existing, vault: facade, userId: e.userId, role: e.role };
|
|
15402
|
+
await registry.runOnDelete(e.collection, existing ?? {}, ctx);
|
|
15403
|
+
});
|
|
15404
|
+
}
|
|
15405
|
+
/**
|
|
15406
|
+
* Register closed-period write guards on the subsystem bus when a
|
|
15407
|
+
* periodsStrategy is configured. Handlers resolve the live Vault from
|
|
15408
|
+
* vaultCache so they always use the up-to-date period cache.
|
|
15409
|
+
*/
|
|
15410
|
+
// Track A — periods migration. Registers the closed-period write guard as a
|
|
15411
|
+
// gate-bus handler (only when periods is opted in, so the write path is
|
|
15412
|
+
// zero-cost otherwise). Each handler resolves the LIVE vault from the cache
|
|
15413
|
+
// per dispatch and delegates to its `_assertTsWritable`, which owns all
|
|
15414
|
+
// period logic. Resolving the live vault makes eviction/re-creation
|
|
15415
|
+
// transparent. Semantics note: if a write reaches the gate through a retained
|
|
15416
|
+
// collection handle whose vault has been evicted from `vaultCache` (e.g. a
|
|
15417
|
+
// post-revocation write on a stale handle), the period check is skipped — the
|
|
15418
|
+
// guard binds to the live vault, not a captured instance. Periods is a
|
|
15419
|
+
// write-integrity guard, not a security boundary, and a re-open reloads the
|
|
15420
|
+
// period cache; the trade-off is intentional.
|
|
15421
|
+
#registerPeriodGate() {
|
|
15422
|
+
if (this.options.periodsStrategy === void 0) return;
|
|
15423
|
+
this.subsystemBus.registerGate("beforePut", async (e) => {
|
|
15424
|
+
const v = this.vaultCache.get(e.vault);
|
|
15425
|
+
if (!v) return;
|
|
15426
|
+
const existing = e.op === "create" ? null : { ts: e.existingTs ?? null, record: e.existing ?? null };
|
|
15427
|
+
await v._assertTsWritable(existing, e.incoming);
|
|
15428
|
+
});
|
|
15429
|
+
this.subsystemBus.registerGate("beforeDelete", async (e) => {
|
|
15430
|
+
if (e.internal) return;
|
|
15431
|
+
const v = this.vaultCache.get(e.vault);
|
|
15432
|
+
if (!v) return;
|
|
15433
|
+
await v._assertTsWritable(
|
|
15434
|
+
{ ts: e.existingTs ?? null, record: e.existing ?? null },
|
|
15435
|
+
null
|
|
15436
|
+
);
|
|
15437
|
+
});
|
|
15438
|
+
}
|
|
15324
15439
|
resetSessionTimer() {
|
|
15325
15440
|
if (this.sessionTimer) clearTimeout(this.sessionTimer);
|
|
15326
15441
|
const idleMs = this.options.sessionPolicy?.idleTimeoutMs ?? this.options.sessionTimeout;
|
|
@@ -15610,8 +15725,6 @@ var init_noydb = __esm({
|
|
|
15610
15725
|
* @throws `NoAccessError` when no keyring exists for the target.
|
|
15611
15726
|
* @throws `PermissionDeniedError` when the role hierarchy rejects.
|
|
15612
15727
|
* @throws `ValidationError` when no field is provided.
|
|
15613
|
-
*
|
|
15614
|
-
* @see #54
|
|
15615
15728
|
*/
|
|
15616
15729
|
async updateUser(vault, options, factors) {
|
|
15617
15730
|
await this.checkGate(vault, "update-user", factors);
|
|
@@ -15947,7 +16060,7 @@ var init_noydb = __esm({
|
|
|
15947
16060
|
* Phase 2. `Collection.dispatchDerivations` consults this so a
|
|
15948
16061
|
* recursive derived-output write inside `Collection.put` can register
|
|
15949
16062
|
* its envelope onto `ctx._executed` and roll back with the main
|
|
15950
|
-
* staged ops on mid-batch failure
|
|
16063
|
+
* staged ops on mid-batch failure.
|
|
15951
16064
|
*
|
|
15952
16065
|
* @internal
|
|
15953
16066
|
*/
|
|
@@ -15976,7 +16089,7 @@ var init_noydb = __esm({
|
|
|
15976
16089
|
* `Collection.putManyAtomic` (via `derivationSource.createTxContext`)
|
|
15977
16090
|
* to publish an active context for the duration of its bulk-atomic
|
|
15978
16091
|
* Phase 2 loop, so recursive derivation-output writes register on
|
|
15979
|
-
* `ctx._executed` and roll back together with the source ops
|
|
16092
|
+
* `ctx._executed` and roll back together with the source ops.
|
|
15980
16093
|
*
|
|
15981
16094
|
* @internal
|
|
15982
16095
|
*/
|
|
@@ -16047,26 +16160,26 @@ var init_noydb = __esm({
|
|
|
16047
16160
|
return this.writeQueueTracker;
|
|
16048
16161
|
}
|
|
16049
16162
|
/**
|
|
16050
|
-
* Register a hook that runs before each write
|
|
16163
|
+
* Register a hook that runs before each write. Awaited; a throw
|
|
16051
16164
|
* aborts the write. Returns an unsubscribe function.
|
|
16052
16165
|
*/
|
|
16053
16166
|
onBeforeWrite(handler) {
|
|
16054
16167
|
return this.writeHooks.onBeforeWrite(handler);
|
|
16055
16168
|
}
|
|
16056
16169
|
/**
|
|
16057
|
-
* Register a hook that runs after each committed write
|
|
16170
|
+
* Register a hook that runs after each committed write. Awaited;
|
|
16058
16171
|
* a handler error is warned, never rolled back. Returns an unsubscribe fn.
|
|
16059
16172
|
*/
|
|
16060
16173
|
onAfterWrite(handler) {
|
|
16061
16174
|
return this.writeHooks.onAfterWrite(handler);
|
|
16062
16175
|
}
|
|
16063
|
-
/** Subscribe to cross-tab write conflicts
|
|
16176
|
+
/** Subscribe to cross-tab write conflicts. Returns an unsubscribe. */
|
|
16064
16177
|
onWriteConflict(fn) {
|
|
16065
16178
|
this.on("write:conflict", fn);
|
|
16066
16179
|
return () => this.off("write:conflict", fn);
|
|
16067
16180
|
}
|
|
16068
16181
|
/**
|
|
16069
|
-
* Enable same-device multi-tab coordination
|
|
16182
|
+
* Enable same-device multi-tab coordination: primary/secondary
|
|
16070
16183
|
* election + presence. Browser-only — a graceful no-op (role 'unknown')
|
|
16071
16184
|
* when Web Locks / BroadcastChannel are unavailable and nothing is
|
|
16072
16185
|
* injected. Idempotent; returns a disposer.
|
|
@@ -16149,7 +16262,11 @@ var init_noydb = __esm({
|
|
|
16149
16262
|
get _writeHooks() {
|
|
16150
16263
|
return this.writeHooks;
|
|
16151
16264
|
}
|
|
16152
|
-
/** @internal
|
|
16265
|
+
/** @internal The observe bus, threaded into every Collection. */
|
|
16266
|
+
get _subsystemBus() {
|
|
16267
|
+
return this.subsystemBus;
|
|
16268
|
+
}
|
|
16269
|
+
/** @internal Stable per-instance id for schema-cutover coordination. */
|
|
16153
16270
|
get _clientId() {
|
|
16154
16271
|
return this.clientId;
|
|
16155
16272
|
}
|
|
@@ -16169,10 +16286,6 @@ var init_noydb = __esm({
|
|
|
16169
16286
|
* survives lock; nothing about it changes when DEKs are scrubbed).
|
|
16170
16287
|
*
|
|
16171
16288
|
* No-op when `vault` is not currently in cache (idempotent).
|
|
16172
|
-
*
|
|
16173
|
-
* Unblocks vLannaAi/niwat#33.
|
|
16174
|
-
*
|
|
16175
|
-
* @see #17
|
|
16176
16289
|
*/
|
|
16177
16290
|
lockVault(vault) {
|
|
16178
16291
|
this.syncEngines.get(vault)?.stopAutoSync();
|
|
@@ -16287,7 +16400,7 @@ var init_noydb = __esm({
|
|
|
16287
16400
|
return merged;
|
|
16288
16401
|
}
|
|
16289
16402
|
/**
|
|
16290
|
-
* Read the current vault-level user-directory toggle
|
|
16403
|
+
* Read the current vault-level user-directory toggle. Returns
|
|
16291
16404
|
* the default-on shape (`{ enabled: true }`) when no `_meta/directory`
|
|
16292
16405
|
* document has been persisted yet.
|
|
16293
16406
|
*
|
|
@@ -16299,7 +16412,7 @@ var init_noydb = __esm({
|
|
|
16299
16412
|
return persisted?.enabled ?? true;
|
|
16300
16413
|
}
|
|
16301
16414
|
/**
|
|
16302
|
-
* Toggle the vault's user-directory listing on or off
|
|
16415
|
+
* Toggle the vault's user-directory listing on or off.
|
|
16303
16416
|
* Owner-only. When disabled, `listUsersWithEnvelopes()` throws
|
|
16304
16417
|
* {@link import('./errors.js').DirectoryDisabledError} for callers
|
|
16305
16418
|
* whose role is neither `owner` nor `admin`.
|
|
@@ -16359,7 +16472,7 @@ var init_noydb = __esm({
|
|
|
16359
16472
|
*
|
|
16360
16473
|
* Two enforcement modes:
|
|
16361
16474
|
*
|
|
16362
|
-
* 1. **Managed-mode mandatory strong-recovery
|
|
16475
|
+
* 1. **Managed-mode mandatory strong-recovery.** When
|
|
16363
16476
|
* `passphraseMode === 'managed'`, the vault MUST have at least
|
|
16364
16477
|
* one **strong** recovery profile (Shamir today). Paper alone is
|
|
16365
16478
|
* rejected because under managed mode the user has no memorized
|
|
@@ -16393,14 +16506,14 @@ var init_noydb = __esm({
|
|
|
16393
16506
|
throw new RecoveryNotEnrolledError();
|
|
16394
16507
|
}
|
|
16395
16508
|
/**
|
|
16396
|
-
* Internal accessor used by tier-2/tier-3 unlock paths
|
|
16509
|
+
* Internal accessor used by tier-2/tier-3 unlock paths
|
|
16397
16510
|
* to mark the active session tier.
|
|
16398
16511
|
* @internal
|
|
16399
16512
|
*/
|
|
16400
16513
|
_setActiveTier(vault, tier) {
|
|
16401
16514
|
this.activeTier.set(vault, tier);
|
|
16402
16515
|
}
|
|
16403
|
-
// ─── Tier-2 enroll / remove
|
|
16516
|
+
// ─── Tier-2 enroll / remove ─────────────────────────────────────
|
|
16404
16517
|
/**
|
|
16405
16518
|
* Add a tier-2 authenticator slot to the calling user's keyring.
|
|
16406
16519
|
* Each slot independently wraps the SAME KEK under a method-specific
|
|
@@ -16430,7 +16543,7 @@ var init_noydb = __esm({
|
|
|
16430
16543
|
const next = await removeAuthenticator(this.options.store, vault, keyring, slotId);
|
|
16431
16544
|
this.keyringCache.set(vault, next);
|
|
16432
16545
|
}
|
|
16433
|
-
/** Read the slot list for a vault. Internal — `describeAuthConfig`
|
|
16546
|
+
/** Read the slot list for a vault. Internal — `describeAuthConfig` consumes this. */
|
|
16434
16547
|
async listAuthenticators(vault) {
|
|
16435
16548
|
const keyring = await this.getKeyringInternal(vault);
|
|
16436
16549
|
return keyring.authenticators;
|
|
@@ -16442,7 +16555,7 @@ var init_noydb = __esm({
|
|
|
16442
16555
|
* are immutable through this method. Anti-slot-swap is structural,
|
|
16443
16556
|
* not gate-driven.
|
|
16444
16557
|
*
|
|
16445
|
-
* `meta` patch semantics (
|
|
16558
|
+
* `meta` patch semantics (top-level merge):
|
|
16446
16559
|
* - Top-level merge — absent keys preserved
|
|
16447
16560
|
* - `null` value — delete that meta key
|
|
16448
16561
|
* - Other values — replace verbatim
|
|
@@ -16460,8 +16573,6 @@ var init_noydb = __esm({
|
|
|
16460
16573
|
*
|
|
16461
16574
|
* @throws `NoAccessError` when no slot with the given id exists.
|
|
16462
16575
|
* @throws `ValidationError` when no patch field is provided.
|
|
16463
|
-
*
|
|
16464
|
-
* @see #55
|
|
16465
16576
|
*/
|
|
16466
16577
|
async updateAuthenticator(vault, slotId, options, factors) {
|
|
16467
16578
|
await this.checkGate(vault, "update-authenticator", factors);
|
|
@@ -16470,7 +16581,7 @@ var init_noydb = __esm({
|
|
|
16470
16581
|
this.keyringCache.set(vault, next);
|
|
16471
16582
|
}
|
|
16472
16583
|
/**
|
|
16473
|
-
* Native WebAuthn enrollment using the **real** internal keyring
|
|
16584
|
+
* Native WebAuthn enrollment using the **real** internal keyring.
|
|
16474
16585
|
*
|
|
16475
16586
|
* Why this exists: when a consumer is using `createNoydb({ secret })`,
|
|
16476
16587
|
* they cannot reach the live `UnlockedKeyring` to feed it to
|
|
@@ -16513,8 +16624,6 @@ var init_noydb = __esm({
|
|
|
16513
16624
|
* a server-side allowlist).
|
|
16514
16625
|
*
|
|
16515
16626
|
* Gated by `enroll-authenticator` like `enrollAuthenticator()` itself.
|
|
16516
|
-
*
|
|
16517
|
-
* @see #16
|
|
16518
16627
|
*/
|
|
16519
16628
|
async enrollWebAuthn(vault, ceremony, factors) {
|
|
16520
16629
|
await this.checkGate(vault, "enroll-authenticator", factors);
|
|
@@ -16541,8 +16650,6 @@ var init_noydb = __esm({
|
|
|
16541
16650
|
* deciding when a new device prompt should appear. Identity is
|
|
16542
16651
|
* `id` + `enrolled_at`; the `meta.credentialId` (base64) is used by
|
|
16543
16652
|
* `allowCredentials` at unlock time.
|
|
16544
|
-
*
|
|
16545
|
-
* @see #16
|
|
16546
16653
|
*/
|
|
16547
16654
|
async listWebAuthnSlots(vault) {
|
|
16548
16655
|
const keyring = await this.getKeyringInternal(vault);
|
|
@@ -16624,7 +16731,7 @@ var init_noydb = __esm({
|
|
|
16624
16731
|
async getPublicEnvelope(vault, opts = {}) {
|
|
16625
16732
|
return readPublicEnvelope(this.options.store, vault, opts);
|
|
16626
16733
|
}
|
|
16627
|
-
// ─── Auth introspection
|
|
16734
|
+
// ─── Auth introspection ─────────────────────────────────────────
|
|
16628
16735
|
/** English summary of the configured auth model. */
|
|
16629
16736
|
async describeAuthConfig(vault) {
|
|
16630
16737
|
return describeAuthConfig(this.options.store, vault);
|
|
@@ -16647,7 +16754,7 @@ var init_noydb = __esm({
|
|
|
16647
16754
|
await this.checkGate(vault, "view-user-auth", factors);
|
|
16648
16755
|
return describeAllUsersAuth(this.options.store, vault);
|
|
16649
16756
|
}
|
|
16650
|
-
// ─── Tier-1 change flows
|
|
16757
|
+
// ─── Tier-1 change flows ────────────────────────────────────────
|
|
16651
16758
|
/**
|
|
16652
16759
|
* Rotate the user's passphrase (user remembers old). Validates the
|
|
16653
16760
|
* new phrase against the configured `passphrase` policy, runs the
|
|
@@ -16655,8 +16762,7 @@ var init_noydb = __esm({
|
|
|
16655
16762
|
*
|
|
16656
16763
|
* Tier-2 authenticator slots are dropped — each slot wraps the old
|
|
16657
16764
|
* KEK and would need its derivation key to be re-presented. Re-enrol
|
|
16658
|
-
* via `db.enrollAuthenticator` after rotation.
|
|
16659
|
-
* v0.1.0-pre.5 limitation.
|
|
16765
|
+
* via `db.enrollAuthenticator` after rotation.
|
|
16660
16766
|
*
|
|
16661
16767
|
* @throws `WeakPassphraseError` on a weak new phrase.
|
|
16662
16768
|
* @throws `PolicyDeniedError` when the gate denies (missing factor, …).
|
|
@@ -16678,8 +16784,8 @@ var init_noydb = __esm({
|
|
|
16678
16784
|
}
|
|
16679
16785
|
/**
|
|
16680
16786
|
* Reset the passphrase using a recovery proof (user forgot the old).
|
|
16681
|
-
*
|
|
16682
|
-
* other
|
|
16787
|
+
* Currently supports the `'paper'` profile end-to-end; the
|
|
16788
|
+
* other profiles throw {@link RecoveryProfileNotImplementedError}.
|
|
16683
16789
|
*
|
|
16684
16790
|
* Burns the used recovery entry on success.
|
|
16685
16791
|
*/
|
|
@@ -16708,7 +16814,7 @@ var init_noydb = __esm({
|
|
|
16708
16814
|
return { newCodes: codes };
|
|
16709
16815
|
}
|
|
16710
16816
|
/**
|
|
16711
|
-
* Deliberate paper-recovery-code regeneration
|
|
16817
|
+
* Deliberate paper-recovery-code regeneration. User knows their
|
|
16712
16818
|
* passphrase but wants a fresh sheet — they lost the printout or
|
|
16713
16819
|
* suspect compromise of the off-site copy.
|
|
16714
16820
|
*
|
|
@@ -16718,7 +16824,7 @@ var init_noydb = __esm({
|
|
|
16718
16824
|
*
|
|
16719
16825
|
* Gated by the `rotate-recovery` policy gate:
|
|
16720
16826
|
* - PERSONAL_POLICY: `{ minTier: 1 }` — knowing the passphrase
|
|
16721
|
-
* suffices, matching the
|
|
16827
|
+
* suffices, matching the lower-level flow's bar.
|
|
16722
16828
|
* - STRICT_POLICY: `{ minTier: 1, factors: [{ anyOf: ['totp',
|
|
16723
16829
|
* 'email-otp', 'webauthn-roaming'] }] }` — rotation is an
|
|
16724
16830
|
* off-site-trust event; require an off-device factor so a
|
|
@@ -16823,7 +16929,7 @@ var init_noydb = __esm({
|
|
|
16823
16929
|
return { newShares: shareStrings, entryId: targetEntryId };
|
|
16824
16930
|
}
|
|
16825
16931
|
/**
|
|
16826
|
-
* **Atomic create-and-enroll for managed-mode vaults
|
|
16932
|
+
* **Atomic create-and-enroll for managed-mode vaults.**
|
|
16827
16933
|
*
|
|
16828
16934
|
* Bootstraps a managed-mode vault and enrolls strong recovery in
|
|
16829
16935
|
* a single ceremony. Under `passphraseMode: 'managed'`, every
|
|
@@ -16894,7 +17000,7 @@ var init_noydb = __esm({
|
|
|
16894
17000
|
return { vault: vaultHandle, recoveryEnrollments };
|
|
16895
17001
|
}
|
|
16896
17002
|
/**
|
|
16897
|
-
* **Recovery flow under managed-passphrase mode
|
|
17003
|
+
* **Recovery flow under managed-passphrase mode.**
|
|
16898
17004
|
*
|
|
16899
17005
|
* Replaces the sealed passphrase of a managed-mode vault with a
|
|
16900
17006
|
* fresh 256-bit random, sealed under the configured
|
|
@@ -16911,7 +17017,7 @@ var init_noydb = __esm({
|
|
|
16911
17017
|
* 5. Drop the keyring cache so the next operation re-derives.
|
|
16912
17018
|
*
|
|
16913
17019
|
* The vault's strong-recovery enrollment is preserved across
|
|
16914
|
-
* recovery (Shamir entries are not burned on use
|
|
17020
|
+
* recovery (Shamir entries are not burned on use).
|
|
16915
17021
|
*
|
|
16916
17022
|
* @throws ValidationError if the Noydb instance is not in managed mode.
|
|
16917
17023
|
*/
|
|
@@ -16959,7 +17065,7 @@ var init_noydb = __esm({
|
|
|
16959
17065
|
}
|
|
16960
17066
|
/**
|
|
16961
17067
|
* Atomic peer-recovery — re-wraps an EXISTING user's keyring under
|
|
16962
|
-
* a fresh temp passphrase in a single store write. Closes
|
|
17068
|
+
* a fresh temp passphrase in a single store write. Closes the
|
|
16963
17069
|
* partial-failure window (the previous compose-from-primitives
|
|
16964
17070
|
* pattern was `db.revoke + db.grant`, two writes — if the issuer
|
|
16965
17071
|
* cancelled between them the target was locked out entirely).
|
|
@@ -16969,7 +17075,7 @@ var init_noydb = __esm({
|
|
|
16969
17075
|
* - Same `userId`, role, permissions, capabilities preserved.
|
|
16970
17076
|
* - DEKs unchanged → every other principal in the vault keeps
|
|
16971
17077
|
* access. No key rotation.
|
|
16972
|
-
* - Allows owner→owner natively
|
|
17078
|
+
* - Allows owner→owner natively. The existing
|
|
16973
17079
|
* `db.revoke` retains its block — peer-recovery is a separate,
|
|
16974
17080
|
* intentionally-named operation.
|
|
16975
17081
|
* - Tier-2 slots dropped (they wrap the old KEK).
|
|
@@ -16998,7 +17104,6 @@ var init_noydb = __esm({
|
|
|
16998
17104
|
* @throws `PrivilegeEscalationError` when the caller lacks a DEK
|
|
16999
17105
|
* the target previously had access to.
|
|
17000
17106
|
*
|
|
17001
|
-
* @see #33 #34 — the issues this method closes.
|
|
17002
17107
|
*/
|
|
17003
17108
|
async recoverUser(vault, options, factors) {
|
|
17004
17109
|
await this.checkGate(vault, "peer-recover-user", factors);
|
|
@@ -17009,7 +17114,7 @@ var init_noydb = __esm({
|
|
|
17009
17114
|
}
|
|
17010
17115
|
}
|
|
17011
17116
|
/**
|
|
17012
|
-
* Persist a recovery enrollment.
|
|
17117
|
+
* Persist a recovery enrollment. Accepts the `'paper'`
|
|
17013
17118
|
* profile.
|
|
17014
17119
|
*
|
|
17015
17120
|
* The hub wraps the user's DEK set (not the KEK) under a code-derived
|
|
@@ -17029,7 +17134,7 @@ var init_noydb = __esm({
|
|
|
17029
17134
|
* showCodesToUser(codes)
|
|
17030
17135
|
* ```
|
|
17031
17136
|
*
|
|
17032
|
-
*
|
|
17137
|
+
* `@noy-db/on-recovery`'s `generateRecoveryCodeSet`
|
|
17033
17138
|
* delegates to `mintPaperRecoveryEntry` internally — its output is
|
|
17034
17139
|
* fed directly to this API. Pick whichever fits your code-gen layer:
|
|
17035
17140
|
*
|
|
@@ -17069,13 +17174,13 @@ var init_noydb = __esm({
|
|
|
17069
17174
|
"#196"
|
|
17070
17175
|
);
|
|
17071
17176
|
}
|
|
17072
|
-
/** Read the persisted recovery entries (paper + Shamir). Used by `describeAuthConfig
|
|
17177
|
+
/** Read the persisted recovery entries (paper + Shamir). Used by `describeAuthConfig`. */
|
|
17073
17178
|
async listRecoveryEntries(vault) {
|
|
17074
17179
|
const paper = await loadPaperRecoveryEntries(this.options.store, vault);
|
|
17075
17180
|
const shamir = await loadShamirRecoveryEntries(this.options.store, vault);
|
|
17076
17181
|
return { paper, shamir };
|
|
17077
17182
|
}
|
|
17078
|
-
// ─── Tier-3 enroll / unlock
|
|
17183
|
+
// ─── Tier-3 enroll / unlock ─────────────────────────────────────
|
|
17079
17184
|
/**
|
|
17080
17185
|
* Register a tier-3 quick-unlock state for the vault. The state is
|
|
17081
17186
|
* an opaque blob produced by `@noy-db/on-pin/enrollPin` (or any
|
|
@@ -17111,11 +17216,11 @@ var init_noydb = __esm({
|
|
|
17111
17216
|
this.quickUnlock.delete(vault);
|
|
17112
17217
|
}
|
|
17113
17218
|
/**
|
|
17114
|
-
* Public accessor for the unlocked keyring of a vault
|
|
17219
|
+
* Public accessor for the unlocked keyring of a vault.
|
|
17115
17220
|
*
|
|
17116
17221
|
* Returns a **defensive shallow copy** so consumers can read the DEK
|
|
17117
17222
|
* map and authenticator list without the risk of mutating the hub's
|
|
17118
|
-
* internal cache
|
|
17223
|
+
* internal cache. Internal hub code paths use a live reference
|
|
17119
17224
|
* via `getKeyringInternal`; ceremonies and external consumers always
|
|
17120
17225
|
* get a snapshot.
|
|
17121
17226
|
*
|