@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/index.cjs
CHANGED
|
@@ -3542,7 +3542,7 @@ var init_registry2 = __esm({
|
|
|
3542
3542
|
guardsFor(collection) {
|
|
3543
3543
|
return this._byCollection.get(collection) ?? [];
|
|
3544
3544
|
}
|
|
3545
|
-
/** Per-collection guard counts, for introspection
|
|
3545
|
+
/** Per-collection guard counts, for introspection. */
|
|
3546
3546
|
summary() {
|
|
3547
3547
|
return [...this._byCollection.entries()].map(([collection, guards]) => ({
|
|
3548
3548
|
collection,
|
|
@@ -8084,7 +8084,7 @@ async function changeSecret(adapter, vault, keyring, newPassphrase, passphraseOp
|
|
|
8084
8084
|
// Tier-2 slots are NOT preserved through `changeSecret` —
|
|
8085
8085
|
// each slot wraps the OLD KEK, so the new keyring has no
|
|
8086
8086
|
// authenticator slots until the user re-enrolls. The higher-level
|
|
8087
|
-
// `db.rotatePassphrase()`
|
|
8087
|
+
// `db.rotatePassphrase()` preserves slots by rewrapping the
|
|
8088
8088
|
// KEK reference, not the KEK itself.
|
|
8089
8089
|
authenticators: [],
|
|
8090
8090
|
...keyring.policy !== void 0 && { policy: keyring.policy }
|
|
@@ -8976,7 +8976,7 @@ var UserApi = class {
|
|
|
8976
8976
|
* the envelope on first call. Optimistic-concurrency safe — a stale
|
|
8977
8977
|
* `_v` (parallel writer on another device) throws `ConflictError`.
|
|
8978
8978
|
*
|
|
8979
|
-
* Patch semantics
|
|
8979
|
+
* Patch semantics:
|
|
8980
8980
|
* - `undefined` (or omitted key) — skip; existing value preserved
|
|
8981
8981
|
* - `null` — delete the field from the merged result
|
|
8982
8982
|
* - any other value — overwrite (deep-merge for plain objects,
|
|
@@ -9032,7 +9032,7 @@ var UserApi = class {
|
|
|
9032
9032
|
this.fireChange(this.writerKeyringId, written);
|
|
9033
9033
|
return written;
|
|
9034
9034
|
}
|
|
9035
|
-
// ─── Visibility
|
|
9035
|
+
// ─── Visibility ──────────────────────────────────────────────────────
|
|
9036
9036
|
/**
|
|
9037
9037
|
* Read the current user's visibility flag from
|
|
9038
9038
|
* `_meta/visibility/<keyringId>`. Returns `{ hidden: false }` when no
|
|
@@ -9677,6 +9677,173 @@ var NO_CRDT = {
|
|
|
9677
9677
|
}
|
|
9678
9678
|
};
|
|
9679
9679
|
|
|
9680
|
+
// src/i18n/core.ts
|
|
9681
|
+
init_errors();
|
|
9682
|
+
function i18nText(options) {
|
|
9683
|
+
return { _noydbI18nText: true, options };
|
|
9684
|
+
}
|
|
9685
|
+
function isI18nTextDescriptor(x) {
|
|
9686
|
+
return typeof x === "object" && x !== null && x._noydbI18nText === true;
|
|
9687
|
+
}
|
|
9688
|
+
function validateI18nTextValue(value, field, descriptor) {
|
|
9689
|
+
const { options } = descriptor;
|
|
9690
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
9691
|
+
throw new MissingTranslationError(
|
|
9692
|
+
field,
|
|
9693
|
+
options.languages,
|
|
9694
|
+
`Field "${field}" must be a { [locale]: string } map, got ${typeof value}.`
|
|
9695
|
+
);
|
|
9696
|
+
}
|
|
9697
|
+
const map = value;
|
|
9698
|
+
for (const [locale, v] of Object.entries(map)) {
|
|
9699
|
+
if (typeof v !== "string") {
|
|
9700
|
+
throw new MissingTranslationError(
|
|
9701
|
+
field,
|
|
9702
|
+
[locale],
|
|
9703
|
+
`Field "${field}": locale "${locale}" must be a string, got ${typeof v}.`
|
|
9704
|
+
);
|
|
9705
|
+
}
|
|
9706
|
+
}
|
|
9707
|
+
const { required } = options;
|
|
9708
|
+
if (required === "all") {
|
|
9709
|
+
const missing = options.languages.filter(
|
|
9710
|
+
(lang) => !(lang in map) || map[lang] === ""
|
|
9711
|
+
);
|
|
9712
|
+
if (missing.length > 0) {
|
|
9713
|
+
throw new MissingTranslationError(
|
|
9714
|
+
field,
|
|
9715
|
+
missing,
|
|
9716
|
+
`Field "${field}" requires all declared languages. Missing: ${missing.join(", ")}.`
|
|
9717
|
+
);
|
|
9718
|
+
}
|
|
9719
|
+
} else if (required === "any") {
|
|
9720
|
+
const present = options.languages.some(
|
|
9721
|
+
(lang) => lang in map && map[lang] !== ""
|
|
9722
|
+
);
|
|
9723
|
+
if (!present) {
|
|
9724
|
+
throw new MissingTranslationError(
|
|
9725
|
+
field,
|
|
9726
|
+
options.languages,
|
|
9727
|
+
`Field "${field}" requires at least one declared language. None present.`
|
|
9728
|
+
);
|
|
9729
|
+
}
|
|
9730
|
+
} else {
|
|
9731
|
+
const requiredList = required;
|
|
9732
|
+
const missing = requiredList.filter(
|
|
9733
|
+
(lang) => !(lang in map) || map[lang] === ""
|
|
9734
|
+
);
|
|
9735
|
+
if (missing.length > 0) {
|
|
9736
|
+
throw new MissingTranslationError(
|
|
9737
|
+
field,
|
|
9738
|
+
missing,
|
|
9739
|
+
`Field "${field}" requires: ${requiredList.join(", ")}. Missing: ${missing.join(", ")}.`
|
|
9740
|
+
);
|
|
9741
|
+
}
|
|
9742
|
+
}
|
|
9743
|
+
}
|
|
9744
|
+
function resolveI18nText(value, locale, fallback, field) {
|
|
9745
|
+
if (locale === "raw") {
|
|
9746
|
+
return value;
|
|
9747
|
+
}
|
|
9748
|
+
if (!locale) {
|
|
9749
|
+
throw new LocaleNotSpecifiedError(field ?? "<unknown>");
|
|
9750
|
+
}
|
|
9751
|
+
if (value[locale] !== void 0 && value[locale] !== "") {
|
|
9752
|
+
return value[locale];
|
|
9753
|
+
}
|
|
9754
|
+
const chain = Array.isArray(fallback) ? fallback : fallback ? [fallback] : [];
|
|
9755
|
+
for (const fb of chain) {
|
|
9756
|
+
if (fb === "any") {
|
|
9757
|
+
const any = Object.values(value).find((v) => v !== "");
|
|
9758
|
+
if (any !== void 0) return any;
|
|
9759
|
+
} else if (value[fb] !== void 0 && value[fb] !== "") {
|
|
9760
|
+
return value[fb];
|
|
9761
|
+
}
|
|
9762
|
+
}
|
|
9763
|
+
throw new LocaleNotSpecifiedError(
|
|
9764
|
+
field ?? "<unknown>",
|
|
9765
|
+
`No translation available for locale "${locale}"` + (chain.length > 0 ? ` or fallback chain [${chain.join(", ")}]` : "") + "."
|
|
9766
|
+
);
|
|
9767
|
+
}
|
|
9768
|
+
function getAtPath(obj, path) {
|
|
9769
|
+
const arrayIdx = path.indexOf("[].");
|
|
9770
|
+
if (arrayIdx !== -1) {
|
|
9771
|
+
const arrayKey = path.slice(0, arrayIdx);
|
|
9772
|
+
const restPath = path.slice(arrayIdx + 3);
|
|
9773
|
+
const arr = obj[arrayKey];
|
|
9774
|
+
if (!Array.isArray(arr)) return [];
|
|
9775
|
+
return arr.flatMap((item) => {
|
|
9776
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return [];
|
|
9777
|
+
return getAtPath(item, restPath);
|
|
9778
|
+
});
|
|
9779
|
+
}
|
|
9780
|
+
const dotIdx = path.indexOf(".");
|
|
9781
|
+
if (dotIdx !== -1) {
|
|
9782
|
+
const head = path.slice(0, dotIdx);
|
|
9783
|
+
const rest = path.slice(dotIdx + 1);
|
|
9784
|
+
const nested = obj[head];
|
|
9785
|
+
if (!nested || typeof nested !== "object" || Array.isArray(nested)) return [];
|
|
9786
|
+
return getAtPath(nested, rest);
|
|
9787
|
+
}
|
|
9788
|
+
const val = obj[path];
|
|
9789
|
+
return val !== void 0 ? [val] : [];
|
|
9790
|
+
}
|
|
9791
|
+
function setAtPathInPlace(obj, path, value) {
|
|
9792
|
+
const dotIdx = path.indexOf(".");
|
|
9793
|
+
if (dotIdx !== -1) {
|
|
9794
|
+
const head = path.slice(0, dotIdx);
|
|
9795
|
+
const rest = path.slice(dotIdx + 1);
|
|
9796
|
+
const nested = obj[head];
|
|
9797
|
+
if (!nested || typeof nested !== "object" || Array.isArray(nested)) return;
|
|
9798
|
+
setAtPathInPlace(nested, rest, value);
|
|
9799
|
+
return;
|
|
9800
|
+
}
|
|
9801
|
+
obj[path] = value;
|
|
9802
|
+
}
|
|
9803
|
+
function applyAtPath(obj, path, locale, fallback) {
|
|
9804
|
+
const arrayIdx = path.indexOf("[].");
|
|
9805
|
+
if (arrayIdx !== -1) {
|
|
9806
|
+
const arrayKey = path.slice(0, arrayIdx);
|
|
9807
|
+
const restPath = path.slice(arrayIdx + 3);
|
|
9808
|
+
const arr = obj[arrayKey];
|
|
9809
|
+
if (!Array.isArray(arr)) return obj;
|
|
9810
|
+
return {
|
|
9811
|
+
...obj,
|
|
9812
|
+
[arrayKey]: arr.map((item) => {
|
|
9813
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return item;
|
|
9814
|
+
return applyAtPath(item, restPath, locale, fallback);
|
|
9815
|
+
})
|
|
9816
|
+
};
|
|
9817
|
+
}
|
|
9818
|
+
const dotIdx = path.indexOf(".");
|
|
9819
|
+
if (dotIdx !== -1) {
|
|
9820
|
+
const head = path.slice(0, dotIdx);
|
|
9821
|
+
const rest = path.slice(dotIdx + 1);
|
|
9822
|
+
const nested = obj[head];
|
|
9823
|
+
if (!nested || typeof nested !== "object" || Array.isArray(nested)) return obj;
|
|
9824
|
+
return {
|
|
9825
|
+
...obj,
|
|
9826
|
+
[head]: applyAtPath(nested, rest, locale, fallback)
|
|
9827
|
+
};
|
|
9828
|
+
}
|
|
9829
|
+
const raw = obj[path];
|
|
9830
|
+
if (raw === void 0 || raw === null) return obj;
|
|
9831
|
+
if (typeof raw !== "object" || Array.isArray(raw)) return obj;
|
|
9832
|
+
return {
|
|
9833
|
+
...obj,
|
|
9834
|
+
[path]: resolveI18nText(raw, locale, fallback, path)
|
|
9835
|
+
};
|
|
9836
|
+
}
|
|
9837
|
+
function applyI18nLocale(record, i18nFields, locale, fallback) {
|
|
9838
|
+
const fieldNames = Object.keys(i18nFields);
|
|
9839
|
+
if (fieldNames.length === 0) return record;
|
|
9840
|
+
let result = record;
|
|
9841
|
+
for (const field of fieldNames) {
|
|
9842
|
+
result = applyAtPath(result, field, locale, fallback);
|
|
9843
|
+
}
|
|
9844
|
+
return result;
|
|
9845
|
+
}
|
|
9846
|
+
|
|
9680
9847
|
// src/i18n/strategy.ts
|
|
9681
9848
|
function notEnabled(op) {
|
|
9682
9849
|
return new Error(
|
|
@@ -10028,7 +10195,7 @@ var Query = class _Query {
|
|
|
10028
10195
|
/**
|
|
10029
10196
|
* @internal — clone this Query with a declared-predicate map
|
|
10030
10197
|
* attached. Used by the materialized-view registry to enable
|
|
10031
|
-
* `.wherePredicate(name, ctx?)` for the MV's query callback
|
|
10198
|
+
* `.wherePredicate(name, ctx?)` for the MV's query callback.
|
|
10032
10199
|
* Consumers don't call this directly.
|
|
10033
10200
|
*/
|
|
10034
10201
|
_withPredicates(predicates) {
|
|
@@ -10041,7 +10208,7 @@ var Query = class _Query {
|
|
|
10041
10208
|
);
|
|
10042
10209
|
}
|
|
10043
10210
|
/**
|
|
10044
|
-
* Filter by a registered deterministic predicate
|
|
10211
|
+
* Filter by a registered deterministic predicate. Requires
|
|
10045
10212
|
* the Query to have been augmented with a predicates map (typically
|
|
10046
10213
|
* via the materialized-view registry — bare Queries constructed
|
|
10047
10214
|
* outside an MV throw on `.wherePredicate()`).
|
|
@@ -11695,7 +11862,7 @@ var NO_BLOBS = {
|
|
|
11695
11862
|
init_errors();
|
|
11696
11863
|
init_ulid();
|
|
11697
11864
|
var TxContext = class {
|
|
11698
|
-
/** Stable id for this transaction; shared by all writes it performs
|
|
11865
|
+
/** Stable id for this transaction; shared by all writes it performs. */
|
|
11699
11866
|
txId = generateULID();
|
|
11700
11867
|
/** @internal */
|
|
11701
11868
|
_ops = [];
|
|
@@ -11705,7 +11872,7 @@ var TxContext = class {
|
|
|
11705
11872
|
* restore prior state via `revertExecuted`. Side-effect writes (e.g.
|
|
11706
11873
|
* recursive derivation outputs fired inside `Collection.put`) are
|
|
11707
11874
|
* appended here in execution order so they roll back alongside the
|
|
11708
|
-
* main staged ops
|
|
11875
|
+
* main staged ops.
|
|
11709
11876
|
*/
|
|
11710
11877
|
_executed = [];
|
|
11711
11878
|
/** @internal */
|
|
@@ -12027,7 +12194,7 @@ async function resolveStaleOnRead(accessor, outputCollection, id) {
|
|
|
12027
12194
|
}
|
|
12028
12195
|
if (out.kind === "array") {
|
|
12029
12196
|
console.warn(
|
|
12030
|
-
`[derivation] unexpected array-shape output "${key}" in lazy resolve path; array-shape derivations require lifecycle: "eager"
|
|
12197
|
+
`[derivation] unexpected array-shape output "${key}" in lazy resolve path; array-shape derivations require lifecycle: "eager".`
|
|
12031
12198
|
);
|
|
12032
12199
|
continue;
|
|
12033
12200
|
}
|
|
@@ -12065,6 +12232,7 @@ var Collection = class {
|
|
|
12065
12232
|
schemaUpdateGate;
|
|
12066
12233
|
schemaFence;
|
|
12067
12234
|
writeHooks;
|
|
12235
|
+
subsystemBus;
|
|
12068
12236
|
activeTxId;
|
|
12069
12237
|
getDEK;
|
|
12070
12238
|
onDirty;
|
|
@@ -12253,42 +12421,14 @@ var Collection = class {
|
|
|
12253
12421
|
syncAdapter;
|
|
12254
12422
|
/** — consent-audit hook, no-op when no scope is active. */
|
|
12255
12423
|
onAccess;
|
|
12256
|
-
/**
|
|
12257
|
-
* accounting-period write guard. Called BEFORE any
|
|
12258
|
-
* adapter write with:
|
|
12259
|
-
* - `existing` — the prior envelope's `_ts` and decrypted record
|
|
12260
|
-
* (or `null` if no prior envelope exists)
|
|
12261
|
-
* - `incoming` — the record being written (or `null` for delete)
|
|
12262
|
-
*
|
|
12263
|
-
* Throws `PeriodClosedError` if either side falls inside a closed
|
|
12264
|
-
* period. Installed by Vault; no-op when no period has been closed.
|
|
12265
|
-
* Async so the Vault can lazy-load the period list from the
|
|
12266
|
-
* adapter on first use.
|
|
12267
|
-
*/
|
|
12268
|
-
periodGuard;
|
|
12269
|
-
/**
|
|
12270
|
-
* Optional back-reference to the owning vault's guard registry + a
|
|
12271
|
-
* read-only vault facade. When present, `Collection.put` and
|
|
12272
|
-
* `Collection.delete` consult the registry for guards declared
|
|
12273
|
-
* against this collection and run their `check` + `frozenFields`
|
|
12274
|
-
* before the adapter write. Absent in unit tests that construct
|
|
12275
|
-
* a Collection directly; production code always sets it via
|
|
12276
|
-
* `Vault.collection()`.
|
|
12277
|
-
*
|
|
12278
|
-
* Typed structurally rather than as `Vault` to avoid a circular
|
|
12279
|
-
* import (mirrors the `refEnforcer` / `joinResolver` pattern).
|
|
12280
|
-
*/
|
|
12281
|
-
guardSource;
|
|
12282
12424
|
/**
|
|
12283
12425
|
* Vault-internal hook for derivation dispatch. When set,
|
|
12284
12426
|
* `Collection.put` consults the registry after the source-write
|
|
12285
12427
|
* commits and writes derived outputs through `getCollection(name).put`.
|
|
12286
|
-
* Same structural-interface pattern as `guardSource` to avoid a
|
|
12287
|
-
* circular Vault import.
|
|
12288
12428
|
*/
|
|
12289
12429
|
derivationSource;
|
|
12290
12430
|
/**
|
|
12291
|
-
* Vault-internal hook for materialized-view dispatch
|
|
12431
|
+
* Vault-internal hook for materialized-view dispatch.
|
|
12292
12432
|
* Parallel to `derivationSource` — when set, `Collection.put` fires
|
|
12293
12433
|
* `MaterializedViewRegistry.onSourceWrite` after the source-write
|
|
12294
12434
|
* commits + after `dispatchDerivations` has run.
|
|
@@ -12344,6 +12484,7 @@ var Collection = class {
|
|
|
12344
12484
|
this.schemaUpdateGate = opts.schemaUpdateGate;
|
|
12345
12485
|
this.schemaFence = opts.schemaFence;
|
|
12346
12486
|
this.writeHooks = opts.writeHooks;
|
|
12487
|
+
this.subsystemBus = opts.subsystemBus;
|
|
12347
12488
|
this.activeTxId = opts.activeTxId;
|
|
12348
12489
|
this.blobStrategy = opts.blobStrategy ?? NO_BLOBS;
|
|
12349
12490
|
this.aggregateStrategy = opts.aggregateStrategy ?? NO_AGGREGATE;
|
|
@@ -12368,8 +12509,6 @@ var Collection = class {
|
|
|
12368
12509
|
this.crdtMode = opts.crdt;
|
|
12369
12510
|
this.syncAdapter = opts.syncAdapter;
|
|
12370
12511
|
this.onAccess = opts.onAccess;
|
|
12371
|
-
this.periodGuard = opts.periodGuard;
|
|
12372
|
-
this.guardSource = opts.guardSource;
|
|
12373
12512
|
this.derivationSource = opts.derivationSource;
|
|
12374
12513
|
this.materializedViewSource = opts.materializedViewSource;
|
|
12375
12514
|
this.tiers = opts.tiers && opts.tiers.length > 0 ? new Set(opts.tiers) : null;
|
|
@@ -12571,21 +12710,23 @@ var Collection = class {
|
|
|
12571
12710
|
}
|
|
12572
12711
|
/**
|
|
12573
12712
|
* Create or update a record. Runs inside the hub's write-queue tracker
|
|
12574
|
-
*
|
|
12713
|
+
* so `hub.writeQueue.pending` reflects this write.
|
|
12575
12714
|
*
|
|
12576
12715
|
* @param id Record identifier.
|
|
12577
12716
|
* @param record The record body (validated by the collection's schema
|
|
12578
12717
|
* if one was attached at `vault.collection(...)` time).
|
|
12579
12718
|
* @param options Optional metadata for audit + import workflows.
|
|
12580
12719
|
* `reason` is stamped onto the resulting ledger entry
|
|
12581
|
-
*
|
|
12720
|
+
* so audit consumers can filter via
|
|
12582
12721
|
* `entries.filter(e => e.reason?.startsWith('import:'))`.
|
|
12583
12722
|
*/
|
|
12584
12723
|
async put(id, record, options) {
|
|
12585
12724
|
await this.schemaUpdateGate?.assertWritable();
|
|
12586
12725
|
await this.schemaFence?.assertWritable(this.name);
|
|
12726
|
+
const hooksActive = this.#hooksActive();
|
|
12727
|
+
const busAfterPut = (this.subsystemBus?.hasHandlers("afterPut") ?? false) && !(this.subsystemBus?.dispatching ?? false);
|
|
12587
12728
|
let event;
|
|
12588
|
-
if (
|
|
12729
|
+
if (hooksActive || busAfterPut) {
|
|
12589
12730
|
const prior = await this.#priorForHook(id);
|
|
12590
12731
|
event = {
|
|
12591
12732
|
op: prior.record === null ? "create" : "update",
|
|
@@ -12600,23 +12741,26 @@ var Collection = class {
|
|
|
12600
12741
|
baseVersion: prior.version,
|
|
12601
12742
|
version: prior.version + 1
|
|
12602
12743
|
};
|
|
12603
|
-
await this.writeHooks.runBefore(event);
|
|
12744
|
+
if (hooksActive) await this.writeHooks.runBefore(event);
|
|
12604
12745
|
}
|
|
12605
12746
|
if (this.writeQueue) await this.writeQueue.track(() => this.putInternal(id, record, options));
|
|
12606
12747
|
else await this.putInternal(id, record, options);
|
|
12607
|
-
if (event)
|
|
12748
|
+
if (event) {
|
|
12749
|
+
if (hooksActive) await this.writeHooks.runAfter(event);
|
|
12750
|
+
if (busAfterPut) await this.subsystemBus.dispatch("afterPut", event);
|
|
12751
|
+
}
|
|
12608
12752
|
}
|
|
12609
|
-
/** @internal
|
|
12753
|
+
/** @internal — true when hooks should fire for this write (handlers exist, not re-entrant). */
|
|
12610
12754
|
#hooksActive() {
|
|
12611
12755
|
return this.writeHooks !== void 0 && this.writeHooks.hasHandlers && !this.writeHooks.suppressed;
|
|
12612
12756
|
}
|
|
12613
12757
|
/**
|
|
12614
|
-
* @internal
|
|
12758
|
+
* @internal — resolve the prior record for a hook's `before` and
|
|
12615
12759
|
* its version. Critically, this uses the SAME basis `putInternal` writes from
|
|
12616
12760
|
* (the in-memory cache in eager mode; lru-then-adapter in lazy) — NOT a fresh
|
|
12617
12761
|
* store read — so `baseVersion`/`version` match the version actually written.
|
|
12618
12762
|
* A separate store read would diverge once another tab has advanced the shared
|
|
12619
|
-
* store past this tab's cache, breaking
|
|
12763
|
+
* store past this tab's cache, breaking cross-tab conflict detection.
|
|
12620
12764
|
*/
|
|
12621
12765
|
async #priorForHook(id) {
|
|
12622
12766
|
if (this.lazy && this.lru) {
|
|
@@ -12638,52 +12782,28 @@ var Collection = class {
|
|
|
12638
12782
|
if (!hasWritePermission(this.keyring, this.name)) {
|
|
12639
12783
|
throw new ReadOnlyError();
|
|
12640
12784
|
}
|
|
12641
|
-
if (this.
|
|
12642
|
-
const registry = this.guardSource.registry();
|
|
12643
|
-
const guards = registry.guardsFor(this.name);
|
|
12644
|
-
if (guards.length > 0) {
|
|
12645
|
-
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
12646
|
-
let existingRecord = null;
|
|
12647
|
-
if (existingEnv) {
|
|
12648
|
-
try {
|
|
12649
|
-
existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
|
|
12650
|
-
} catch {
|
|
12651
|
-
existingRecord = null;
|
|
12652
|
-
}
|
|
12653
|
-
}
|
|
12654
|
-
const incomingRecord = record;
|
|
12655
|
-
const ctx = {
|
|
12656
|
-
existing: existingRecord,
|
|
12657
|
-
vault: this.guardSource.readOnlyVault(),
|
|
12658
|
-
userId: this.keyring.userId,
|
|
12659
|
-
role: this.keyring.role
|
|
12660
|
-
};
|
|
12661
|
-
if (registry.isAmendmentActive()) {
|
|
12662
|
-
const vBefore = existingEnv?._v ?? 0;
|
|
12663
|
-
registry.collectChange(this.name, id, existingRecord, incomingRecord, vBefore, vBefore + 1);
|
|
12664
|
-
} else {
|
|
12665
|
-
await registry.runChecks(this.name, incomingRecord, ctx);
|
|
12666
|
-
const { GuardExecutor: GuardExecutor2 } = await Promise.resolve().then(() => (init_executor(), executor_exports));
|
|
12667
|
-
for (const g of guards) {
|
|
12668
|
-
await GuardExecutor2.checkFrozenFields(g, id, existingRecord, incomingRecord);
|
|
12669
|
-
}
|
|
12670
|
-
}
|
|
12671
|
-
}
|
|
12672
|
-
}
|
|
12673
|
-
if (this.periodGuard !== void 0) {
|
|
12785
|
+
if (this.subsystemBus?.hasGateHandlers("beforePut")) {
|
|
12674
12786
|
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
12675
|
-
let
|
|
12787
|
+
let existingRecord = null;
|
|
12676
12788
|
if (existingEnv) {
|
|
12677
12789
|
try {
|
|
12678
|
-
|
|
12790
|
+
existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
|
|
12679
12791
|
} catch {
|
|
12680
|
-
|
|
12792
|
+
existingRecord = null;
|
|
12681
12793
|
}
|
|
12682
12794
|
}
|
|
12683
|
-
await this.
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12795
|
+
await this.subsystemBus.dispatchGate("beforePut", {
|
|
12796
|
+
op: existingEnv ? "update" : "create",
|
|
12797
|
+
vault: this.vault,
|
|
12798
|
+
collection: this.name,
|
|
12799
|
+
docId: id,
|
|
12800
|
+
incoming: record,
|
|
12801
|
+
existing: existingRecord,
|
|
12802
|
+
existingVersion: existingEnv?._v ?? 0,
|
|
12803
|
+
existingTs: existingEnv?._ts,
|
|
12804
|
+
userId: this.keyring.userId,
|
|
12805
|
+
role: this.keyring.role
|
|
12806
|
+
});
|
|
12687
12807
|
}
|
|
12688
12808
|
if (this.schema !== void 0) {
|
|
12689
12809
|
record = await validateSchemaInput(this.schema, record, `put(${id})`);
|
|
@@ -12692,7 +12812,9 @@ var Collection = class {
|
|
|
12692
12812
|
const obj = record;
|
|
12693
12813
|
for (const [field, descriptor] of Object.entries(this.i18nFields)) {
|
|
12694
12814
|
if (!descriptor.options.autoTranslate) continue;
|
|
12695
|
-
const
|
|
12815
|
+
const leafValues = getAtPath(obj, field);
|
|
12816
|
+
if (leafValues.length !== 1) continue;
|
|
12817
|
+
const value = leafValues[0];
|
|
12696
12818
|
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
12697
12819
|
const map = value;
|
|
12698
12820
|
const { languages, required } = descriptor.options;
|
|
@@ -12716,8 +12838,7 @@ var Collection = class {
|
|
|
12716
12838
|
this.name
|
|
12717
12839
|
);
|
|
12718
12840
|
}
|
|
12719
|
-
;
|
|
12720
|
-
record[field] = translated;
|
|
12841
|
+
setAtPathInPlace(obj, field, translated);
|
|
12721
12842
|
}
|
|
12722
12843
|
}
|
|
12723
12844
|
if (this.i18nPutValidator !== void 0) {
|
|
@@ -12869,7 +12990,7 @@ var Collection = class {
|
|
|
12869
12990
|
* Fire registered MV strategies whose dependency set includes this
|
|
12870
12991
|
* collection. Eager-mode MVs re-materialize inline via
|
|
12871
12992
|
* `MaterializedViewExecutor.refresh`; lazy / manual modes are
|
|
12872
|
-
* no-ops in the foundation
|
|
12993
|
+
* no-ops in the foundation; wired in the lazy-mode implementation.
|
|
12873
12994
|
*
|
|
12874
12995
|
* Skips entirely when the record being written is itself an
|
|
12875
12996
|
* MV-emitted row (carries `_materializedFrom`) — defensive guard
|
|
@@ -13013,13 +13134,15 @@ var Collection = class {
|
|
|
13013
13134
|
}
|
|
13014
13135
|
/**
|
|
13015
13136
|
* Delete a record by ID. Runs inside the hub's write-queue tracker
|
|
13016
|
-
*
|
|
13137
|
+
* so `hub.writeQueue.pending` reflects this write.
|
|
13017
13138
|
*/
|
|
13018
13139
|
async delete(id) {
|
|
13019
13140
|
await this.schemaUpdateGate?.assertWritable();
|
|
13020
13141
|
await this.schemaFence?.assertWritable(this.name);
|
|
13142
|
+
const hooksActive = this.#hooksActive();
|
|
13143
|
+
const busAfterDelete = (this.subsystemBus?.hasHandlers("afterDelete") ?? false) && !(this.subsystemBus?.dispatching ?? false);
|
|
13021
13144
|
let event;
|
|
13022
|
-
if (
|
|
13145
|
+
if (hooksActive || busAfterDelete) {
|
|
13023
13146
|
const prior = await this.#priorForHook(id);
|
|
13024
13147
|
event = {
|
|
13025
13148
|
op: "delete",
|
|
@@ -13034,14 +13157,17 @@ var Collection = class {
|
|
|
13034
13157
|
baseVersion: prior.version,
|
|
13035
13158
|
version: prior.version + 1
|
|
13036
13159
|
};
|
|
13037
|
-
await this.writeHooks.runBefore(event);
|
|
13160
|
+
if (hooksActive) await this.writeHooks.runBefore(event);
|
|
13038
13161
|
}
|
|
13039
13162
|
if (this.writeQueue) await this.writeQueue.track(() => this.deleteInternal(id));
|
|
13040
13163
|
else await this.deleteInternal(id);
|
|
13041
|
-
if (event)
|
|
13164
|
+
if (event) {
|
|
13165
|
+
if (hooksActive) await this.writeHooks.runAfter(event);
|
|
13166
|
+
if (busAfterDelete) await this.subsystemBus.dispatch("afterDelete", event);
|
|
13167
|
+
}
|
|
13042
13168
|
}
|
|
13043
13169
|
/**
|
|
13044
|
-
* @internal
|
|
13170
|
+
* @internal — bulk-rewrite every record through a cutover transform.
|
|
13045
13171
|
* Raw adapter path (bypasses the write gate + guards — the transform is
|
|
13046
13172
|
* trusted and runs only during the `migrating` phase). Bumps each
|
|
13047
13173
|
* record's `_v` and appends a ledger `op:'migration'` entry.
|
|
@@ -13080,8 +13206,7 @@ var Collection = class {
|
|
|
13080
13206
|
}
|
|
13081
13207
|
/**
|
|
13082
13208
|
* @internal — system-internal delete that bypasses user-facing
|
|
13083
|
-
* delete hooks (`onDelete`,
|
|
13084
|
-
* enforcer). Used by derivation tombstones (#144) and MV refresh
|
|
13209
|
+
* delete hooks (`onDelete`, FK ref enforcer). Used by derivation tombstones and MV refresh
|
|
13085
13210
|
* (Dim 14 v2) — system housekeeping shouldn't trip user invariants
|
|
13086
13211
|
* registered against the output collection. The ledger entry and
|
|
13087
13212
|
* history snapshot still fire so backup integrity and time-travel
|
|
@@ -13093,7 +13218,7 @@ var Collection = class {
|
|
|
13093
13218
|
*
|
|
13094
13219
|
* When a `txCtx` is supplied, the prior envelope is captured and
|
|
13095
13220
|
* pushed onto `txCtx._executed` BEFORE the delete fires — mirrors
|
|
13096
|
-
* the
|
|
13221
|
+
* the rollback hardening for puts. Callers outside a
|
|
13097
13222
|
* multi-record transaction pass `null` and skip the tracking.
|
|
13098
13223
|
*
|
|
13099
13224
|
* Amendment composition: if `_internalDelete` runs while a vault's
|
|
@@ -13136,58 +13261,27 @@ var Collection = class {
|
|
|
13136
13261
|
if (!hasWritePermission(this.keyring, this.name)) {
|
|
13137
13262
|
throw new ReadOnlyError();
|
|
13138
13263
|
}
|
|
13139
|
-
if (this.
|
|
13140
|
-
const registry = this.guardSource.registry();
|
|
13141
|
-
const guards = registry.guardsFor(this.name);
|
|
13142
|
-
if (guards.length > 0) {
|
|
13143
|
-
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
13144
|
-
if (existingEnv) {
|
|
13145
|
-
let existingRecord = null;
|
|
13146
|
-
try {
|
|
13147
|
-
existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
|
|
13148
|
-
} catch {
|
|
13149
|
-
existingRecord = null;
|
|
13150
|
-
}
|
|
13151
|
-
if (registry.isAmendmentActive()) {
|
|
13152
|
-
const vBefore = existingEnv._v;
|
|
13153
|
-
registry.collectChange(
|
|
13154
|
-
this.name,
|
|
13155
|
-
id,
|
|
13156
|
-
existingRecord,
|
|
13157
|
-
null,
|
|
13158
|
-
vBefore,
|
|
13159
|
-
vBefore
|
|
13160
|
-
);
|
|
13161
|
-
} else if (!internal) {
|
|
13162
|
-
const ctx = {
|
|
13163
|
-
existing: existingRecord,
|
|
13164
|
-
vault: this.guardSource.readOnlyVault(),
|
|
13165
|
-
userId: this.keyring.userId,
|
|
13166
|
-
role: this.keyring.role
|
|
13167
|
-
};
|
|
13168
|
-
await registry.runOnDelete(
|
|
13169
|
-
this.name,
|
|
13170
|
-
existingRecord ?? {},
|
|
13171
|
-
ctx
|
|
13172
|
-
);
|
|
13173
|
-
}
|
|
13174
|
-
}
|
|
13175
|
-
}
|
|
13176
|
-
}
|
|
13177
|
-
if (!internal && this.periodGuard !== void 0) {
|
|
13264
|
+
if (this.subsystemBus?.hasGateHandlers("beforeDelete")) {
|
|
13178
13265
|
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
13179
|
-
let priorRecord = null;
|
|
13180
13266
|
if (existingEnv) {
|
|
13267
|
+
let existingRecord = null;
|
|
13181
13268
|
try {
|
|
13182
|
-
|
|
13269
|
+
existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
|
|
13183
13270
|
} catch {
|
|
13184
|
-
|
|
13271
|
+
existingRecord = null;
|
|
13185
13272
|
}
|
|
13273
|
+
await this.subsystemBus.dispatchGate("beforeDelete", {
|
|
13274
|
+
vault: this.vault,
|
|
13275
|
+
collection: this.name,
|
|
13276
|
+
docId: id,
|
|
13277
|
+
existing: existingRecord,
|
|
13278
|
+
existingVersion: existingEnv._v,
|
|
13279
|
+
existingTs: existingEnv._ts,
|
|
13280
|
+
internal,
|
|
13281
|
+
userId: this.keyring.userId,
|
|
13282
|
+
role: this.keyring.role
|
|
13283
|
+
});
|
|
13186
13284
|
}
|
|
13187
|
-
await this.periodGuard(
|
|
13188
|
-
existingEnv ? { ts: existingEnv._ts, record: priorRecord } : null,
|
|
13189
|
-
null
|
|
13190
|
-
);
|
|
13191
13285
|
}
|
|
13192
13286
|
if (!internal && this.refEnforcer !== void 0) {
|
|
13193
13287
|
await this.refEnforcer.enforceRefsOnDelete(this.name, id);
|
|
@@ -13248,7 +13342,7 @@ var Collection = class {
|
|
|
13248
13342
|
}
|
|
13249
13343
|
/**
|
|
13250
13344
|
* Cascade deletes of array-shape derived rows when a source row is
|
|
13251
|
-
* deleted
|
|
13345
|
+
* deleted. Reads each registered strategy's fanout sidecar
|
|
13252
13346
|
* for this source id, deletes every listed derived row, then
|
|
13253
13347
|
* deletes the sidecar itself.
|
|
13254
13348
|
*
|
|
@@ -13287,8 +13381,8 @@ var Collection = class {
|
|
|
13287
13381
|
}
|
|
13288
13382
|
}
|
|
13289
13383
|
/**
|
|
13290
|
-
* Mirror of {@link dispatchMaterializedViews} for the delete path
|
|
13291
|
-
*
|
|
13384
|
+
* Mirror of {@link dispatchMaterializedViews} for the delete path.
|
|
13385
|
+
* No record content is available (it's gone), so the
|
|
13292
13386
|
* `_materializedFrom` skip used by the put-side dispatch doesn't
|
|
13293
13387
|
* apply here — instead, the recursion guard is the `internal` gate
|
|
13294
13388
|
* at the `_doDelete` call site above.
|
|
@@ -13818,7 +13912,7 @@ var Collection = class {
|
|
|
13818
13912
|
* .aggregate({ total: sum('amount'), n: count() })
|
|
13819
13913
|
* ```
|
|
13820
13914
|
*
|
|
13821
|
-
* **Lazy-MV gap
|
|
13915
|
+
* **Lazy-MV gap:** `scan()` is synchronous-build and does
|
|
13822
13916
|
* NOT trigger lazy materialized-view resolve-on-read. For lazy
|
|
13823
13917
|
* MVs, call `list()` (which DOES resolve) or `vault.refreshView(name)`
|
|
13824
13918
|
* before scanning. Same shape as the `query()` limitation.
|
|
@@ -13899,7 +13993,7 @@ var Collection = class {
|
|
|
13899
13993
|
this.indexes?.upsert(id, record, previous ? previous.record : null);
|
|
13900
13994
|
}
|
|
13901
13995
|
/**
|
|
13902
|
-
*
|
|
13996
|
+
* Apply a peer tab's committed write to THIS tab's in-memory view:
|
|
13903
13997
|
* re-read the (already-persisted) envelope from the shared store + refresh
|
|
13904
13998
|
* cache/indexes, then emit a `change` event so reactive consumers re-render.
|
|
13905
13999
|
* Never writes to the store and never fires write hooks, so it cannot loop.
|
|
@@ -13908,7 +14002,7 @@ var Collection = class {
|
|
|
13908
14002
|
await this._invalidateCacheEntry(id);
|
|
13909
14003
|
this.emitter.emit("change", { vault: this.vault, collection: this.name, id, action });
|
|
13910
14004
|
}
|
|
13911
|
-
/** @internal
|
|
14005
|
+
/** @internal — the current in-memory record without a store read (for conflict capture). */
|
|
13912
14006
|
_peekCached(id) {
|
|
13913
14007
|
const entry = this.lazy && this.lru ? this.lru.get(id) : this.cache.get(id);
|
|
13914
14008
|
return entry ? entry.record : null;
|
|
@@ -14959,7 +15053,7 @@ var OverlayedCollection = class {
|
|
|
14959
15053
|
// error pointing at the relevant issue — so consumers don't hit a
|
|
14960
15054
|
// cryptic `undefined is not a function` runtime crash.
|
|
14961
15055
|
//
|
|
14962
|
-
//
|
|
15056
|
+
// Throw-stubs so consumers get actionable errors rather than cryptic crashes.
|
|
14963
15057
|
/** @throws — chainable Query<T> over a virtual collection is deferred. */
|
|
14964
15058
|
query() {
|
|
14965
15059
|
throw new Error(
|
|
@@ -16270,23 +16364,23 @@ var Vault = class {
|
|
|
16270
16364
|
* `null` for vaults that never register any guard strategy. The
|
|
16271
16365
|
* runtime class is dynamic-imported on demand so consumers that
|
|
16272
16366
|
* never use guards don't pull `GuardRegistry`/`GuardExecutor` into
|
|
16273
|
-
* their bundle
|
|
16367
|
+
* their bundle.
|
|
16274
16368
|
*/
|
|
16275
16369
|
guardRegistry = null;
|
|
16276
16370
|
/**
|
|
16277
16371
|
* Per-vault derivation registry. Same lazy-load contract as
|
|
16278
16372
|
* `guardRegistry` — `null` until `_initDerivations()` runs with at
|
|
16279
|
-
* least one strategy handle.
|
|
16373
|
+
* least one strategy handle.
|
|
16280
16374
|
*/
|
|
16281
16375
|
derivationRegistry = null;
|
|
16282
16376
|
/**
|
|
16283
|
-
* Per-vault materialized-view registry
|
|
16377
|
+
* Per-vault materialized-view registry. Same lazy-load
|
|
16284
16378
|
* contract as `derivationRegistry` — `null` until
|
|
16285
16379
|
* `_initMaterializedViews()` runs with at least one MV handle.
|
|
16286
16380
|
*/
|
|
16287
16381
|
materializedViewRegistry = null;
|
|
16288
16382
|
/**
|
|
16289
|
-
* Per-vault overlay registry
|
|
16383
|
+
* Per-vault overlay registry. Same lazy-load contract as
|
|
16290
16384
|
* `materializedViewRegistry` — `null` until `_initOverlayedViews()`
|
|
16291
16385
|
* runs with at least one handle.
|
|
16292
16386
|
*/
|
|
@@ -16307,7 +16401,7 @@ var Vault = class {
|
|
|
16307
16401
|
* target this vault session's keyringId. There is no method to write
|
|
16308
16402
|
* another principal's envelope (own-only write rule, structural).
|
|
16309
16403
|
* - Read-anyone: `get(keyringId)`, `list()` — read other principals'
|
|
16310
|
-
* envelopes, subject to the `view-team-profiles` policy gate
|
|
16404
|
+
* envelopes, subject to the `view-team-profiles` policy gate.
|
|
16311
16405
|
* - Reactive: `subscribe(id, cb)`, `live(id)` — fire on local writes.
|
|
16312
16406
|
*
|
|
16313
16407
|
* @see docs/superpowers/specs/2026-05-05-user-envelope-design.md
|
|
@@ -16327,12 +16421,12 @@ var Vault = class {
|
|
|
16327
16421
|
*/
|
|
16328
16422
|
reloadKeyring;
|
|
16329
16423
|
collectionCache = /* @__PURE__ */ new Map();
|
|
16330
|
-
/**
|
|
16424
|
+
/** Vault-level schema cutover fence/controller. */
|
|
16331
16425
|
schemaFence;
|
|
16332
|
-
/**
|
|
16426
|
+
/** Per-client heartbeat/watcher; started lazily on cutover registration. */
|
|
16333
16427
|
#fenceWatcher;
|
|
16334
16428
|
#fenceCoordinationStarted = false;
|
|
16335
|
-
/**
|
|
16429
|
+
/** Per-collection registered schema-update strategy names. */
|
|
16336
16430
|
#schemaUpdateNames = /* @__PURE__ */ new Map();
|
|
16337
16431
|
/**
|
|
16338
16432
|
* per-collection `blobFields` retention/TTL config.
|
|
@@ -16406,8 +16500,7 @@ var Vault = class {
|
|
|
16406
16500
|
* Cache of closed/opened accounting periods.
|
|
16407
16501
|
* Populated on first `closePeriod` / `openPeriod` / `listPeriods` /
|
|
16408
16502
|
* per-collection write call. Kept in memory as an ordered list (by
|
|
16409
|
-
* `closedAt`) so
|
|
16410
|
-
* each collection's put/delete path.
|
|
16503
|
+
* `closedAt`) so period checks run fast when the gate bus fires.
|
|
16411
16504
|
*
|
|
16412
16505
|
* Sentinel `null` means "not yet loaded" — the first consumer
|
|
16413
16506
|
* triggers a one-time `loadPeriods()` pass. Every subsequent
|
|
@@ -16602,6 +16695,7 @@ var Vault = class {
|
|
|
16602
16695
|
emitter: this.emitter,
|
|
16603
16696
|
writeQueue: this.noydb._writeQueueTracker,
|
|
16604
16697
|
writeHooks: this.noydb._writeHooks,
|
|
16698
|
+
subsystemBus: this.noydb._subsystemBus,
|
|
16605
16699
|
activeTxId: () => this.noydb._activeTxContextOrNull?.txId ?? null,
|
|
16606
16700
|
schemaUpdateGate,
|
|
16607
16701
|
schemaFence: this.schemaFence,
|
|
@@ -16624,18 +16718,12 @@ var Vault = class {
|
|
|
16624
16718
|
defaultLocale: this.locale,
|
|
16625
16719
|
onRegisterConflictResolver: this.onRegisterConflictResolver,
|
|
16626
16720
|
onAccess: (op, id) => this._logConsent(op, collectionName, id),
|
|
16627
|
-
|
|
16628
|
-
// Guard
|
|
16629
|
-
//
|
|
16630
|
-
//
|
|
16631
|
-
// `if (this.
|
|
16632
|
-
//
|
|
16633
|
-
...this.guardRegistry !== null ? {
|
|
16634
|
-
guardSource: {
|
|
16635
|
-
registry: () => this.guardRegistry,
|
|
16636
|
-
readOnlyVault: () => this._ensureReadOnlyFacade()
|
|
16637
|
-
}
|
|
16638
|
-
} : {},
|
|
16721
|
+
// Derivation source is only wired when the corresponding registry
|
|
16722
|
+
// has been initialised. Guard source was removed in Track A slice 3b
|
|
16723
|
+
// — guards now run via the gate bus in Noydb.#registerGuardGate.
|
|
16724
|
+
// Vaults without derivations skip this so `Collection.put`'s
|
|
16725
|
+
// `if (this.derivationSource)` branch no-ops without touching the
|
|
16726
|
+
// derivation subsystem code.
|
|
16639
16727
|
...this.derivationRegistry !== null ? {
|
|
16640
16728
|
derivationSource: {
|
|
16641
16729
|
registry: () => this.derivationRegistry,
|
|
@@ -16725,7 +16813,7 @@ var Vault = class {
|
|
|
16725
16813
|
await Promise.allSettled(pending);
|
|
16726
16814
|
}
|
|
16727
16815
|
/**
|
|
16728
|
-
* Run a coordinated schema cutover
|
|
16816
|
+
* Run a coordinated schema cutover. Drains pending writes, waits
|
|
16729
16817
|
* for the active client set to quiesce (the ack-barrier), applies every
|
|
16730
16818
|
* pending collection transform in bulk, bumps the vault schema generation,
|
|
16731
16819
|
* and clears the fence. Returns the count of collections migrated.
|
|
@@ -16743,9 +16831,9 @@ var Vault = class {
|
|
|
16743
16831
|
await coll._applyCutoverTransform(transform);
|
|
16744
16832
|
}
|
|
16745
16833
|
/**
|
|
16746
|
-
*
|
|
16834
|
+
* Refresh a loaded collection's view of one document from a peer
|
|
16747
16835
|
* tab's broadcast. No-op when the collection isn't loaded in this tab
|
|
16748
|
-
* (it will read fresh on next open). Mirrors
|
|
16836
|
+
* (it will read fresh on next open). Mirrors `#runCutoverTransform`'s guard.
|
|
16749
16837
|
*/
|
|
16750
16838
|
async _applyRemoteWrite(collectionName, docId, action) {
|
|
16751
16839
|
const coll = this.collectionCache.get(collectionName);
|
|
@@ -16753,9 +16841,9 @@ var Vault = class {
|
|
|
16753
16841
|
await coll._applyRemoteChange(docId, action);
|
|
16754
16842
|
}
|
|
16755
16843
|
/**
|
|
16756
|
-
*
|
|
16844
|
+
* For a detected conflict: capture this tab's clobbered record,
|
|
16757
16845
|
* read the common ancestor from history, converge the cache to the store's
|
|
16758
|
-
* authoritative value (the
|
|
16846
|
+
* authoritative value (the re-read), and return all three for the
|
|
16759
16847
|
* WriteConflict payload. Returns null when the collection isn't loaded.
|
|
16760
16848
|
*/
|
|
16761
16849
|
async _captureAndConverge(collectionName, docId, action, baseV) {
|
|
@@ -16772,15 +16860,15 @@ var Vault = class {
|
|
|
16772
16860
|
const remote = await coll.get(docId);
|
|
16773
16861
|
return { local, remote, base };
|
|
16774
16862
|
}
|
|
16775
|
-
/** Recover a stuck cutover fence
|
|
16863
|
+
/** Recover a stuck cutover fence — reset to normal without bumping. */
|
|
16776
16864
|
async abortSchemaCutover() {
|
|
16777
16865
|
await this.schemaFence.abort();
|
|
16778
16866
|
}
|
|
16779
|
-
/** Current schema-cutover fence state for this vault
|
|
16867
|
+
/** Current schema-cutover fence state for this vault. Thin live read. */
|
|
16780
16868
|
async schemaFenceState() {
|
|
16781
16869
|
return loadFence(this.adapter, this.name);
|
|
16782
16870
|
}
|
|
16783
|
-
/** @internal Start the per-client heartbeat + fence watcher once a cutover is registered
|
|
16871
|
+
/** @internal Start the per-client heartbeat + fence watcher once a cutover is registered. */
|
|
16784
16872
|
_ensureFenceCoordination() {
|
|
16785
16873
|
if (this.#fenceCoordinationStarted) return;
|
|
16786
16874
|
this.#fenceCoordinationStarted = true;
|
|
@@ -16816,9 +16904,11 @@ var Vault = class {
|
|
|
16816
16904
|
if (!record || typeof record !== "object") return;
|
|
16817
16905
|
const obj = record;
|
|
16818
16906
|
for (const [field, descriptor] of Object.entries(i18nFields)) {
|
|
16819
|
-
const
|
|
16820
|
-
|
|
16821
|
-
|
|
16907
|
+
const values = getAtPath(obj, field);
|
|
16908
|
+
for (const value of values) {
|
|
16909
|
+
if (value === void 0 || value === null) continue;
|
|
16910
|
+
this.i18nStrategy.validateI18nTextValue(value, field, descriptor);
|
|
16911
|
+
}
|
|
16822
16912
|
}
|
|
16823
16913
|
}
|
|
16824
16914
|
/**
|
|
@@ -17453,7 +17543,7 @@ var Vault = class {
|
|
|
17453
17543
|
* Dynamic-imports `GuardRegistry` + `ReadOnlyVaultFacade` and seeds
|
|
17454
17544
|
* the registry with the supplied strategy handles. No-op when the
|
|
17455
17545
|
* handles array is empty — keeps the guard subsystem out of the
|
|
17456
|
-
* floor bundle for consumers that don't use guards
|
|
17546
|
+
* floor bundle for consumers that don't use guards.
|
|
17457
17547
|
*
|
|
17458
17548
|
* The read-only facade is eagerly instantiated here so the sync
|
|
17459
17549
|
* accessor `_getReadOnlyFacade()` (called from the tx amendment
|
|
@@ -17471,10 +17561,9 @@ var Vault = class {
|
|
|
17471
17561
|
this.readOnlyFacade = new ReadOnlyVaultFacade2(this);
|
|
17472
17562
|
}
|
|
17473
17563
|
/**
|
|
17474
|
-
* @internal —
|
|
17475
|
-
* vaults that never registered any guard
|
|
17476
|
-
* gate on null
|
|
17477
|
-
* `Collection` already do this transitively).
|
|
17564
|
+
* @internal — The gate handler in Noydb.#registerGuardGate calls into
|
|
17565
|
+
* this. Returns `null` for vaults that never registered any guard
|
|
17566
|
+
* strategy. Callers MUST gate on null.
|
|
17478
17567
|
*/
|
|
17479
17568
|
_getGuardRegistry() {
|
|
17480
17569
|
return this.guardRegistry;
|
|
@@ -17485,7 +17574,7 @@ var Vault = class {
|
|
|
17485
17574
|
* derivation strategies (async because `strategyHash` computation
|
|
17486
17575
|
* goes through `crypto.subtle.digest`). No-op when the handles
|
|
17487
17576
|
* array is empty — keeps the derivation subsystem out of the floor
|
|
17488
|
-
* bundle for consumers that don't use derivations
|
|
17577
|
+
* bundle for consumers that don't use derivations. Throws
|
|
17489
17578
|
* `DerivationCycleError` if a cycle is detected after registration.
|
|
17490
17579
|
*/
|
|
17491
17580
|
async _initDerivations(handles) {
|
|
@@ -17517,7 +17606,7 @@ var Vault = class {
|
|
|
17517
17606
|
* MV spec (which invokes its `query()` once for dependency
|
|
17518
17607
|
* analysis), then runs the unified cycle detection across the MV +
|
|
17519
17608
|
* derivation graphs. No-op when the handles array is empty — keeps
|
|
17520
|
-
* the MV subsystem out of the floor bundle (mirrors
|
|
17609
|
+
* the MV subsystem out of the floor bundle (mirrors the derivation lazy-import pattern).
|
|
17521
17610
|
* Throws `MaterializedViewCycleError` if a cycle is detected.
|
|
17522
17611
|
*/
|
|
17523
17612
|
async _initMaterializedViews(handles) {
|
|
@@ -17574,13 +17663,13 @@ var Vault = class {
|
|
|
17574
17663
|
return this.overlayedViewRegistry;
|
|
17575
17664
|
}
|
|
17576
17665
|
/**
|
|
17577
|
-
* Manual re-materialize for a single registered MV
|
|
17666
|
+
* Manual re-materialize for a single registered MV. Useful
|
|
17578
17667
|
* for `refresh: 'manual'` MVs (whose consumer drives refreshes
|
|
17579
17668
|
* externally), for stale-bit recovery on vault re-open, and as the
|
|
17580
17669
|
* explicit bulk-recompute escape hatch after a strategy change.
|
|
17581
17670
|
*
|
|
17582
|
-
* Returns `{ written, deleted, failed }`. `deleted` is always 0
|
|
17583
|
-
*
|
|
17671
|
+
* Returns `{ written, deleted, failed }`. `deleted` is always 0
|
|
17672
|
+
* when tombstoning is not enabled.
|
|
17584
17673
|
*
|
|
17585
17674
|
* Throws if `name` is not a registered MV.
|
|
17586
17675
|
*/
|
|
@@ -17676,22 +17765,19 @@ var Vault = class {
|
|
|
17676
17765
|
/**
|
|
17677
17766
|
* @internal — exposed for `runTransaction({ amendment: true })` so
|
|
17678
17767
|
* the amendment invariant runner can pass the SAME read-only vault
|
|
17679
|
-
* facade that the
|
|
17680
|
-
*
|
|
17681
|
-
* `
|
|
17682
|
-
*
|
|
17683
|
-
*
|
|
17768
|
+
* facade that the gate handler in Noydb.#registerGuardGate uses.
|
|
17769
|
+
* Eagerly instantiated by `_initGuards()` so this accessor stays
|
|
17770
|
+
* synchronous; returns `null` for vaults that never registered any
|
|
17771
|
+
* guard (amendments require at least one guard, so the caller should
|
|
17772
|
+
* never see null).
|
|
17684
17773
|
*/
|
|
17685
17774
|
_getReadOnlyFacade() {
|
|
17686
17775
|
return this.readOnlyFacade;
|
|
17687
17776
|
}
|
|
17688
17777
|
/**
|
|
17689
|
-
* Internal lazy-allocator for the read-only facade. Used
|
|
17690
|
-
*
|
|
17691
|
-
*
|
|
17692
|
-
* invocation (theoretically impossible — `Noydb.openVault` awaits
|
|
17693
|
-
* `_initGuards` before returning — but we keep the defensive lazy
|
|
17694
|
-
* path so the closure's contract stays "always returns a facade").
|
|
17778
|
+
* Internal lazy-allocator for the read-only facade. Used as a
|
|
17779
|
+
* defensive fallback; in practice `_initGuards()` eagerly
|
|
17780
|
+
* instantiates this, so the lazy path is a no-op.
|
|
17695
17781
|
*/
|
|
17696
17782
|
_ensureReadOnlyFacade() {
|
|
17697
17783
|
if (this.readOnlyFacade !== null) return this.readOnlyFacade;
|
|
@@ -18146,7 +18232,7 @@ var Vault = class {
|
|
|
18146
18232
|
const all = await this._loadPeriodsCache();
|
|
18147
18233
|
return all.find((p) => p.name === name) ?? null;
|
|
18148
18234
|
}
|
|
18149
|
-
/** @internal —
|
|
18235
|
+
/** @internal — called by the gate bus before put/delete. */
|
|
18150
18236
|
async _assertTsWritable(existing, incoming) {
|
|
18151
18237
|
if (existing === null && incoming === null) return;
|
|
18152
18238
|
if (this.periodCache === null) {
|
|
@@ -18234,7 +18320,7 @@ var Vault = class {
|
|
|
18234
18320
|
return dumpVaultSchema(this, opts);
|
|
18235
18321
|
}
|
|
18236
18322
|
/**
|
|
18237
|
-
* Lightweight read of the vault's registered schema
|
|
18323
|
+
* Lightweight read of the vault's registered schema: collections
|
|
18238
18324
|
* (+ doc counts), guards, materialized views, schema-update strategies,
|
|
18239
18325
|
* and the unlocked user's grants. Cheap — one `adapter.list` per
|
|
18240
18326
|
* collection, no decryption. For a full snapshot + stats use dumpSchema().
|
|
@@ -19021,6 +19107,110 @@ var WriteHookRegistry = class {
|
|
|
19021
19107
|
}
|
|
19022
19108
|
};
|
|
19023
19109
|
|
|
19110
|
+
// src/subsystem-bus.ts
|
|
19111
|
+
var SubsystemBus = class {
|
|
19112
|
+
#handlers = /* @__PURE__ */ new Map();
|
|
19113
|
+
#gateHandlers = /* @__PURE__ */ new Map();
|
|
19114
|
+
#depth = 0;
|
|
19115
|
+
/** Register a handler for an observe point. Returns an unsubscribe fn. */
|
|
19116
|
+
register(point, handler) {
|
|
19117
|
+
let arr = this.#handlers.get(point);
|
|
19118
|
+
if (!arr) {
|
|
19119
|
+
arr = [];
|
|
19120
|
+
this.#handlers.set(point, arr);
|
|
19121
|
+
}
|
|
19122
|
+
arr.push(handler);
|
|
19123
|
+
return () => {
|
|
19124
|
+
const a = this.#handlers.get(point);
|
|
19125
|
+
if (!a) return;
|
|
19126
|
+
const i = a.indexOf(handler);
|
|
19127
|
+
if (i >= 0) a.splice(i, 1);
|
|
19128
|
+
};
|
|
19129
|
+
}
|
|
19130
|
+
/** Cheap gate for the write path — true when any handler is registered for the point. */
|
|
19131
|
+
hasHandlers(point) {
|
|
19132
|
+
const a = this.#handlers.get(point);
|
|
19133
|
+
return a !== void 0 && a.length > 0;
|
|
19134
|
+
}
|
|
19135
|
+
/**
|
|
19136
|
+
* True while one or more dispatches are in flight. Backed by a depth counter
|
|
19137
|
+
* so that two concurrent async dispatches (`Promise.all([put('a'), put('b')])`
|
|
19138
|
+
* each captured `busAfterPut=true` at their respective put() tops while depth
|
|
19139
|
+
* was 0) both proceed independently — the counter stays > 0 until BOTH finish,
|
|
19140
|
+
* so any nested write attempted by a handler still sees `dispatching === true`
|
|
19141
|
+
* and is suppressed by the write-path gate in `collection.ts`
|
|
19142
|
+
* (`busAfterPut = hasHandlers('afterPut') && !dispatching`). Re-entrancy
|
|
19143
|
+
* suppression lives exclusively on that write-path gate; concurrent independent
|
|
19144
|
+
* dispatches must not drop each other's events.
|
|
19145
|
+
*/
|
|
19146
|
+
get dispatching() {
|
|
19147
|
+
return this.#depth > 0;
|
|
19148
|
+
}
|
|
19149
|
+
/**
|
|
19150
|
+
* Dispatch in registration order, awaited. Per-handler errors are warned, not
|
|
19151
|
+
* thrown — an observe handler must never abort a completed write. A
|
|
19152
|
+
* re-entrancy guard suppresses nested firing so a handler that itself writes
|
|
19153
|
+
* cannot loop (same rationale as WriteHookRegistry.#suppressed).
|
|
19154
|
+
*/
|
|
19155
|
+
async dispatch(point, event) {
|
|
19156
|
+
const a = this.#handlers.get(point);
|
|
19157
|
+
if (!a || a.length === 0) return;
|
|
19158
|
+
this.#depth++;
|
|
19159
|
+
try {
|
|
19160
|
+
for (const h of a.slice()) {
|
|
19161
|
+
try {
|
|
19162
|
+
await h(event);
|
|
19163
|
+
} catch (err) {
|
|
19164
|
+
console.warn(
|
|
19165
|
+
`[noy-db] subsystem observe handler failed at ${point}: ` + (err instanceof Error ? err.message : String(err))
|
|
19166
|
+
);
|
|
19167
|
+
}
|
|
19168
|
+
}
|
|
19169
|
+
} finally {
|
|
19170
|
+
this.#depth--;
|
|
19171
|
+
}
|
|
19172
|
+
}
|
|
19173
|
+
/** Register a write-gating handler. A throw from the handler ABORTS the write. Returns an unsubscribe fn. */
|
|
19174
|
+
registerGate(point, handler) {
|
|
19175
|
+
let arr = this.#gateHandlers.get(point);
|
|
19176
|
+
if (!arr) {
|
|
19177
|
+
arr = [];
|
|
19178
|
+
this.#gateHandlers.set(point, arr);
|
|
19179
|
+
}
|
|
19180
|
+
arr.push(handler);
|
|
19181
|
+
return () => {
|
|
19182
|
+
const a = this.#gateHandlers.get(point);
|
|
19183
|
+
if (!a) return;
|
|
19184
|
+
const i = a.indexOf(handler);
|
|
19185
|
+
if (i >= 0) a.splice(i, 1);
|
|
19186
|
+
};
|
|
19187
|
+
}
|
|
19188
|
+
/** Cheap gate for the write path — true when any gate handler is registered for the point. */
|
|
19189
|
+
hasGateHandlers(point) {
|
|
19190
|
+
const a = this.#gateHandlers.get(point);
|
|
19191
|
+
return a !== void 0 && a.length > 0;
|
|
19192
|
+
}
|
|
19193
|
+
/**
|
|
19194
|
+
* Run gate handlers in registration order, awaited. Unlike `dispatch`
|
|
19195
|
+
* (observe), a handler throw is NOT swallowed — it PROPAGATES, aborting the
|
|
19196
|
+
* write before it reaches the store. The first throw stops the remaining
|
|
19197
|
+
* handlers (fail-fast). This is the seam guards/periods migrate onto.
|
|
19198
|
+
*
|
|
19199
|
+
* Note: gate handlers are validators that read, not write. A gate handler
|
|
19200
|
+
* that writes back into the same collection would re-enter the write path
|
|
19201
|
+
* and re-dispatch this point; loop-suppression for that case is deferred to
|
|
19202
|
+
* the migration slice (contract: gate handlers must not perform writes that
|
|
19203
|
+
* re-trigger their own point).
|
|
19204
|
+
*/
|
|
19205
|
+
async dispatchGate(point, event) {
|
|
19206
|
+
const a = this.#gateHandlers.get(point);
|
|
19207
|
+
if (!a || a.length === 0) return;
|
|
19208
|
+
for (const h of a.slice()) {
|
|
19209
|
+
await h(event);
|
|
19210
|
+
}
|
|
19211
|
+
}
|
|
19212
|
+
};
|
|
19213
|
+
|
|
19024
19214
|
// src/tab-coordination.ts
|
|
19025
19215
|
var TabCoordinator = class {
|
|
19026
19216
|
tabId;
|
|
@@ -19336,9 +19526,9 @@ var PERSONAL_POLICY = Object.freeze({
|
|
|
19336
19526
|
minTier: 1,
|
|
19337
19527
|
enabled: true
|
|
19338
19528
|
},
|
|
19339
|
-
// rotate-recovery
|
|
19340
|
-
// when the user remembers their passphrase. PERSONAL
|
|
19341
|
-
//
|
|
19529
|
+
// rotate-recovery: deliberate paper-sheet regeneration
|
|
19530
|
+
// when the user remembers their passphrase. PERSONAL allows tier-1 —
|
|
19531
|
+
// knowing the passphrase is enough.
|
|
19342
19532
|
"rotate-recovery": { minTier: 1 },
|
|
19343
19533
|
"enroll-authenticator": { minTier: 1 },
|
|
19344
19534
|
"remove-authenticator": { minTier: 1 },
|
|
@@ -19372,7 +19562,7 @@ var PERSONAL_POLICY = Object.freeze({
|
|
|
19372
19562
|
minTier: 1,
|
|
19373
19563
|
enabled: false
|
|
19374
19564
|
},
|
|
19375
|
-
// ─── User envelope gates
|
|
19565
|
+
// ─── User envelope gates ──────────────────────────────────────────
|
|
19376
19566
|
// edit-own-profile: tier 3 floor — any active session can edit their
|
|
19377
19567
|
// own profile/preferences. Tightening to require a TOTP for
|
|
19378
19568
|
// profile changes is a one-line override.
|
|
@@ -19399,7 +19589,7 @@ var STRICT_POLICY = Object.freeze({
|
|
|
19399
19589
|
minTier: 1,
|
|
19400
19590
|
enabled: true
|
|
19401
19591
|
},
|
|
19402
|
-
// rotate-recovery
|
|
19592
|
+
// rotate-recovery: STRICT requires an off-device factor —
|
|
19403
19593
|
// rotating recovery is an off-site-trust event; a stolen unlocked
|
|
19404
19594
|
// laptop must not be able to silently mint a new sheet for the
|
|
19405
19595
|
// attacker. Matches the `peer-recover-user` STRICT default.
|
|
@@ -19472,7 +19662,7 @@ var STRICT_POLICY = Object.freeze({
|
|
|
19472
19662
|
minTier: 1,
|
|
19473
19663
|
enabled: false
|
|
19474
19664
|
},
|
|
19475
|
-
// ─── User envelope gates
|
|
19665
|
+
// ─── User envelope gates ──────────────────────────────────────────
|
|
19476
19666
|
// STRICT: profile edits require a TOTP/email-OTP factor (typical
|
|
19477
19667
|
// shared-workstation hardening — your name/avatar shouldn't change
|
|
19478
19668
|
// without a fresh second-factor proof).
|
|
@@ -19583,13 +19773,14 @@ var Noydb = class {
|
|
|
19583
19773
|
emitter = new NoydbEventEmitter();
|
|
19584
19774
|
writeQueueTracker = new WriteQueueTracker();
|
|
19585
19775
|
writeHooks = new WriteHookRegistry();
|
|
19776
|
+
subsystemBus = new SubsystemBus();
|
|
19586
19777
|
clientId = generateULID();
|
|
19587
19778
|
vaultCache = /* @__PURE__ */ new Map();
|
|
19588
19779
|
keyringCache = /* @__PURE__ */ new Map();
|
|
19589
19780
|
syncEngines = /* @__PURE__ */ new Map();
|
|
19590
19781
|
/**
|
|
19591
19782
|
* Per-vault active session tier — defaults to `1` after a passphrase
|
|
19592
|
-
* unlock; tier-2 / tier-3 unlocks
|
|
19783
|
+
* unlock; tier-2 / tier-3 unlocks downgrade it. Used by
|
|
19593
19784
|
* {@link checkGate} to evaluate `gate.minTier`.
|
|
19594
19785
|
*/
|
|
19595
19786
|
activeTier = /* @__PURE__ */ new Map();
|
|
@@ -19599,14 +19790,14 @@ var Noydb = class {
|
|
|
19599
19790
|
*/
|
|
19600
19791
|
policyCache = /* @__PURE__ */ new Map();
|
|
19601
19792
|
/**
|
|
19602
|
-
* One-shot bypass for the managed-mode strong-recovery check
|
|
19793
|
+
* One-shot bypass for the managed-mode strong-recovery check.
|
|
19603
19794
|
* Set true by {@link openVaultAndEnrollRecovery} for the duration of
|
|
19604
19795
|
* the bootstrap window so the keyring can be created before the
|
|
19605
19796
|
* strong recovery is enrolled. Always cleared (try/finally).
|
|
19606
19797
|
* @internal
|
|
19607
19798
|
*/
|
|
19608
19799
|
_skipNextManagedRecoveryCheck = false;
|
|
19609
|
-
/** Per-vault tier-3 (PIN / quick-resume) state
|
|
19800
|
+
/** Per-vault tier-3 (PIN / quick-resume) state. */
|
|
19610
19801
|
quickUnlock = new QuickUnlockStore();
|
|
19611
19802
|
/**
|
|
19612
19803
|
* Resolved public-envelope schema. Lazily computed once from
|
|
@@ -19616,9 +19807,9 @@ var Noydb = class {
|
|
|
19616
19807
|
publicEnvelopeSchema;
|
|
19617
19808
|
closed = false;
|
|
19618
19809
|
sessionTimer = null;
|
|
19619
|
-
/** Same-device multi-tab coordinator
|
|
19810
|
+
/** Same-device multi-tab coordinator; created on `enableTabCoordination()`. */
|
|
19620
19811
|
tabCoordinator;
|
|
19621
|
-
/** Cross-tab write relay
|
|
19812
|
+
/** Cross-tab write relay; created on `enableTabCoordination()`. */
|
|
19622
19813
|
writeRelay;
|
|
19623
19814
|
/** Per-vault policy enforcers. */
|
|
19624
19815
|
policyEnforcers = /* @__PURE__ */ new Map();
|
|
@@ -19631,8 +19822,8 @@ var Noydb = class {
|
|
|
19631
19822
|
* the same function's `finally` block. Side-effect writes triggered
|
|
19632
19823
|
* during a staged op's `Collection.put` (today: eager derivation
|
|
19633
19824
|
* outputs) register their pre-write envelope on `_executed` here so
|
|
19634
|
-
* a mid-batch failure rolls them back alongside the main staged ops
|
|
19635
|
-
*
|
|
19825
|
+
* a mid-batch failure rolls them back alongside the main staged ops.
|
|
19826
|
+
* `null` outside of Phase 2.
|
|
19636
19827
|
* @internal
|
|
19637
19828
|
*/
|
|
19638
19829
|
_activeTxContext = null;
|
|
@@ -19653,8 +19844,95 @@ var Noydb = class {
|
|
|
19653
19844
|
if (options.sessionPolicy) {
|
|
19654
19845
|
this.sessionStrategy.validateSessionPolicy(options.sessionPolicy);
|
|
19655
19846
|
}
|
|
19847
|
+
this.#registerGuardGate();
|
|
19848
|
+
this.#registerPeriodGate();
|
|
19656
19849
|
this.resetSessionTimer();
|
|
19657
19850
|
}
|
|
19851
|
+
// Track A — guards migration. Registers record-lock / field-freeze / onDelete
|
|
19852
|
+
// / amendment-collect as gate-bus handlers (only when guards are opted in, so
|
|
19853
|
+
// the write path is zero-cost otherwise). Resolves the live vault's
|
|
19854
|
+
// GuardRegistry per dispatch. Registered BEFORE the period gate so guard
|
|
19855
|
+
// checks run first. The amendment branch is a side-effect (collectChange),
|
|
19856
|
+
// NOT a throw — and runs even for internal deletes (an amendment invariant
|
|
19857
|
+
// must see system housekeeping tombstones); onDelete/checks run only for
|
|
19858
|
+
// user (non-internal) operations.
|
|
19859
|
+
#registerGuardGate() {
|
|
19860
|
+
if (this.options.guardStrategies === void 0) return;
|
|
19861
|
+
this.subsystemBus.registerGate("beforePut", async (e) => {
|
|
19862
|
+
const v = this.vaultCache.get(e.vault);
|
|
19863
|
+
if (!v) return;
|
|
19864
|
+
const registry = v._getGuardRegistry();
|
|
19865
|
+
if (!registry) return;
|
|
19866
|
+
const guards = registry.guardsFor(e.collection);
|
|
19867
|
+
if (guards.length === 0) return;
|
|
19868
|
+
const existing = e.existing ?? null;
|
|
19869
|
+
const incoming = e.incoming;
|
|
19870
|
+
if (registry.isAmendmentActive()) {
|
|
19871
|
+
registry.collectChange(e.collection, e.docId, existing, incoming, e.existingVersion, e.existingVersion + 1);
|
|
19872
|
+
return;
|
|
19873
|
+
}
|
|
19874
|
+
const facade = v._getReadOnlyFacade();
|
|
19875
|
+
if (!facade) return;
|
|
19876
|
+
const ctx = { existing, vault: facade, userId: e.userId, role: e.role };
|
|
19877
|
+
await registry.runChecks(e.collection, incoming, ctx);
|
|
19878
|
+
const { GuardExecutor: GuardExecutor2 } = await Promise.resolve().then(() => (init_executor(), executor_exports));
|
|
19879
|
+
for (const g of guards) {
|
|
19880
|
+
await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming);
|
|
19881
|
+
}
|
|
19882
|
+
});
|
|
19883
|
+
this.subsystemBus.registerGate("beforeDelete", async (e) => {
|
|
19884
|
+
const v = this.vaultCache.get(e.vault);
|
|
19885
|
+
if (!v) return;
|
|
19886
|
+
const registry = v._getGuardRegistry();
|
|
19887
|
+
if (!registry) return;
|
|
19888
|
+
const guards = registry.guardsFor(e.collection);
|
|
19889
|
+
if (guards.length === 0) return;
|
|
19890
|
+
const existing = e.existing ?? null;
|
|
19891
|
+
if (registry.isAmendmentActive()) {
|
|
19892
|
+
registry.collectChange(e.collection, e.docId, existing, null, e.existingVersion, e.existingVersion);
|
|
19893
|
+
return;
|
|
19894
|
+
}
|
|
19895
|
+
if (e.internal) return;
|
|
19896
|
+
const facade = v._getReadOnlyFacade();
|
|
19897
|
+
if (!facade) return;
|
|
19898
|
+
const ctx = { existing, vault: facade, userId: e.userId, role: e.role };
|
|
19899
|
+
await registry.runOnDelete(e.collection, existing ?? {}, ctx);
|
|
19900
|
+
});
|
|
19901
|
+
}
|
|
19902
|
+
/**
|
|
19903
|
+
* Register closed-period write guards on the subsystem bus when a
|
|
19904
|
+
* periodsStrategy is configured. Handlers resolve the live Vault from
|
|
19905
|
+
* vaultCache so they always use the up-to-date period cache.
|
|
19906
|
+
*/
|
|
19907
|
+
// Track A — periods migration. Registers the closed-period write guard as a
|
|
19908
|
+
// gate-bus handler (only when periods is opted in, so the write path is
|
|
19909
|
+
// zero-cost otherwise). Each handler resolves the LIVE vault from the cache
|
|
19910
|
+
// per dispatch and delegates to its `_assertTsWritable`, which owns all
|
|
19911
|
+
// period logic. Resolving the live vault makes eviction/re-creation
|
|
19912
|
+
// transparent. Semantics note: if a write reaches the gate through a retained
|
|
19913
|
+
// collection handle whose vault has been evicted from `vaultCache` (e.g. a
|
|
19914
|
+
// post-revocation write on a stale handle), the period check is skipped — the
|
|
19915
|
+
// guard binds to the live vault, not a captured instance. Periods is a
|
|
19916
|
+
// write-integrity guard, not a security boundary, and a re-open reloads the
|
|
19917
|
+
// period cache; the trade-off is intentional.
|
|
19918
|
+
#registerPeriodGate() {
|
|
19919
|
+
if (this.options.periodsStrategy === void 0) return;
|
|
19920
|
+
this.subsystemBus.registerGate("beforePut", async (e) => {
|
|
19921
|
+
const v = this.vaultCache.get(e.vault);
|
|
19922
|
+
if (!v) return;
|
|
19923
|
+
const existing = e.op === "create" ? null : { ts: e.existingTs ?? null, record: e.existing ?? null };
|
|
19924
|
+
await v._assertTsWritable(existing, e.incoming);
|
|
19925
|
+
});
|
|
19926
|
+
this.subsystemBus.registerGate("beforeDelete", async (e) => {
|
|
19927
|
+
if (e.internal) return;
|
|
19928
|
+
const v = this.vaultCache.get(e.vault);
|
|
19929
|
+
if (!v) return;
|
|
19930
|
+
await v._assertTsWritable(
|
|
19931
|
+
{ ts: e.existingTs ?? null, record: e.existing ?? null },
|
|
19932
|
+
null
|
|
19933
|
+
);
|
|
19934
|
+
});
|
|
19935
|
+
}
|
|
19658
19936
|
resetSessionTimer() {
|
|
19659
19937
|
if (this.sessionTimer) clearTimeout(this.sessionTimer);
|
|
19660
19938
|
const idleMs = this.options.sessionPolicy?.idleTimeoutMs ?? this.options.sessionTimeout;
|
|
@@ -19944,8 +20222,6 @@ var Noydb = class {
|
|
|
19944
20222
|
* @throws `NoAccessError` when no keyring exists for the target.
|
|
19945
20223
|
* @throws `PermissionDeniedError` when the role hierarchy rejects.
|
|
19946
20224
|
* @throws `ValidationError` when no field is provided.
|
|
19947
|
-
*
|
|
19948
|
-
* @see #54
|
|
19949
20225
|
*/
|
|
19950
20226
|
async updateUser(vault, options, factors) {
|
|
19951
20227
|
await this.checkGate(vault, "update-user", factors);
|
|
@@ -20281,7 +20557,7 @@ var Noydb = class {
|
|
|
20281
20557
|
* Phase 2. `Collection.dispatchDerivations` consults this so a
|
|
20282
20558
|
* recursive derived-output write inside `Collection.put` can register
|
|
20283
20559
|
* its envelope onto `ctx._executed` and roll back with the main
|
|
20284
|
-
* staged ops on mid-batch failure
|
|
20560
|
+
* staged ops on mid-batch failure.
|
|
20285
20561
|
*
|
|
20286
20562
|
* @internal
|
|
20287
20563
|
*/
|
|
@@ -20310,7 +20586,7 @@ var Noydb = class {
|
|
|
20310
20586
|
* `Collection.putManyAtomic` (via `derivationSource.createTxContext`)
|
|
20311
20587
|
* to publish an active context for the duration of its bulk-atomic
|
|
20312
20588
|
* Phase 2 loop, so recursive derivation-output writes register on
|
|
20313
|
-
* `ctx._executed` and roll back together with the source ops
|
|
20589
|
+
* `ctx._executed` and roll back together with the source ops.
|
|
20314
20590
|
*
|
|
20315
20591
|
* @internal
|
|
20316
20592
|
*/
|
|
@@ -20381,26 +20657,26 @@ var Noydb = class {
|
|
|
20381
20657
|
return this.writeQueueTracker;
|
|
20382
20658
|
}
|
|
20383
20659
|
/**
|
|
20384
|
-
* Register a hook that runs before each write
|
|
20660
|
+
* Register a hook that runs before each write. Awaited; a throw
|
|
20385
20661
|
* aborts the write. Returns an unsubscribe function.
|
|
20386
20662
|
*/
|
|
20387
20663
|
onBeforeWrite(handler) {
|
|
20388
20664
|
return this.writeHooks.onBeforeWrite(handler);
|
|
20389
20665
|
}
|
|
20390
20666
|
/**
|
|
20391
|
-
* Register a hook that runs after each committed write
|
|
20667
|
+
* Register a hook that runs after each committed write. Awaited;
|
|
20392
20668
|
* a handler error is warned, never rolled back. Returns an unsubscribe fn.
|
|
20393
20669
|
*/
|
|
20394
20670
|
onAfterWrite(handler) {
|
|
20395
20671
|
return this.writeHooks.onAfterWrite(handler);
|
|
20396
20672
|
}
|
|
20397
|
-
/** Subscribe to cross-tab write conflicts
|
|
20673
|
+
/** Subscribe to cross-tab write conflicts. Returns an unsubscribe. */
|
|
20398
20674
|
onWriteConflict(fn) {
|
|
20399
20675
|
this.on("write:conflict", fn);
|
|
20400
20676
|
return () => this.off("write:conflict", fn);
|
|
20401
20677
|
}
|
|
20402
20678
|
/**
|
|
20403
|
-
* Enable same-device multi-tab coordination
|
|
20679
|
+
* Enable same-device multi-tab coordination: primary/secondary
|
|
20404
20680
|
* election + presence. Browser-only — a graceful no-op (role 'unknown')
|
|
20405
20681
|
* when Web Locks / BroadcastChannel are unavailable and nothing is
|
|
20406
20682
|
* injected. Idempotent; returns a disposer.
|
|
@@ -20483,7 +20759,11 @@ var Noydb = class {
|
|
|
20483
20759
|
get _writeHooks() {
|
|
20484
20760
|
return this.writeHooks;
|
|
20485
20761
|
}
|
|
20486
|
-
/** @internal
|
|
20762
|
+
/** @internal The observe bus, threaded into every Collection. */
|
|
20763
|
+
get _subsystemBus() {
|
|
20764
|
+
return this.subsystemBus;
|
|
20765
|
+
}
|
|
20766
|
+
/** @internal Stable per-instance id for schema-cutover coordination. */
|
|
20487
20767
|
get _clientId() {
|
|
20488
20768
|
return this.clientId;
|
|
20489
20769
|
}
|
|
@@ -20503,10 +20783,6 @@ var Noydb = class {
|
|
|
20503
20783
|
* survives lock; nothing about it changes when DEKs are scrubbed).
|
|
20504
20784
|
*
|
|
20505
20785
|
* No-op when `vault` is not currently in cache (idempotent).
|
|
20506
|
-
*
|
|
20507
|
-
* Unblocks vLannaAi/niwat#33.
|
|
20508
|
-
*
|
|
20509
|
-
* @see #17
|
|
20510
20786
|
*/
|
|
20511
20787
|
lockVault(vault) {
|
|
20512
20788
|
this.syncEngines.get(vault)?.stopAutoSync();
|
|
@@ -20621,7 +20897,7 @@ var Noydb = class {
|
|
|
20621
20897
|
return merged;
|
|
20622
20898
|
}
|
|
20623
20899
|
/**
|
|
20624
|
-
* Read the current vault-level user-directory toggle
|
|
20900
|
+
* Read the current vault-level user-directory toggle. Returns
|
|
20625
20901
|
* the default-on shape (`{ enabled: true }`) when no `_meta/directory`
|
|
20626
20902
|
* document has been persisted yet.
|
|
20627
20903
|
*
|
|
@@ -20633,7 +20909,7 @@ var Noydb = class {
|
|
|
20633
20909
|
return persisted?.enabled ?? true;
|
|
20634
20910
|
}
|
|
20635
20911
|
/**
|
|
20636
|
-
* Toggle the vault's user-directory listing on or off
|
|
20912
|
+
* Toggle the vault's user-directory listing on or off.
|
|
20637
20913
|
* Owner-only. When disabled, `listUsersWithEnvelopes()` throws
|
|
20638
20914
|
* {@link import('./errors.js').DirectoryDisabledError} for callers
|
|
20639
20915
|
* whose role is neither `owner` nor `admin`.
|
|
@@ -20693,7 +20969,7 @@ var Noydb = class {
|
|
|
20693
20969
|
*
|
|
20694
20970
|
* Two enforcement modes:
|
|
20695
20971
|
*
|
|
20696
|
-
* 1. **Managed-mode mandatory strong-recovery
|
|
20972
|
+
* 1. **Managed-mode mandatory strong-recovery.** When
|
|
20697
20973
|
* `passphraseMode === 'managed'`, the vault MUST have at least
|
|
20698
20974
|
* one **strong** recovery profile (Shamir today). Paper alone is
|
|
20699
20975
|
* rejected because under managed mode the user has no memorized
|
|
@@ -20727,14 +21003,14 @@ var Noydb = class {
|
|
|
20727
21003
|
throw new RecoveryNotEnrolledError();
|
|
20728
21004
|
}
|
|
20729
21005
|
/**
|
|
20730
|
-
* Internal accessor used by tier-2/tier-3 unlock paths
|
|
21006
|
+
* Internal accessor used by tier-2/tier-3 unlock paths
|
|
20731
21007
|
* to mark the active session tier.
|
|
20732
21008
|
* @internal
|
|
20733
21009
|
*/
|
|
20734
21010
|
_setActiveTier(vault, tier) {
|
|
20735
21011
|
this.activeTier.set(vault, tier);
|
|
20736
21012
|
}
|
|
20737
|
-
// ─── Tier-2 enroll / remove
|
|
21013
|
+
// ─── Tier-2 enroll / remove ─────────────────────────────────────
|
|
20738
21014
|
/**
|
|
20739
21015
|
* Add a tier-2 authenticator slot to the calling user's keyring.
|
|
20740
21016
|
* Each slot independently wraps the SAME KEK under a method-specific
|
|
@@ -20764,7 +21040,7 @@ var Noydb = class {
|
|
|
20764
21040
|
const next = await removeAuthenticator(this.options.store, vault, keyring, slotId);
|
|
20765
21041
|
this.keyringCache.set(vault, next);
|
|
20766
21042
|
}
|
|
20767
|
-
/** Read the slot list for a vault. Internal — `describeAuthConfig`
|
|
21043
|
+
/** Read the slot list for a vault. Internal — `describeAuthConfig` consumes this. */
|
|
20768
21044
|
async listAuthenticators(vault) {
|
|
20769
21045
|
const keyring = await this.getKeyringInternal(vault);
|
|
20770
21046
|
return keyring.authenticators;
|
|
@@ -20776,7 +21052,7 @@ var Noydb = class {
|
|
|
20776
21052
|
* are immutable through this method. Anti-slot-swap is structural,
|
|
20777
21053
|
* not gate-driven.
|
|
20778
21054
|
*
|
|
20779
|
-
* `meta` patch semantics (
|
|
21055
|
+
* `meta` patch semantics (top-level merge):
|
|
20780
21056
|
* - Top-level merge — absent keys preserved
|
|
20781
21057
|
* - `null` value — delete that meta key
|
|
20782
21058
|
* - Other values — replace verbatim
|
|
@@ -20794,8 +21070,6 @@ var Noydb = class {
|
|
|
20794
21070
|
*
|
|
20795
21071
|
* @throws `NoAccessError` when no slot with the given id exists.
|
|
20796
21072
|
* @throws `ValidationError` when no patch field is provided.
|
|
20797
|
-
*
|
|
20798
|
-
* @see #55
|
|
20799
21073
|
*/
|
|
20800
21074
|
async updateAuthenticator(vault, slotId, options, factors) {
|
|
20801
21075
|
await this.checkGate(vault, "update-authenticator", factors);
|
|
@@ -20804,7 +21078,7 @@ var Noydb = class {
|
|
|
20804
21078
|
this.keyringCache.set(vault, next);
|
|
20805
21079
|
}
|
|
20806
21080
|
/**
|
|
20807
|
-
* Native WebAuthn enrollment using the **real** internal keyring
|
|
21081
|
+
* Native WebAuthn enrollment using the **real** internal keyring.
|
|
20808
21082
|
*
|
|
20809
21083
|
* Why this exists: when a consumer is using `createNoydb({ secret })`,
|
|
20810
21084
|
* they cannot reach the live `UnlockedKeyring` to feed it to
|
|
@@ -20847,8 +21121,6 @@ var Noydb = class {
|
|
|
20847
21121
|
* a server-side allowlist).
|
|
20848
21122
|
*
|
|
20849
21123
|
* Gated by `enroll-authenticator` like `enrollAuthenticator()` itself.
|
|
20850
|
-
*
|
|
20851
|
-
* @see #16
|
|
20852
21124
|
*/
|
|
20853
21125
|
async enrollWebAuthn(vault, ceremony, factors) {
|
|
20854
21126
|
await this.checkGate(vault, "enroll-authenticator", factors);
|
|
@@ -20875,8 +21147,6 @@ var Noydb = class {
|
|
|
20875
21147
|
* deciding when a new device prompt should appear. Identity is
|
|
20876
21148
|
* `id` + `enrolled_at`; the `meta.credentialId` (base64) is used by
|
|
20877
21149
|
* `allowCredentials` at unlock time.
|
|
20878
|
-
*
|
|
20879
|
-
* @see #16
|
|
20880
21150
|
*/
|
|
20881
21151
|
async listWebAuthnSlots(vault) {
|
|
20882
21152
|
const keyring = await this.getKeyringInternal(vault);
|
|
@@ -20958,7 +21228,7 @@ var Noydb = class {
|
|
|
20958
21228
|
async getPublicEnvelope(vault, opts = {}) {
|
|
20959
21229
|
return readPublicEnvelope(this.options.store, vault, opts);
|
|
20960
21230
|
}
|
|
20961
|
-
// ─── Auth introspection
|
|
21231
|
+
// ─── Auth introspection ─────────────────────────────────────────
|
|
20962
21232
|
/** English summary of the configured auth model. */
|
|
20963
21233
|
async describeAuthConfig(vault) {
|
|
20964
21234
|
return describeAuthConfig(this.options.store, vault);
|
|
@@ -20981,7 +21251,7 @@ var Noydb = class {
|
|
|
20981
21251
|
await this.checkGate(vault, "view-user-auth", factors);
|
|
20982
21252
|
return describeAllUsersAuth(this.options.store, vault);
|
|
20983
21253
|
}
|
|
20984
|
-
// ─── Tier-1 change flows
|
|
21254
|
+
// ─── Tier-1 change flows ────────────────────────────────────────
|
|
20985
21255
|
/**
|
|
20986
21256
|
* Rotate the user's passphrase (user remembers old). Validates the
|
|
20987
21257
|
* new phrase against the configured `passphrase` policy, runs the
|
|
@@ -20989,8 +21259,7 @@ var Noydb = class {
|
|
|
20989
21259
|
*
|
|
20990
21260
|
* Tier-2 authenticator slots are dropped — each slot wraps the old
|
|
20991
21261
|
* KEK and would need its derivation key to be re-presented. Re-enrol
|
|
20992
|
-
* via `db.enrollAuthenticator` after rotation.
|
|
20993
|
-
* v0.1.0-pre.5 limitation.
|
|
21262
|
+
* via `db.enrollAuthenticator` after rotation.
|
|
20994
21263
|
*
|
|
20995
21264
|
* @throws `WeakPassphraseError` on a weak new phrase.
|
|
20996
21265
|
* @throws `PolicyDeniedError` when the gate denies (missing factor, …).
|
|
@@ -21012,8 +21281,8 @@ var Noydb = class {
|
|
|
21012
21281
|
}
|
|
21013
21282
|
/**
|
|
21014
21283
|
* Reset the passphrase using a recovery proof (user forgot the old).
|
|
21015
|
-
*
|
|
21016
|
-
* other
|
|
21284
|
+
* Currently supports the `'paper'` profile end-to-end; the
|
|
21285
|
+
* other profiles throw {@link RecoveryProfileNotImplementedError}.
|
|
21017
21286
|
*
|
|
21018
21287
|
* Burns the used recovery entry on success.
|
|
21019
21288
|
*/
|
|
@@ -21042,7 +21311,7 @@ var Noydb = class {
|
|
|
21042
21311
|
return { newCodes: codes };
|
|
21043
21312
|
}
|
|
21044
21313
|
/**
|
|
21045
|
-
* Deliberate paper-recovery-code regeneration
|
|
21314
|
+
* Deliberate paper-recovery-code regeneration. User knows their
|
|
21046
21315
|
* passphrase but wants a fresh sheet — they lost the printout or
|
|
21047
21316
|
* suspect compromise of the off-site copy.
|
|
21048
21317
|
*
|
|
@@ -21052,7 +21321,7 @@ var Noydb = class {
|
|
|
21052
21321
|
*
|
|
21053
21322
|
* Gated by the `rotate-recovery` policy gate:
|
|
21054
21323
|
* - PERSONAL_POLICY: `{ minTier: 1 }` — knowing the passphrase
|
|
21055
|
-
* suffices, matching the
|
|
21324
|
+
* suffices, matching the lower-level flow's bar.
|
|
21056
21325
|
* - STRICT_POLICY: `{ minTier: 1, factors: [{ anyOf: ['totp',
|
|
21057
21326
|
* 'email-otp', 'webauthn-roaming'] }] }` — rotation is an
|
|
21058
21327
|
* off-site-trust event; require an off-device factor so a
|
|
@@ -21157,7 +21426,7 @@ var Noydb = class {
|
|
|
21157
21426
|
return { newShares: shareStrings, entryId: targetEntryId };
|
|
21158
21427
|
}
|
|
21159
21428
|
/**
|
|
21160
|
-
* **Atomic create-and-enroll for managed-mode vaults
|
|
21429
|
+
* **Atomic create-and-enroll for managed-mode vaults.**
|
|
21161
21430
|
*
|
|
21162
21431
|
* Bootstraps a managed-mode vault and enrolls strong recovery in
|
|
21163
21432
|
* a single ceremony. Under `passphraseMode: 'managed'`, every
|
|
@@ -21228,7 +21497,7 @@ var Noydb = class {
|
|
|
21228
21497
|
return { vault: vaultHandle, recoveryEnrollments };
|
|
21229
21498
|
}
|
|
21230
21499
|
/**
|
|
21231
|
-
* **Recovery flow under managed-passphrase mode
|
|
21500
|
+
* **Recovery flow under managed-passphrase mode.**
|
|
21232
21501
|
*
|
|
21233
21502
|
* Replaces the sealed passphrase of a managed-mode vault with a
|
|
21234
21503
|
* fresh 256-bit random, sealed under the configured
|
|
@@ -21245,7 +21514,7 @@ var Noydb = class {
|
|
|
21245
21514
|
* 5. Drop the keyring cache so the next operation re-derives.
|
|
21246
21515
|
*
|
|
21247
21516
|
* The vault's strong-recovery enrollment is preserved across
|
|
21248
|
-
* recovery (Shamir entries are not burned on use
|
|
21517
|
+
* recovery (Shamir entries are not burned on use).
|
|
21249
21518
|
*
|
|
21250
21519
|
* @throws ValidationError if the Noydb instance is not in managed mode.
|
|
21251
21520
|
*/
|
|
@@ -21293,7 +21562,7 @@ var Noydb = class {
|
|
|
21293
21562
|
}
|
|
21294
21563
|
/**
|
|
21295
21564
|
* Atomic peer-recovery — re-wraps an EXISTING user's keyring under
|
|
21296
|
-
* a fresh temp passphrase in a single store write. Closes
|
|
21565
|
+
* a fresh temp passphrase in a single store write. Closes the
|
|
21297
21566
|
* partial-failure window (the previous compose-from-primitives
|
|
21298
21567
|
* pattern was `db.revoke + db.grant`, two writes — if the issuer
|
|
21299
21568
|
* cancelled between them the target was locked out entirely).
|
|
@@ -21303,7 +21572,7 @@ var Noydb = class {
|
|
|
21303
21572
|
* - Same `userId`, role, permissions, capabilities preserved.
|
|
21304
21573
|
* - DEKs unchanged → every other principal in the vault keeps
|
|
21305
21574
|
* access. No key rotation.
|
|
21306
|
-
* - Allows owner→owner natively
|
|
21575
|
+
* - Allows owner→owner natively. The existing
|
|
21307
21576
|
* `db.revoke` retains its block — peer-recovery is a separate,
|
|
21308
21577
|
* intentionally-named operation.
|
|
21309
21578
|
* - Tier-2 slots dropped (they wrap the old KEK).
|
|
@@ -21332,7 +21601,6 @@ var Noydb = class {
|
|
|
21332
21601
|
* @throws `PrivilegeEscalationError` when the caller lacks a DEK
|
|
21333
21602
|
* the target previously had access to.
|
|
21334
21603
|
*
|
|
21335
|
-
* @see #33 #34 — the issues this method closes.
|
|
21336
21604
|
*/
|
|
21337
21605
|
async recoverUser(vault, options, factors) {
|
|
21338
21606
|
await this.checkGate(vault, "peer-recover-user", factors);
|
|
@@ -21343,7 +21611,7 @@ var Noydb = class {
|
|
|
21343
21611
|
}
|
|
21344
21612
|
}
|
|
21345
21613
|
/**
|
|
21346
|
-
* Persist a recovery enrollment.
|
|
21614
|
+
* Persist a recovery enrollment. Accepts the `'paper'`
|
|
21347
21615
|
* profile.
|
|
21348
21616
|
*
|
|
21349
21617
|
* The hub wraps the user's DEK set (not the KEK) under a code-derived
|
|
@@ -21363,7 +21631,7 @@ var Noydb = class {
|
|
|
21363
21631
|
* showCodesToUser(codes)
|
|
21364
21632
|
* ```
|
|
21365
21633
|
*
|
|
21366
|
-
*
|
|
21634
|
+
* `@noy-db/on-recovery`'s `generateRecoveryCodeSet`
|
|
21367
21635
|
* delegates to `mintPaperRecoveryEntry` internally — its output is
|
|
21368
21636
|
* fed directly to this API. Pick whichever fits your code-gen layer:
|
|
21369
21637
|
*
|
|
@@ -21403,13 +21671,13 @@ var Noydb = class {
|
|
|
21403
21671
|
"#196"
|
|
21404
21672
|
);
|
|
21405
21673
|
}
|
|
21406
|
-
/** Read the persisted recovery entries (paper + Shamir). Used by `describeAuthConfig
|
|
21674
|
+
/** Read the persisted recovery entries (paper + Shamir). Used by `describeAuthConfig`. */
|
|
21407
21675
|
async listRecoveryEntries(vault) {
|
|
21408
21676
|
const paper = await loadPaperRecoveryEntries(this.options.store, vault);
|
|
21409
21677
|
const shamir = await loadShamirRecoveryEntries(this.options.store, vault);
|
|
21410
21678
|
return { paper, shamir };
|
|
21411
21679
|
}
|
|
21412
|
-
// ─── Tier-3 enroll / unlock
|
|
21680
|
+
// ─── Tier-3 enroll / unlock ─────────────────────────────────────
|
|
21413
21681
|
/**
|
|
21414
21682
|
* Register a tier-3 quick-unlock state for the vault. The state is
|
|
21415
21683
|
* an opaque blob produced by `@noy-db/on-pin/enrollPin` (or any
|
|
@@ -21445,11 +21713,11 @@ var Noydb = class {
|
|
|
21445
21713
|
this.quickUnlock.delete(vault);
|
|
21446
21714
|
}
|
|
21447
21715
|
/**
|
|
21448
|
-
* Public accessor for the unlocked keyring of a vault
|
|
21716
|
+
* Public accessor for the unlocked keyring of a vault.
|
|
21449
21717
|
*
|
|
21450
21718
|
* Returns a **defensive shallow copy** so consumers can read the DEK
|
|
21451
21719
|
* map and authenticator list without the risk of mutating the hub's
|
|
21452
|
-
* internal cache
|
|
21720
|
+
* internal cache. Internal hub code paths use a live reference
|
|
21453
21721
|
* via `getKeyringInternal`; ceremonies and external consumers always
|
|
21454
21722
|
* get a snapshot.
|
|
21455
21723
|
*
|
|
@@ -22388,7 +22656,7 @@ function withDerivation(spec) {
|
|
|
22388
22656
|
if (outputSpec.shape === "array") {
|
|
22389
22657
|
if (lifecycleMode !== "eager") {
|
|
22390
22658
|
throw new ValidationError(
|
|
22391
|
-
`withDerivation: shape 'array' supports lifecycle 'eager' only in this release
|
|
22659
|
+
`withDerivation: shape 'array' supports lifecycle 'eager' only in this release Output "${outputKey}" declared lifecycle '${lifecycleMode}'. Switch to \`lifecycle: "eager"\` or use shape: "record".`
|
|
22392
22660
|
);
|
|
22393
22661
|
}
|
|
22394
22662
|
if (typeof outputSpec.key !== "function") {
|
|
@@ -22524,114 +22792,6 @@ function withOverlayedView(spec) {
|
|
|
22524
22792
|
// src/index.ts
|
|
22525
22793
|
init_errors();
|
|
22526
22794
|
init_errors();
|
|
22527
|
-
|
|
22528
|
-
// src/i18n/core.ts
|
|
22529
|
-
init_errors();
|
|
22530
|
-
function i18nText(options) {
|
|
22531
|
-
return { _noydbI18nText: true, options };
|
|
22532
|
-
}
|
|
22533
|
-
function isI18nTextDescriptor(x) {
|
|
22534
|
-
return typeof x === "object" && x !== null && x._noydbI18nText === true;
|
|
22535
|
-
}
|
|
22536
|
-
function validateI18nTextValue(value, field, descriptor) {
|
|
22537
|
-
const { options } = descriptor;
|
|
22538
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
22539
|
-
throw new MissingTranslationError(
|
|
22540
|
-
field,
|
|
22541
|
-
options.languages,
|
|
22542
|
-
`Field "${field}" must be a { [locale]: string } map, got ${typeof value}.`
|
|
22543
|
-
);
|
|
22544
|
-
}
|
|
22545
|
-
const map = value;
|
|
22546
|
-
for (const [locale, v] of Object.entries(map)) {
|
|
22547
|
-
if (typeof v !== "string") {
|
|
22548
|
-
throw new MissingTranslationError(
|
|
22549
|
-
field,
|
|
22550
|
-
[locale],
|
|
22551
|
-
`Field "${field}": locale "${locale}" must be a string, got ${typeof v}.`
|
|
22552
|
-
);
|
|
22553
|
-
}
|
|
22554
|
-
}
|
|
22555
|
-
const { required } = options;
|
|
22556
|
-
if (required === "all") {
|
|
22557
|
-
const missing = options.languages.filter(
|
|
22558
|
-
(lang) => !(lang in map) || map[lang] === ""
|
|
22559
|
-
);
|
|
22560
|
-
if (missing.length > 0) {
|
|
22561
|
-
throw new MissingTranslationError(
|
|
22562
|
-
field,
|
|
22563
|
-
missing,
|
|
22564
|
-
`Field "${field}" requires all declared languages. Missing: ${missing.join(", ")}.`
|
|
22565
|
-
);
|
|
22566
|
-
}
|
|
22567
|
-
} else if (required === "any") {
|
|
22568
|
-
const present = options.languages.some(
|
|
22569
|
-
(lang) => lang in map && map[lang] !== ""
|
|
22570
|
-
);
|
|
22571
|
-
if (!present) {
|
|
22572
|
-
throw new MissingTranslationError(
|
|
22573
|
-
field,
|
|
22574
|
-
options.languages,
|
|
22575
|
-
`Field "${field}" requires at least one declared language. None present.`
|
|
22576
|
-
);
|
|
22577
|
-
}
|
|
22578
|
-
} else {
|
|
22579
|
-
const requiredList = required;
|
|
22580
|
-
const missing = requiredList.filter(
|
|
22581
|
-
(lang) => !(lang in map) || map[lang] === ""
|
|
22582
|
-
);
|
|
22583
|
-
if (missing.length > 0) {
|
|
22584
|
-
throw new MissingTranslationError(
|
|
22585
|
-
field,
|
|
22586
|
-
missing,
|
|
22587
|
-
`Field "${field}" requires: ${requiredList.join(", ")}. Missing: ${missing.join(", ")}.`
|
|
22588
|
-
);
|
|
22589
|
-
}
|
|
22590
|
-
}
|
|
22591
|
-
}
|
|
22592
|
-
function resolveI18nText(value, locale, fallback, field) {
|
|
22593
|
-
if (locale === "raw") {
|
|
22594
|
-
return value;
|
|
22595
|
-
}
|
|
22596
|
-
if (!locale) {
|
|
22597
|
-
throw new LocaleNotSpecifiedError(field ?? "<unknown>");
|
|
22598
|
-
}
|
|
22599
|
-
if (value[locale] !== void 0 && value[locale] !== "") {
|
|
22600
|
-
return value[locale];
|
|
22601
|
-
}
|
|
22602
|
-
const chain = Array.isArray(fallback) ? fallback : fallback ? [fallback] : [];
|
|
22603
|
-
for (const fb of chain) {
|
|
22604
|
-
if (fb === "any") {
|
|
22605
|
-
const any = Object.values(value).find((v) => v !== "");
|
|
22606
|
-
if (any !== void 0) return any;
|
|
22607
|
-
} else if (value[fb] !== void 0 && value[fb] !== "") {
|
|
22608
|
-
return value[fb];
|
|
22609
|
-
}
|
|
22610
|
-
}
|
|
22611
|
-
throw new LocaleNotSpecifiedError(
|
|
22612
|
-
field ?? "<unknown>",
|
|
22613
|
-
`No translation available for locale "${locale}"` + (chain.length > 0 ? ` or fallback chain [${chain.join(", ")}]` : "") + "."
|
|
22614
|
-
);
|
|
22615
|
-
}
|
|
22616
|
-
function applyI18nLocale(record, i18nFields, locale, fallback) {
|
|
22617
|
-
const fieldNames = Object.keys(i18nFields);
|
|
22618
|
-
if (fieldNames.length === 0) return record;
|
|
22619
|
-
const result = { ...record };
|
|
22620
|
-
for (const field of fieldNames) {
|
|
22621
|
-
const raw = result[field];
|
|
22622
|
-
if (raw === void 0 || raw === null) continue;
|
|
22623
|
-
if (typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
22624
|
-
result[field] = resolveI18nText(
|
|
22625
|
-
raw,
|
|
22626
|
-
locale,
|
|
22627
|
-
fallback,
|
|
22628
|
-
field
|
|
22629
|
-
);
|
|
22630
|
-
}
|
|
22631
|
-
return result;
|
|
22632
|
-
}
|
|
22633
|
-
|
|
22634
|
-
// src/index.ts
|
|
22635
22795
|
init_errors();
|
|
22636
22796
|
|
|
22637
22797
|
// src/team/sync-credentials.ts
|