@noy-db/hub 0.2.0-pre.4 → 0.2.0-pre.6
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 +496 -344
- 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-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-KMI2NBBF.js → chunk-JICBEFBT.js} +181 -6
- package/dist/chunk-JICBEFBT.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-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-T6HQMVML.js → chunk-W277AG6N.js} +411 -308
- package/dist/chunk-W277AG6N.js.map +1 -0
- 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-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-Cvo-xCQC.d.ts} +1 -1
- package/dist/{dev-unlock-De3mjQWv.d.cts → dev-unlock-Dy1qVpkL.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-gVn_uKhp.d.ts → hash-BAlWR4WD.d.ts} +1 -1
- package/dist/{hash-vBCB0-Ps.d.cts → hash-BgEQklQc.d.cts} +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 +75 -10
- 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 +16 -14
- 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 +538 -378
- 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 +50 -52
- 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-FY2666NY.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-CSLcfytP.d.cts → types-DVlvNn2c.d.cts} +362 -307
- package/dist/{types-D9eB0Rvh.d.ts → types-DlnZh1_i.d.ts} +362 -307
- package/dist/{ulid-CiM2OAeM.d.ts → ulid-CzPONlhG.d.ts} +19 -19
- package/dist/{ulid-CG2YvAbg.d.cts → ulid-r98nkjVd.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-B98shCV8.d.ts} +1 -1
- package/dist/{with-derivation-DWajFh4K.d.cts → with-derivation-BMQ9pIHe.d.cts} +1 -1
- package/dist/{with-guard-DF_Ul3DT.d.cts → with-guard-DUnC3JDN.d.cts} +1 -1
- package/dist/{with-guard-DR7U-l4v.d.ts → with-guard-DmT50nVG.d.ts} +1 -1
- package/dist/{with-materialized-view-qtoJ3xKJ.d.ts → with-materialized-view-Bp_M3sNG.d.ts} +2 -2
- package/dist/{with-materialized-view-_piodoIz.d.cts → with-materialized-view-eMTZ65_J.d.cts} +2 -2
- package/dist/{with-overlayed-view-DFaRfgMr.d.ts → with-overlayed-view-BoY6PB3n.d.cts} +2 -2
- package/dist/{with-overlayed-view-DwzCKxn2.d.cts → with-overlayed-view-zzSnRQmS.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-A6SWRXUQ.js +0 -118
- package/dist/chunk-A6SWRXUQ.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-KMI2NBBF.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-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-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-FY2666NY.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 }
|
|
@@ -3471,6 +3471,48 @@ var init_strategy = __esm({
|
|
|
3471
3471
|
}
|
|
3472
3472
|
});
|
|
3473
3473
|
|
|
3474
|
+
// src/i18n/core.ts
|
|
3475
|
+
function getAtPath(obj, path) {
|
|
3476
|
+
const arrayIdx = path.indexOf("[].");
|
|
3477
|
+
if (arrayIdx !== -1) {
|
|
3478
|
+
const arrayKey = path.slice(0, arrayIdx);
|
|
3479
|
+
const restPath = path.slice(arrayIdx + 3);
|
|
3480
|
+
const arr = obj[arrayKey];
|
|
3481
|
+
if (!Array.isArray(arr)) return [];
|
|
3482
|
+
return arr.flatMap((item) => {
|
|
3483
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return [];
|
|
3484
|
+
return getAtPath(item, restPath);
|
|
3485
|
+
});
|
|
3486
|
+
}
|
|
3487
|
+
const dotIdx = path.indexOf(".");
|
|
3488
|
+
if (dotIdx !== -1) {
|
|
3489
|
+
const head = path.slice(0, dotIdx);
|
|
3490
|
+
const rest = path.slice(dotIdx + 1);
|
|
3491
|
+
const nested = obj[head];
|
|
3492
|
+
if (!nested || typeof nested !== "object" || Array.isArray(nested)) return [];
|
|
3493
|
+
return getAtPath(nested, rest);
|
|
3494
|
+
}
|
|
3495
|
+
const val = obj[path];
|
|
3496
|
+
return val !== void 0 ? [val] : [];
|
|
3497
|
+
}
|
|
3498
|
+
function setAtPathInPlace(obj, path, value) {
|
|
3499
|
+
const dotIdx = path.indexOf(".");
|
|
3500
|
+
if (dotIdx !== -1) {
|
|
3501
|
+
const head = path.slice(0, dotIdx);
|
|
3502
|
+
const rest = path.slice(dotIdx + 1);
|
|
3503
|
+
const nested = obj[head];
|
|
3504
|
+
if (!nested || typeof nested !== "object" || Array.isArray(nested)) return;
|
|
3505
|
+
setAtPathInPlace(nested, rest, value);
|
|
3506
|
+
return;
|
|
3507
|
+
}
|
|
3508
|
+
obj[path] = value;
|
|
3509
|
+
}
|
|
3510
|
+
var init_core = __esm({
|
|
3511
|
+
"src/i18n/core.ts"() {
|
|
3512
|
+
"use strict";
|
|
3513
|
+
}
|
|
3514
|
+
});
|
|
3515
|
+
|
|
3474
3516
|
// src/i18n/strategy.ts
|
|
3475
3517
|
function notEnabled(op) {
|
|
3476
3518
|
return new Error(
|
|
@@ -4173,7 +4215,7 @@ var init_builder = __esm({
|
|
|
4173
4215
|
/**
|
|
4174
4216
|
* @internal — clone this Query with a declared-predicate map
|
|
4175
4217
|
* attached. Used by the materialized-view registry to enable
|
|
4176
|
-
* `.wherePredicate(name, ctx?)` for the MV's query callback
|
|
4218
|
+
* `.wherePredicate(name, ctx?)` for the MV's query callback.
|
|
4177
4219
|
* Consumers don't call this directly.
|
|
4178
4220
|
*/
|
|
4179
4221
|
_withPredicates(predicates) {
|
|
@@ -4186,7 +4228,7 @@ var init_builder = __esm({
|
|
|
4186
4228
|
);
|
|
4187
4229
|
}
|
|
4188
4230
|
/**
|
|
4189
|
-
* Filter by a registered deterministic predicate
|
|
4231
|
+
* Filter by a registered deterministic predicate. Requires
|
|
4190
4232
|
* the Query to have been augmented with a predicates map (typically
|
|
4191
4233
|
* via the materialized-view registry — bare Queries constructed
|
|
4192
4234
|
* outside an MV throw on `.wherePredicate()`).
|
|
@@ -5604,7 +5646,7 @@ var init_transaction = __esm({
|
|
|
5604
5646
|
init_errors();
|
|
5605
5647
|
init_ulid();
|
|
5606
5648
|
TxContext = class {
|
|
5607
|
-
/** Stable id for this transaction; shared by all writes it performs
|
|
5649
|
+
/** Stable id for this transaction; shared by all writes it performs. */
|
|
5608
5650
|
txId = generateULID();
|
|
5609
5651
|
/** @internal */
|
|
5610
5652
|
_ops = [];
|
|
@@ -5614,7 +5656,7 @@ var init_transaction = __esm({
|
|
|
5614
5656
|
* restore prior state via `revertExecuted`. Side-effect writes (e.g.
|
|
5615
5657
|
* recursive derivation outputs fired inside `Collection.put`) are
|
|
5616
5658
|
* appended here in execution order so they roll back alongside the
|
|
5617
|
-
* main staged ops
|
|
5659
|
+
* main staged ops.
|
|
5618
5660
|
*/
|
|
5619
5661
|
_executed = [];
|
|
5620
5662
|
/** @internal */
|
|
@@ -5914,7 +5956,7 @@ async function resolveStaleOnRead(accessor, outputCollection, id) {
|
|
|
5914
5956
|
}
|
|
5915
5957
|
if (out.kind === "array") {
|
|
5916
5958
|
console.warn(
|
|
5917
|
-
`[derivation] unexpected array-shape output "${key}" in lazy resolve path; array-shape derivations require lifecycle: "eager"
|
|
5959
|
+
`[derivation] unexpected array-shape output "${key}" in lazy resolve path; array-shape derivations require lifecycle: "eager".`
|
|
5918
5960
|
);
|
|
5919
5961
|
continue;
|
|
5920
5962
|
}
|
|
@@ -6436,81 +6478,6 @@ var init_stale2 = __esm({
|
|
|
6436
6478
|
}
|
|
6437
6479
|
});
|
|
6438
6480
|
|
|
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
6481
|
// src/derivations/fanout-sidecar.ts
|
|
6515
6482
|
var fanout_sidecar_exports = {};
|
|
6516
6483
|
__export(fanout_sidecar_exports, {
|
|
@@ -6617,6 +6584,7 @@ var init_collection = __esm({
|
|
|
6617
6584
|
"use strict";
|
|
6618
6585
|
init_types();
|
|
6619
6586
|
init_strategy();
|
|
6587
|
+
init_core();
|
|
6620
6588
|
init_strategy2();
|
|
6621
6589
|
init_crypto();
|
|
6622
6590
|
init_errors();
|
|
@@ -6648,6 +6616,7 @@ var init_collection = __esm({
|
|
|
6648
6616
|
schemaUpdateGate;
|
|
6649
6617
|
schemaFence;
|
|
6650
6618
|
writeHooks;
|
|
6619
|
+
subsystemBus;
|
|
6651
6620
|
activeTxId;
|
|
6652
6621
|
getDEK;
|
|
6653
6622
|
onDirty;
|
|
@@ -6836,42 +6805,14 @@ var init_collection = __esm({
|
|
|
6836
6805
|
syncAdapter;
|
|
6837
6806
|
/** — consent-audit hook, no-op when no scope is active. */
|
|
6838
6807
|
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
6808
|
/**
|
|
6866
6809
|
* Vault-internal hook for derivation dispatch. When set,
|
|
6867
6810
|
* `Collection.put` consults the registry after the source-write
|
|
6868
6811
|
* commits and writes derived outputs through `getCollection(name).put`.
|
|
6869
|
-
* Same structural-interface pattern as `guardSource` to avoid a
|
|
6870
|
-
* circular Vault import.
|
|
6871
6812
|
*/
|
|
6872
6813
|
derivationSource;
|
|
6873
6814
|
/**
|
|
6874
|
-
* Vault-internal hook for materialized-view dispatch
|
|
6815
|
+
* Vault-internal hook for materialized-view dispatch.
|
|
6875
6816
|
* Parallel to `derivationSource` — when set, `Collection.put` fires
|
|
6876
6817
|
* `MaterializedViewRegistry.onSourceWrite` after the source-write
|
|
6877
6818
|
* commits + after `dispatchDerivations` has run.
|
|
@@ -6927,6 +6868,7 @@ var init_collection = __esm({
|
|
|
6927
6868
|
this.schemaUpdateGate = opts.schemaUpdateGate;
|
|
6928
6869
|
this.schemaFence = opts.schemaFence;
|
|
6929
6870
|
this.writeHooks = opts.writeHooks;
|
|
6871
|
+
this.subsystemBus = opts.subsystemBus;
|
|
6930
6872
|
this.activeTxId = opts.activeTxId;
|
|
6931
6873
|
this.blobStrategy = opts.blobStrategy ?? NO_BLOBS;
|
|
6932
6874
|
this.aggregateStrategy = opts.aggregateStrategy ?? NO_AGGREGATE;
|
|
@@ -6951,8 +6893,6 @@ var init_collection = __esm({
|
|
|
6951
6893
|
this.crdtMode = opts.crdt;
|
|
6952
6894
|
this.syncAdapter = opts.syncAdapter;
|
|
6953
6895
|
this.onAccess = opts.onAccess;
|
|
6954
|
-
this.periodGuard = opts.periodGuard;
|
|
6955
|
-
this.guardSource = opts.guardSource;
|
|
6956
6896
|
this.derivationSource = opts.derivationSource;
|
|
6957
6897
|
this.materializedViewSource = opts.materializedViewSource;
|
|
6958
6898
|
this.tiers = opts.tiers && opts.tiers.length > 0 ? new Set(opts.tiers) : null;
|
|
@@ -7154,21 +7094,23 @@ var init_collection = __esm({
|
|
|
7154
7094
|
}
|
|
7155
7095
|
/**
|
|
7156
7096
|
* Create or update a record. Runs inside the hub's write-queue tracker
|
|
7157
|
-
*
|
|
7097
|
+
* so `hub.writeQueue.pending` reflects this write.
|
|
7158
7098
|
*
|
|
7159
7099
|
* @param id Record identifier.
|
|
7160
7100
|
* @param record The record body (validated by the collection's schema
|
|
7161
7101
|
* if one was attached at `vault.collection(...)` time).
|
|
7162
7102
|
* @param options Optional metadata for audit + import workflows.
|
|
7163
7103
|
* `reason` is stamped onto the resulting ledger entry
|
|
7164
|
-
*
|
|
7104
|
+
* so audit consumers can filter via
|
|
7165
7105
|
* `entries.filter(e => e.reason?.startsWith('import:'))`.
|
|
7166
7106
|
*/
|
|
7167
7107
|
async put(id, record, options) {
|
|
7168
7108
|
await this.schemaUpdateGate?.assertWritable();
|
|
7169
7109
|
await this.schemaFence?.assertWritable(this.name);
|
|
7110
|
+
const hooksActive = this.#hooksActive();
|
|
7111
|
+
const busAfterPut = (this.subsystemBus?.hasHandlers("afterPut") ?? false) && !(this.subsystemBus?.dispatching ?? false);
|
|
7170
7112
|
let event;
|
|
7171
|
-
if (
|
|
7113
|
+
if (hooksActive || busAfterPut) {
|
|
7172
7114
|
const prior = await this.#priorForHook(id);
|
|
7173
7115
|
event = {
|
|
7174
7116
|
op: prior.record === null ? "create" : "update",
|
|
@@ -7183,23 +7125,26 @@ var init_collection = __esm({
|
|
|
7183
7125
|
baseVersion: prior.version,
|
|
7184
7126
|
version: prior.version + 1
|
|
7185
7127
|
};
|
|
7186
|
-
await this.writeHooks.runBefore(event);
|
|
7128
|
+
if (hooksActive) await this.writeHooks.runBefore(event);
|
|
7187
7129
|
}
|
|
7188
7130
|
if (this.writeQueue) await this.writeQueue.track(() => this.putInternal(id, record, options));
|
|
7189
7131
|
else await this.putInternal(id, record, options);
|
|
7190
|
-
if (event)
|
|
7132
|
+
if (event) {
|
|
7133
|
+
if (hooksActive) await this.writeHooks.runAfter(event);
|
|
7134
|
+
if (busAfterPut) await this.subsystemBus.dispatch("afterPut", event);
|
|
7135
|
+
}
|
|
7191
7136
|
}
|
|
7192
|
-
/** @internal
|
|
7137
|
+
/** @internal — true when hooks should fire for this write (handlers exist, not re-entrant). */
|
|
7193
7138
|
#hooksActive() {
|
|
7194
7139
|
return this.writeHooks !== void 0 && this.writeHooks.hasHandlers && !this.writeHooks.suppressed;
|
|
7195
7140
|
}
|
|
7196
7141
|
/**
|
|
7197
|
-
* @internal
|
|
7142
|
+
* @internal — resolve the prior record for a hook's `before` and
|
|
7198
7143
|
* its version. Critically, this uses the SAME basis `putInternal` writes from
|
|
7199
7144
|
* (the in-memory cache in eager mode; lru-then-adapter in lazy) — NOT a fresh
|
|
7200
7145
|
* store read — so `baseVersion`/`version` match the version actually written.
|
|
7201
7146
|
* A separate store read would diverge once another tab has advanced the shared
|
|
7202
|
-
* store past this tab's cache, breaking
|
|
7147
|
+
* store past this tab's cache, breaking cross-tab conflict detection.
|
|
7203
7148
|
*/
|
|
7204
7149
|
async #priorForHook(id) {
|
|
7205
7150
|
if (this.lazy && this.lru) {
|
|
@@ -7221,52 +7166,28 @@ var init_collection = __esm({
|
|
|
7221
7166
|
if (!hasWritePermission(this.keyring, this.name)) {
|
|
7222
7167
|
throw new ReadOnlyError();
|
|
7223
7168
|
}
|
|
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) {
|
|
7169
|
+
if (this.subsystemBus?.hasGateHandlers("beforePut")) {
|
|
7257
7170
|
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
7258
|
-
let
|
|
7171
|
+
let existingRecord = null;
|
|
7259
7172
|
if (existingEnv) {
|
|
7260
7173
|
try {
|
|
7261
|
-
|
|
7174
|
+
existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
|
|
7262
7175
|
} catch {
|
|
7263
|
-
|
|
7176
|
+
existingRecord = null;
|
|
7264
7177
|
}
|
|
7265
7178
|
}
|
|
7266
|
-
await this.
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7179
|
+
await this.subsystemBus.dispatchGate("beforePut", {
|
|
7180
|
+
op: existingEnv ? "update" : "create",
|
|
7181
|
+
vault: this.vault,
|
|
7182
|
+
collection: this.name,
|
|
7183
|
+
docId: id,
|
|
7184
|
+
incoming: record,
|
|
7185
|
+
existing: existingRecord,
|
|
7186
|
+
existingVersion: existingEnv?._v ?? 0,
|
|
7187
|
+
existingTs: existingEnv?._ts,
|
|
7188
|
+
userId: this.keyring.userId,
|
|
7189
|
+
role: this.keyring.role
|
|
7190
|
+
});
|
|
7270
7191
|
}
|
|
7271
7192
|
if (this.schema !== void 0) {
|
|
7272
7193
|
record = await validateSchemaInput(this.schema, record, `put(${id})`);
|
|
@@ -7275,7 +7196,9 @@ var init_collection = __esm({
|
|
|
7275
7196
|
const obj = record;
|
|
7276
7197
|
for (const [field, descriptor] of Object.entries(this.i18nFields)) {
|
|
7277
7198
|
if (!descriptor.options.autoTranslate) continue;
|
|
7278
|
-
const
|
|
7199
|
+
const leafValues = getAtPath(obj, field);
|
|
7200
|
+
if (leafValues.length !== 1) continue;
|
|
7201
|
+
const value = leafValues[0];
|
|
7279
7202
|
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
7280
7203
|
const map = value;
|
|
7281
7204
|
const { languages, required } = descriptor.options;
|
|
@@ -7299,8 +7222,7 @@ var init_collection = __esm({
|
|
|
7299
7222
|
this.name
|
|
7300
7223
|
);
|
|
7301
7224
|
}
|
|
7302
|
-
;
|
|
7303
|
-
record[field] = translated;
|
|
7225
|
+
setAtPathInPlace(obj, field, translated);
|
|
7304
7226
|
}
|
|
7305
7227
|
}
|
|
7306
7228
|
if (this.i18nPutValidator !== void 0) {
|
|
@@ -7452,7 +7374,7 @@ var init_collection = __esm({
|
|
|
7452
7374
|
* Fire registered MV strategies whose dependency set includes this
|
|
7453
7375
|
* collection. Eager-mode MVs re-materialize inline via
|
|
7454
7376
|
* `MaterializedViewExecutor.refresh`; lazy / manual modes are
|
|
7455
|
-
* no-ops in the foundation
|
|
7377
|
+
* no-ops in the foundation; wired in the lazy-mode implementation.
|
|
7456
7378
|
*
|
|
7457
7379
|
* Skips entirely when the record being written is itself an
|
|
7458
7380
|
* MV-emitted row (carries `_materializedFrom`) — defensive guard
|
|
@@ -7596,13 +7518,15 @@ var init_collection = __esm({
|
|
|
7596
7518
|
}
|
|
7597
7519
|
/**
|
|
7598
7520
|
* Delete a record by ID. Runs inside the hub's write-queue tracker
|
|
7599
|
-
*
|
|
7521
|
+
* so `hub.writeQueue.pending` reflects this write.
|
|
7600
7522
|
*/
|
|
7601
7523
|
async delete(id) {
|
|
7602
7524
|
await this.schemaUpdateGate?.assertWritable();
|
|
7603
7525
|
await this.schemaFence?.assertWritable(this.name);
|
|
7526
|
+
const hooksActive = this.#hooksActive();
|
|
7527
|
+
const busAfterDelete = (this.subsystemBus?.hasHandlers("afterDelete") ?? false) && !(this.subsystemBus?.dispatching ?? false);
|
|
7604
7528
|
let event;
|
|
7605
|
-
if (
|
|
7529
|
+
if (hooksActive || busAfterDelete) {
|
|
7606
7530
|
const prior = await this.#priorForHook(id);
|
|
7607
7531
|
event = {
|
|
7608
7532
|
op: "delete",
|
|
@@ -7617,14 +7541,17 @@ var init_collection = __esm({
|
|
|
7617
7541
|
baseVersion: prior.version,
|
|
7618
7542
|
version: prior.version + 1
|
|
7619
7543
|
};
|
|
7620
|
-
await this.writeHooks.runBefore(event);
|
|
7544
|
+
if (hooksActive) await this.writeHooks.runBefore(event);
|
|
7621
7545
|
}
|
|
7622
7546
|
if (this.writeQueue) await this.writeQueue.track(() => this.deleteInternal(id));
|
|
7623
7547
|
else await this.deleteInternal(id);
|
|
7624
|
-
if (event)
|
|
7548
|
+
if (event) {
|
|
7549
|
+
if (hooksActive) await this.writeHooks.runAfter(event);
|
|
7550
|
+
if (busAfterDelete) await this.subsystemBus.dispatch("afterDelete", event);
|
|
7551
|
+
}
|
|
7625
7552
|
}
|
|
7626
7553
|
/**
|
|
7627
|
-
* @internal
|
|
7554
|
+
* @internal — bulk-rewrite every record through a cutover transform.
|
|
7628
7555
|
* Raw adapter path (bypasses the write gate + guards — the transform is
|
|
7629
7556
|
* trusted and runs only during the `migrating` phase). Bumps each
|
|
7630
7557
|
* record's `_v` and appends a ledger `op:'migration'` entry.
|
|
@@ -7663,8 +7590,7 @@ var init_collection = __esm({
|
|
|
7663
7590
|
}
|
|
7664
7591
|
/**
|
|
7665
7592
|
* @internal — system-internal delete that bypasses user-facing
|
|
7666
|
-
* delete hooks (`onDelete`,
|
|
7667
|
-
* enforcer). Used by derivation tombstones (#144) and MV refresh
|
|
7593
|
+
* delete hooks (`onDelete`, FK ref enforcer). Used by derivation tombstones and MV refresh
|
|
7668
7594
|
* (Dim 14 v2) — system housekeeping shouldn't trip user invariants
|
|
7669
7595
|
* registered against the output collection. The ledger entry and
|
|
7670
7596
|
* history snapshot still fire so backup integrity and time-travel
|
|
@@ -7676,7 +7602,7 @@ var init_collection = __esm({
|
|
|
7676
7602
|
*
|
|
7677
7603
|
* When a `txCtx` is supplied, the prior envelope is captured and
|
|
7678
7604
|
* pushed onto `txCtx._executed` BEFORE the delete fires — mirrors
|
|
7679
|
-
* the
|
|
7605
|
+
* the rollback hardening for puts. Callers outside a
|
|
7680
7606
|
* multi-record transaction pass `null` and skip the tracking.
|
|
7681
7607
|
*
|
|
7682
7608
|
* Amendment composition: if `_internalDelete` runs while a vault's
|
|
@@ -7719,58 +7645,27 @@ var init_collection = __esm({
|
|
|
7719
7645
|
if (!hasWritePermission(this.keyring, this.name)) {
|
|
7720
7646
|
throw new ReadOnlyError();
|
|
7721
7647
|
}
|
|
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) {
|
|
7648
|
+
if (this.subsystemBus?.hasGateHandlers("beforeDelete")) {
|
|
7761
7649
|
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
7762
|
-
let priorRecord = null;
|
|
7763
7650
|
if (existingEnv) {
|
|
7651
|
+
let existingRecord = null;
|
|
7764
7652
|
try {
|
|
7765
|
-
|
|
7653
|
+
existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
|
|
7766
7654
|
} catch {
|
|
7767
|
-
|
|
7655
|
+
existingRecord = null;
|
|
7768
7656
|
}
|
|
7657
|
+
await this.subsystemBus.dispatchGate("beforeDelete", {
|
|
7658
|
+
vault: this.vault,
|
|
7659
|
+
collection: this.name,
|
|
7660
|
+
docId: id,
|
|
7661
|
+
existing: existingRecord,
|
|
7662
|
+
existingVersion: existingEnv._v,
|
|
7663
|
+
existingTs: existingEnv._ts,
|
|
7664
|
+
internal,
|
|
7665
|
+
userId: this.keyring.userId,
|
|
7666
|
+
role: this.keyring.role
|
|
7667
|
+
});
|
|
7769
7668
|
}
|
|
7770
|
-
await this.periodGuard(
|
|
7771
|
-
existingEnv ? { ts: existingEnv._ts, record: priorRecord } : null,
|
|
7772
|
-
null
|
|
7773
|
-
);
|
|
7774
7669
|
}
|
|
7775
7670
|
if (!internal && this.refEnforcer !== void 0) {
|
|
7776
7671
|
await this.refEnforcer.enforceRefsOnDelete(this.name, id);
|
|
@@ -7831,7 +7726,7 @@ var init_collection = __esm({
|
|
|
7831
7726
|
}
|
|
7832
7727
|
/**
|
|
7833
7728
|
* Cascade deletes of array-shape derived rows when a source row is
|
|
7834
|
-
* deleted
|
|
7729
|
+
* deleted. Reads each registered strategy's fanout sidecar
|
|
7835
7730
|
* for this source id, deletes every listed derived row, then
|
|
7836
7731
|
* deletes the sidecar itself.
|
|
7837
7732
|
*
|
|
@@ -7870,8 +7765,8 @@ var init_collection = __esm({
|
|
|
7870
7765
|
}
|
|
7871
7766
|
}
|
|
7872
7767
|
/**
|
|
7873
|
-
* Mirror of {@link dispatchMaterializedViews} for the delete path
|
|
7874
|
-
*
|
|
7768
|
+
* Mirror of {@link dispatchMaterializedViews} for the delete path.
|
|
7769
|
+
* No record content is available (it's gone), so the
|
|
7875
7770
|
* `_materializedFrom` skip used by the put-side dispatch doesn't
|
|
7876
7771
|
* apply here — instead, the recursion guard is the `internal` gate
|
|
7877
7772
|
* at the `_doDelete` call site above.
|
|
@@ -8401,7 +8296,7 @@ var init_collection = __esm({
|
|
|
8401
8296
|
* .aggregate({ total: sum('amount'), n: count() })
|
|
8402
8297
|
* ```
|
|
8403
8298
|
*
|
|
8404
|
-
* **Lazy-MV gap
|
|
8299
|
+
* **Lazy-MV gap:** `scan()` is synchronous-build and does
|
|
8405
8300
|
* NOT trigger lazy materialized-view resolve-on-read. For lazy
|
|
8406
8301
|
* MVs, call `list()` (which DOES resolve) or `vault.refreshView(name)`
|
|
8407
8302
|
* before scanning. Same shape as the `query()` limitation.
|
|
@@ -8482,7 +8377,7 @@ var init_collection = __esm({
|
|
|
8482
8377
|
this.indexes?.upsert(id, record, previous ? previous.record : null);
|
|
8483
8378
|
}
|
|
8484
8379
|
/**
|
|
8485
|
-
*
|
|
8380
|
+
* Apply a peer tab's committed write to THIS tab's in-memory view:
|
|
8486
8381
|
* re-read the (already-persisted) envelope from the shared store + refresh
|
|
8487
8382
|
* cache/indexes, then emit a `change` event so reactive consumers re-render.
|
|
8488
8383
|
* Never writes to the store and never fires write hooks, so it cannot loop.
|
|
@@ -8491,7 +8386,7 @@ var init_collection = __esm({
|
|
|
8491
8386
|
await this._invalidateCacheEntry(id);
|
|
8492
8387
|
this.emitter.emit("change", { vault: this.vault, collection: this.name, id, action });
|
|
8493
8388
|
}
|
|
8494
|
-
/** @internal
|
|
8389
|
+
/** @internal — the current in-memory record without a store read (for conflict capture). */
|
|
8495
8390
|
_peekCached(id) {
|
|
8496
8391
|
const entry = this.lazy && this.lru ? this.lru.get(id) : this.cache.get(id);
|
|
8497
8392
|
return entry ? entry.record : null;
|
|
@@ -9511,7 +9406,7 @@ var init_virtual_collection = __esm({
|
|
|
9511
9406
|
// error pointing at the relevant issue — so consumers don't hit a
|
|
9512
9407
|
// cryptic `undefined is not a function` runtime crash.
|
|
9513
9408
|
//
|
|
9514
|
-
//
|
|
9409
|
+
// Throw-stubs so consumers get actionable errors rather than cryptic crashes.
|
|
9515
9410
|
/** @throws — chainable Query<T> over a virtual collection is deferred. */
|
|
9516
9411
|
query() {
|
|
9517
9412
|
throw new Error(
|
|
@@ -10087,7 +9982,7 @@ var init_api = __esm({
|
|
|
10087
9982
|
* the envelope on first call. Optimistic-concurrency safe — a stale
|
|
10088
9983
|
* `_v` (parallel writer on another device) throws `ConflictError`.
|
|
10089
9984
|
*
|
|
10090
|
-
* Patch semantics
|
|
9985
|
+
* Patch semantics:
|
|
10091
9986
|
* - `undefined` (or omitted key) — skip; existing value preserved
|
|
10092
9987
|
* - `null` — delete the field from the merged result
|
|
10093
9988
|
* - any other value — overwrite (deep-merge for plain objects,
|
|
@@ -10143,7 +10038,7 @@ var init_api = __esm({
|
|
|
10143
10038
|
this.fireChange(this.writerKeyringId, written);
|
|
10144
10039
|
return written;
|
|
10145
10040
|
}
|
|
10146
|
-
// ─── Visibility
|
|
10041
|
+
// ─── Visibility ──────────────────────────────────────────────────────
|
|
10147
10042
|
/**
|
|
10148
10043
|
* Read the current user's visibility flag from
|
|
10149
10044
|
* `_meta/visibility/<keyringId>`. Returns `{ hidden: false }` when no
|
|
@@ -11204,7 +11099,7 @@ var init_registry2 = __esm({
|
|
|
11204
11099
|
guardsFor(collection) {
|
|
11205
11100
|
return this._byCollection.get(collection) ?? [];
|
|
11206
11101
|
}
|
|
11207
|
-
/** Per-collection guard counts, for introspection
|
|
11102
|
+
/** Per-collection guard counts, for introspection. */
|
|
11208
11103
|
summary() {
|
|
11209
11104
|
return [...this._byCollection.entries()].map(([collection, guards]) => ({
|
|
11210
11105
|
collection,
|
|
@@ -11599,6 +11494,7 @@ var init_vault = __esm({
|
|
|
11599
11494
|
init_strategy9();
|
|
11600
11495
|
init_refs();
|
|
11601
11496
|
init_dictionary();
|
|
11497
|
+
init_core();
|
|
11602
11498
|
init_strategy2();
|
|
11603
11499
|
init_sync_strategy();
|
|
11604
11500
|
init_errors();
|
|
@@ -11662,23 +11558,23 @@ var init_vault = __esm({
|
|
|
11662
11558
|
* `null` for vaults that never register any guard strategy. The
|
|
11663
11559
|
* runtime class is dynamic-imported on demand so consumers that
|
|
11664
11560
|
* never use guards don't pull `GuardRegistry`/`GuardExecutor` into
|
|
11665
|
-
* their bundle
|
|
11561
|
+
* their bundle.
|
|
11666
11562
|
*/
|
|
11667
11563
|
guardRegistry = null;
|
|
11668
11564
|
/**
|
|
11669
11565
|
* Per-vault derivation registry. Same lazy-load contract as
|
|
11670
11566
|
* `guardRegistry` — `null` until `_initDerivations()` runs with at
|
|
11671
|
-
* least one strategy handle.
|
|
11567
|
+
* least one strategy handle.
|
|
11672
11568
|
*/
|
|
11673
11569
|
derivationRegistry = null;
|
|
11674
11570
|
/**
|
|
11675
|
-
* Per-vault materialized-view registry
|
|
11571
|
+
* Per-vault materialized-view registry. Same lazy-load
|
|
11676
11572
|
* contract as `derivationRegistry` — `null` until
|
|
11677
11573
|
* `_initMaterializedViews()` runs with at least one MV handle.
|
|
11678
11574
|
*/
|
|
11679
11575
|
materializedViewRegistry = null;
|
|
11680
11576
|
/**
|
|
11681
|
-
* Per-vault overlay registry
|
|
11577
|
+
* Per-vault overlay registry. Same lazy-load contract as
|
|
11682
11578
|
* `materializedViewRegistry` — `null` until `_initOverlayedViews()`
|
|
11683
11579
|
* runs with at least one handle.
|
|
11684
11580
|
*/
|
|
@@ -11699,7 +11595,7 @@ var init_vault = __esm({
|
|
|
11699
11595
|
* target this vault session's keyringId. There is no method to write
|
|
11700
11596
|
* another principal's envelope (own-only write rule, structural).
|
|
11701
11597
|
* - Read-anyone: `get(keyringId)`, `list()` — read other principals'
|
|
11702
|
-
* envelopes, subject to the `view-team-profiles` policy gate
|
|
11598
|
+
* envelopes, subject to the `view-team-profiles` policy gate.
|
|
11703
11599
|
* - Reactive: `subscribe(id, cb)`, `live(id)` — fire on local writes.
|
|
11704
11600
|
*
|
|
11705
11601
|
* @see docs/superpowers/specs/2026-05-05-user-envelope-design.md
|
|
@@ -11719,12 +11615,12 @@ var init_vault = __esm({
|
|
|
11719
11615
|
*/
|
|
11720
11616
|
reloadKeyring;
|
|
11721
11617
|
collectionCache = /* @__PURE__ */ new Map();
|
|
11722
|
-
/**
|
|
11618
|
+
/** Vault-level schema cutover fence/controller. */
|
|
11723
11619
|
schemaFence;
|
|
11724
|
-
/**
|
|
11620
|
+
/** Per-client heartbeat/watcher; started lazily on cutover registration. */
|
|
11725
11621
|
#fenceWatcher;
|
|
11726
11622
|
#fenceCoordinationStarted = false;
|
|
11727
|
-
/**
|
|
11623
|
+
/** Per-collection registered schema-update strategy names. */
|
|
11728
11624
|
#schemaUpdateNames = /* @__PURE__ */ new Map();
|
|
11729
11625
|
/**
|
|
11730
11626
|
* per-collection `blobFields` retention/TTL config.
|
|
@@ -11798,8 +11694,7 @@ var init_vault = __esm({
|
|
|
11798
11694
|
* Cache of closed/opened accounting periods.
|
|
11799
11695
|
* Populated on first `closePeriod` / `openPeriod` / `listPeriods` /
|
|
11800
11696
|
* per-collection write call. Kept in memory as an ordered list (by
|
|
11801
|
-
* `closedAt`) so
|
|
11802
|
-
* each collection's put/delete path.
|
|
11697
|
+
* `closedAt`) so period checks run fast when the gate bus fires.
|
|
11803
11698
|
*
|
|
11804
11699
|
* Sentinel `null` means "not yet loaded" — the first consumer
|
|
11805
11700
|
* triggers a one-time `loadPeriods()` pass. Every subsequent
|
|
@@ -11994,6 +11889,7 @@ var init_vault = __esm({
|
|
|
11994
11889
|
emitter: this.emitter,
|
|
11995
11890
|
writeQueue: this.noydb._writeQueueTracker,
|
|
11996
11891
|
writeHooks: this.noydb._writeHooks,
|
|
11892
|
+
subsystemBus: this.noydb._subsystemBus,
|
|
11997
11893
|
activeTxId: () => this.noydb._activeTxContextOrNull?.txId ?? null,
|
|
11998
11894
|
schemaUpdateGate,
|
|
11999
11895
|
schemaFence: this.schemaFence,
|
|
@@ -12016,18 +11912,12 @@ var init_vault = __esm({
|
|
|
12016
11912
|
defaultLocale: this.locale,
|
|
12017
11913
|
onRegisterConflictResolver: this.onRegisterConflictResolver,
|
|
12018
11914
|
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
|
-
} : {},
|
|
11915
|
+
// Derivation source is only wired when the corresponding registry
|
|
11916
|
+
// has been initialised. Guard source was removed in Track A slice 3b
|
|
11917
|
+
// — guards now run via the gate bus in Noydb.#registerGuardGate.
|
|
11918
|
+
// Vaults without derivations skip this so `Collection.put`'s
|
|
11919
|
+
// `if (this.derivationSource)` branch no-ops without touching the
|
|
11920
|
+
// derivation subsystem code.
|
|
12031
11921
|
...this.derivationRegistry !== null ? {
|
|
12032
11922
|
derivationSource: {
|
|
12033
11923
|
registry: () => this.derivationRegistry,
|
|
@@ -12117,7 +12007,7 @@ var init_vault = __esm({
|
|
|
12117
12007
|
await Promise.allSettled(pending);
|
|
12118
12008
|
}
|
|
12119
12009
|
/**
|
|
12120
|
-
* Run a coordinated schema cutover
|
|
12010
|
+
* Run a coordinated schema cutover. Drains pending writes, waits
|
|
12121
12011
|
* for the active client set to quiesce (the ack-barrier), applies every
|
|
12122
12012
|
* pending collection transform in bulk, bumps the vault schema generation,
|
|
12123
12013
|
* and clears the fence. Returns the count of collections migrated.
|
|
@@ -12135,9 +12025,9 @@ var init_vault = __esm({
|
|
|
12135
12025
|
await coll._applyCutoverTransform(transform);
|
|
12136
12026
|
}
|
|
12137
12027
|
/**
|
|
12138
|
-
*
|
|
12028
|
+
* Refresh a loaded collection's view of one document from a peer
|
|
12139
12029
|
* tab's broadcast. No-op when the collection isn't loaded in this tab
|
|
12140
|
-
* (it will read fresh on next open). Mirrors
|
|
12030
|
+
* (it will read fresh on next open). Mirrors `#runCutoverTransform`'s guard.
|
|
12141
12031
|
*/
|
|
12142
12032
|
async _applyRemoteWrite(collectionName, docId, action) {
|
|
12143
12033
|
const coll = this.collectionCache.get(collectionName);
|
|
@@ -12145,9 +12035,9 @@ var init_vault = __esm({
|
|
|
12145
12035
|
await coll._applyRemoteChange(docId, action);
|
|
12146
12036
|
}
|
|
12147
12037
|
/**
|
|
12148
|
-
*
|
|
12038
|
+
* For a detected conflict: capture this tab's clobbered record,
|
|
12149
12039
|
* read the common ancestor from history, converge the cache to the store's
|
|
12150
|
-
* authoritative value (the
|
|
12040
|
+
* authoritative value (the re-read), and return all three for the
|
|
12151
12041
|
* WriteConflict payload. Returns null when the collection isn't loaded.
|
|
12152
12042
|
*/
|
|
12153
12043
|
async _captureAndConverge(collectionName, docId, action, baseV) {
|
|
@@ -12164,15 +12054,15 @@ var init_vault = __esm({
|
|
|
12164
12054
|
const remote = await coll.get(docId);
|
|
12165
12055
|
return { local, remote, base };
|
|
12166
12056
|
}
|
|
12167
|
-
/** Recover a stuck cutover fence
|
|
12057
|
+
/** Recover a stuck cutover fence — reset to normal without bumping. */
|
|
12168
12058
|
async abortSchemaCutover() {
|
|
12169
12059
|
await this.schemaFence.abort();
|
|
12170
12060
|
}
|
|
12171
|
-
/** Current schema-cutover fence state for this vault
|
|
12061
|
+
/** Current schema-cutover fence state for this vault. Thin live read. */
|
|
12172
12062
|
async schemaFenceState() {
|
|
12173
12063
|
return loadFence(this.adapter, this.name);
|
|
12174
12064
|
}
|
|
12175
|
-
/** @internal Start the per-client heartbeat + fence watcher once a cutover is registered
|
|
12065
|
+
/** @internal Start the per-client heartbeat + fence watcher once a cutover is registered. */
|
|
12176
12066
|
_ensureFenceCoordination() {
|
|
12177
12067
|
if (this.#fenceCoordinationStarted) return;
|
|
12178
12068
|
this.#fenceCoordinationStarted = true;
|
|
@@ -12208,9 +12098,11 @@ var init_vault = __esm({
|
|
|
12208
12098
|
if (!record || typeof record !== "object") return;
|
|
12209
12099
|
const obj = record;
|
|
12210
12100
|
for (const [field, descriptor] of Object.entries(i18nFields)) {
|
|
12211
|
-
const
|
|
12212
|
-
|
|
12213
|
-
|
|
12101
|
+
const values = getAtPath(obj, field);
|
|
12102
|
+
for (const value of values) {
|
|
12103
|
+
if (value === void 0 || value === null) continue;
|
|
12104
|
+
this.i18nStrategy.validateI18nTextValue(value, field, descriptor);
|
|
12105
|
+
}
|
|
12214
12106
|
}
|
|
12215
12107
|
}
|
|
12216
12108
|
/**
|
|
@@ -12845,7 +12737,7 @@ var init_vault = __esm({
|
|
|
12845
12737
|
* Dynamic-imports `GuardRegistry` + `ReadOnlyVaultFacade` and seeds
|
|
12846
12738
|
* the registry with the supplied strategy handles. No-op when the
|
|
12847
12739
|
* handles array is empty — keeps the guard subsystem out of the
|
|
12848
|
-
* floor bundle for consumers that don't use guards
|
|
12740
|
+
* floor bundle for consumers that don't use guards.
|
|
12849
12741
|
*
|
|
12850
12742
|
* The read-only facade is eagerly instantiated here so the sync
|
|
12851
12743
|
* accessor `_getReadOnlyFacade()` (called from the tx amendment
|
|
@@ -12863,10 +12755,9 @@ var init_vault = __esm({
|
|
|
12863
12755
|
this.readOnlyFacade = new ReadOnlyVaultFacade2(this);
|
|
12864
12756
|
}
|
|
12865
12757
|
/**
|
|
12866
|
-
* @internal —
|
|
12867
|
-
* vaults that never registered any guard
|
|
12868
|
-
* gate on null
|
|
12869
|
-
* `Collection` already do this transitively).
|
|
12758
|
+
* @internal — The gate handler in Noydb.#registerGuardGate calls into
|
|
12759
|
+
* this. Returns `null` for vaults that never registered any guard
|
|
12760
|
+
* strategy. Callers MUST gate on null.
|
|
12870
12761
|
*/
|
|
12871
12762
|
_getGuardRegistry() {
|
|
12872
12763
|
return this.guardRegistry;
|
|
@@ -12877,7 +12768,7 @@ var init_vault = __esm({
|
|
|
12877
12768
|
* derivation strategies (async because `strategyHash` computation
|
|
12878
12769
|
* goes through `crypto.subtle.digest`). No-op when the handles
|
|
12879
12770
|
* array is empty — keeps the derivation subsystem out of the floor
|
|
12880
|
-
* bundle for consumers that don't use derivations
|
|
12771
|
+
* bundle for consumers that don't use derivations. Throws
|
|
12881
12772
|
* `DerivationCycleError` if a cycle is detected after registration.
|
|
12882
12773
|
*/
|
|
12883
12774
|
async _initDerivations(handles) {
|
|
@@ -12909,7 +12800,7 @@ var init_vault = __esm({
|
|
|
12909
12800
|
* MV spec (which invokes its `query()` once for dependency
|
|
12910
12801
|
* analysis), then runs the unified cycle detection across the MV +
|
|
12911
12802
|
* derivation graphs. No-op when the handles array is empty — keeps
|
|
12912
|
-
* the MV subsystem out of the floor bundle (mirrors
|
|
12803
|
+
* the MV subsystem out of the floor bundle (mirrors the derivation lazy-import pattern).
|
|
12913
12804
|
* Throws `MaterializedViewCycleError` if a cycle is detected.
|
|
12914
12805
|
*/
|
|
12915
12806
|
async _initMaterializedViews(handles) {
|
|
@@ -12966,13 +12857,13 @@ var init_vault = __esm({
|
|
|
12966
12857
|
return this.overlayedViewRegistry;
|
|
12967
12858
|
}
|
|
12968
12859
|
/**
|
|
12969
|
-
* Manual re-materialize for a single registered MV
|
|
12860
|
+
* Manual re-materialize for a single registered MV. Useful
|
|
12970
12861
|
* for `refresh: 'manual'` MVs (whose consumer drives refreshes
|
|
12971
12862
|
* externally), for stale-bit recovery on vault re-open, and as the
|
|
12972
12863
|
* explicit bulk-recompute escape hatch after a strategy change.
|
|
12973
12864
|
*
|
|
12974
|
-
* Returns `{ written, deleted, failed }`. `deleted` is always 0
|
|
12975
|
-
*
|
|
12865
|
+
* Returns `{ written, deleted, failed }`. `deleted` is always 0
|
|
12866
|
+
* when tombstoning is not enabled.
|
|
12976
12867
|
*
|
|
12977
12868
|
* Throws if `name` is not a registered MV.
|
|
12978
12869
|
*/
|
|
@@ -13068,22 +12959,19 @@ var init_vault = __esm({
|
|
|
13068
12959
|
/**
|
|
13069
12960
|
* @internal — exposed for `runTransaction({ amendment: true })` so
|
|
13070
12961
|
* the amendment invariant runner can pass the SAME read-only vault
|
|
13071
|
-
* facade that the
|
|
13072
|
-
*
|
|
13073
|
-
* `
|
|
13074
|
-
*
|
|
13075
|
-
*
|
|
12962
|
+
* facade that the gate handler in Noydb.#registerGuardGate uses.
|
|
12963
|
+
* Eagerly instantiated by `_initGuards()` so this accessor stays
|
|
12964
|
+
* synchronous; returns `null` for vaults that never registered any
|
|
12965
|
+
* guard (amendments require at least one guard, so the caller should
|
|
12966
|
+
* never see null).
|
|
13076
12967
|
*/
|
|
13077
12968
|
_getReadOnlyFacade() {
|
|
13078
12969
|
return this.readOnlyFacade;
|
|
13079
12970
|
}
|
|
13080
12971
|
/**
|
|
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").
|
|
12972
|
+
* Internal lazy-allocator for the read-only facade. Used as a
|
|
12973
|
+
* defensive fallback; in practice `_initGuards()` eagerly
|
|
12974
|
+
* instantiates this, so the lazy path is a no-op.
|
|
13087
12975
|
*/
|
|
13088
12976
|
_ensureReadOnlyFacade() {
|
|
13089
12977
|
if (this.readOnlyFacade !== null) return this.readOnlyFacade;
|
|
@@ -13538,7 +13426,7 @@ var init_vault = __esm({
|
|
|
13538
13426
|
const all = await this._loadPeriodsCache();
|
|
13539
13427
|
return all.find((p) => p.name === name) ?? null;
|
|
13540
13428
|
}
|
|
13541
|
-
/** @internal —
|
|
13429
|
+
/** @internal — called by the gate bus before put/delete. */
|
|
13542
13430
|
async _assertTsWritable(existing, incoming) {
|
|
13543
13431
|
if (existing === null && incoming === null) return;
|
|
13544
13432
|
if (this.periodCache === null) {
|
|
@@ -13626,7 +13514,7 @@ var init_vault = __esm({
|
|
|
13626
13514
|
return dumpVaultSchema(this, opts);
|
|
13627
13515
|
}
|
|
13628
13516
|
/**
|
|
13629
|
-
* Lightweight read of the vault's registered schema
|
|
13517
|
+
* Lightweight read of the vault's registered schema: collections
|
|
13630
13518
|
* (+ doc counts), guards, materialized views, schema-update strategies,
|
|
13631
13519
|
* and the unlocked user's grants. Cheap — one `adapter.list` per
|
|
13632
13520
|
* collection, no decryption. For a full snapshot + stats use dumpSchema().
|
|
@@ -14433,6 +14321,116 @@ var init_write_hooks = __esm({
|
|
|
14433
14321
|
}
|
|
14434
14322
|
});
|
|
14435
14323
|
|
|
14324
|
+
// src/subsystem-bus.ts
|
|
14325
|
+
var SubsystemBus;
|
|
14326
|
+
var init_subsystem_bus = __esm({
|
|
14327
|
+
"src/subsystem-bus.ts"() {
|
|
14328
|
+
"use strict";
|
|
14329
|
+
SubsystemBus = class {
|
|
14330
|
+
#handlers = /* @__PURE__ */ new Map();
|
|
14331
|
+
#gateHandlers = /* @__PURE__ */ new Map();
|
|
14332
|
+
#depth = 0;
|
|
14333
|
+
/** Register a handler for an observe point. Returns an unsubscribe fn. */
|
|
14334
|
+
register(point, handler) {
|
|
14335
|
+
let arr = this.#handlers.get(point);
|
|
14336
|
+
if (!arr) {
|
|
14337
|
+
arr = [];
|
|
14338
|
+
this.#handlers.set(point, arr);
|
|
14339
|
+
}
|
|
14340
|
+
arr.push(handler);
|
|
14341
|
+
return () => {
|
|
14342
|
+
const a = this.#handlers.get(point);
|
|
14343
|
+
if (!a) return;
|
|
14344
|
+
const i = a.indexOf(handler);
|
|
14345
|
+
if (i >= 0) a.splice(i, 1);
|
|
14346
|
+
};
|
|
14347
|
+
}
|
|
14348
|
+
/** Cheap gate for the write path — true when any handler is registered for the point. */
|
|
14349
|
+
hasHandlers(point) {
|
|
14350
|
+
const a = this.#handlers.get(point);
|
|
14351
|
+
return a !== void 0 && a.length > 0;
|
|
14352
|
+
}
|
|
14353
|
+
/**
|
|
14354
|
+
* True while one or more dispatches are in flight. Backed by a depth counter
|
|
14355
|
+
* so that two concurrent async dispatches (`Promise.all([put('a'), put('b')])`
|
|
14356
|
+
* each captured `busAfterPut=true` at their respective put() tops while depth
|
|
14357
|
+
* was 0) both proceed independently — the counter stays > 0 until BOTH finish,
|
|
14358
|
+
* so any nested write attempted by a handler still sees `dispatching === true`
|
|
14359
|
+
* and is suppressed by the write-path gate in `collection.ts`
|
|
14360
|
+
* (`busAfterPut = hasHandlers('afterPut') && !dispatching`). Re-entrancy
|
|
14361
|
+
* suppression lives exclusively on that write-path gate; concurrent independent
|
|
14362
|
+
* dispatches must not drop each other's events.
|
|
14363
|
+
*/
|
|
14364
|
+
get dispatching() {
|
|
14365
|
+
return this.#depth > 0;
|
|
14366
|
+
}
|
|
14367
|
+
/**
|
|
14368
|
+
* Dispatch in registration order, awaited. Per-handler errors are warned, not
|
|
14369
|
+
* thrown — an observe handler must never abort a completed write. A
|
|
14370
|
+
* re-entrancy guard suppresses nested firing so a handler that itself writes
|
|
14371
|
+
* cannot loop (same rationale as WriteHookRegistry.#suppressed).
|
|
14372
|
+
*/
|
|
14373
|
+
async dispatch(point, event) {
|
|
14374
|
+
const a = this.#handlers.get(point);
|
|
14375
|
+
if (!a || a.length === 0) return;
|
|
14376
|
+
this.#depth++;
|
|
14377
|
+
try {
|
|
14378
|
+
for (const h of a.slice()) {
|
|
14379
|
+
try {
|
|
14380
|
+
await h(event);
|
|
14381
|
+
} catch (err) {
|
|
14382
|
+
console.warn(
|
|
14383
|
+
`[noy-db] subsystem observe handler failed at ${point}: ` + (err instanceof Error ? err.message : String(err))
|
|
14384
|
+
);
|
|
14385
|
+
}
|
|
14386
|
+
}
|
|
14387
|
+
} finally {
|
|
14388
|
+
this.#depth--;
|
|
14389
|
+
}
|
|
14390
|
+
}
|
|
14391
|
+
/** Register a write-gating handler. A throw from the handler ABORTS the write. Returns an unsubscribe fn. */
|
|
14392
|
+
registerGate(point, handler) {
|
|
14393
|
+
let arr = this.#gateHandlers.get(point);
|
|
14394
|
+
if (!arr) {
|
|
14395
|
+
arr = [];
|
|
14396
|
+
this.#gateHandlers.set(point, arr);
|
|
14397
|
+
}
|
|
14398
|
+
arr.push(handler);
|
|
14399
|
+
return () => {
|
|
14400
|
+
const a = this.#gateHandlers.get(point);
|
|
14401
|
+
if (!a) return;
|
|
14402
|
+
const i = a.indexOf(handler);
|
|
14403
|
+
if (i >= 0) a.splice(i, 1);
|
|
14404
|
+
};
|
|
14405
|
+
}
|
|
14406
|
+
/** Cheap gate for the write path — true when any gate handler is registered for the point. */
|
|
14407
|
+
hasGateHandlers(point) {
|
|
14408
|
+
const a = this.#gateHandlers.get(point);
|
|
14409
|
+
return a !== void 0 && a.length > 0;
|
|
14410
|
+
}
|
|
14411
|
+
/**
|
|
14412
|
+
* Run gate handlers in registration order, awaited. Unlike `dispatch`
|
|
14413
|
+
* (observe), a handler throw is NOT swallowed — it PROPAGATES, aborting the
|
|
14414
|
+
* write before it reaches the store. The first throw stops the remaining
|
|
14415
|
+
* handlers (fail-fast). This is the seam guards/periods migrate onto.
|
|
14416
|
+
*
|
|
14417
|
+
* Note: gate handlers are validators that read, not write. A gate handler
|
|
14418
|
+
* that writes back into the same collection would re-enter the write path
|
|
14419
|
+
* and re-dispatch this point; loop-suppression for that case is deferred to
|
|
14420
|
+
* the migration slice (contract: gate handlers must not perform writes that
|
|
14421
|
+
* re-trigger their own point).
|
|
14422
|
+
*/
|
|
14423
|
+
async dispatchGate(point, event) {
|
|
14424
|
+
const a = this.#gateHandlers.get(point);
|
|
14425
|
+
if (!a || a.length === 0) return;
|
|
14426
|
+
for (const h of a.slice()) {
|
|
14427
|
+
await h(event);
|
|
14428
|
+
}
|
|
14429
|
+
}
|
|
14430
|
+
};
|
|
14431
|
+
}
|
|
14432
|
+
});
|
|
14433
|
+
|
|
14436
14434
|
// src/tab-coordination.ts
|
|
14437
14435
|
function isPresenceMsg(x) {
|
|
14438
14436
|
if (x === null || typeof x !== "object") return false;
|
|
@@ -14935,9 +14933,9 @@ var init_presets = __esm({
|
|
|
14935
14933
|
minTier: 1,
|
|
14936
14934
|
enabled: true
|
|
14937
14935
|
},
|
|
14938
|
-
// rotate-recovery
|
|
14939
|
-
// when the user remembers their passphrase. PERSONAL
|
|
14940
|
-
//
|
|
14936
|
+
// rotate-recovery: deliberate paper-sheet regeneration
|
|
14937
|
+
// when the user remembers their passphrase. PERSONAL allows tier-1 —
|
|
14938
|
+
// knowing the passphrase is enough.
|
|
14941
14939
|
"rotate-recovery": { minTier: 1 },
|
|
14942
14940
|
"enroll-authenticator": { minTier: 1 },
|
|
14943
14941
|
"remove-authenticator": { minTier: 1 },
|
|
@@ -14971,7 +14969,7 @@ var init_presets = __esm({
|
|
|
14971
14969
|
minTier: 1,
|
|
14972
14970
|
enabled: false
|
|
14973
14971
|
},
|
|
14974
|
-
// ─── User envelope gates
|
|
14972
|
+
// ─── User envelope gates ──────────────────────────────────────────
|
|
14975
14973
|
// edit-own-profile: tier 3 floor — any active session can edit their
|
|
14976
14974
|
// own profile/preferences. Tightening to require a TOTP for
|
|
14977
14975
|
// profile changes is a one-line override.
|
|
@@ -14998,7 +14996,7 @@ var init_presets = __esm({
|
|
|
14998
14996
|
minTier: 1,
|
|
14999
14997
|
enabled: true
|
|
15000
14998
|
},
|
|
15001
|
-
// rotate-recovery
|
|
14999
|
+
// rotate-recovery: STRICT requires an off-device factor —
|
|
15002
15000
|
// rotating recovery is an off-site-trust event; a stolen unlocked
|
|
15003
15001
|
// laptop must not be able to silently mint a new sheet for the
|
|
15004
15002
|
// attacker. Matches the `peer-recover-user` STRICT default.
|
|
@@ -15071,7 +15069,7 @@ var init_presets = __esm({
|
|
|
15071
15069
|
minTier: 1,
|
|
15072
15070
|
enabled: false
|
|
15073
15071
|
},
|
|
15074
|
-
// ─── User envelope gates
|
|
15072
|
+
// ─── User envelope gates ──────────────────────────────────────────
|
|
15075
15073
|
// STRICT: profile edits require a TOTP/email-OTP factor (typical
|
|
15076
15074
|
// shared-workstation hardening — your name/avatar shouldn't change
|
|
15077
15075
|
// without a fresh second-factor proof).
|
|
@@ -15154,6 +15152,81 @@ var init_policy2 = __esm({
|
|
|
15154
15152
|
}
|
|
15155
15153
|
});
|
|
15156
15154
|
|
|
15155
|
+
// src/guards/executor.ts
|
|
15156
|
+
var executor_exports3 = {};
|
|
15157
|
+
__export(executor_exports3, {
|
|
15158
|
+
GuardExecutor: () => GuardExecutor
|
|
15159
|
+
});
|
|
15160
|
+
function deepEqual(a, b) {
|
|
15161
|
+
if (a === b) return true;
|
|
15162
|
+
if (a === null || b === null) return a === b;
|
|
15163
|
+
if (typeof a !== typeof b) return false;
|
|
15164
|
+
if (typeof a !== "object") return a === b;
|
|
15165
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
15166
|
+
if (Array.isArray(a)) {
|
|
15167
|
+
const aa = a;
|
|
15168
|
+
const bb = b;
|
|
15169
|
+
if (aa.length !== bb.length) return false;
|
|
15170
|
+
for (let i = 0; i < aa.length; i++) if (!deepEqual(aa[i], bb[i])) return false;
|
|
15171
|
+
return true;
|
|
15172
|
+
}
|
|
15173
|
+
const ao = a;
|
|
15174
|
+
const bo = b;
|
|
15175
|
+
const ak = Object.keys(ao);
|
|
15176
|
+
const bk = Object.keys(bo);
|
|
15177
|
+
if (ak.length !== bk.length) return false;
|
|
15178
|
+
for (const k of ak) {
|
|
15179
|
+
if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;
|
|
15180
|
+
if (!deepEqual(ao[k], bo[k])) return false;
|
|
15181
|
+
}
|
|
15182
|
+
return true;
|
|
15183
|
+
}
|
|
15184
|
+
var GuardExecutor;
|
|
15185
|
+
var init_executor3 = __esm({
|
|
15186
|
+
"src/guards/executor.ts"() {
|
|
15187
|
+
"use strict";
|
|
15188
|
+
init_errors();
|
|
15189
|
+
GuardExecutor = {
|
|
15190
|
+
/**
|
|
15191
|
+
* Compare existing vs incoming for each `frozenFields.fields` entry
|
|
15192
|
+
* when `frozenFields.when(existing)` is true. Throws
|
|
15193
|
+
* `FieldFrozenError` listing every changed frozen field.
|
|
15194
|
+
*/
|
|
15195
|
+
async checkFrozenFields(guard, id, existing, incoming) {
|
|
15196
|
+
const ff = guard.frozenFields;
|
|
15197
|
+
if (!ff) return;
|
|
15198
|
+
if (existing === null) return;
|
|
15199
|
+
if (!ff.when(existing)) return;
|
|
15200
|
+
const changed = [];
|
|
15201
|
+
for (const f of ff.fields) {
|
|
15202
|
+
if (existing[f] !== incoming[f]) {
|
|
15203
|
+
if (!deepEqual(existing[f], incoming[f])) changed.push(String(f));
|
|
15204
|
+
}
|
|
15205
|
+
}
|
|
15206
|
+
if (changed.length > 0) {
|
|
15207
|
+
throw new FieldFrozenError(guard.collection, id, changed);
|
|
15208
|
+
}
|
|
15209
|
+
},
|
|
15210
|
+
/**
|
|
15211
|
+
* Run a single guard's invariant over its slice of the change-set.
|
|
15212
|
+
* Any throw is converted to `InvariantError` unless it already is one.
|
|
15213
|
+
*/
|
|
15214
|
+
async runInvariant(guard, changes, ctx) {
|
|
15215
|
+
const amendment = guard.amendment;
|
|
15216
|
+
if (!amendment) return;
|
|
15217
|
+
try {
|
|
15218
|
+
await amendment.invariant(changes, ctx);
|
|
15219
|
+
} catch (err) {
|
|
15220
|
+
if (err instanceof InvariantError) throw err;
|
|
15221
|
+
throw new InvariantError(
|
|
15222
|
+
err instanceof Error ? err.message : `invariant violated: ${String(err)}`
|
|
15223
|
+
);
|
|
15224
|
+
}
|
|
15225
|
+
}
|
|
15226
|
+
};
|
|
15227
|
+
}
|
|
15228
|
+
});
|
|
15229
|
+
|
|
15157
15230
|
// src/noydb.ts
|
|
15158
15231
|
var noydb_exports = {};
|
|
15159
15232
|
__export(noydb_exports, {
|
|
@@ -15226,6 +15299,7 @@ var init_noydb = __esm({
|
|
|
15226
15299
|
init_events();
|
|
15227
15300
|
init_write_queue();
|
|
15228
15301
|
init_write_hooks();
|
|
15302
|
+
init_subsystem_bus();
|
|
15229
15303
|
init_tab_coordination();
|
|
15230
15304
|
init_tab_write_relay();
|
|
15231
15305
|
init_keyring();
|
|
@@ -15249,13 +15323,14 @@ var init_noydb = __esm({
|
|
|
15249
15323
|
emitter = new NoydbEventEmitter();
|
|
15250
15324
|
writeQueueTracker = new WriteQueueTracker();
|
|
15251
15325
|
writeHooks = new WriteHookRegistry();
|
|
15326
|
+
subsystemBus = new SubsystemBus();
|
|
15252
15327
|
clientId = generateULID();
|
|
15253
15328
|
vaultCache = /* @__PURE__ */ new Map();
|
|
15254
15329
|
keyringCache = /* @__PURE__ */ new Map();
|
|
15255
15330
|
syncEngines = /* @__PURE__ */ new Map();
|
|
15256
15331
|
/**
|
|
15257
15332
|
* Per-vault active session tier — defaults to `1` after a passphrase
|
|
15258
|
-
* unlock; tier-2 / tier-3 unlocks
|
|
15333
|
+
* unlock; tier-2 / tier-3 unlocks downgrade it. Used by
|
|
15259
15334
|
* {@link checkGate} to evaluate `gate.minTier`.
|
|
15260
15335
|
*/
|
|
15261
15336
|
activeTier = /* @__PURE__ */ new Map();
|
|
@@ -15265,14 +15340,14 @@ var init_noydb = __esm({
|
|
|
15265
15340
|
*/
|
|
15266
15341
|
policyCache = /* @__PURE__ */ new Map();
|
|
15267
15342
|
/**
|
|
15268
|
-
* One-shot bypass for the managed-mode strong-recovery check
|
|
15343
|
+
* One-shot bypass for the managed-mode strong-recovery check.
|
|
15269
15344
|
* Set true by {@link openVaultAndEnrollRecovery} for the duration of
|
|
15270
15345
|
* the bootstrap window so the keyring can be created before the
|
|
15271
15346
|
* strong recovery is enrolled. Always cleared (try/finally).
|
|
15272
15347
|
* @internal
|
|
15273
15348
|
*/
|
|
15274
15349
|
_skipNextManagedRecoveryCheck = false;
|
|
15275
|
-
/** Per-vault tier-3 (PIN / quick-resume) state
|
|
15350
|
+
/** Per-vault tier-3 (PIN / quick-resume) state. */
|
|
15276
15351
|
quickUnlock = new QuickUnlockStore();
|
|
15277
15352
|
/**
|
|
15278
15353
|
* Resolved public-envelope schema. Lazily computed once from
|
|
@@ -15282,9 +15357,9 @@ var init_noydb = __esm({
|
|
|
15282
15357
|
publicEnvelopeSchema;
|
|
15283
15358
|
closed = false;
|
|
15284
15359
|
sessionTimer = null;
|
|
15285
|
-
/** Same-device multi-tab coordinator
|
|
15360
|
+
/** Same-device multi-tab coordinator; created on `enableTabCoordination()`. */
|
|
15286
15361
|
tabCoordinator;
|
|
15287
|
-
/** Cross-tab write relay
|
|
15362
|
+
/** Cross-tab write relay; created on `enableTabCoordination()`. */
|
|
15288
15363
|
writeRelay;
|
|
15289
15364
|
/** Per-vault policy enforcers. */
|
|
15290
15365
|
policyEnforcers = /* @__PURE__ */ new Map();
|
|
@@ -15297,8 +15372,8 @@ var init_noydb = __esm({
|
|
|
15297
15372
|
* the same function's `finally` block. Side-effect writes triggered
|
|
15298
15373
|
* during a staged op's `Collection.put` (today: eager derivation
|
|
15299
15374
|
* outputs) register their pre-write envelope on `_executed` here so
|
|
15300
|
-
* a mid-batch failure rolls them back alongside the main staged ops
|
|
15301
|
-
*
|
|
15375
|
+
* a mid-batch failure rolls them back alongside the main staged ops.
|
|
15376
|
+
* `null` outside of Phase 2.
|
|
15302
15377
|
* @internal
|
|
15303
15378
|
*/
|
|
15304
15379
|
_activeTxContext = null;
|
|
@@ -15319,8 +15394,95 @@ var init_noydb = __esm({
|
|
|
15319
15394
|
if (options.sessionPolicy) {
|
|
15320
15395
|
this.sessionStrategy.validateSessionPolicy(options.sessionPolicy);
|
|
15321
15396
|
}
|
|
15397
|
+
this.#registerGuardGate();
|
|
15398
|
+
this.#registerPeriodGate();
|
|
15322
15399
|
this.resetSessionTimer();
|
|
15323
15400
|
}
|
|
15401
|
+
// Track A — guards migration. Registers record-lock / field-freeze / onDelete
|
|
15402
|
+
// / amendment-collect as gate-bus handlers (only when guards are opted in, so
|
|
15403
|
+
// the write path is zero-cost otherwise). Resolves the live vault's
|
|
15404
|
+
// GuardRegistry per dispatch. Registered BEFORE the period gate so guard
|
|
15405
|
+
// checks run first. The amendment branch is a side-effect (collectChange),
|
|
15406
|
+
// NOT a throw — and runs even for internal deletes (an amendment invariant
|
|
15407
|
+
// must see system housekeeping tombstones); onDelete/checks run only for
|
|
15408
|
+
// user (non-internal) operations.
|
|
15409
|
+
#registerGuardGate() {
|
|
15410
|
+
if (this.options.guardStrategies === void 0) return;
|
|
15411
|
+
this.subsystemBus.registerGate("beforePut", async (e) => {
|
|
15412
|
+
const v = this.vaultCache.get(e.vault);
|
|
15413
|
+
if (!v) return;
|
|
15414
|
+
const registry = v._getGuardRegistry();
|
|
15415
|
+
if (!registry) return;
|
|
15416
|
+
const guards = registry.guardsFor(e.collection);
|
|
15417
|
+
if (guards.length === 0) return;
|
|
15418
|
+
const existing = e.existing ?? null;
|
|
15419
|
+
const incoming = e.incoming;
|
|
15420
|
+
if (registry.isAmendmentActive()) {
|
|
15421
|
+
registry.collectChange(e.collection, e.docId, existing, incoming, e.existingVersion, e.existingVersion + 1);
|
|
15422
|
+
return;
|
|
15423
|
+
}
|
|
15424
|
+
const facade = v._getReadOnlyFacade();
|
|
15425
|
+
if (!facade) return;
|
|
15426
|
+
const ctx = { existing, vault: facade, userId: e.userId, role: e.role };
|
|
15427
|
+
await registry.runChecks(e.collection, incoming, ctx);
|
|
15428
|
+
const { GuardExecutor: GuardExecutor2 } = await Promise.resolve().then(() => (init_executor3(), executor_exports3));
|
|
15429
|
+
for (const g of guards) {
|
|
15430
|
+
await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming);
|
|
15431
|
+
}
|
|
15432
|
+
});
|
|
15433
|
+
this.subsystemBus.registerGate("beforeDelete", async (e) => {
|
|
15434
|
+
const v = this.vaultCache.get(e.vault);
|
|
15435
|
+
if (!v) return;
|
|
15436
|
+
const registry = v._getGuardRegistry();
|
|
15437
|
+
if (!registry) return;
|
|
15438
|
+
const guards = registry.guardsFor(e.collection);
|
|
15439
|
+
if (guards.length === 0) return;
|
|
15440
|
+
const existing = e.existing ?? null;
|
|
15441
|
+
if (registry.isAmendmentActive()) {
|
|
15442
|
+
registry.collectChange(e.collection, e.docId, existing, null, e.existingVersion, e.existingVersion);
|
|
15443
|
+
return;
|
|
15444
|
+
}
|
|
15445
|
+
if (e.internal) return;
|
|
15446
|
+
const facade = v._getReadOnlyFacade();
|
|
15447
|
+
if (!facade) return;
|
|
15448
|
+
const ctx = { existing, vault: facade, userId: e.userId, role: e.role };
|
|
15449
|
+
await registry.runOnDelete(e.collection, existing ?? {}, ctx);
|
|
15450
|
+
});
|
|
15451
|
+
}
|
|
15452
|
+
/**
|
|
15453
|
+
* Register closed-period write guards on the subsystem bus when a
|
|
15454
|
+
* periodsStrategy is configured. Handlers resolve the live Vault from
|
|
15455
|
+
* vaultCache so they always use the up-to-date period cache.
|
|
15456
|
+
*/
|
|
15457
|
+
// Track A — periods migration. Registers the closed-period write guard as a
|
|
15458
|
+
// gate-bus handler (only when periods is opted in, so the write path is
|
|
15459
|
+
// zero-cost otherwise). Each handler resolves the LIVE vault from the cache
|
|
15460
|
+
// per dispatch and delegates to its `_assertTsWritable`, which owns all
|
|
15461
|
+
// period logic. Resolving the live vault makes eviction/re-creation
|
|
15462
|
+
// transparent. Semantics note: if a write reaches the gate through a retained
|
|
15463
|
+
// collection handle whose vault has been evicted from `vaultCache` (e.g. a
|
|
15464
|
+
// post-revocation write on a stale handle), the period check is skipped — the
|
|
15465
|
+
// guard binds to the live vault, not a captured instance. Periods is a
|
|
15466
|
+
// write-integrity guard, not a security boundary, and a re-open reloads the
|
|
15467
|
+
// period cache; the trade-off is intentional.
|
|
15468
|
+
#registerPeriodGate() {
|
|
15469
|
+
if (this.options.periodsStrategy === void 0) return;
|
|
15470
|
+
this.subsystemBus.registerGate("beforePut", async (e) => {
|
|
15471
|
+
const v = this.vaultCache.get(e.vault);
|
|
15472
|
+
if (!v) return;
|
|
15473
|
+
const existing = e.op === "create" ? null : { ts: e.existingTs ?? null, record: e.existing ?? null };
|
|
15474
|
+
await v._assertTsWritable(existing, e.incoming);
|
|
15475
|
+
});
|
|
15476
|
+
this.subsystemBus.registerGate("beforeDelete", async (e) => {
|
|
15477
|
+
if (e.internal) return;
|
|
15478
|
+
const v = this.vaultCache.get(e.vault);
|
|
15479
|
+
if (!v) return;
|
|
15480
|
+
await v._assertTsWritable(
|
|
15481
|
+
{ ts: e.existingTs ?? null, record: e.existing ?? null },
|
|
15482
|
+
null
|
|
15483
|
+
);
|
|
15484
|
+
});
|
|
15485
|
+
}
|
|
15324
15486
|
resetSessionTimer() {
|
|
15325
15487
|
if (this.sessionTimer) clearTimeout(this.sessionTimer);
|
|
15326
15488
|
const idleMs = this.options.sessionPolicy?.idleTimeoutMs ?? this.options.sessionTimeout;
|
|
@@ -15610,8 +15772,6 @@ var init_noydb = __esm({
|
|
|
15610
15772
|
* @throws `NoAccessError` when no keyring exists for the target.
|
|
15611
15773
|
* @throws `PermissionDeniedError` when the role hierarchy rejects.
|
|
15612
15774
|
* @throws `ValidationError` when no field is provided.
|
|
15613
|
-
*
|
|
15614
|
-
* @see #54
|
|
15615
15775
|
*/
|
|
15616
15776
|
async updateUser(vault, options, factors) {
|
|
15617
15777
|
await this.checkGate(vault, "update-user", factors);
|
|
@@ -15947,7 +16107,7 @@ var init_noydb = __esm({
|
|
|
15947
16107
|
* Phase 2. `Collection.dispatchDerivations` consults this so a
|
|
15948
16108
|
* recursive derived-output write inside `Collection.put` can register
|
|
15949
16109
|
* its envelope onto `ctx._executed` and roll back with the main
|
|
15950
|
-
* staged ops on mid-batch failure
|
|
16110
|
+
* staged ops on mid-batch failure.
|
|
15951
16111
|
*
|
|
15952
16112
|
* @internal
|
|
15953
16113
|
*/
|
|
@@ -15976,7 +16136,7 @@ var init_noydb = __esm({
|
|
|
15976
16136
|
* `Collection.putManyAtomic` (via `derivationSource.createTxContext`)
|
|
15977
16137
|
* to publish an active context for the duration of its bulk-atomic
|
|
15978
16138
|
* Phase 2 loop, so recursive derivation-output writes register on
|
|
15979
|
-
* `ctx._executed` and roll back together with the source ops
|
|
16139
|
+
* `ctx._executed` and roll back together with the source ops.
|
|
15980
16140
|
*
|
|
15981
16141
|
* @internal
|
|
15982
16142
|
*/
|
|
@@ -16047,26 +16207,26 @@ var init_noydb = __esm({
|
|
|
16047
16207
|
return this.writeQueueTracker;
|
|
16048
16208
|
}
|
|
16049
16209
|
/**
|
|
16050
|
-
* Register a hook that runs before each write
|
|
16210
|
+
* Register a hook that runs before each write. Awaited; a throw
|
|
16051
16211
|
* aborts the write. Returns an unsubscribe function.
|
|
16052
16212
|
*/
|
|
16053
16213
|
onBeforeWrite(handler) {
|
|
16054
16214
|
return this.writeHooks.onBeforeWrite(handler);
|
|
16055
16215
|
}
|
|
16056
16216
|
/**
|
|
16057
|
-
* Register a hook that runs after each committed write
|
|
16217
|
+
* Register a hook that runs after each committed write. Awaited;
|
|
16058
16218
|
* a handler error is warned, never rolled back. Returns an unsubscribe fn.
|
|
16059
16219
|
*/
|
|
16060
16220
|
onAfterWrite(handler) {
|
|
16061
16221
|
return this.writeHooks.onAfterWrite(handler);
|
|
16062
16222
|
}
|
|
16063
|
-
/** Subscribe to cross-tab write conflicts
|
|
16223
|
+
/** Subscribe to cross-tab write conflicts. Returns an unsubscribe. */
|
|
16064
16224
|
onWriteConflict(fn) {
|
|
16065
16225
|
this.on("write:conflict", fn);
|
|
16066
16226
|
return () => this.off("write:conflict", fn);
|
|
16067
16227
|
}
|
|
16068
16228
|
/**
|
|
16069
|
-
* Enable same-device multi-tab coordination
|
|
16229
|
+
* Enable same-device multi-tab coordination: primary/secondary
|
|
16070
16230
|
* election + presence. Browser-only — a graceful no-op (role 'unknown')
|
|
16071
16231
|
* when Web Locks / BroadcastChannel are unavailable and nothing is
|
|
16072
16232
|
* injected. Idempotent; returns a disposer.
|
|
@@ -16149,7 +16309,11 @@ var init_noydb = __esm({
|
|
|
16149
16309
|
get _writeHooks() {
|
|
16150
16310
|
return this.writeHooks;
|
|
16151
16311
|
}
|
|
16152
|
-
/** @internal
|
|
16312
|
+
/** @internal The observe bus, threaded into every Collection. */
|
|
16313
|
+
get _subsystemBus() {
|
|
16314
|
+
return this.subsystemBus;
|
|
16315
|
+
}
|
|
16316
|
+
/** @internal Stable per-instance id for schema-cutover coordination. */
|
|
16153
16317
|
get _clientId() {
|
|
16154
16318
|
return this.clientId;
|
|
16155
16319
|
}
|
|
@@ -16169,10 +16333,6 @@ var init_noydb = __esm({
|
|
|
16169
16333
|
* survives lock; nothing about it changes when DEKs are scrubbed).
|
|
16170
16334
|
*
|
|
16171
16335
|
* No-op when `vault` is not currently in cache (idempotent).
|
|
16172
|
-
*
|
|
16173
|
-
* Unblocks vLannaAi/niwat#33.
|
|
16174
|
-
*
|
|
16175
|
-
* @see #17
|
|
16176
16336
|
*/
|
|
16177
16337
|
lockVault(vault) {
|
|
16178
16338
|
this.syncEngines.get(vault)?.stopAutoSync();
|
|
@@ -16287,7 +16447,7 @@ var init_noydb = __esm({
|
|
|
16287
16447
|
return merged;
|
|
16288
16448
|
}
|
|
16289
16449
|
/**
|
|
16290
|
-
* Read the current vault-level user-directory toggle
|
|
16450
|
+
* Read the current vault-level user-directory toggle. Returns
|
|
16291
16451
|
* the default-on shape (`{ enabled: true }`) when no `_meta/directory`
|
|
16292
16452
|
* document has been persisted yet.
|
|
16293
16453
|
*
|
|
@@ -16299,7 +16459,7 @@ var init_noydb = __esm({
|
|
|
16299
16459
|
return persisted?.enabled ?? true;
|
|
16300
16460
|
}
|
|
16301
16461
|
/**
|
|
16302
|
-
* Toggle the vault's user-directory listing on or off
|
|
16462
|
+
* Toggle the vault's user-directory listing on or off.
|
|
16303
16463
|
* Owner-only. When disabled, `listUsersWithEnvelopes()` throws
|
|
16304
16464
|
* {@link import('./errors.js').DirectoryDisabledError} for callers
|
|
16305
16465
|
* whose role is neither `owner` nor `admin`.
|
|
@@ -16359,7 +16519,7 @@ var init_noydb = __esm({
|
|
|
16359
16519
|
*
|
|
16360
16520
|
* Two enforcement modes:
|
|
16361
16521
|
*
|
|
16362
|
-
* 1. **Managed-mode mandatory strong-recovery
|
|
16522
|
+
* 1. **Managed-mode mandatory strong-recovery.** When
|
|
16363
16523
|
* `passphraseMode === 'managed'`, the vault MUST have at least
|
|
16364
16524
|
* one **strong** recovery profile (Shamir today). Paper alone is
|
|
16365
16525
|
* rejected because under managed mode the user has no memorized
|
|
@@ -16393,14 +16553,14 @@ var init_noydb = __esm({
|
|
|
16393
16553
|
throw new RecoveryNotEnrolledError();
|
|
16394
16554
|
}
|
|
16395
16555
|
/**
|
|
16396
|
-
* Internal accessor used by tier-2/tier-3 unlock paths
|
|
16556
|
+
* Internal accessor used by tier-2/tier-3 unlock paths
|
|
16397
16557
|
* to mark the active session tier.
|
|
16398
16558
|
* @internal
|
|
16399
16559
|
*/
|
|
16400
16560
|
_setActiveTier(vault, tier) {
|
|
16401
16561
|
this.activeTier.set(vault, tier);
|
|
16402
16562
|
}
|
|
16403
|
-
// ─── Tier-2 enroll / remove
|
|
16563
|
+
// ─── Tier-2 enroll / remove ─────────────────────────────────────
|
|
16404
16564
|
/**
|
|
16405
16565
|
* Add a tier-2 authenticator slot to the calling user's keyring.
|
|
16406
16566
|
* Each slot independently wraps the SAME KEK under a method-specific
|
|
@@ -16430,7 +16590,7 @@ var init_noydb = __esm({
|
|
|
16430
16590
|
const next = await removeAuthenticator(this.options.store, vault, keyring, slotId);
|
|
16431
16591
|
this.keyringCache.set(vault, next);
|
|
16432
16592
|
}
|
|
16433
|
-
/** Read the slot list for a vault. Internal — `describeAuthConfig`
|
|
16593
|
+
/** Read the slot list for a vault. Internal — `describeAuthConfig` consumes this. */
|
|
16434
16594
|
async listAuthenticators(vault) {
|
|
16435
16595
|
const keyring = await this.getKeyringInternal(vault);
|
|
16436
16596
|
return keyring.authenticators;
|
|
@@ -16442,7 +16602,7 @@ var init_noydb = __esm({
|
|
|
16442
16602
|
* are immutable through this method. Anti-slot-swap is structural,
|
|
16443
16603
|
* not gate-driven.
|
|
16444
16604
|
*
|
|
16445
|
-
* `meta` patch semantics (
|
|
16605
|
+
* `meta` patch semantics (top-level merge):
|
|
16446
16606
|
* - Top-level merge — absent keys preserved
|
|
16447
16607
|
* - `null` value — delete that meta key
|
|
16448
16608
|
* - Other values — replace verbatim
|
|
@@ -16460,8 +16620,6 @@ var init_noydb = __esm({
|
|
|
16460
16620
|
*
|
|
16461
16621
|
* @throws `NoAccessError` when no slot with the given id exists.
|
|
16462
16622
|
* @throws `ValidationError` when no patch field is provided.
|
|
16463
|
-
*
|
|
16464
|
-
* @see #55
|
|
16465
16623
|
*/
|
|
16466
16624
|
async updateAuthenticator(vault, slotId, options, factors) {
|
|
16467
16625
|
await this.checkGate(vault, "update-authenticator", factors);
|
|
@@ -16470,7 +16628,7 @@ var init_noydb = __esm({
|
|
|
16470
16628
|
this.keyringCache.set(vault, next);
|
|
16471
16629
|
}
|
|
16472
16630
|
/**
|
|
16473
|
-
* Native WebAuthn enrollment using the **real** internal keyring
|
|
16631
|
+
* Native WebAuthn enrollment using the **real** internal keyring.
|
|
16474
16632
|
*
|
|
16475
16633
|
* Why this exists: when a consumer is using `createNoydb({ secret })`,
|
|
16476
16634
|
* they cannot reach the live `UnlockedKeyring` to feed it to
|
|
@@ -16513,8 +16671,6 @@ var init_noydb = __esm({
|
|
|
16513
16671
|
* a server-side allowlist).
|
|
16514
16672
|
*
|
|
16515
16673
|
* Gated by `enroll-authenticator` like `enrollAuthenticator()` itself.
|
|
16516
|
-
*
|
|
16517
|
-
* @see #16
|
|
16518
16674
|
*/
|
|
16519
16675
|
async enrollWebAuthn(vault, ceremony, factors) {
|
|
16520
16676
|
await this.checkGate(vault, "enroll-authenticator", factors);
|
|
@@ -16541,8 +16697,6 @@ var init_noydb = __esm({
|
|
|
16541
16697
|
* deciding when a new device prompt should appear. Identity is
|
|
16542
16698
|
* `id` + `enrolled_at`; the `meta.credentialId` (base64) is used by
|
|
16543
16699
|
* `allowCredentials` at unlock time.
|
|
16544
|
-
*
|
|
16545
|
-
* @see #16
|
|
16546
16700
|
*/
|
|
16547
16701
|
async listWebAuthnSlots(vault) {
|
|
16548
16702
|
const keyring = await this.getKeyringInternal(vault);
|
|
@@ -16624,7 +16778,7 @@ var init_noydb = __esm({
|
|
|
16624
16778
|
async getPublicEnvelope(vault, opts = {}) {
|
|
16625
16779
|
return readPublicEnvelope(this.options.store, vault, opts);
|
|
16626
16780
|
}
|
|
16627
|
-
// ─── Auth introspection
|
|
16781
|
+
// ─── Auth introspection ─────────────────────────────────────────
|
|
16628
16782
|
/** English summary of the configured auth model. */
|
|
16629
16783
|
async describeAuthConfig(vault) {
|
|
16630
16784
|
return describeAuthConfig(this.options.store, vault);
|
|
@@ -16647,7 +16801,7 @@ var init_noydb = __esm({
|
|
|
16647
16801
|
await this.checkGate(vault, "view-user-auth", factors);
|
|
16648
16802
|
return describeAllUsersAuth(this.options.store, vault);
|
|
16649
16803
|
}
|
|
16650
|
-
// ─── Tier-1 change flows
|
|
16804
|
+
// ─── Tier-1 change flows ────────────────────────────────────────
|
|
16651
16805
|
/**
|
|
16652
16806
|
* Rotate the user's passphrase (user remembers old). Validates the
|
|
16653
16807
|
* new phrase against the configured `passphrase` policy, runs the
|
|
@@ -16655,8 +16809,7 @@ var init_noydb = __esm({
|
|
|
16655
16809
|
*
|
|
16656
16810
|
* Tier-2 authenticator slots are dropped — each slot wraps the old
|
|
16657
16811
|
* 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.
|
|
16812
|
+
* via `db.enrollAuthenticator` after rotation.
|
|
16660
16813
|
*
|
|
16661
16814
|
* @throws `WeakPassphraseError` on a weak new phrase.
|
|
16662
16815
|
* @throws `PolicyDeniedError` when the gate denies (missing factor, …).
|
|
@@ -16678,8 +16831,8 @@ var init_noydb = __esm({
|
|
|
16678
16831
|
}
|
|
16679
16832
|
/**
|
|
16680
16833
|
* Reset the passphrase using a recovery proof (user forgot the old).
|
|
16681
|
-
*
|
|
16682
|
-
* other
|
|
16834
|
+
* Currently supports the `'paper'` profile end-to-end; the
|
|
16835
|
+
* other profiles throw {@link RecoveryProfileNotImplementedError}.
|
|
16683
16836
|
*
|
|
16684
16837
|
* Burns the used recovery entry on success.
|
|
16685
16838
|
*/
|
|
@@ -16708,7 +16861,7 @@ var init_noydb = __esm({
|
|
|
16708
16861
|
return { newCodes: codes };
|
|
16709
16862
|
}
|
|
16710
16863
|
/**
|
|
16711
|
-
* Deliberate paper-recovery-code regeneration
|
|
16864
|
+
* Deliberate paper-recovery-code regeneration. User knows their
|
|
16712
16865
|
* passphrase but wants a fresh sheet — they lost the printout or
|
|
16713
16866
|
* suspect compromise of the off-site copy.
|
|
16714
16867
|
*
|
|
@@ -16718,7 +16871,7 @@ var init_noydb = __esm({
|
|
|
16718
16871
|
*
|
|
16719
16872
|
* Gated by the `rotate-recovery` policy gate:
|
|
16720
16873
|
* - PERSONAL_POLICY: `{ minTier: 1 }` — knowing the passphrase
|
|
16721
|
-
* suffices, matching the
|
|
16874
|
+
* suffices, matching the lower-level flow's bar.
|
|
16722
16875
|
* - STRICT_POLICY: `{ minTier: 1, factors: [{ anyOf: ['totp',
|
|
16723
16876
|
* 'email-otp', 'webauthn-roaming'] }] }` — rotation is an
|
|
16724
16877
|
* off-site-trust event; require an off-device factor so a
|
|
@@ -16823,7 +16976,7 @@ var init_noydb = __esm({
|
|
|
16823
16976
|
return { newShares: shareStrings, entryId: targetEntryId };
|
|
16824
16977
|
}
|
|
16825
16978
|
/**
|
|
16826
|
-
* **Atomic create-and-enroll for managed-mode vaults
|
|
16979
|
+
* **Atomic create-and-enroll for managed-mode vaults.**
|
|
16827
16980
|
*
|
|
16828
16981
|
* Bootstraps a managed-mode vault and enrolls strong recovery in
|
|
16829
16982
|
* a single ceremony. Under `passphraseMode: 'managed'`, every
|
|
@@ -16894,7 +17047,7 @@ var init_noydb = __esm({
|
|
|
16894
17047
|
return { vault: vaultHandle, recoveryEnrollments };
|
|
16895
17048
|
}
|
|
16896
17049
|
/**
|
|
16897
|
-
* **Recovery flow under managed-passphrase mode
|
|
17050
|
+
* **Recovery flow under managed-passphrase mode.**
|
|
16898
17051
|
*
|
|
16899
17052
|
* Replaces the sealed passphrase of a managed-mode vault with a
|
|
16900
17053
|
* fresh 256-bit random, sealed under the configured
|
|
@@ -16911,7 +17064,7 @@ var init_noydb = __esm({
|
|
|
16911
17064
|
* 5. Drop the keyring cache so the next operation re-derives.
|
|
16912
17065
|
*
|
|
16913
17066
|
* The vault's strong-recovery enrollment is preserved across
|
|
16914
|
-
* recovery (Shamir entries are not burned on use
|
|
17067
|
+
* recovery (Shamir entries are not burned on use).
|
|
16915
17068
|
*
|
|
16916
17069
|
* @throws ValidationError if the Noydb instance is not in managed mode.
|
|
16917
17070
|
*/
|
|
@@ -16959,7 +17112,7 @@ var init_noydb = __esm({
|
|
|
16959
17112
|
}
|
|
16960
17113
|
/**
|
|
16961
17114
|
* Atomic peer-recovery — re-wraps an EXISTING user's keyring under
|
|
16962
|
-
* a fresh temp passphrase in a single store write. Closes
|
|
17115
|
+
* a fresh temp passphrase in a single store write. Closes the
|
|
16963
17116
|
* partial-failure window (the previous compose-from-primitives
|
|
16964
17117
|
* pattern was `db.revoke + db.grant`, two writes — if the issuer
|
|
16965
17118
|
* cancelled between them the target was locked out entirely).
|
|
@@ -16969,7 +17122,7 @@ var init_noydb = __esm({
|
|
|
16969
17122
|
* - Same `userId`, role, permissions, capabilities preserved.
|
|
16970
17123
|
* - DEKs unchanged → every other principal in the vault keeps
|
|
16971
17124
|
* access. No key rotation.
|
|
16972
|
-
* - Allows owner→owner natively
|
|
17125
|
+
* - Allows owner→owner natively. The existing
|
|
16973
17126
|
* `db.revoke` retains its block — peer-recovery is a separate,
|
|
16974
17127
|
* intentionally-named operation.
|
|
16975
17128
|
* - Tier-2 slots dropped (they wrap the old KEK).
|
|
@@ -16998,7 +17151,6 @@ var init_noydb = __esm({
|
|
|
16998
17151
|
* @throws `PrivilegeEscalationError` when the caller lacks a DEK
|
|
16999
17152
|
* the target previously had access to.
|
|
17000
17153
|
*
|
|
17001
|
-
* @see #33 #34 — the issues this method closes.
|
|
17002
17154
|
*/
|
|
17003
17155
|
async recoverUser(vault, options, factors) {
|
|
17004
17156
|
await this.checkGate(vault, "peer-recover-user", factors);
|
|
@@ -17009,7 +17161,7 @@ var init_noydb = __esm({
|
|
|
17009
17161
|
}
|
|
17010
17162
|
}
|
|
17011
17163
|
/**
|
|
17012
|
-
* Persist a recovery enrollment.
|
|
17164
|
+
* Persist a recovery enrollment. Accepts the `'paper'`
|
|
17013
17165
|
* profile.
|
|
17014
17166
|
*
|
|
17015
17167
|
* The hub wraps the user's DEK set (not the KEK) under a code-derived
|
|
@@ -17029,7 +17181,7 @@ var init_noydb = __esm({
|
|
|
17029
17181
|
* showCodesToUser(codes)
|
|
17030
17182
|
* ```
|
|
17031
17183
|
*
|
|
17032
|
-
*
|
|
17184
|
+
* `@noy-db/on-recovery`'s `generateRecoveryCodeSet`
|
|
17033
17185
|
* delegates to `mintPaperRecoveryEntry` internally — its output is
|
|
17034
17186
|
* fed directly to this API. Pick whichever fits your code-gen layer:
|
|
17035
17187
|
*
|
|
@@ -17069,13 +17221,13 @@ var init_noydb = __esm({
|
|
|
17069
17221
|
"#196"
|
|
17070
17222
|
);
|
|
17071
17223
|
}
|
|
17072
|
-
/** Read the persisted recovery entries (paper + Shamir). Used by `describeAuthConfig
|
|
17224
|
+
/** Read the persisted recovery entries (paper + Shamir). Used by `describeAuthConfig`. */
|
|
17073
17225
|
async listRecoveryEntries(vault) {
|
|
17074
17226
|
const paper = await loadPaperRecoveryEntries(this.options.store, vault);
|
|
17075
17227
|
const shamir = await loadShamirRecoveryEntries(this.options.store, vault);
|
|
17076
17228
|
return { paper, shamir };
|
|
17077
17229
|
}
|
|
17078
|
-
// ─── Tier-3 enroll / unlock
|
|
17230
|
+
// ─── Tier-3 enroll / unlock ─────────────────────────────────────
|
|
17079
17231
|
/**
|
|
17080
17232
|
* Register a tier-3 quick-unlock state for the vault. The state is
|
|
17081
17233
|
* an opaque blob produced by `@noy-db/on-pin/enrollPin` (or any
|
|
@@ -17111,11 +17263,11 @@ var init_noydb = __esm({
|
|
|
17111
17263
|
this.quickUnlock.delete(vault);
|
|
17112
17264
|
}
|
|
17113
17265
|
/**
|
|
17114
|
-
* Public accessor for the unlocked keyring of a vault
|
|
17266
|
+
* Public accessor for the unlocked keyring of a vault.
|
|
17115
17267
|
*
|
|
17116
17268
|
* Returns a **defensive shallow copy** so consumers can read the DEK
|
|
17117
17269
|
* map and authenticator list without the risk of mutating the hub's
|
|
17118
|
-
* internal cache
|
|
17270
|
+
* internal cache. Internal hub code paths use a live reference
|
|
17119
17271
|
* via `getKeyringInternal`; ceremonies and external consumers always
|
|
17120
17272
|
* get a snapshot.
|
|
17121
17273
|
*
|