@noy-db/hub 0.2.0-pre.15 → 0.2.0-pre.17
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 +106 -10
- package/dist/aggregate/index.cjs.map +1 -1
- package/dist/aggregate/index.d.cts +3 -2
- package/dist/aggregate/index.d.ts +3 -2
- package/dist/aggregate/index.js +4 -4
- package/dist/attestation/index.cjs.map +1 -1
- package/dist/attestation/index.d.cts +5 -3
- package/dist/attestation/index.d.ts +5 -3
- package/dist/attestation/index.js +6 -6
- package/dist/blobs/index.cjs +226 -11
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +6 -4
- package/dist/blobs/index.d.ts +6 -4
- package/dist/blobs/index.js +6 -5
- package/dist/blobs/index.js.map +1 -1
- package/dist/bundle/index.cjs +1268 -141
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +7 -5
- package/dist/bundle/index.d.ts +7 -5
- package/dist/bundle/index.js +21 -10
- package/dist/bundle/index.js.map +1 -1
- package/dist/{chunk-5LQG6ZO2.js → chunk-2FU2FTXD.js} +9 -4
- package/dist/chunk-2FU2FTXD.js.map +1 -0
- package/dist/{chunk-JD3OZAI4.js → chunk-3G3W65EQ.js} +2 -2
- package/dist/{chunk-XWH4MXIU.js → chunk-5AXTH4QZ.js} +2 -2
- package/dist/{chunk-4TBBMHVC.js → chunk-5LIROIDM.js} +2 -2
- package/dist/{chunk-3EWXMOK3.js → chunk-7H2GEJ3O.js} +28 -13
- package/dist/chunk-7H2GEJ3O.js.map +1 -0
- package/dist/{chunk-WGHU7BLI.js → chunk-AEIKD3PP.js} +52 -38
- package/dist/chunk-AEIKD3PP.js.map +1 -0
- package/dist/{chunk-KI6HAJWL.js → chunk-BH3X5L6A.js} +4 -4
- package/dist/{chunk-BQ65SS5A.js → chunk-BJSLBUJ7.js} +2 -2
- package/dist/{chunk-L2FE64BU.js → chunk-BL5GYANC.js} +3 -3
- package/dist/{chunk-A5ZOOZFB.js → chunk-BSZOCSDZ.js} +4 -4
- package/dist/{chunk-ZNQYHJXX.js → chunk-C3HYQPV4.js} +2 -2
- package/dist/{chunk-PE4AQGFH.js → chunk-CD2AVTEM.js} +5 -5
- package/dist/{chunk-7EFFHEN5.js → chunk-D77ZQSQQ.js} +852 -143
- package/dist/chunk-D77ZQSQQ.js.map +1 -0
- package/dist/{chunk-56DJ7JVK.js → chunk-DWEBTE2W.js} +5 -5
- package/dist/{chunk-Z4DO7YSI.js → chunk-DYYYUW5D.js} +2 -2
- package/dist/{chunk-NSCVNK5K.js → chunk-E77UKJYL.js} +5 -5
- package/dist/{chunk-KIP6JLTF.js → chunk-F4G63NTZ.js} +2 -2
- package/dist/{chunk-NU6Q3FOR.js → chunk-FEJDVE3Z.js} +12 -2
- package/dist/{chunk-NU6Q3FOR.js.map → chunk-FEJDVE3Z.js.map} +1 -1
- package/dist/{chunk-DQU36Q7I.js → chunk-GP3SDSH2.js} +14 -5
- package/dist/chunk-GP3SDSH2.js.map +1 -0
- package/dist/{chunk-IQLVUT37.js → chunk-H2MRGONI.js} +2 -2
- package/dist/{chunk-EYVQHAGH.js → chunk-HGVSHKZW.js} +8 -5
- package/dist/chunk-HGVSHKZW.js.map +1 -0
- package/dist/chunk-I5IUYN7B.js +125 -0
- package/dist/chunk-I5IUYN7B.js.map +1 -0
- package/dist/{chunk-CJORTUJ2.js → chunk-J7RWBXFY.js} +2 -2
- package/dist/{chunk-AAVWKNZW.js → chunk-JDWE6JMX.js} +2 -2
- package/dist/{chunk-6AJBSQU4.js → chunk-KCEHMDZF.js} +3 -3
- package/dist/{chunk-TS26M2SB.js → chunk-M476FOQ7.js} +2 -2
- package/dist/{chunk-F4OJZIWQ.js → chunk-NBBMMJ2H.js} +4 -4
- package/dist/{chunk-CZI2A4MQ.js → chunk-NYSYPFXJ.js} +3 -3
- package/dist/{chunk-EGD5DXFT.js → chunk-PDULVIBY.js} +14 -2
- package/dist/chunk-PDULVIBY.js.map +1 -0
- package/dist/{chunk-Z6FNBOTC.js → chunk-PDVP3C2I.js} +1 -1
- package/dist/{chunk-Z6FNBOTC.js.map → chunk-PDVP3C2I.js.map} +1 -1
- package/dist/{chunk-COFPAMX6.js → chunk-QHM6XEAH.js} +6 -6
- package/dist/{chunk-C5T5AFWN.js → chunk-QO6RGLLD.js} +12 -6
- package/dist/chunk-QO6RGLLD.js.map +1 -0
- package/dist/{chunk-7HT2MEZ5.js → chunk-ROPJVUG3.js} +23 -6
- package/dist/chunk-ROPJVUG3.js.map +1 -0
- package/dist/{chunk-VU7SWWT5.js → chunk-ROVO6NPJ.js} +11 -7
- package/dist/chunk-ROVO6NPJ.js.map +1 -0
- package/dist/{chunk-6RR3MNMG.js → chunk-SHX5QBCI.js} +3 -3
- package/dist/{chunk-GC4V7RU7.js → chunk-SISBMAPO.js} +1 -1
- package/dist/chunk-SISBMAPO.js.map +1 -0
- package/dist/{chunk-BIYRQQV6.js → chunk-SNMJ7SB3.js} +5 -5
- package/dist/{chunk-7PS7EOCF.js → chunk-TIDXB5DF.js} +4 -4
- package/dist/chunk-U5QCMH3W.js +151 -0
- package/dist/chunk-U5QCMH3W.js.map +1 -0
- package/dist/{chunk-YULZKK4F.js → chunk-UNTGHX5A.js} +37 -2
- package/dist/chunk-UNTGHX5A.js.map +1 -0
- package/dist/{chunk-FWPKCXTN.js → chunk-WIAOUFFB.js} +2 -2
- package/dist/{chunk-LX3CB26H.js → chunk-WV7WV6JO.js} +195 -17
- package/dist/chunk-WV7WV6JO.js.map +1 -0
- package/dist/{chunk-X73VS74Y.js → chunk-XJV6OB4D.js} +2 -2
- package/dist/{chunk-OHVFWCJP.js → chunk-XMHUK5PN.js} +49 -19
- package/dist/chunk-XMHUK5PN.js.map +1 -0
- package/dist/{chunk-WBAYSNUQ.js → chunk-XMVHEWF6.js} +4 -4
- package/dist/{chunk-HOR4R722.js → chunk-XPIHJ34I.js} +30 -4
- package/dist/chunk-XPIHJ34I.js.map +1 -0
- package/dist/{chunk-DKO2QFSA.js → chunk-YYVZYTWW.js} +3 -3
- package/dist/{chunk-535SSHBS.js → chunk-ZEGSDPB7.js} +81 -2
- package/dist/chunk-ZEGSDPB7.js.map +1 -0
- package/dist/{chunk-YHPM5D7Y.js → chunk-ZNGPEV5J.js} +63 -4
- package/dist/chunk-ZNGPEV5J.js.map +1 -0
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +6 -4
- package/dist/consent/index.d.ts +6 -4
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-QXQOHMHF.js → crypto-7BN2HDWG.js} +7 -3
- package/dist/{delegation-NIQ43IPU.js → delegation-MGH5SODX.js} +5 -5
- package/dist/derivations/index.cjs +24 -3
- package/dist/derivations/index.cjs.map +1 -1
- package/dist/derivations/index.d.cts +7 -5
- package/dist/derivations/index.d.ts +7 -5
- package/dist/derivations/index.js +4 -4
- package/dist/{dev-unlock-iAS8z9jc.d.ts → dev-unlock-CI1ijTML.d.ts} +1 -1
- package/dist/{dev-unlock-nVkuRLLe.d.cts → dev-unlock-iXbYFAWl.d.cts} +1 -1
- package/dist/{strategy-CbneC7bS.d.ts → errors-Dz64FA65.d.cts} +98 -727
- package/dist/{strategy-CbneC7bS.d.cts → errors-Dz64FA65.d.ts} +98 -727
- package/dist/executor-3W63Y44O.js +11 -0
- package/dist/executor-CFFWPWBJ.js +8 -0
- package/dist/executor-VDQQOR4F.js +8 -0
- package/dist/{fanout-sidecar-N6OJX6QR.js → fanout-sidecar-FIJJ46YG.js} +2 -2
- package/dist/forget/index.cjs +43 -0
- package/dist/forget/index.cjs.map +1 -0
- package/dist/forget/index.d.cts +1 -0
- package/dist/forget/index.d.ts +1 -0
- package/dist/forget/index.js +14 -0
- package/dist/guards/index.cjs +9 -5
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +7 -5
- package/dist/guards/index.d.ts +7 -5
- package/dist/guards/index.js +6 -6
- package/dist/{hash-DHOnRarj.d.ts → hash-blk7Bkes.d.ts} +1 -1
- package/dist/{hash-Cv6byZs7.d.cts → hash-tEcM5fnv.d.cts} +1 -1
- package/dist/history/index.cjs +27 -4
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +7 -5
- package/dist/history/index.d.ts +7 -5
- package/dist/history/index.js +9 -7
- package/dist/history/index.js.map +1 -1
- package/dist/i18n/index.cjs +53 -0
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +6 -4
- package/dist/i18n/index.d.ts +6 -4
- package/dist/i18n/index.js +16 -8
- package/dist/i18n/index.js.map +1 -1
- package/dist/{immutable-guard-BehB1YGB.d.ts → immutable-guard-B5M95nbq.d.ts} +16 -1
- package/dist/{immutable-guard-yBEOYmif.d.cts → immutable-guard-qN3zF8o1.d.cts} +16 -1
- package/dist/index-C-SSRIxP.d.cts +348 -0
- package/dist/index-C-SSRIxP.d.ts +348 -0
- package/dist/{index-XNB2r6bX.d.ts → index-DpU6KWof.d.ts} +9 -1
- package/dist/{index-D95VK1Qy.d.cts → index-u-kWzSrL.d.cts} +9 -1
- package/dist/index.cjs +2715 -1302
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -12
- package/dist/index.d.ts +16 -12
- package/dist/index.js +132 -106
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.js +4 -4
- package/dist/issue-TTMGHQ2J.js +12 -0
- package/dist/{ledger-CWSE3BLF.js → ledger-LFVLHE5H.js} +6 -6
- package/dist/materialized-views/index.cjs +407 -5
- package/dist/materialized-views/index.cjs.map +1 -1
- package/dist/materialized-views/index.d.cts +7 -5
- package/dist/materialized-views/index.d.ts +7 -5
- package/dist/materialized-views/index.js +12 -12
- package/dist/noydb-36S6GQNC.js +37 -0
- package/dist/overlay-views/index.cjs +47 -17
- package/dist/overlay-views/index.cjs.map +1 -1
- package/dist/overlay-views/index.d.cts +28 -8
- package/dist/overlay-views/index.d.ts +28 -8
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +6 -4
- package/dist/periods/index.d.ts +6 -4
- package/dist/periods/index.js +6 -6
- package/dist/{public-envelope-SYHEYQ3X.js → public-envelope-RXZNP3V6.js} +4 -4
- package/dist/query/index.cjs +28 -11
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +3 -2
- package/dist/query/index.d.ts +3 -2
- package/dist/query/index.js +6 -6
- package/dist/registry-3YFLZ7WD.js +8 -0
- package/dist/{registry-DK5YWAAA.js → registry-SECUWSGY.js} +3 -3
- package/dist/registry-TGZISEWC.js +8 -0
- package/dist/{revoke-ZDFKMR5E.js → revoke-B54H2S2W.js} +6 -6
- package/dist/sealed-record/index.cjs +139 -0
- package/dist/sealed-record/index.cjs.map +1 -0
- package/dist/sealed-record/index.d.cts +123 -0
- package/dist/sealed-record/index.d.ts +123 -0
- package/dist/sealed-record/index.js +42 -0
- package/dist/sealed-record/index.js.map +1 -0
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +7 -5
- package/dist/session/index.d.ts +7 -5
- package/dist/session/index.js +3 -3
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +6 -4
- package/dist/shadow/index.d.ts +6 -4
- package/dist/shadow/index.js +2 -2
- package/dist/{signer-P5D7Y72U.js → signer-YSXZT574.js} +5 -5
- package/dist/snapshots/index.cjs.map +1 -1
- package/dist/snapshots/index.d.cts +6 -4
- package/dist/snapshots/index.d.ts +6 -4
- package/dist/snapshots/index.js +4 -4
- package/dist/{stale-JH67FU57.js → stale-TOA36SRK.js} +2 -2
- package/dist/stale-TOA36SRK.js.map +1 -0
- package/dist/{state-vault-TMXZRTY5.js → state-vault-W2OEABNO.js} +3 -3
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +6 -4
- package/dist/store/index.d.ts +6 -4
- package/dist/store/index.js +2 -2
- package/dist/strategy-4M9jo172.d.ts +739 -0
- package/dist/strategy-CLC1j79g.d.cts +739 -0
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +5 -3
- package/dist/sync/index.d.ts +5 -3
- package/dist/sync/index.js +4 -4
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +6 -4
- package/dist/team/index.d.ts +6 -4
- package/dist/team/index.js +8 -8
- package/dist/tx/index.cjs +66 -3
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +24 -6
- package/dist/tx/index.d.ts +24 -6
- package/dist/tx/index.js +9 -5
- package/dist/tx/index.js.map +1 -1
- package/dist/{types-4t1-tWS4.d.ts → types-CljIHm_J.d.ts} +1127 -606
- package/dist/{types-BpPV5uyy.d.cts → types-CrSpRDuG.d.cts} +1127 -606
- package/dist/{ulid-DAfenvFd.d.ts → ulid-CWfL2Vfv.d.ts} +1 -1
- package/dist/{ulid-CiPrpGqm.d.cts → ulid-CrI7PPbA.d.cts} +1 -1
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.js +1 -1
- package/dist/{vault-group-KOM7QRJG.js → vault-group-DHAHFX2A.js} +4 -4
- package/dist/{with-derivation-OK9M2sJE.d.ts → with-derivation-BZ2y4bzF.d.ts} +1 -1
- package/dist/{with-derivation-DBqJB3dQ.d.cts → with-derivation-Bozs8DmD.d.cts} +1 -1
- package/dist/{with-materialized-view-Dt-ufPWQ.d.ts → with-materialized-view-B892zYZV.d.ts} +1 -1
- package/dist/{with-materialized-view-NzuxYPDF.d.cts → with-materialized-view-NzF71cG_.d.cts} +1 -1
- package/dist/{with-overlayed-view-eDvMs6LO.d.ts → with-overlayed-view-CR6m7CHe.d.ts} +1 -1
- package/dist/{with-overlayed-view-CC0_ocy-.d.cts → with-overlayed-view-UI8qSGL4.d.cts} +1 -1
- package/package.json +23 -3
- package/dist/chunk-3EWXMOK3.js.map +0 -1
- package/dist/chunk-535SSHBS.js.map +0 -1
- package/dist/chunk-5LQG6ZO2.js.map +0 -1
- package/dist/chunk-7EFFHEN5.js.map +0 -1
- package/dist/chunk-7HT2MEZ5.js.map +0 -1
- package/dist/chunk-C5T5AFWN.js.map +0 -1
- package/dist/chunk-DQU36Q7I.js.map +0 -1
- package/dist/chunk-EGD5DXFT.js.map +0 -1
- package/dist/chunk-EYVQHAGH.js.map +0 -1
- package/dist/chunk-GC4V7RU7.js.map +0 -1
- package/dist/chunk-HOR4R722.js.map +0 -1
- package/dist/chunk-LX3CB26H.js.map +0 -1
- package/dist/chunk-OHVFWCJP.js.map +0 -1
- package/dist/chunk-VU7SWWT5.js.map +0 -1
- package/dist/chunk-WGHU7BLI.js.map +0 -1
- package/dist/chunk-YHPM5D7Y.js.map +0 -1
- package/dist/chunk-YULZKK4F.js.map +0 -1
- package/dist/executor-6ZDSDZ6V.js +0 -8
- package/dist/executor-HSSRXDOB.js +0 -11
- package/dist/executor-IDZDAFNH.js +0 -8
- package/dist/issue-ADVS4OVP.js +0 -12
- package/dist/noydb-GZGFBA4E.js +0 -35
- package/dist/registry-IUZQVVBB.js +0 -8
- package/dist/registry-XGLNADIE.js +0 -8
- /package/dist/{chunk-JD3OZAI4.js.map → chunk-3G3W65EQ.js.map} +0 -0
- /package/dist/{chunk-XWH4MXIU.js.map → chunk-5AXTH4QZ.js.map} +0 -0
- /package/dist/{chunk-4TBBMHVC.js.map → chunk-5LIROIDM.js.map} +0 -0
- /package/dist/{chunk-KI6HAJWL.js.map → chunk-BH3X5L6A.js.map} +0 -0
- /package/dist/{chunk-BQ65SS5A.js.map → chunk-BJSLBUJ7.js.map} +0 -0
- /package/dist/{chunk-L2FE64BU.js.map → chunk-BL5GYANC.js.map} +0 -0
- /package/dist/{chunk-A5ZOOZFB.js.map → chunk-BSZOCSDZ.js.map} +0 -0
- /package/dist/{chunk-ZNQYHJXX.js.map → chunk-C3HYQPV4.js.map} +0 -0
- /package/dist/{chunk-PE4AQGFH.js.map → chunk-CD2AVTEM.js.map} +0 -0
- /package/dist/{chunk-56DJ7JVK.js.map → chunk-DWEBTE2W.js.map} +0 -0
- /package/dist/{chunk-Z4DO7YSI.js.map → chunk-DYYYUW5D.js.map} +0 -0
- /package/dist/{chunk-NSCVNK5K.js.map → chunk-E77UKJYL.js.map} +0 -0
- /package/dist/{chunk-KIP6JLTF.js.map → chunk-F4G63NTZ.js.map} +0 -0
- /package/dist/{chunk-IQLVUT37.js.map → chunk-H2MRGONI.js.map} +0 -0
- /package/dist/{chunk-CJORTUJ2.js.map → chunk-J7RWBXFY.js.map} +0 -0
- /package/dist/{chunk-AAVWKNZW.js.map → chunk-JDWE6JMX.js.map} +0 -0
- /package/dist/{chunk-6AJBSQU4.js.map → chunk-KCEHMDZF.js.map} +0 -0
- /package/dist/{chunk-TS26M2SB.js.map → chunk-M476FOQ7.js.map} +0 -0
- /package/dist/{chunk-F4OJZIWQ.js.map → chunk-NBBMMJ2H.js.map} +0 -0
- /package/dist/{chunk-CZI2A4MQ.js.map → chunk-NYSYPFXJ.js.map} +0 -0
- /package/dist/{chunk-COFPAMX6.js.map → chunk-QHM6XEAH.js.map} +0 -0
- /package/dist/{chunk-6RR3MNMG.js.map → chunk-SHX5QBCI.js.map} +0 -0
- /package/dist/{chunk-BIYRQQV6.js.map → chunk-SNMJ7SB3.js.map} +0 -0
- /package/dist/{chunk-7PS7EOCF.js.map → chunk-TIDXB5DF.js.map} +0 -0
- /package/dist/{chunk-FWPKCXTN.js.map → chunk-WIAOUFFB.js.map} +0 -0
- /package/dist/{chunk-X73VS74Y.js.map → chunk-XJV6OB4D.js.map} +0 -0
- /package/dist/{chunk-WBAYSNUQ.js.map → chunk-XMVHEWF6.js.map} +0 -0
- /package/dist/{chunk-DKO2QFSA.js.map → chunk-YYVZYTWW.js.map} +0 -0
- /package/dist/{crypto-QXQOHMHF.js.map → crypto-7BN2HDWG.js.map} +0 -0
- /package/dist/{delegation-NIQ43IPU.js.map → delegation-MGH5SODX.js.map} +0 -0
- /package/dist/{executor-6ZDSDZ6V.js.map → executor-3W63Y44O.js.map} +0 -0
- /package/dist/{executor-HSSRXDOB.js.map → executor-CFFWPWBJ.js.map} +0 -0
- /package/dist/{executor-IDZDAFNH.js.map → executor-VDQQOR4F.js.map} +0 -0
- /package/dist/{fanout-sidecar-N6OJX6QR.js.map → fanout-sidecar-FIJJ46YG.js.map} +0 -0
- /package/dist/{issue-ADVS4OVP.js.map → forget/index.js.map} +0 -0
- /package/dist/{ledger-CWSE3BLF.js.map → issue-TTMGHQ2J.js.map} +0 -0
- /package/dist/{noydb-GZGFBA4E.js.map → ledger-LFVLHE5H.js.map} +0 -0
- /package/dist/{public-envelope-SYHEYQ3X.js.map → noydb-36S6GQNC.js.map} +0 -0
- /package/dist/{registry-DK5YWAAA.js.map → public-envelope-RXZNP3V6.js.map} +0 -0
- /package/dist/{registry-IUZQVVBB.js.map → registry-3YFLZ7WD.js.map} +0 -0
- /package/dist/{registry-XGLNADIE.js.map → registry-SECUWSGY.js.map} +0 -0
- /package/dist/{revoke-ZDFKMR5E.js.map → registry-TGZISEWC.js.map} +0 -0
- /package/dist/{signer-P5D7Y72U.js.map → revoke-B54H2S2W.js.map} +0 -0
- /package/dist/{stale-JH67FU57.js.map → signer-YSXZT574.js.map} +0 -0
- /package/dist/{state-vault-TMXZRTY5.js.map → state-vault-W2OEABNO.js.map} +0 -0
- /package/dist/{vault-group-KOM7QRJG.js.map → vault-group-DHAHFX2A.js.map} +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { I as IndexStrategy, d as LazyQuery } from './lazy-builder-ChSqcF5t.js';
|
|
2
|
-
import {
|
|
2
|
+
import { L as LiveAggregation, b as AggregateSpec, a as AggregateResult, v as MoneyDescriptor, A as AggregateStrategy } from './strategy-4M9jo172.js';
|
|
3
3
|
import { C as CrdtStrategy, a as CrdtMode, b as CrdtState } from './strategy-BSxFXGzb.js';
|
|
4
|
-
import { L as
|
|
4
|
+
import { L as LedgerEntry, F as ForgetStrategy, S as SubjectRef, b as ForgetResult } from './index-C-SSRIxP.js';
|
|
5
|
+
import { N as NoydbError } from './errors-Dz64FA65.js';
|
|
6
|
+
import { L as LiveQuery, Q as Query, c as JoinStrategy, j as RefRegistry, R as RefDescriptor, d as JoinableSource, l as RefViolation, S as ScanBuilder } from './index-DpU6KWof.js';
|
|
5
7
|
import { I as IndexDef, O as Operator, F as FieldClause, C as CollectionIndexes } from './predicate-BmhBSPCH.js';
|
|
6
8
|
import { AttestationFieldSchema, RevocationList } from '@noy-db/attestation';
|
|
7
9
|
|
|
@@ -415,6 +417,7 @@ declare class BlobSet {
|
|
|
415
417
|
private readonly encrypted;
|
|
416
418
|
private readonly userId;
|
|
417
419
|
private readonly maxBlobBytes;
|
|
420
|
+
private readonly erasableBlobs;
|
|
418
421
|
constructor(opts: {
|
|
419
422
|
store: NoydbStore;
|
|
420
423
|
vault: string;
|
|
@@ -424,7 +427,18 @@ declare class BlobSet {
|
|
|
424
427
|
encrypted: boolean;
|
|
425
428
|
userId?: string;
|
|
426
429
|
maxBlobBytes?: number;
|
|
430
|
+
erasableBlobs?: boolean;
|
|
427
431
|
});
|
|
432
|
+
/**
|
|
433
|
+
* Resolve the key the blob's CHUNKS are encrypted under.
|
|
434
|
+
*
|
|
435
|
+
* - `_cek` present (erasable blob) → unwrap the per-blob content CEK under
|
|
436
|
+
* the `_blob` DEK. Deleting the BlobObject (at `refCount → 0`) makes this
|
|
437
|
+
* key unrecoverable → the chunks are crypto-shredded.
|
|
438
|
+
* - `_cek` absent (legacy) → the `_blob` DEK encrypts chunks directly.
|
|
439
|
+
* - unencrypted vault → `null` (chunks stored as plaintext base64).
|
|
440
|
+
*/
|
|
441
|
+
private resolveChunkKey;
|
|
428
442
|
/** The internal collection that holds slot metadata for this collection's blobs. */
|
|
429
443
|
private get slotsCollection();
|
|
430
444
|
/** The internal collection that holds published versions for this collection's blobs. */
|
|
@@ -442,6 +456,73 @@ declare class BlobSet {
|
|
|
442
456
|
* CAS retry loop for refCount changes on a BlobObject.
|
|
443
457
|
*/
|
|
444
458
|
private casUpdateRefCount;
|
|
459
|
+
/**
|
|
460
|
+
* Release `n` references to a blob and reclaim it at refCount 0 (#365 slice 4).
|
|
461
|
+
*
|
|
462
|
+
* The single reclaim choke point for every reference-drop path — slot
|
|
463
|
+
* delete/overwrite, published-version delete, and `forget()` shred — so the
|
|
464
|
+
* refCount-0 policy is uniform:
|
|
465
|
+
* - **erasable blob** (`_cek` present) → delete the `BlobObject` (the SOLE
|
|
466
|
+
* copy of the wrapped content CEK → chunks permanently undecryptable) and
|
|
467
|
+
* reclaim the chunks. The crypto-shred is EAGER on every path: GDPR erasure
|
|
468
|
+
* must not wait on orphan retention.
|
|
469
|
+
* - **legacy blob** (no `_cek`) → reclaimed only when `reclaimLegacy` (the
|
|
470
|
+
* `forget()` erasure path); otherwise left for deferred GC so the existing
|
|
471
|
+
* `BlobLifecyclePolicy.orphanRetentionDays` semantics are preserved.
|
|
472
|
+
*
|
|
473
|
+
* @returns `'shredded'` (erasable, refCount 0, chunks dead) · `'retainedShared'`
|
|
474
|
+
* (erasable, still referenced) · `'residue'` (legacy — not a crypto-shred).
|
|
475
|
+
*/
|
|
476
|
+
private releaseRef;
|
|
477
|
+
/**
|
|
478
|
+
* Crypto-shred this record's blob attachments (#365 slice 2) — called by
|
|
479
|
+
* `vault.forget()`.
|
|
480
|
+
*
|
|
481
|
+
* For each distinct eTag the record references (a record may attach the same
|
|
482
|
+
* content under several slot names → several refCount holds): decrement the
|
|
483
|
+
* blob's refCount by that many. When it reaches 0:
|
|
484
|
+
* - **erasable blob** (`_cek` present) → delete the `BlobObject` (the SOLE
|
|
485
|
+
* recoverable copy of the wrapped content CEK → chunks permanently
|
|
486
|
+
* undecryptable) and reclaim the chunk bytes. This is the crypto-shred.
|
|
487
|
+
* - **legacy blob** (no `_cek`) → chunks are under the shared `_blob` DEK and
|
|
488
|
+
* stay decryptable until byte-deleted; we delete the orphaned chunks +
|
|
489
|
+
* index but report it as residue, not a cryptographic erasure.
|
|
490
|
+
* When refCount stays > 0 the content legitimately persists for its other
|
|
491
|
+
* owner — reported as `retainedShared` (or `residue` if legacy).
|
|
492
|
+
*
|
|
493
|
+
* Finally drops the record's slot map, severing the subject's link.
|
|
494
|
+
*/
|
|
495
|
+
shredAllForRecord(): Promise<{
|
|
496
|
+
shredded: string[];
|
|
497
|
+
retainedShared: string[];
|
|
498
|
+
residue: string[];
|
|
499
|
+
}>;
|
|
500
|
+
/** CAS retry loop for an arbitrary BlobObject mutation. */
|
|
501
|
+
private casUpdateBlobObject;
|
|
502
|
+
/**
|
|
503
|
+
* Migrate this record's LEGACY blobs (no `_cek`, chunks under the shared
|
|
504
|
+
* `_blob` DEK) to per-blob content CEKs so they become crypto-shreddable
|
|
505
|
+
* (#365 slice 3). Returns the eTags migrated vs. already-erasable.
|
|
506
|
+
*
|
|
507
|
+
* **Explicit maintenance pass** (mirrors the record-CEK migration posture):
|
|
508
|
+
* re-encrypts the existing compressed chunks IN PLACE under a fresh content
|
|
509
|
+
* CEK — preserving the eTag, chunkCount, chunkSize, and compression — then
|
|
510
|
+
* flips the `_cek` discriminant. Crash-safe + idempotent via `_cekPending`:
|
|
511
|
+
* 1. persist the wrapped content CEK in `_cekPending` (readers ignore it →
|
|
512
|
+
* the blob stays readable under the `_blob` DEK; the key survives a crash);
|
|
513
|
+
* 2. re-encrypt each chunk under the content CEK (a resume reads an
|
|
514
|
+
* already-migrated chunk under the content CEK, else under the `_blob` DEK);
|
|
515
|
+
* 3. promote `_cekPending` → `_cek` (atomic flip). Reads now use the CEK.
|
|
516
|
+
* A re-run after a crash resumes from whichever phase was reached.
|
|
517
|
+
*
|
|
518
|
+
* Dedup-safe: migrating a shared blob (refCount > 1) re-keys it for every
|
|
519
|
+
* referencer at once; a non-erasable collection still reads it (it unwraps
|
|
520
|
+
* `_cek` under the `_blob` DEK it holds).
|
|
521
|
+
*/
|
|
522
|
+
migrate(): Promise<{
|
|
523
|
+
migrated: string[];
|
|
524
|
+
alreadyErasable: string[];
|
|
525
|
+
}>;
|
|
445
526
|
private writeChunk;
|
|
446
527
|
private readChunk;
|
|
447
528
|
private versionKey;
|
|
@@ -598,6 +679,13 @@ interface BlobStrategyOpenArgs {
|
|
|
598
679
|
readonly getDEK: (collectionName: string) => Promise<CryptoKey>;
|
|
599
680
|
readonly encrypted: boolean;
|
|
600
681
|
readonly userId: string;
|
|
682
|
+
/**
|
|
683
|
+
* Collection opts into per-record keys (`perRecordKeys`), so its blobs are
|
|
684
|
+
* erasable: new uploads mint a per-blob content CEK (crypto-shreddable at
|
|
685
|
+
* `refCount → 0`). Off → legacy shared-`_blob`-DEK chunks. See the per-blob
|
|
686
|
+
* CEK design spec.
|
|
687
|
+
*/
|
|
688
|
+
readonly erasableBlobs?: boolean;
|
|
601
689
|
}
|
|
602
690
|
/**
|
|
603
691
|
* The seam interface. `@internal` — do not build public APIs on this
|
|
@@ -807,224 +895,6 @@ interface ConsentStrategy {
|
|
|
807
895
|
read(adapter: NoydbStore, vault: string, encrypted: boolean, getDEK: (collectionName: string) => Promise<CryptoKey>, filter?: ConsentAuditFilter): Promise<ConsentAuditEntry[]>;
|
|
808
896
|
}
|
|
809
897
|
|
|
810
|
-
/**
|
|
811
|
-
* Ledger entry shape + canonical JSON + sha256 helpers.
|
|
812
|
-
*
|
|
813
|
-
* This file holds the PURE primitives used by the hash-chained ledger:
|
|
814
|
-
* the entry type, the deterministic (sort-stable) JSON encoder, and
|
|
815
|
-
* the sha256 hasher that produces `prevHash` and `ledger.head()`.
|
|
816
|
-
*
|
|
817
|
-
* Everything here is validator-free and side-effect free — the only
|
|
818
|
-
* runtime dep is Web Crypto's `subtle.digest` for the sha256 call,
|
|
819
|
-
* which we already use for every other hashing operation in the core.
|
|
820
|
-
*
|
|
821
|
-
* The hash chain property works like this:
|
|
822
|
-
*
|
|
823
|
-
* hash(entry[i]) = sha256(canonicalJSON(entry[i]))
|
|
824
|
-
* entry[i+1].prevHash = hash(entry[i])
|
|
825
|
-
*
|
|
826
|
-
* Any modification to `entry[i]` (field values, field order, whitespace)
|
|
827
|
-
* produces a different `hash(entry[i])`, which means `entry[i+1]`'s
|
|
828
|
-
* stored `prevHash` no longer matches the recomputed hash, which means
|
|
829
|
-
* `verify()` returns `{ ok: false, divergedAt: i + 1 }`. The chain is
|
|
830
|
-
* append-only and tamper-evident without external anchoring.
|
|
831
|
-
*/
|
|
832
|
-
/**
|
|
833
|
-
* A single ledger entry in its plaintext form — what gets serialized,
|
|
834
|
-
* hashed, and then encrypted with the ledger DEK before being written
|
|
835
|
-
* to the `_ledger/` adapter collection.
|
|
836
|
-
*
|
|
837
|
-
* ## Why hash the ciphertext, not the plaintext?
|
|
838
|
-
*
|
|
839
|
-
* `payloadHash` is the sha256 of the record's ENCRYPTED envelope bytes,
|
|
840
|
-
* not its plaintext. This matters:
|
|
841
|
-
*
|
|
842
|
-
* 1. **Zero-knowledge preserved.** A user (or a third party) can
|
|
843
|
-
* verify the ledger against the stored envelopes without any
|
|
844
|
-
* decryption keys. The adapter layer already holds only
|
|
845
|
-
* ciphertext, so hashing the ciphertext keeps the ledger at the
|
|
846
|
-
* same privacy level as the adapter.
|
|
847
|
-
*
|
|
848
|
-
* 2. **Determinism.** Plaintext → ciphertext is randomized by the
|
|
849
|
-
* fresh per-write IV, so `hash(plaintext)` would need extra
|
|
850
|
-
* normalization. `hash(ciphertext)` is already deterministic and
|
|
851
|
-
* unique per write.
|
|
852
|
-
*
|
|
853
|
-
* 3. **Detection property.** If an attacker modifies even one byte of
|
|
854
|
-
* the stored ciphertext (trying to flip a record), the hash
|
|
855
|
-
* changes, the ledger's recorded `payloadHash` no longer matches,
|
|
856
|
-
* and a data-integrity check fails. We don't do that check in
|
|
857
|
-
* `verify()` today, but the
|
|
858
|
-
* hook is there for a future `verifyIntegrity()` follow-up.
|
|
859
|
-
*
|
|
860
|
-
* Fields marked `op`, `collection`, `id`, `version`, `ts`, `actor` are
|
|
861
|
-
* plaintext METADATA about the operation — NOT the record itself. The
|
|
862
|
-
* entry is still encrypted at rest via the ledger DEK, but adapters
|
|
863
|
-
* could theoretically infer operation patterns from the sizes and
|
|
864
|
-
* timestamps. This is an accepted trade-off for the tamper-evidence
|
|
865
|
-
* property; full ORAM-level privacy is out of scope for noy-db.
|
|
866
|
-
*/
|
|
867
|
-
interface LedgerEntry {
|
|
868
|
-
/**
|
|
869
|
-
* Zero-based sequential position of this entry in the chain. The
|
|
870
|
-
* canonical adapter key is this number zero-padded to 10 digits
|
|
871
|
-
* (`"0000000001"`) so lexicographic ordering matches numeric order.
|
|
872
|
-
*/
|
|
873
|
-
readonly index: number;
|
|
874
|
-
/**
|
|
875
|
-
* Hex-encoded sha256 of the canonical JSON of the PREVIOUS entry.
|
|
876
|
-
* The genesis entry (index 0) has `prevHash === ''` — the first
|
|
877
|
-
* entry in a fresh vault has nothing to point back to.
|
|
878
|
-
*/
|
|
879
|
-
readonly prevHash: string;
|
|
880
|
-
/**
|
|
881
|
-
* Which kind of mutation this entry records. only supports
|
|
882
|
-
* data operations (`put`, `delete`, `amendment`). Access-control
|
|
883
|
-
* operations (`grant`, `revoke`, `rotate`) will be added in a
|
|
884
|
-
* follow-up once the keyring write path is instrumented — that's
|
|
885
|
-
* tracked in the epic issue.
|
|
886
|
-
*
|
|
887
|
-
* `'amendment'` is the multi-record audit entry written by the
|
|
888
|
-
* guards subsystem when an admin/owner uses `withTransactions(...)`
|
|
889
|
-
* to repair a constraint-violating state. See `amendment` field
|
|
890
|
-
* below for the structured payload.
|
|
891
|
-
*
|
|
892
|
-
* `'lifecycle'` records a non-data audit event (e.g. partition
|
|
893
|
-
* handover) — `collection`/`id` are empty and the event detail
|
|
894
|
-
* lives in `reason` (e.g. `'partition-handed-over:<sealId>'`). Like
|
|
895
|
-
* `amendment`, it carries no data envelope, so `verifyBackupIntegrity`
|
|
896
|
-
* skips it in the data cross-check (it still participates in the
|
|
897
|
-
* tamper-evident chain).
|
|
898
|
-
*/
|
|
899
|
-
readonly op: 'put' | 'delete' | 'amendment' | 'lifecycle' | 'migration';
|
|
900
|
-
/** The collection the mutation targeted. */
|
|
901
|
-
readonly collection: string;
|
|
902
|
-
/** The record id the mutation targeted. */
|
|
903
|
-
readonly id: string;
|
|
904
|
-
/**
|
|
905
|
-
* The record version AFTER this mutation. For `put` this is the
|
|
906
|
-
* newly assigned version; for `delete` this is the version that
|
|
907
|
-
* was deleted (the last version visible to reads).
|
|
908
|
-
*/
|
|
909
|
-
readonly version: number;
|
|
910
|
-
/** ISO timestamp of the mutation. */
|
|
911
|
-
readonly ts: string;
|
|
912
|
-
/** User id of the actor who performed the mutation. */
|
|
913
|
-
readonly actor: string;
|
|
914
|
-
/**
|
|
915
|
-
* Hex-encoded sha256 of the encrypted envelope's `_data` field.
|
|
916
|
-
* For `put`, this is the hash of the new ciphertext. For `delete`,
|
|
917
|
-
* it's the hash of the last visible ciphertext at deletion time,
|
|
918
|
-
* or the empty string if nothing was there to delete. Hashing the
|
|
919
|
-
* ciphertext (not the plaintext) preserves zero-knowledge — see
|
|
920
|
-
* the file docstring.
|
|
921
|
-
*/
|
|
922
|
-
readonly payloadHash: string;
|
|
923
|
-
/**
|
|
924
|
-
* Optional human-readable tag describing why this mutation happened.
|
|
925
|
-
* Threaded through `collection.put(_, _, { reason })`. Common
|
|
926
|
-
* values include `'import:csv'`, `'import:json'`, `'import:xlsx'` from
|
|
927
|
-
* `as-*` ImportPlan.apply(), but consumers can use any string for
|
|
928
|
-
* domain-specific audit filtering. Auto-strip via `canonicalJson` —
|
|
929
|
-
* absent on the wire, never serialized as `null`.
|
|
930
|
-
*
|
|
931
|
-
* Audit consumers filter: `entries.filter(e => e.reason?.startsWith('import:'))`.
|
|
932
|
-
*/
|
|
933
|
-
readonly reason?: string;
|
|
934
|
-
/**
|
|
935
|
-
* Optional hex-encoded sha256 of the encrypted JSON Patch delta
|
|
936
|
-
* blob stored alongside this entry in `_ledger_deltas/`. Present
|
|
937
|
-
* only for `put` operations that had a previous version — the
|
|
938
|
-
* genesis put of a new record, and every `delete`, leave this
|
|
939
|
-
* field undefined.
|
|
940
|
-
*
|
|
941
|
-
* The delta payload itself lives in a sibling internal collection
|
|
942
|
-
* (`_ledger_deltas/<paddedIndex>`) and is encrypted with the
|
|
943
|
-
* ledger DEK. Callers use `ledger.loadDelta(index)` to decrypt and
|
|
944
|
-
* deserialize it when reconstructing a historical version.
|
|
945
|
-
*
|
|
946
|
-
* Why optional instead of always-present: the first put of a
|
|
947
|
-
* record has no previous version to diff against, so storing an
|
|
948
|
-
* empty patch would be noise. For deletes there's no "next" state
|
|
949
|
-
* to describe with a delta. Both cases set this field to undefined.
|
|
950
|
-
*
|
|
951
|
-
* Note: the canonical-JSON hasher treats `undefined` as invalid
|
|
952
|
-
* (it's one of the guard rails), so on the wire this field is
|
|
953
|
-
* either `{ deltaHash: '<hex>' }` or absent from the JSON
|
|
954
|
-
* entirely — never `{ deltaHash: undefined }`.
|
|
955
|
-
*/
|
|
956
|
-
readonly deltaHash?: string;
|
|
957
|
-
/**
|
|
958
|
-
* Present only when `op === 'amendment'`. Records the human reason,
|
|
959
|
-
* the role of the actor, the (collection, id, vBefore, vAfter) tuple
|
|
960
|
-
* for every record touched, and which guard invariants passed.
|
|
961
|
-
*
|
|
962
|
-
* See docs/superpowers/specs/2026-05-18-guards-design.md.
|
|
963
|
-
*/
|
|
964
|
-
readonly amendment?: {
|
|
965
|
-
readonly reason: string;
|
|
966
|
-
readonly role: 'admin' | 'owner';
|
|
967
|
-
readonly changes: ReadonlyArray<{
|
|
968
|
-
readonly collection: string;
|
|
969
|
-
readonly id: string;
|
|
970
|
-
readonly vBefore: number;
|
|
971
|
-
readonly vAfter: number;
|
|
972
|
-
}>;
|
|
973
|
-
readonly invariantsPassed: ReadonlyArray<string>;
|
|
974
|
-
};
|
|
975
|
-
}
|
|
976
|
-
/**
|
|
977
|
-
* Canonical (sort-stable) JSON encoder.
|
|
978
|
-
*
|
|
979
|
-
* This function is the load-bearing primitive of the hash chain:
|
|
980
|
-
* `sha256(canonicalJSON(entry))` must produce the same hex string
|
|
981
|
-
* every time, on every machine, for the same logical entry — otherwise
|
|
982
|
-
* `verify()` would return `{ ok: false }` on cross-platform reads.
|
|
983
|
-
*
|
|
984
|
-
* JavaScript's `JSON.stringify` is almost canonical, but NOT quite:
|
|
985
|
-
* it preserves the insertion order of object keys, which means
|
|
986
|
-
* `{a:1,b:2}` and `{b:2,a:1}` serialize differently. We fix this by
|
|
987
|
-
* recursively walking objects and sorting their keys before
|
|
988
|
-
* concatenation.
|
|
989
|
-
*
|
|
990
|
-
* Arrays keep their original order (reordering them would change
|
|
991
|
-
* semantics). Numbers, strings, booleans, and `null` use the default
|
|
992
|
-
* JSON encoding. `undefined` and functions are rejected — ledger
|
|
993
|
-
* entries are plain data, and silently dropping `undefined` would
|
|
994
|
-
* break the "same input → same hash" property if a caller forgot to
|
|
995
|
-
* omit a field.
|
|
996
|
-
*
|
|
997
|
-
* Performance: one pass per nesting level; O(n log n) for key sorting
|
|
998
|
-
* at each object. Entries are small (< 1 KB) so this is negligible
|
|
999
|
-
* compared to the sha256 call.
|
|
1000
|
-
*/
|
|
1001
|
-
declare function canonicalJson(value: unknown): string;
|
|
1002
|
-
/**
|
|
1003
|
-
* Compute a hex-encoded sha256 of a string via Web Crypto's subtle API.
|
|
1004
|
-
*
|
|
1005
|
-
* We use hex (not base64) for hashes because hex is case-insensitive,
|
|
1006
|
-
* fixed-length (64 chars), and easier to compare visually in debug
|
|
1007
|
-
* output. Base64 would save a few bytes in storage but every encrypted
|
|
1008
|
-
* ledger entry is already much larger than the hash itself.
|
|
1009
|
-
*/
|
|
1010
|
-
declare function sha256Hex(input: string): Promise<string>;
|
|
1011
|
-
/**
|
|
1012
|
-
* Compute the canonical hash of a ledger entry. Short wrapper around
|
|
1013
|
-
* `canonicalJson` + `sha256Hex`; callers use this instead of composing
|
|
1014
|
-
* the two functions every time, so any future change to the hashing
|
|
1015
|
-
* pipeline (e.g., adding a domain-separation prefix) lives in one place.
|
|
1016
|
-
*/
|
|
1017
|
-
declare function hashEntry(entry: LedgerEntry): Promise<string>;
|
|
1018
|
-
/**
|
|
1019
|
-
* Pad an index to the canonical 10-digit form used as the adapter key.
|
|
1020
|
-
* Ten digits is enough for ~10 billion ledger entries per vault
|
|
1021
|
-
* — far beyond any realistic use case, but cheap enough that the extra
|
|
1022
|
-
* digits don't hurt storage.
|
|
1023
|
-
*/
|
|
1024
|
-
declare function paddedIndex(index: number): string;
|
|
1025
|
-
/** Parse a padded adapter key back into a number. Returns NaN on malformed input. */
|
|
1026
|
-
declare function parseIndex(key: string): number;
|
|
1027
|
-
|
|
1028
898
|
/**
|
|
1029
899
|
* RFC 6902 JSON Patch — compute + apply.
|
|
1030
900
|
*
|
|
@@ -2572,6 +2442,88 @@ declare function dictKey<Keys extends string>(name: string, keys?: readonly Keys
|
|
|
2572
2442
|
}): DictKeyDescriptor<Keys>;
|
|
2573
2443
|
/** Runtime predicate for detecting a DictKeyDescriptor. */
|
|
2574
2444
|
declare function isDictKeyDescriptor(x: unknown): x is DictKeyDescriptor;
|
|
2445
|
+
/**
|
|
2446
|
+
* Descriptor returned by `staticDict()`. A sibling to {@link DictKeyDescriptor}
|
|
2447
|
+
* for **closed, defined-in-code, identical-across-vaults** enums (honorific,
|
|
2448
|
+
* civil-status, gender, religion, ContactTitle, status…).
|
|
2449
|
+
*
|
|
2450
|
+
* Unlike `dictKey`, the labels are supplied **at registration in code** and
|
|
2451
|
+
* resolved through the *same* label machinery, but with **no `_dict_*`
|
|
2452
|
+
* per-vault encrypted copy** and **no `rename()`**. The record stores only
|
|
2453
|
+
* the **code**; a static dict has no mutation surface.
|
|
2454
|
+
*
|
|
2455
|
+
* **Hybrid resolution.** Because a static dict is pure code with no
|
|
2456
|
+
* vault-locale dependency, it can — via a configured `displayLocale` — emit a
|
|
2457
|
+
* `<field>Label` even under a **locale-less read** (the property a locale-less
|
|
2458
|
+
* consumer needs and `dictKey` cannot provide). When a locale *is* active it
|
|
2459
|
+
* behaves exactly like `dictKey` (honoring `onMissing`/`substitute`).
|
|
2460
|
+
*
|
|
2461
|
+
* ```ts
|
|
2462
|
+
* const workers = vault.collection<Worker>('workers', {
|
|
2463
|
+
* dictKeyFields: {
|
|
2464
|
+
* civilStatus: staticDict('civilStatus', {
|
|
2465
|
+
* adultMale: { th: 'นาย', en: 'Mr' },
|
|
2466
|
+
* adultFemale: { th: 'นาง', en: 'Mrs' },
|
|
2467
|
+
* }, { displayLocale: 'th' }),
|
|
2468
|
+
* },
|
|
2469
|
+
* })
|
|
2470
|
+
* ```
|
|
2471
|
+
*/
|
|
2472
|
+
interface StaticDictDescriptor<Keys extends string = string> {
|
|
2473
|
+
readonly _noydbStaticDict: true;
|
|
2474
|
+
/** Which dictionary this field references (the registry name). */
|
|
2475
|
+
readonly name: string;
|
|
2476
|
+
/** The in-code label table: key → { locale → label }. */
|
|
2477
|
+
readonly table: Readonly<Record<Keys, Readonly<Record<string, string>>>>;
|
|
2478
|
+
/** Declared valid keys — derived from `Object.keys(table)`. */
|
|
2479
|
+
readonly keys: readonly Keys[];
|
|
2480
|
+
/**
|
|
2481
|
+
* Locale used to emit `<field>Label` under a **locale-less** read — the
|
|
2482
|
+
* hybrid hinge. When unset, a static dict behaves like `dictKey` under a
|
|
2483
|
+
* locale-less read (no label); a locale-less consumer almost always wants
|
|
2484
|
+
* it set.
|
|
2485
|
+
*/
|
|
2486
|
+
readonly displayLocale?: string;
|
|
2487
|
+
/** Same `onMissing` policy engine as `dictKey`. Default `'null'`. */
|
|
2488
|
+
readonly onMissing?: OnMissingPolicy;
|
|
2489
|
+
/** Ordered preferred-substitute locales for label resolution. */
|
|
2490
|
+
readonly substitute?: readonly string[];
|
|
2491
|
+
/**
|
|
2492
|
+
* Validate the stored code against `keys` on every `put()`. Default `true`
|
|
2493
|
+
* — codes are closed by construction, so an unknown code is a bug. Set
|
|
2494
|
+
* `false` to allow open codes (skips the `UnknownDictCodeError` guard).
|
|
2495
|
+
*/
|
|
2496
|
+
readonly validateCodes?: boolean;
|
|
2497
|
+
}
|
|
2498
|
+
/**
|
|
2499
|
+
* Create a `StaticDictDescriptor` for a code-provided enum field — a sibling
|
|
2500
|
+
* to {@link dictKey} for closed, code-defined, identical-across-vaults enums.
|
|
2501
|
+
*
|
|
2502
|
+
* The labels live in `table` (no `_dict_*` collection, no `rename()`); the
|
|
2503
|
+
* record stores only the stable code. Pass `displayLocale` so `<field>Label`
|
|
2504
|
+
* resolves even under a locale-less read.
|
|
2505
|
+
*
|
|
2506
|
+
* @param name The dictionary name (used for the readonly-guard registry and
|
|
2507
|
+
* the query label seam — never creates a `_dict_<name>` key).
|
|
2508
|
+
* @param table `{ key: { locale: label } }` map.
|
|
2509
|
+
* @param opts `displayLocale` (locale-less label), `onMissing`, `substitute`.
|
|
2510
|
+
*
|
|
2511
|
+
* @example
|
|
2512
|
+
* ```ts
|
|
2513
|
+
* staticDict('civilStatus', {
|
|
2514
|
+
* adultMale: { th: 'นาย', en: 'Mr' },
|
|
2515
|
+
* adultFemale: { th: 'นาง', en: 'Mrs' },
|
|
2516
|
+
* }, { displayLocale: 'th' })
|
|
2517
|
+
* ```
|
|
2518
|
+
*/
|
|
2519
|
+
declare function staticDict<const T extends Record<string, Record<string, string>>>(name: string, table: T, opts?: {
|
|
2520
|
+
displayLocale?: string;
|
|
2521
|
+
onMissing?: OnMissingPolicy;
|
|
2522
|
+
substitute?: readonly string[];
|
|
2523
|
+
validateCodes?: boolean;
|
|
2524
|
+
}): StaticDictDescriptor<Extract<keyof T, string>>;
|
|
2525
|
+
/** Runtime predicate for detecting a StaticDictDescriptor. */
|
|
2526
|
+
declare function isStaticDictDescriptor(x: unknown): x is StaticDictDescriptor;
|
|
2575
2527
|
/**
|
|
2576
2528
|
* One entry in a `_dict_*` collection. The record `id` (adapter-side
|
|
2577
2529
|
* key) IS the stable dictionary key (e.g. `'paid'`). The `labels`
|
|
@@ -3475,6 +3427,12 @@ interface HistoryStrategy {
|
|
|
3475
3427
|
* `NO_HISTORY`.
|
|
3476
3428
|
*/
|
|
3477
3429
|
clearHistory(adapter: NoydbStore, vault: string, collection?: string, recordId?: string): Promise<number>;
|
|
3430
|
+
/**
|
|
3431
|
+
* Crypto-shred (overwrite-to-tombstone) every `_history` version of a
|
|
3432
|
+
* record for GDPR erasure (#304). Returns the number of versions newly
|
|
3433
|
+
* tombstoned, or `0` under `NO_HISTORY` (no history = nothing to shred).
|
|
3434
|
+
*/
|
|
3435
|
+
tombstoneHistory(adapter: NoydbStore, vault: string, collection: string, recordId: string, actor: string): Promise<number>;
|
|
3478
3436
|
/**
|
|
3479
3437
|
* Compute the SHA-256 hash of an envelope's encrypted payload, used
|
|
3480
3438
|
* by `LedgerStore.append` to track tamper-evidence. Returns the
|
|
@@ -4903,6 +4861,208 @@ interface SnapshotStrategy {
|
|
|
4903
4861
|
readonly policy?: SnapshotPolicy;
|
|
4904
4862
|
}
|
|
4905
4863
|
|
|
4864
|
+
/**
|
|
4865
|
+
* Minimum read surface exposed to guard `check` functions. Intentionally
|
|
4866
|
+
* narrow — guards can read other collections but never write.
|
|
4867
|
+
*
|
|
4868
|
+
* `query()` returns the same chainable builder used elsewhere. `Query<T>`
|
|
4869
|
+
* has no write terminals (no `.update()` / `.delete()`) so exposing it
|
|
4870
|
+
* here preserves the read-only contract while letting guards aggregate
|
|
4871
|
+
* with `.where().aggregate()` / `.groupBy()` / `.join()` instead of
|
|
4872
|
+
* decrypting every sibling row via `.list()`.
|
|
4873
|
+
*/
|
|
4874
|
+
interface ReadOnlyVaultFacade$1 {
|
|
4875
|
+
collection<T = unknown>(name: string): {
|
|
4876
|
+
get(id: string): Promise<T | null>;
|
|
4877
|
+
list(): Promise<T[]>;
|
|
4878
|
+
query(): Query<T>;
|
|
4879
|
+
};
|
|
4880
|
+
}
|
|
4881
|
+
/**
|
|
4882
|
+
* Runtime context passed to `check` and `invariant` callbacks.
|
|
4883
|
+
* `existing` is the currently-persisted record (null for inserts).
|
|
4884
|
+
*/
|
|
4885
|
+
interface GuardContext<T> {
|
|
4886
|
+
existing: T | null;
|
|
4887
|
+
vault: ReadOnlyVaultFacade$1;
|
|
4888
|
+
userId: string;
|
|
4889
|
+
role: Role;
|
|
4890
|
+
}
|
|
4891
|
+
/**
|
|
4892
|
+
* One {before, after} pair handed to an `invariant` function. `before`
|
|
4893
|
+
* is null for inserts; `after` reflects the proposed post-commit record.
|
|
4894
|
+
*/
|
|
4895
|
+
interface GuardChange<T> {
|
|
4896
|
+
before: T | null;
|
|
4897
|
+
after: T;
|
|
4898
|
+
}
|
|
4899
|
+
/** @internal — output of {@link withGuard}. */
|
|
4900
|
+
interface GuardStrategyHandle<T extends Record<string, unknown>> {
|
|
4901
|
+
readonly __noydb_strategy: 'guard';
|
|
4902
|
+
readonly spec: GuardStrategy<T>;
|
|
4903
|
+
}
|
|
4904
|
+
/**
|
|
4905
|
+
* Existential erasure of `GuardStrategyHandle<T>` — used as the
|
|
4906
|
+
* element type of `ReadonlyArray<>` fields where the per-handle T
|
|
4907
|
+
* differs (e.g. `guardStrategies: [invoiceGuard, disbursementGuard]`).
|
|
4908
|
+
*
|
|
4909
|
+
* Background: `GuardStrategyHandle<T>` is INVARIANT in T because T
|
|
4910
|
+
* appears in callback positions on the spec (`check(incoming: T, ctx)`,
|
|
4911
|
+
* `invariant(changes: ReadonlyArray<GuardChange<T>>, ctx)`). So
|
|
4912
|
+
* `Handle<Invoice>` is not assignable to `Handle<Record<string, unknown>>`.
|
|
4913
|
+
* A bounded existential ("there exists some T satisfying the constraint
|
|
4914
|
+
* such that this is a Handle<T>") is the right shape; TypeScript has
|
|
4915
|
+
* no first-class existentials, so we fake it with a structurally narrow
|
|
4916
|
+
* interface that ERASES T from both the discriminant and the spec.
|
|
4917
|
+
*
|
|
4918
|
+
* Consumers continue to construct typed handles via `withGuard<T>(...)`
|
|
4919
|
+
* which returns `GuardStrategyHandle<T>`. Both `Handle<Invoice>` and
|
|
4920
|
+
* `Handle<Disbursement>` structurally assign to `GuardStrategyHandleAny`,
|
|
4921
|
+
* so an array of them is `GuardStrategyHandleAny[]`.
|
|
4922
|
+
*
|
|
4923
|
+
* Internal code that needs T re-narrows via the runtime discriminant
|
|
4924
|
+
* (`__noydb_strategy === 'guard'`) plus per-handle type information
|
|
4925
|
+
* carried by the registry.
|
|
4926
|
+
*
|
|
4927
|
+
* NOT exported from the public barrel — keeping this internal
|
|
4928
|
+
* discourages consumers from constructing it directly. Used only as
|
|
4929
|
+
* the array-element type on `Vault` / `NoydbOptions.guardStrategies`.
|
|
4930
|
+
*
|
|
4931
|
+
* @internal
|
|
4932
|
+
*/
|
|
4933
|
+
interface GuardStrategyHandleAny {
|
|
4934
|
+
readonly __noydb_strategy: 'guard';
|
|
4935
|
+
readonly spec: GuardStrategy<any>;
|
|
4936
|
+
}
|
|
4937
|
+
/** Public registration shape. See `withGuard()`. */
|
|
4938
|
+
interface GuardStrategy<T extends Record<string, unknown>> {
|
|
4939
|
+
collection: string;
|
|
4940
|
+
/**
|
|
4941
|
+
* Fires on `Collection.put` (insert + update). The `incoming` argument
|
|
4942
|
+
* is the record being written. Throw to cancel the put.
|
|
4943
|
+
*
|
|
4944
|
+
* Does NOT fire on `Collection.delete` — use {@link onDelete} for
|
|
4945
|
+
* delete-time validation. Skipped during an amendment transaction
|
|
4946
|
+
* (`db.transaction({ amendment: true })`) — admin/owner override.
|
|
4947
|
+
*/
|
|
4948
|
+
check?: (incoming: T, ctx: GuardContext<T>) => Promise<void> | void;
|
|
4949
|
+
/**
|
|
4950
|
+
* Fires on user-initiated `Collection.delete` before the adapter
|
|
4951
|
+
* delete and before the ledger append. The `existing` argument is
|
|
4952
|
+
* the currently-persisted record. Throw to cancel the delete — no
|
|
4953
|
+
* partial state, no tombstone ledger entry.
|
|
4954
|
+
*
|
|
4955
|
+
* Skipped during an amendment transaction (admin/owner override) —
|
|
4956
|
+
* amendments are the unlock primitive. To make a delete TRULY
|
|
4957
|
+
* unconditional (e.g. legal-document immutability rules), pair
|
|
4958
|
+
* `onDelete` with an `amendment.invariant` that re-throws on any
|
|
4959
|
+
* `before !== null && after === null` change:
|
|
4960
|
+
*
|
|
4961
|
+
* ```ts
|
|
4962
|
+
* withGuard<Receipt>({
|
|
4963
|
+
* collection: 'receipts',
|
|
4964
|
+
* onDelete: () => { throw new RecordLockedError(...) },
|
|
4965
|
+
* amendment: {
|
|
4966
|
+
* roles: ['admin', 'owner'],
|
|
4967
|
+
* invariant: (changes) => {
|
|
4968
|
+
* for (const c of changes) {
|
|
4969
|
+
* if (c.before !== null && c.after === null) {
|
|
4970
|
+
* throw new RecordLockedError(...) // wrapped as InvariantError
|
|
4971
|
+
* }
|
|
4972
|
+
* }
|
|
4973
|
+
* },
|
|
4974
|
+
* },
|
|
4975
|
+
* })
|
|
4976
|
+
* ```
|
|
4977
|
+
*
|
|
4978
|
+
* Also skipped on system-internal deletes (derivation tombstones,
|
|
4979
|
+
* MV refresh from Dim 14 v2) — those use `_internalDelete`
|
|
4980
|
+
* which bypasses every user-facing delete hook. Housekeeping ops are
|
|
4981
|
+
* NOT user-initiated and should not trip user invariants.
|
|
4982
|
+
*
|
|
4983
|
+
* Delete of an absent record is a no-op and does not consult any
|
|
4984
|
+
* guard, matching the idempotent-delete contract.
|
|
4985
|
+
*/
|
|
4986
|
+
onDelete?: (existing: T, ctx: GuardContext<T>) => Promise<void> | void;
|
|
4987
|
+
frozenFields?: {
|
|
4988
|
+
when: (existing: T) => boolean;
|
|
4989
|
+
fields: ReadonlyArray<keyof T>;
|
|
4990
|
+
};
|
|
4991
|
+
amendment?: {
|
|
4992
|
+
roles: ReadonlyArray<'admin' | 'owner'>;
|
|
4993
|
+
invariant: (changes: ReadonlyArray<GuardChange<T>>, ctx: GuardContext<T>) => Promise<void> | void;
|
|
4994
|
+
};
|
|
4995
|
+
}
|
|
4996
|
+
|
|
4997
|
+
/**
|
|
4998
|
+
* Commit-time changeset invariants for ordinary transactions.
|
|
4999
|
+
*
|
|
5000
|
+
* Today the only set-level invariant hook is `amendment.invariant`, which
|
|
5001
|
+
* fires solely inside `db.transaction({ amendment: true }, …)` and requires
|
|
5002
|
+
* an admin/owner role plus a registered guard. That is the right shape for
|
|
5003
|
+
* the WORM-override path, but normal application transactions also need
|
|
5004
|
+
* cross-record invariants that hold on every commit — e.g. niwat's
|
|
5005
|
+
* `assertR1` ("every payment is 100% receipted").
|
|
5006
|
+
*
|
|
5007
|
+
* `withTransactions({ invariants: [...] })` registers such invariants. Each
|
|
5008
|
+
* one names a `scope` (a collection) and a `check(changes, ctx)` callback
|
|
5009
|
+
* that receives the commit's change-set for that collection.
|
|
5010
|
+
*
|
|
5011
|
+
* ## Semantics
|
|
5012
|
+
*
|
|
5013
|
+
* - **`scope`** is a collection name. An invariant fires only when the
|
|
5014
|
+
* committed transaction wrote at least one record in that collection.
|
|
5015
|
+
* - **When** — at commit, AFTER Phase 2 has executed all staged ops (so
|
|
5016
|
+
* `after` reflects the written record), and after the amendment commit
|
|
5017
|
+
* phase. It runs for BOTH ordinary and amendment transactions — an
|
|
5018
|
+
* amendment is still a commit and is still subject to these invariants.
|
|
5019
|
+
* - **`changes`** — one `{ before, after }` pair per touched id (deduped to
|
|
5020
|
+
* the last write per id, preserving write order). `before` is the
|
|
5021
|
+
* plaintext prior record (captured in Phase 1 before the overwrite),
|
|
5022
|
+
* `null` for an insert. `after` is the written record, `null` for a
|
|
5023
|
+
* delete.
|
|
5024
|
+
* - **Throw → revert.** A throw reverts every executed op (via the shared
|
|
5025
|
+
* `revertExecuted` unwind) and is surfaced as `InvariantError` (an
|
|
5026
|
+
* `InvariantError` thrown by the check passes through unwrapped).
|
|
5027
|
+
*
|
|
5028
|
+
* ```ts
|
|
5029
|
+
* createNoydb({ txStrategy: withTransactions({
|
|
5030
|
+
* invariants: [{
|
|
5031
|
+
* scope: 'payments',
|
|
5032
|
+
* check(changes) {
|
|
5033
|
+
* for (const { after } of changes) {
|
|
5034
|
+
* const p = after as Payment | null
|
|
5035
|
+
* if (p && p.receiptAmount !== p.amount) {
|
|
5036
|
+
* throw new InvariantError('R1: payment not fully receipted')
|
|
5037
|
+
* }
|
|
5038
|
+
* }
|
|
5039
|
+
* },
|
|
5040
|
+
* }],
|
|
5041
|
+
* }) })
|
|
5042
|
+
* ```
|
|
5043
|
+
*/
|
|
5044
|
+
|
|
5045
|
+
/**
|
|
5046
|
+
* A commit-time set-level invariant for ordinary (and amendment)
|
|
5047
|
+
* transactions. See module docs for full semantics.
|
|
5048
|
+
*/
|
|
5049
|
+
interface TransactionInvariant {
|
|
5050
|
+
/**
|
|
5051
|
+
* The collection this invariant watches. The `check` fires only when the
|
|
5052
|
+
* committed transaction wrote at least one record in this collection.
|
|
5053
|
+
*/
|
|
5054
|
+
readonly scope: string;
|
|
5055
|
+
/**
|
|
5056
|
+
* Validate the change-set for {@link scope}. `changes` carries one
|
|
5057
|
+
* `{ before, after }` pair per touched id (deduped to last-write,
|
|
5058
|
+
* write-order preserved); `before` is the plaintext prior record (null
|
|
5059
|
+
* on insert), `after` is the written record (null on delete). Throw to
|
|
5060
|
+
* revert the whole transaction — the throw is surfaced as
|
|
5061
|
+
* `InvariantError`.
|
|
5062
|
+
*/
|
|
5063
|
+
check: (changes: ReadonlyArray<GuardChange<unknown>>, ctx: GuardContext<unknown>) => Promise<void> | void;
|
|
5064
|
+
}
|
|
5065
|
+
|
|
4906
5066
|
/**
|
|
4907
5067
|
* Multi-record atomic transactions.
|
|
4908
5068
|
*
|
|
@@ -5088,7 +5248,7 @@ declare class TxCollection<T> {
|
|
|
5088
5248
|
* helper) so nested side-effect derivation writes get registered for
|
|
5089
5249
|
* revert alongside the bulk-put source ops.
|
|
5090
5250
|
*/
|
|
5091
|
-
declare function runTransaction<T>(db: Noydb, fn: (tx: TxContext) => Promise<T> | T, options?: AmendmentTxOptions): Promise<T>;
|
|
5251
|
+
declare function runTransaction<T>(db: Noydb, fn: (tx: TxContext) => Promise<T> | T, options?: AmendmentTxOptions, txInvariants?: ReadonlyArray<TransactionInvariant>): Promise<T>;
|
|
5092
5252
|
|
|
5093
5253
|
/**
|
|
5094
5254
|
* Dry-run transactions. Runs the tx body to STAGE ops, then builds
|
|
@@ -5535,6 +5695,7 @@ declare class Noydb {
|
|
|
5535
5695
|
private readonly policyEnforcers;
|
|
5536
5696
|
private readonly vaultTemplates;
|
|
5537
5697
|
private readonly txStrategy;
|
|
5698
|
+
private readonly forgetStrategy;
|
|
5538
5699
|
private readonly sessionStrategy;
|
|
5539
5700
|
private readonly syncStrategy;
|
|
5540
5701
|
private readonly snapshotStrategy;
|
|
@@ -5559,6 +5720,8 @@ declare class Noydb {
|
|
|
5559
5720
|
/** Audit log for all translator invocations in this session. Cleared on `close()`. */
|
|
5560
5721
|
private readonly _translatorAuditLog;
|
|
5561
5722
|
constructor(options: NoydbOptions);
|
|
5723
|
+
/** @internal — resolved forget strategy (NO_FORGET when not configured). */
|
|
5724
|
+
get _forgetStrategy(): ForgetStrategy;
|
|
5562
5725
|
private resetSessionTimer;
|
|
5563
5726
|
/**
|
|
5564
5727
|
* Attach a policy enforcer for a vault.
|
|
@@ -6654,145 +6817,12 @@ declare function loadActiveDelegations(store: NoydbStore, vault: string, user: U
|
|
|
6654
6817
|
declare function revokeDelegation(store: NoydbStore, vault: string, id: string): Promise<void>;
|
|
6655
6818
|
|
|
6656
6819
|
/**
|
|
6657
|
-
*
|
|
6658
|
-
* narrow
|
|
6659
|
-
*
|
|
6660
|
-
* `
|
|
6661
|
-
*
|
|
6662
|
-
*
|
|
6663
|
-
* with `.where().aggregate()` / `.groupBy()` / `.join()` instead of
|
|
6664
|
-
* decrypting every sibling row via `.list()`.
|
|
6665
|
-
*/
|
|
6666
|
-
interface ReadOnlyVaultFacade$1 {
|
|
6667
|
-
collection<T = unknown>(name: string): {
|
|
6668
|
-
get(id: string): Promise<T | null>;
|
|
6669
|
-
list(): Promise<T[]>;
|
|
6670
|
-
query(): Query<T>;
|
|
6671
|
-
};
|
|
6672
|
-
}
|
|
6673
|
-
/**
|
|
6674
|
-
* Runtime context passed to `check` and `invariant` callbacks.
|
|
6675
|
-
* `existing` is the currently-persisted record (null for inserts).
|
|
6676
|
-
*/
|
|
6677
|
-
interface GuardContext<T> {
|
|
6678
|
-
existing: T | null;
|
|
6679
|
-
vault: ReadOnlyVaultFacade$1;
|
|
6680
|
-
userId: string;
|
|
6681
|
-
role: Role;
|
|
6682
|
-
}
|
|
6683
|
-
/**
|
|
6684
|
-
* One {before, after} pair handed to an `invariant` function. `before`
|
|
6685
|
-
* is null for inserts; `after` reflects the proposed post-commit record.
|
|
6686
|
-
*/
|
|
6687
|
-
interface GuardChange<T> {
|
|
6688
|
-
before: T | null;
|
|
6689
|
-
after: T;
|
|
6690
|
-
}
|
|
6691
|
-
/** @internal — output of {@link withGuard}. */
|
|
6692
|
-
interface GuardStrategyHandle<T extends Record<string, unknown>> {
|
|
6693
|
-
readonly __noydb_strategy: 'guard';
|
|
6694
|
-
readonly spec: GuardStrategy<T>;
|
|
6695
|
-
}
|
|
6696
|
-
/**
|
|
6697
|
-
* Existential erasure of `GuardStrategyHandle<T>` — used as the
|
|
6698
|
-
* element type of `ReadonlyArray<>` fields where the per-handle T
|
|
6699
|
-
* differs (e.g. `guardStrategies: [invoiceGuard, disbursementGuard]`).
|
|
6700
|
-
*
|
|
6701
|
-
* Background: `GuardStrategyHandle<T>` is INVARIANT in T because T
|
|
6702
|
-
* appears in callback positions on the spec (`check(incoming: T, ctx)`,
|
|
6703
|
-
* `invariant(changes: ReadonlyArray<GuardChange<T>>, ctx)`). So
|
|
6704
|
-
* `Handle<Invoice>` is not assignable to `Handle<Record<string, unknown>>`.
|
|
6705
|
-
* A bounded existential ("there exists some T satisfying the constraint
|
|
6706
|
-
* such that this is a Handle<T>") is the right shape; TypeScript has
|
|
6707
|
-
* no first-class existentials, so we fake it with a structurally narrow
|
|
6708
|
-
* interface that ERASES T from both the discriminant and the spec.
|
|
6709
|
-
*
|
|
6710
|
-
* Consumers continue to construct typed handles via `withGuard<T>(...)`
|
|
6711
|
-
* which returns `GuardStrategyHandle<T>`. Both `Handle<Invoice>` and
|
|
6712
|
-
* `Handle<Disbursement>` structurally assign to `GuardStrategyHandleAny`,
|
|
6713
|
-
* so an array of them is `GuardStrategyHandleAny[]`.
|
|
6714
|
-
*
|
|
6715
|
-
* Internal code that needs T re-narrows via the runtime discriminant
|
|
6716
|
-
* (`__noydb_strategy === 'guard'`) plus per-handle type information
|
|
6717
|
-
* carried by the registry.
|
|
6718
|
-
*
|
|
6719
|
-
* NOT exported from the public barrel — keeping this internal
|
|
6720
|
-
* discourages consumers from constructing it directly. Used only as
|
|
6721
|
-
* the array-element type on `Vault` / `NoydbOptions.guardStrategies`.
|
|
6722
|
-
*
|
|
6723
|
-
* @internal
|
|
6724
|
-
*/
|
|
6725
|
-
interface GuardStrategyHandleAny {
|
|
6726
|
-
readonly __noydb_strategy: 'guard';
|
|
6727
|
-
readonly spec: GuardStrategy<any>;
|
|
6728
|
-
}
|
|
6729
|
-
/** Public registration shape. See `withGuard()`. */
|
|
6730
|
-
interface GuardStrategy<T extends Record<string, unknown>> {
|
|
6731
|
-
collection: string;
|
|
6732
|
-
/**
|
|
6733
|
-
* Fires on `Collection.put` (insert + update). The `incoming` argument
|
|
6734
|
-
* is the record being written. Throw to cancel the put.
|
|
6735
|
-
*
|
|
6736
|
-
* Does NOT fire on `Collection.delete` — use {@link onDelete} for
|
|
6737
|
-
* delete-time validation. Skipped during an amendment transaction
|
|
6738
|
-
* (`db.transaction({ amendment: true })`) — admin/owner override.
|
|
6739
|
-
*/
|
|
6740
|
-
check?: (incoming: T, ctx: GuardContext<T>) => Promise<void> | void;
|
|
6741
|
-
/**
|
|
6742
|
-
* Fires on user-initiated `Collection.delete` before the adapter
|
|
6743
|
-
* delete and before the ledger append. The `existing` argument is
|
|
6744
|
-
* the currently-persisted record. Throw to cancel the delete — no
|
|
6745
|
-
* partial state, no tombstone ledger entry.
|
|
6746
|
-
*
|
|
6747
|
-
* Skipped during an amendment transaction (admin/owner override) —
|
|
6748
|
-
* amendments are the unlock primitive. To make a delete TRULY
|
|
6749
|
-
* unconditional (e.g. legal-document immutability rules), pair
|
|
6750
|
-
* `onDelete` with an `amendment.invariant` that re-throws on any
|
|
6751
|
-
* `before !== null && after === null` change:
|
|
6752
|
-
*
|
|
6753
|
-
* ```ts
|
|
6754
|
-
* withGuard<Receipt>({
|
|
6755
|
-
* collection: 'receipts',
|
|
6756
|
-
* onDelete: () => { throw new RecordLockedError(...) },
|
|
6757
|
-
* amendment: {
|
|
6758
|
-
* roles: ['admin', 'owner'],
|
|
6759
|
-
* invariant: (changes) => {
|
|
6760
|
-
* for (const c of changes) {
|
|
6761
|
-
* if (c.before !== null && c.after === null) {
|
|
6762
|
-
* throw new RecordLockedError(...) // wrapped as InvariantError
|
|
6763
|
-
* }
|
|
6764
|
-
* }
|
|
6765
|
-
* },
|
|
6766
|
-
* },
|
|
6767
|
-
* })
|
|
6768
|
-
* ```
|
|
6769
|
-
*
|
|
6770
|
-
* Also skipped on system-internal deletes (derivation tombstones,
|
|
6771
|
-
* MV refresh from Dim 14 v2) — those use `_internalDelete`
|
|
6772
|
-
* which bypasses every user-facing delete hook. Housekeeping ops are
|
|
6773
|
-
* NOT user-initiated and should not trip user invariants.
|
|
6774
|
-
*
|
|
6775
|
-
* Delete of an absent record is a no-op and does not consult any
|
|
6776
|
-
* guard, matching the idempotent-delete contract.
|
|
6777
|
-
*/
|
|
6778
|
-
onDelete?: (existing: T, ctx: GuardContext<T>) => Promise<void> | void;
|
|
6779
|
-
frozenFields?: {
|
|
6780
|
-
when: (existing: T) => boolean;
|
|
6781
|
-
fields: ReadonlyArray<keyof T>;
|
|
6782
|
-
};
|
|
6783
|
-
amendment?: {
|
|
6784
|
-
roles: ReadonlyArray<'admin' | 'owner'>;
|
|
6785
|
-
invariant: (changes: ReadonlyArray<GuardChange<T>>, ctx: GuardContext<T>) => Promise<void> | void;
|
|
6786
|
-
};
|
|
6787
|
-
}
|
|
6788
|
-
|
|
6789
|
-
/**
|
|
6790
|
-
* Runtime context handed to `derive(source, ctx)`. Mirrors `GuardContext`'s
|
|
6791
|
-
* narrow shape: read-only vault access, no write capability, no
|
|
6792
|
-
* transaction handle. Determinism is the consumer's responsibility — the
|
|
6793
|
-
* strategy hash includes `derive.toString()`, so the source string fixes
|
|
6794
|
-
* the function's inputs; whatever sibling reads `derive` performs must
|
|
6795
|
-
* yield the same outputs for the same source.
|
|
6820
|
+
* Runtime context handed to `derive(source, ctx)`. Mirrors `GuardContext`'s
|
|
6821
|
+
* narrow shape: read-only vault access, no write capability, no
|
|
6822
|
+
* transaction handle. Determinism is the consumer's responsibility — the
|
|
6823
|
+
* strategy hash includes `derive.toString()`, so the source string fixes
|
|
6824
|
+
* the function's inputs; whatever sibling reads `derive` performs must
|
|
6825
|
+
* yield the same outputs for the same source.
|
|
6796
6826
|
*/
|
|
6797
6827
|
interface DerivationContext {
|
|
6798
6828
|
vault: ReadOnlyVaultFacade$1;
|
|
@@ -6886,6 +6916,32 @@ type OutputSpec = RecordOutputSpec | ArrayOutputSpec;
|
|
|
6886
6916
|
interface DerivationStrategy<TSource extends Record<string, unknown>, TOutputs extends Record<string, Record<string, unknown>>> {
|
|
6887
6917
|
/** Source collection name. */
|
|
6888
6918
|
source: string;
|
|
6919
|
+
/**
|
|
6920
|
+
* Additional collections whose writes ALSO re-fire this derivation
|
|
6921
|
+
* (issue #344). By default only writes to the single declared
|
|
6922
|
+
* `source` re-trigger a derivation; a `derive` that reads sibling
|
|
6923
|
+
* collections via `ctx.vault` therefore goes stale when those
|
|
6924
|
+
* siblings change. Declare those sibling collections here to wire
|
|
6925
|
+
* them as extra triggers.
|
|
6926
|
+
*
|
|
6927
|
+
* **SAME-ID assumption (load-bearing):** when a write lands on one of
|
|
6928
|
+
* these declared collections, the derivation re-fires with the
|
|
6929
|
+
* PRIMARY `source` record read at the SAME id as the written record
|
|
6930
|
+
* — NOT the written sibling record itself. If no primary `source`
|
|
6931
|
+
* record exists at that id, the re-fire is a silent no-op (no throw,
|
|
6932
|
+
* no output mutation). Model your collections so the sibling and the
|
|
6933
|
+
* primary share an id (the common money/accounting case: an
|
|
6934
|
+
* `allocations` row and its `payments`/`bills` keyed by the same id),
|
|
6935
|
+
* or trigger off a collection you control the id-space of.
|
|
6936
|
+
*
|
|
6937
|
+
* Declared collections participate in cycle detection and are indexed
|
|
6938
|
+
* in the registry's `_bySource` exactly like `source`, so a sibling
|
|
6939
|
+
* that is also a derivation output forms a detectable cycle.
|
|
6940
|
+
*
|
|
6941
|
+
* Each entry must be a non-empty string and must not equal `source`
|
|
6942
|
+
* (validated at `withDerivation()` construction time).
|
|
6943
|
+
*/
|
|
6944
|
+
sources?: ReadonlyArray<string>;
|
|
6889
6945
|
/** v1: only deterministic derivations supported. */
|
|
6890
6946
|
deterministic: true;
|
|
6891
6947
|
/**
|
|
@@ -7053,8 +7109,39 @@ interface UnionSource<TRow extends Record<string, unknown>> {
|
|
|
7053
7109
|
* unified stream and never reaches `groupBy` / `aggregate`. This
|
|
7054
7110
|
* removes the need for sentinel rows (e.g. `{ amount: 0 }`) whose
|
|
7055
7111
|
* sole purpose is to be aggregated away (#297).
|
|
7112
|
+
*
|
|
7113
|
+
* When this arm declares {@link join}, the aliased right-side
|
|
7114
|
+
* record(s) are attached to the source row under each leg's `as`
|
|
7115
|
+
* BEFORE `map` runs, so `map` can read `sourceRow[leg.as]`.
|
|
7056
7116
|
*/
|
|
7057
7117
|
readonly map: (sourceRow: Record<string, unknown>) => TRow | null | undefined;
|
|
7118
|
+
/**
|
|
7119
|
+
* Optional FK joins to apply to this arm's rows before {@link map}.
|
|
7120
|
+
* Each leg resolves a `ref()`-declared foreign key on the arm's
|
|
7121
|
+
* source collection into an attached right-side record under
|
|
7122
|
+
* `as` — the same machinery as the query-form `Query.join()`.
|
|
7123
|
+
*
|
|
7124
|
+
* The right-side collections must be listed in the strategy's
|
|
7125
|
+
* {@link MaterializedViewStrategy.sources} so writes to them trigger
|
|
7126
|
+
* MV refresh — registration throws `MaterializedViewConfigError`
|
|
7127
|
+
* otherwise (the union dependency set comes from arm `collection`s
|
|
7128
|
+
* alone, which would not include join targets).
|
|
7129
|
+
*/
|
|
7130
|
+
readonly join?: ReadonlyArray<UnionArmJoin>;
|
|
7131
|
+
}
|
|
7132
|
+
/**
|
|
7133
|
+
* One FK join leg on a UNION arm. Mirrors the option shape of the
|
|
7134
|
+
* query-form `Query.join(field, { as, maxRows?, strategy? })`.
|
|
7135
|
+
*/
|
|
7136
|
+
interface UnionArmJoin {
|
|
7137
|
+
/** FK field on the arm's source collection (must have a `ref()` declared). */
|
|
7138
|
+
readonly field: string;
|
|
7139
|
+
/** Alias under which the resolved right-side record attaches on the source row. */
|
|
7140
|
+
readonly as: string;
|
|
7141
|
+
/** Per-side row ceiling override. `undefined` → the join default. */
|
|
7142
|
+
readonly maxRows?: number;
|
|
7143
|
+
/** Planner strategy override. `undefined` → auto-select. */
|
|
7144
|
+
readonly strategy?: JoinStrategy;
|
|
7058
7145
|
}
|
|
7059
7146
|
/**
|
|
7060
7147
|
* Registration shape passed to `withMaterializedView()`.
|
|
@@ -7126,6 +7213,25 @@ interface MaterializedViewStrategy<TRow extends Record<string, unknown>> {
|
|
|
7126
7213
|
* UNION-mode only. Ignored if {@link query} is set.
|
|
7127
7214
|
*/
|
|
7128
7215
|
aggregate?: AggregateSpec;
|
|
7216
|
+
/**
|
|
7217
|
+
* Money descriptors for the UNION-mode aggregate, keyed by the
|
|
7218
|
+
* OUTPUT/intermediate field name as it appears in the mapped row and
|
|
7219
|
+
* in {@link aggregate} (NOT the source collection's field name). When
|
|
7220
|
+
* declared, any `sum` / `min` / `max` over a keyed field is rewritten
|
|
7221
|
+
* into an exact per-currency BigInt reducer — without this, money
|
|
7222
|
+
* aggregation in UNION mode silently runs in float (since the
|
|
7223
|
+
* concatenated mapped stream is a plain array with no collection
|
|
7224
|
+
* money context to inherit).
|
|
7225
|
+
*
|
|
7226
|
+
* UNION-mode only — the query form inherits its money descriptors
|
|
7227
|
+
* from the source collection automatically. The descriptor's
|
|
7228
|
+
* currency/scale must match what the arms' `map()` emits for that
|
|
7229
|
+
* field (each arm maps into the same unified shape, so one descriptor
|
|
7230
|
+
* per output field covers all arms). Meaningless without
|
|
7231
|
+
* {@link aggregate}; registration throws `MaterializedViewConfigError`
|
|
7232
|
+
* if declared alone.
|
|
7233
|
+
*/
|
|
7234
|
+
moneyFields?: Record<string, MoneyDescriptor>;
|
|
7129
7235
|
/**
|
|
7130
7236
|
* Pure function from a materialized row → stable id used in the
|
|
7131
7237
|
* output collection. Required — explicit always beats default-with-pitfalls
|
|
@@ -7322,6 +7428,50 @@ interface OverlayedViewStrategy {
|
|
|
7322
7428
|
*/
|
|
7323
7429
|
shadowField: string;
|
|
7324
7430
|
shadowValue: unknown;
|
|
7431
|
+
/**
|
|
7432
|
+
* Optional field-level merge mode (AU+032 / #348). When present, a
|
|
7433
|
+
* row whose `shadowField` does NOT equal `shadowValue` is no longer
|
|
7434
|
+
* forced all-base: the rules below let an intermediate status pull a
|
|
7435
|
+
* declared subset of fields from the overlay while every other field
|
|
7436
|
+
* still comes from the base row.
|
|
7437
|
+
*
|
|
7438
|
+
* Absent `mergeMode` preserves the original binary behaviour
|
|
7439
|
+
* exactly — overlay wins entirely iff `shadowField === shadowValue`,
|
|
7440
|
+
* otherwise base wins.
|
|
7441
|
+
*/
|
|
7442
|
+
mergeMode?: OverlayFieldMergeMode;
|
|
7443
|
+
}
|
|
7444
|
+
/**
|
|
7445
|
+
* One field-level merge rule. When the overlay row's `shadowField`
|
|
7446
|
+
* equals `whenStatus`, the merged row takes `overlayFields` from the
|
|
7447
|
+
* overlay and every other field from the base row.
|
|
7448
|
+
*
|
|
7449
|
+
* Always include `shadowField` in `overlayFields` so the status value
|
|
7450
|
+
* itself propagates to the merged read (otherwise the merged row would
|
|
7451
|
+
* carry the base's status, which is usually `undefined`).
|
|
7452
|
+
*/
|
|
7453
|
+
interface OverlayFieldMergeRule {
|
|
7454
|
+
/** The `overlay[shadowField]` value this rule matches. */
|
|
7455
|
+
readonly whenStatus: unknown;
|
|
7456
|
+
/**
|
|
7457
|
+
* Field names pulled from the overlay row when this rule matches.
|
|
7458
|
+
* Fields absent on the overlay row are skipped (base value kept).
|
|
7459
|
+
*/
|
|
7460
|
+
readonly overlayFields: readonly string[];
|
|
7461
|
+
}
|
|
7462
|
+
/**
|
|
7463
|
+
* Field-level merge configuration. `rules` are evaluated in
|
|
7464
|
+
* declaration order and the FIRST rule whose `whenStatus` matches the
|
|
7465
|
+
* overlay's `shadowField` wins; later rules are not consulted.
|
|
7466
|
+
*
|
|
7467
|
+
* The binary shadow check (`shadowField === shadowValue` → overlay
|
|
7468
|
+
* wins entirely) is always the implicit, highest-priority rule applied
|
|
7469
|
+
* BEFORE any `rules` entry, so adding `mergeMode` never changes the
|
|
7470
|
+
* full-override path.
|
|
7471
|
+
*/
|
|
7472
|
+
interface OverlayFieldMergeMode {
|
|
7473
|
+
readonly kind: 'field-merge';
|
|
7474
|
+
readonly rules: readonly OverlayFieldMergeRule[];
|
|
7325
7475
|
}
|
|
7326
7476
|
/** Returned by `withOverlayedView()` and consumed by `createNoydb`. */
|
|
7327
7477
|
interface OverlayedViewStrategyHandle {
|
|
@@ -7398,11 +7548,53 @@ interface NextOptions {
|
|
|
7398
7548
|
/** Deferred mode: reject after this many ms if still unsealed (reserved; not yet enforced in this slice). */
|
|
7399
7549
|
readonly timeoutMs?: number;
|
|
7400
7550
|
}
|
|
7551
|
+
/**
|
|
7552
|
+
* Partitioning for a CAS sequence (#345). A partitioned sequence is an
|
|
7553
|
+
* independent counter scoped to one tuple of values — e.g.
|
|
7554
|
+
* `sequence('invoice', { partition: [2026, 'EU'] })` numbers EU-2026 invoices
|
|
7555
|
+
* separately from `[2026, 'US']` and from the bare `invoice` series.
|
|
7556
|
+
*
|
|
7557
|
+
* Partition components are URI-encoded (so `/`, null bytes and other
|
|
7558
|
+
* separators in a value can never collide with the structural separators) and
|
|
7559
|
+
* `'/'`-joined, then appended to the series with a null-byte (`\x00`)
|
|
7560
|
+
* separator. The null byte is illegal in a plain series name, which guarantees
|
|
7561
|
+
* a partitioned key is always disjoint from any unpartitioned series.
|
|
7562
|
+
*/
|
|
7563
|
+
interface SequenceOptions {
|
|
7564
|
+
/** Partition tuple. Each component is URI-encoded and `'/'`-joined. */
|
|
7565
|
+
readonly partition?: readonly (string | number)[];
|
|
7566
|
+
}
|
|
7567
|
+
/**
|
|
7568
|
+
* Resolve the CAS storage key for a (series, partition) pair.
|
|
7569
|
+
*
|
|
7570
|
+
* With no partition the key is `series` verbatim. With a partition the key is
|
|
7571
|
+
* `${series}\x00${parts}` where `parts` is each component passed through
|
|
7572
|
+
* `encodeURIComponent(String(part))` and `'/'`-joined. The null-byte separator
|
|
7573
|
+
* is illegal in a plain series name, so partitioned keys never collide with
|
|
7574
|
+
* unpartitioned ones; URI-encoding keeps any component containing `/` distinct
|
|
7575
|
+
* from a multi-component partition.
|
|
7576
|
+
*
|
|
7577
|
+
* @throws {ValidationError} if any partition component is empty after `String()`
|
|
7578
|
+
* or is a non-finite number (`NaN`, `±Infinity`).
|
|
7579
|
+
*/
|
|
7580
|
+
declare function resolveSequenceKey(series: string, opts?: SequenceOptions): string;
|
|
7401
7581
|
interface SequenceHandle {
|
|
7402
7582
|
/** Atomically allocate and return the next value (1, 2, 3, …). Deferred series resolve at the next pass. */
|
|
7403
7583
|
next(opts?: NextOptions): Promise<number>;
|
|
7404
7584
|
/** Read the current value without allocating. Returns 0 if never used. */
|
|
7405
7585
|
peek(): Promise<number>;
|
|
7586
|
+
/**
|
|
7587
|
+
* Set-if-greater: advance the counter to at least `n`. A no-op if the
|
|
7588
|
+
* current value is already `>= n` (so it never rewinds), and `seedTo(0)` is
|
|
7589
|
+
* a no-op. Idempotent and CAS-safe under concurrent `next()` / `seedTo()`.
|
|
7590
|
+
*
|
|
7591
|
+
* Use after a bundle / CSV import to fast-forward the counter past the
|
|
7592
|
+
* highest imported serial, so subsequent `next()` calls cannot re-use a
|
|
7593
|
+
* number that is already on a record.
|
|
7594
|
+
*
|
|
7595
|
+
* Online-only: throws {@link SequenceOfflineError} on a non-CAS store.
|
|
7596
|
+
*/
|
|
7597
|
+
seedTo(n: number): Promise<void>;
|
|
7406
7598
|
}
|
|
7407
7599
|
declare class SequenceStore {
|
|
7408
7600
|
private readonly adapter;
|
|
@@ -7433,6 +7625,7 @@ declare class SequenceStore {
|
|
|
7433
7625
|
private encryptState;
|
|
7434
7626
|
peek(name: string): Promise<number>;
|
|
7435
7627
|
next(name: string): Promise<number>;
|
|
7628
|
+
seedTo(name: string, n: number): Promise<void>;
|
|
7436
7629
|
}
|
|
7437
7630
|
|
|
7438
7631
|
/**
|
|
@@ -7528,6 +7721,287 @@ declare class ReadOnlyVaultFacade implements ReadOnlyVaultFacade$1 {
|
|
|
7528
7721
|
};
|
|
7529
7722
|
}
|
|
7530
7723
|
|
|
7724
|
+
/**
|
|
7725
|
+
* Managed-passphrase mode — rubber-hose-resistant vaults.
|
|
7726
|
+
*
|
|
7727
|
+
* A vault mode where the passphrase is machine-generated and never
|
|
7728
|
+
* exposed to the user, sealed under a developer-provided
|
|
7729
|
+
* {@link SealingKeyProvider} (macOS Keychain, Windows Credential
|
|
7730
|
+
* Manager, libsecret, AWS KMS, …). The user has no secret to give
|
|
7731
|
+
* up to coercion — they can't reveal what they don't know.
|
|
7732
|
+
*
|
|
7733
|
+
* ## Components in this file
|
|
7734
|
+
*
|
|
7735
|
+
* - {@link SealingKeyProvider} — the interface concrete providers
|
|
7736
|
+
* implement. Provider implementations live OUTSIDE hub (per-
|
|
7737
|
+
* platform packages).
|
|
7738
|
+
* - {@link MemorySealingKeyProvider} — in-memory test provider; uses
|
|
7739
|
+
* a deterministic per-instance "key" so two providers with
|
|
7740
|
+
* different ids cannot unseal each other's outputs.
|
|
7741
|
+
* - {@link RecipientHint} — public material a sender uses to seal
|
|
7742
|
+
* plaintext for a specific recipient; published by
|
|
7743
|
+
* {@link RecipientSealer.publishRecipientHint} and transported
|
|
7744
|
+
* out-of-band to the sender before bundle writes.
|
|
7745
|
+
* - {@link RecipientSealer} — interface for asymmetric/granted
|
|
7746
|
+
* providers that support recipient-target sealing (RSA-OAEP,
|
|
7747
|
+
* cloud-KMS asymmetric, etc.); distinct from self-only
|
|
7748
|
+
* {@link SealingKeyProvider} (macOS Keychain, WebAuthn-PRF).
|
|
7749
|
+
* - {@link MemoryRecipientSealer} — in-process reference
|
|
7750
|
+
* implementation of both `RecipientSealer` and
|
|
7751
|
+
* `SealingKeyProvider` using real WebCrypto RSA-OAEP + AES-GCM;
|
|
7752
|
+
* safe for tests and same-process sender/recipient scenarios.
|
|
7753
|
+
* - {@link loadSealedPassphrase} / {@link saveSealedPassphrase} —
|
|
7754
|
+
* plaintext envelope storage at `_meta/sealed-passphrase`.
|
|
7755
|
+
* Mirrors the `_meta/handle` and `_meta/public-envelope` AES-
|
|
7756
|
+
* GCM-bypassed patterns. The sealing layer (provider's job)
|
|
7757
|
+
* is the security boundary; hub doesn't have a key to encrypt
|
|
7758
|
+
* with at this layer — that's the whole point of the design.
|
|
7759
|
+
* - {@link resolveManagedSecret} — orchestrates the "generate +
|
|
7760
|
+
* seal + persist on first open; unseal on reopen" flow.
|
|
7761
|
+
* Returns the plaintext passphrase string that the rest of the
|
|
7762
|
+
* `createNoydb` keyring path consumes.
|
|
7763
|
+
*
|
|
7764
|
+
* Deferred to follow-ups:
|
|
7765
|
+
* - Block `rotate-passphrase` policy gate under managed mode.
|
|
7766
|
+
* - Mandatory strong-recovery enforcement.
|
|
7767
|
+
* - Recovery flow under managed mode (generates fresh sealed phrase).
|
|
7768
|
+
*
|
|
7769
|
+
* @see docs/subsystems/session-tiers.md → Managed-passphrase mode
|
|
7770
|
+
*
|
|
7771
|
+
* @module
|
|
7772
|
+
*/
|
|
7773
|
+
|
|
7774
|
+
/**
|
|
7775
|
+
* The contract concrete providers (per-platform key stores) implement
|
|
7776
|
+
* to seal and unseal a hub-generated random passphrase. The plaintext
|
|
7777
|
+
* passphrase NEVER leaves hub-controlled memory in unsealed form —
|
|
7778
|
+
* the provider receives the bytes, returns opaque sealed bytes, and
|
|
7779
|
+
* later reverses the operation. Hub treats the sealed bytes as
|
|
7780
|
+
* fully opaque.
|
|
7781
|
+
*
|
|
7782
|
+
* Implementations live OUTSIDE `@noy-db/hub` (separate packages
|
|
7783
|
+
* per the issue's "Concrete providers (live outside hub)" note):
|
|
7784
|
+
*
|
|
7785
|
+
* | Platform | Package (TBD) | Backing |
|
|
7786
|
+
* |---|---|---|
|
|
7787
|
+
* | macOS | `@noy-db/seal-macos-keychain` | Security.framework |
|
|
7788
|
+
* | Windows | `@noy-db/seal-wincred` | Credential Manager |
|
|
7789
|
+
* | Linux | `@noy-db/seal-libsecret` | libsecret / secret-service |
|
|
7790
|
+
* | Cloud / server | `@noy-db/seal-aws-kms` | AWS KMS Decrypt |
|
|
7791
|
+
*/
|
|
7792
|
+
interface SealingKeyProvider {
|
|
7793
|
+
/**
|
|
7794
|
+
* Non-sensitive identifier disclosed in the persisted envelope.
|
|
7795
|
+
* Surfaced to consumers via `loadSealedPassphrase().providerId` so
|
|
7796
|
+
* a vault opened with the wrong provider class can detect the
|
|
7797
|
+
* mismatch and surface a clear error. NOT secret — fine to log.
|
|
7798
|
+
*
|
|
7799
|
+
* Suggested format: `<family>:<scope>` — e.g. `macos-keychain:com.acme.app`,
|
|
7800
|
+
* `aws-kms:arn:aws:kms:us-east-1:123:key/abc`. The hub never
|
|
7801
|
+
* parses this; it's purely audit metadata.
|
|
7802
|
+
*/
|
|
7803
|
+
readonly id: string;
|
|
7804
|
+
/** Seal raw passphrase bytes. Output bytes are opaque to hub. */
|
|
7805
|
+
seal(passphrase: Uint8Array): Promise<Uint8Array>;
|
|
7806
|
+
/**
|
|
7807
|
+
* Reverse {@link seal}. MUST throw on tamper, wrong-provider, or
|
|
7808
|
+
* any other failure — hub treats a thrown error as "this provider
|
|
7809
|
+
* cannot unlock this vault" and surfaces it to the caller.
|
|
7810
|
+
*/
|
|
7811
|
+
unseal(sealed: Uint8Array): Promise<Uint8Array>;
|
|
7812
|
+
}
|
|
7813
|
+
/**
|
|
7814
|
+
* In-memory test provider. NOT secure — uses a deterministic
|
|
7815
|
+
* per-instance "key" (16-byte SHA-256 of `id`) XOR'd over the
|
|
7816
|
+
* passphrase plus a 4-byte provider-id fingerprint prefix. The XOR is
|
|
7817
|
+
* sufficient to make different `id` values produce mutually-unsealable
|
|
7818
|
+
* outputs (the contract tests for that), but offers ZERO real
|
|
7819
|
+
* confidentiality — never use outside tests.
|
|
7820
|
+
*
|
|
7821
|
+
* Replace with a real platform provider in production.
|
|
7822
|
+
*/
|
|
7823
|
+
declare class MemorySealingKeyProvider implements SealingKeyProvider {
|
|
7824
|
+
readonly id: string;
|
|
7825
|
+
private readonly fingerprint;
|
|
7826
|
+
private readonly keyBytes;
|
|
7827
|
+
constructor(opts: {
|
|
7828
|
+
id: string;
|
|
7829
|
+
});
|
|
7830
|
+
seal(passphrase: Uint8Array): Promise<Uint8Array>;
|
|
7831
|
+
unseal(sealed: Uint8Array): Promise<Uint8Array>;
|
|
7832
|
+
}
|
|
7833
|
+
/**
|
|
7834
|
+
* Public material a sender uses to seal-for-this-recipient. Published by
|
|
7835
|
+
* a recipient's RecipientSealer; transported to the sender out-of-band
|
|
7836
|
+
* (email, S3, in-app message). The sender obtains the hint, supplies it
|
|
7837
|
+
* to writeNoydbBundle's sealedCredentials.perUser[userId].hint, and the
|
|
7838
|
+
* hub seals each user's credential against it. Per foundation §11.4.
|
|
7839
|
+
*/
|
|
7840
|
+
type RecipientHint = {
|
|
7841
|
+
readonly v: 1;
|
|
7842
|
+
/** Recipient's provider id; matches the SealedAutoUnlockEntry.pid they'll unseal under. */
|
|
7843
|
+
readonly pid: string;
|
|
7844
|
+
/** Algorithm the sender uses to produce the seal. Slice 1 ships RSA-OAEP-SHA256 only. */
|
|
7845
|
+
readonly alg: 'rsa-oaep-sha256';
|
|
7846
|
+
/** Public material — alg-specific. For 'rsa-oaep-sha256': { publicKeyPem: string }. */
|
|
7847
|
+
readonly material: Readonly<Record<string, unknown>>;
|
|
7848
|
+
};
|
|
7849
|
+
/**
|
|
7850
|
+
* Handover-capable provider. Implemented additionally by asymmetric/granted
|
|
7851
|
+
* providers (cloud-KMS asymmetric, Azure RSA Key Vault, AWS KMS with grant).
|
|
7852
|
+
* Self-only providers (macOS Keychain, env-var, WebAuthn-PRF) do NOT
|
|
7853
|
+
* implement this — the §11.2 capability matrix lives in the type system.
|
|
7854
|
+
*
|
|
7855
|
+
* Per foundation §11.4. A function that requires recipient-target sealing
|
|
7856
|
+
* takes `RecipientSealer`, not `SealingKeyProvider` — the compiler rejects
|
|
7857
|
+
* passing a self-only provider at the spec site.
|
|
7858
|
+
*/
|
|
7859
|
+
interface RecipientSealer {
|
|
7860
|
+
readonly id: string;
|
|
7861
|
+
/** Produce hint material a sender uses to seal-for-this-recipient. */
|
|
7862
|
+
publishRecipientHint(): Promise<RecipientHint>;
|
|
7863
|
+
/**
|
|
7864
|
+
* Seal plaintext for the recipient described by `hint`. Returns opaque
|
|
7865
|
+
* bytes — same contract as `SealingKeyProvider.seal()`. The bundle
|
|
7866
|
+
* layer base64-encodes the bytes into `SealedAutoUnlockEntry.sealed`
|
|
7867
|
+
* without inspecting them.
|
|
7868
|
+
*/
|
|
7869
|
+
sealForRecipient(plaintext: Uint8Array, hint: RecipientHint): Promise<Uint8Array>;
|
|
7870
|
+
}
|
|
7871
|
+
/**
|
|
7872
|
+
* Shared RSA-OAEP-SHA256 + AES-GCM seal in the canonical recipient-target
|
|
7873
|
+
* TLV wire format. Mints a fresh 32-byte CEK, AES-GCM-encrypts `plaintext`
|
|
7874
|
+
* under it, RSA-OAEP-SHA256-wraps the CEK to `publicKeyPem`, and packs:
|
|
7875
|
+
*
|
|
7876
|
+
* byte 0 : version (0x01)
|
|
7877
|
+
* bytes 1..256 : RSA-OAEP-wrapped CEK (fixed 256 bytes at RSA-2048)
|
|
7878
|
+
* bytes 257..268: AES-GCM IV (12 bytes)
|
|
7879
|
+
* bytes 269.. : AES-GCM ciphertext ‖ 16-byte tag
|
|
7880
|
+
*
|
|
7881
|
+
* This is the single source of truth for the wire format — both
|
|
7882
|
+
* {@link MemoryRecipientSealer} and external sealers (e.g. `@noy-db/at-aws-kms`'s
|
|
7883
|
+
* asymmetric-KMS recipient sealer) call it so a blob sealed by one unseals
|
|
7884
|
+
* by the other. WebCrypto RSA-OAEP/SHA-256 here is wire-compatible with
|
|
7885
|
+
* AWS KMS `RSAES_OAEP_SHA_256` (both RSAES-OAEP, SHA-256 hash, MGF1-SHA256,
|
|
7886
|
+
* empty label).
|
|
7887
|
+
*
|
|
7888
|
+
* @public — re-exported from the hub barrel for external sealer packages.
|
|
7889
|
+
*/
|
|
7890
|
+
declare function sealRsaOaepTlv(plaintext: Uint8Array, publicKeyPem: string): Promise<Uint8Array>;
|
|
7891
|
+
/**
|
|
7892
|
+
* Parse a {@link sealRsaOaepTlv} blob into its three segments without
|
|
7893
|
+
* decrypting. The `wrapped` CEK is RSA-OAEP-SHA256 ciphertext over a
|
|
7894
|
+
* 32-byte CEK — the unwrap step is pluggable: {@link MemoryRecipientSealer}
|
|
7895
|
+
* decrypts it with a local RSA private key; `@noy-db/at-aws-kms` hands it to
|
|
7896
|
+
* KMS `Decrypt`. After unwrapping, pass the CEK + `iv` + `ct` to
|
|
7897
|
+
* {@link aesGcmOpen}.
|
|
7898
|
+
*
|
|
7899
|
+
* @public — re-exported from the hub barrel for external sealer packages.
|
|
7900
|
+
*/
|
|
7901
|
+
declare function parseRsaOaepTlv(bytes: Uint8Array): {
|
|
7902
|
+
wrapped: Uint8Array;
|
|
7903
|
+
iv: Uint8Array;
|
|
7904
|
+
ct: Uint8Array;
|
|
7905
|
+
};
|
|
7906
|
+
/**
|
|
7907
|
+
* AES-GCM-decrypt the `ct` segment of a {@link parseRsaOaepTlv} result under
|
|
7908
|
+
* the unwrapped 32-byte CEK and its `iv`. Throws on a bad tag (tamper) — the
|
|
7909
|
+
* same authenticated-decryption guarantee the TLV relies on.
|
|
7910
|
+
*
|
|
7911
|
+
* @public — re-exported from the hub barrel for external sealer packages.
|
|
7912
|
+
*/
|
|
7913
|
+
declare function aesGcmOpen(cekBytes: Uint8Array, iv: Uint8Array, ct: Uint8Array): Promise<Uint8Array>;
|
|
7914
|
+
/**
|
|
7915
|
+
* Reference implementation of `RecipientSealer` + `SealingKeyProvider`.
|
|
7916
|
+
* Uses WebCrypto RSA-OAEP-SHA256 (2048-bit) to wrap a fresh 32-byte
|
|
7917
|
+
* AES-GCM CEK, AES-GCM-encrypts plaintext under it, and packs the
|
|
7918
|
+
* result into a self-describing TLV:
|
|
7919
|
+
*
|
|
7920
|
+
* byte 0 : version (0x01)
|
|
7921
|
+
* bytes 1..256 : RSA-OAEP-wrapped CEK (fixed 256 bytes at RSA-2048)
|
|
7922
|
+
* bytes 257..268: AES-GCM IV (12 bytes)
|
|
7923
|
+
* bytes 269.. : AES-GCM ciphertext ‖ 16-byte tag
|
|
7924
|
+
*
|
|
7925
|
+
* Implements BOTH interfaces. `seal(plaintext)` (self-target) is just
|
|
7926
|
+
* `sealForRecipient(plaintext, this own hint)` — same TLV. Convenient
|
|
7927
|
+
* for tests where one provider plays both ends. Real cloud providers
|
|
7928
|
+
* (`at-aws-kms`, etc.) will pick their own internal layouts; the only
|
|
7929
|
+
* contract is round-trip identity.
|
|
7930
|
+
*
|
|
7931
|
+
* SAFE for production within its scope — the cryptography is real
|
|
7932
|
+
* (RSA-OAEP + AES-GCM via WebCrypto), but the keypair lives in-process
|
|
7933
|
+
* and is regenerated on every construction. Not suitable as a managed
|
|
7934
|
+
* keychain; use it for tests and for shipping bundles where the
|
|
7935
|
+
* recipient instance lives in the same process as the sender (rare).
|
|
7936
|
+
*/
|
|
7937
|
+
declare class MemoryRecipientSealer implements SealingKeyProvider, RecipientSealer {
|
|
7938
|
+
readonly id: string;
|
|
7939
|
+
private readonly keypair;
|
|
7940
|
+
constructor(opts: {
|
|
7941
|
+
id: string;
|
|
7942
|
+
});
|
|
7943
|
+
publishRecipientHint(): Promise<RecipientHint>;
|
|
7944
|
+
sealForRecipient(plaintext: Uint8Array, hint: RecipientHint): Promise<Uint8Array>;
|
|
7945
|
+
seal(plaintext: Uint8Array): Promise<Uint8Array>;
|
|
7946
|
+
unseal(bytes: Uint8Array): Promise<Uint8Array>;
|
|
7947
|
+
}
|
|
7948
|
+
/** Reserved id for the managed-passphrase envelope under `_meta`. */
|
|
7949
|
+
declare const SEALED_PASSPHRASE_RECORD_ID: "sealed-passphrase";
|
|
7950
|
+
/** Plaintext payload stored inside the `_meta/sealed-passphrase` envelope. */
|
|
7951
|
+
interface SealedPassphrase {
|
|
7952
|
+
readonly _noydb_sealed: 1;
|
|
7953
|
+
readonly providerId: string;
|
|
7954
|
+
/** Sealed bytes. Base64-encoded on the wire; decoded on load. */
|
|
7955
|
+
readonly sealed: Uint8Array;
|
|
7956
|
+
}
|
|
7957
|
+
/**
|
|
7958
|
+
* Wire-format envelope persisted at `_meta/sealed-passphrase` for
|
|
7959
|
+
* managed-mode vaults. The provider produces raw sealed bytes via
|
|
7960
|
+
* {@link SealingKeyProvider.seal}; this wrapper carries the dispatch
|
|
7961
|
+
* metadata hub needs to pick the right provider on the unseal path.
|
|
7962
|
+
*
|
|
7963
|
+
* Stability boundary: once shipped, the wire format only grows by
|
|
7964
|
+
* adding optional fields. See the at-* sealing dimension foundation
|
|
7965
|
+
* doc, §11.9.1.
|
|
7966
|
+
*
|
|
7967
|
+
* v1 shape (this release): `{ v: 1, _noydb_sealed: 1, pid, payload }`.
|
|
7968
|
+
*
|
|
7969
|
+
* Legacy shape (earlier releases): `{ _noydb_sealed: 1, providerId, sealed }`
|
|
7970
|
+
* — accepted on read for backwards compatibility; never produced on
|
|
7971
|
+
* write going forward.
|
|
7972
|
+
*/
|
|
7973
|
+
interface SealedEnvelope {
|
|
7974
|
+
/** Envelope schema version. v1 is the current shape. */
|
|
7975
|
+
readonly v: 1;
|
|
7976
|
+
/** Magic marker for forensics + legacy-shape detection. */
|
|
7977
|
+
readonly _noydb_sealed: 1;
|
|
7978
|
+
/** Matches the producing provider's `.id`. Dispatch key on unseal. */
|
|
7979
|
+
readonly pid: string;
|
|
7980
|
+
/** Sealed bytes from the provider, base64-encoded on the wire. */
|
|
7981
|
+
readonly payload: string;
|
|
7982
|
+
}
|
|
7983
|
+
/**
|
|
7984
|
+
* Parse a `_meta/sealed-passphrase` `_data` JSON string into the
|
|
7985
|
+
* in-memory {@link SealedPassphrase} representation. Accepts both:
|
|
7986
|
+
*
|
|
7987
|
+
* 1. v1 wire format `{ v: 1, _noydb_sealed: 1, pid, payload }` —
|
|
7988
|
+
* the current shape.
|
|
7989
|
+
* 2. Legacy wire format `{ _noydb_sealed: 1, providerId, sealed }` —
|
|
7990
|
+
* read-only; never written
|
|
7991
|
+
* going forward.
|
|
7992
|
+
*
|
|
7993
|
+
* Returns `undefined` for any input that doesn't match either shape,
|
|
7994
|
+
* so callers can fall back to "no managed-mode envelope present."
|
|
7995
|
+
*
|
|
7996
|
+
* @internal — exported only for the migration safety-net test suite.
|
|
7997
|
+
*/
|
|
7998
|
+
declare function parseSealedEnvelope(raw: unknown): SealedPassphrase | undefined;
|
|
7999
|
+
declare function saveSealedPassphrase(store: NoydbStore, vault: string, payload: {
|
|
8000
|
+
readonly providerId: string;
|
|
8001
|
+
readonly sealed: Uint8Array;
|
|
8002
|
+
}): Promise<void>;
|
|
8003
|
+
declare function loadSealedPassphrase(store: NoydbStore, vault: string): Promise<SealedPassphrase | undefined>;
|
|
8004
|
+
|
|
7531
8005
|
/**
|
|
7532
8006
|
* `vault.exportBlobs()` — bulk blob extraction primitive.
|
|
7533
8007
|
*
|
|
@@ -8406,6 +8880,7 @@ declare class Vault {
|
|
|
8406
8880
|
private readonly periodsStrategy;
|
|
8407
8881
|
private readonly shadowStrategy;
|
|
8408
8882
|
private readonly historyStrategy;
|
|
8883
|
+
private readonly forgetStrategy;
|
|
8409
8884
|
private readonly i18nStrategy;
|
|
8410
8885
|
private readonly syncStrategy;
|
|
8411
8886
|
/**
|
|
@@ -8567,6 +9042,27 @@ declare class Vault {
|
|
|
8567
9042
|
* Populated by `collection()` when the `dictKeyFields` option is passed.
|
|
8568
9043
|
*/
|
|
8569
9044
|
private readonly dictKeyFieldRegistry;
|
|
9045
|
+
/**
|
|
9046
|
+
* Names of dictionaries backed by a `staticDict()` descriptor (#291).
|
|
9047
|
+
* A static dict skips the `dictKeyFieldRegistry` rename machinery, but the
|
|
9048
|
+
* vault must still *know* a name is static so `vault.dictionary(name)` can
|
|
9049
|
+
* refuse mutation (`StaticDictReadonlyError`). Populated at `collection()`
|
|
9050
|
+
* config time whenever a `StaticDictDescriptor` is seen.
|
|
9051
|
+
*/
|
|
9052
|
+
private readonly staticDictNames;
|
|
9053
|
+
/**
|
|
9054
|
+
* Static-dict descriptors keyed by dictionary name (#291). Backs the
|
|
9055
|
+
* read-path label resolver (resolve from the in-memory table) and the
|
|
9056
|
+
* query-seam `resolveDictSource` snapshot. Last writer wins when the same
|
|
9057
|
+
* name is registered by multiple collections (identical-across-vaults by
|
|
9058
|
+
* construction, so the tables match).
|
|
9059
|
+
*/
|
|
9060
|
+
private readonly staticByName;
|
|
9061
|
+
/**
|
|
9062
|
+
* Per-collection map of field name → StaticDictDescriptor (#291). Used by
|
|
9063
|
+
* `enforceStaticDictOnPut` to validate stored codes against `desc.keys`.
|
|
9064
|
+
*/
|
|
9065
|
+
private readonly staticDescriptorByField;
|
|
8570
9066
|
/**
|
|
8571
9067
|
* Registry of i18nText fields declared across all collections. Keyed
|
|
8572
9068
|
* by collection name → field name → I18nTextDescriptor. Used by
|
|
@@ -8626,6 +9122,7 @@ declare class Vault {
|
|
|
8626
9122
|
syncStrategy?: SyncStrategy | undefined;
|
|
8627
9123
|
guardStrategies?: ReadonlyArray<GuardStrategyHandleAny> | undefined;
|
|
8628
9124
|
numberingConfigs?: ReadonlyArray<DeferredNumberingConfig> | undefined;
|
|
9125
|
+
forgetStrategy?: ForgetStrategy | undefined;
|
|
8629
9126
|
});
|
|
8630
9127
|
/**
|
|
8631
9128
|
* Construct (or reconstruct) the lazy DEK resolver. Captures the
|
|
@@ -8675,8 +9172,8 @@ declare class Vault {
|
|
|
8675
9172
|
refs?: Record<string, RefDescriptor>;
|
|
8676
9173
|
/** — declare i18nText fields for locale-aware reads. */
|
|
8677
9174
|
i18nFields?: Record<string, I18nTextDescriptor>;
|
|
8678
|
-
/** — declare dictKey fields for label resolution on reads. */
|
|
8679
|
-
dictKeyFields?: Record<string, DictKeyDescriptor>;
|
|
9175
|
+
/** — declare dictKey / staticDict fields for label resolution on reads. */
|
|
9176
|
+
dictKeyFields?: Record<string, DictKeyDescriptor | StaticDictDescriptor>;
|
|
8680
9177
|
/** — declare money() fields for currency-safe decimal storage/formatting. */
|
|
8681
9178
|
moneyFields?: Record<string, MoneyDescriptor>;
|
|
8682
9179
|
/** — declare computed scalar fields, evaluated on write (schema-owned). */
|
|
@@ -8693,6 +9190,14 @@ declare class Vault {
|
|
|
8693
9190
|
deterministicFields?: readonly string[];
|
|
8694
9191
|
/** — explicit ack that deterministic encryption leaks equality. */
|
|
8695
9192
|
acknowledgeDeterministicRisk?: boolean;
|
|
9193
|
+
/**
|
|
9194
|
+
* — per-record content-encryption keys. When `true`, every record
|
|
9195
|
+
* body is encrypted under a fresh per-record CEK wrapped under the
|
|
9196
|
+
* collection DEK (`_cek`), stable across versions. Foundation for
|
|
9197
|
+
* per-record erasure (#304) / record-scoped sealing (#306). Off by
|
|
9198
|
+
* default; non-adopting collections take the legacy path unchanged.
|
|
9199
|
+
*/
|
|
9200
|
+
perRecordKeys?: boolean;
|
|
8696
9201
|
/**
|
|
8697
9202
|
* declarative blob retention / TTL policy per slot
|
|
8698
9203
|
* name. Values are `{ retainDays?, evictWhen? }`. Evaluated only
|
|
@@ -8779,6 +9284,14 @@ declare class Vault {
|
|
|
8779
9284
|
* `MissingTranslationError` when a required translation is absent.
|
|
8780
9285
|
*/
|
|
8781
9286
|
enforceI18nOnPut(collectionName: string, record: unknown): void;
|
|
9287
|
+
/**
|
|
9288
|
+
* Validate staticDict codes on a `put()` (#291). For each `staticDict()`
|
|
9289
|
+
* field, every stored code must be a declared key of the descriptor's
|
|
9290
|
+
* table, else `UnknownDictCodeError`. Opt out per descriptor with
|
|
9291
|
+
* `{ validateCodes: false }`. Supports scalar, dotted, and `[].`-wildcard
|
|
9292
|
+
* field paths via `getAtPath` (same path support as i18n validation).
|
|
9293
|
+
*/
|
|
9294
|
+
enforceStaticDictOnPut(collectionName: string, record: unknown): void;
|
|
8782
9295
|
/**
|
|
8783
9296
|
* Apply locale resolution to a record for the given collection.
|
|
8784
9297
|
*
|
|
@@ -8961,7 +9474,7 @@ declare class Vault {
|
|
|
8961
9474
|
* const cur = await vault.sequence('invoice-2026').peek() // current value, no allocation
|
|
8962
9475
|
* ```
|
|
8963
9476
|
*/
|
|
8964
|
-
sequence(
|
|
9477
|
+
sequence(series: string, opts?: SequenceOptions): SequenceHandle;
|
|
8965
9478
|
/** @internal — lazily build the deferred-numbering engine with a cache-coherent stamp. */
|
|
8966
9479
|
private deferred;
|
|
8967
9480
|
/**
|
|
@@ -9119,6 +9632,102 @@ declare class Vault {
|
|
|
9119
9632
|
* throws on null; this one stays silent so the off-path no-ops.
|
|
9120
9633
|
*/
|
|
9121
9634
|
private getLedgerOrNull;
|
|
9635
|
+
/** @internal — add a subject→record ref to the encrypted subject index. */
|
|
9636
|
+
_addSubjectRef(subjectId: string, ref: SubjectRef): Promise<void>;
|
|
9637
|
+
/** @internal — drop a subject→record ref from the encrypted subject index. */
|
|
9638
|
+
_removeSubjectRef(subjectId: string, ref: SubjectRef): Promise<void>;
|
|
9639
|
+
/**
|
|
9640
|
+
* Rebuild the encrypted subject index from canonical records. The recovery
|
|
9641
|
+
* path for the documented read-modify-write race (RISK #3). Returns the
|
|
9642
|
+
* number of distinct subjects re-indexed.
|
|
9643
|
+
*/
|
|
9644
|
+
rebuildSubjectIndex(): Promise<number>;
|
|
9645
|
+
/**
|
|
9646
|
+
* GDPR crypto-shred of a data subject (#304). Consults the encrypted subject
|
|
9647
|
+
* index and, per matching record:
|
|
9648
|
+
* - rewrites the LIVE envelope to a tombstone (drops `_iv`/`_data`/`_cek`/`_det`),
|
|
9649
|
+
* - tombstones every `_history` version of the record,
|
|
9650
|
+
* so the body and all prior versions become permanently undecryptable while
|
|
9651
|
+
* the collection DEK and every OTHER record stay intact. Then appends ONE
|
|
9652
|
+
* `op:'forget'` ledger entry whose `payloadHash` is `sha256Hex(subjectId)` —
|
|
9653
|
+
* the chain still `verify()`s, PROVING the subject existed and was erased
|
|
9654
|
+
* without retaining any plaintext.
|
|
9655
|
+
*
|
|
9656
|
+
* Reports — but does not silently swallow — two completeness gaps:
|
|
9657
|
+
* - `unmigratedRecords`: a record whose body was NOT yet migrated to a
|
|
9658
|
+
* per-record CEK (legacy body still under the shared collection DEK). It
|
|
9659
|
+
* is still tombstoned, but its pre-shred ciphertext (if leaked to a
|
|
9660
|
+
* backup before migration) stays decryptable. Migrate, then re-forget.
|
|
9661
|
+
* - `blobResidueCollections`: a shredded record still has blob attachments,
|
|
9662
|
+
* which are keyed off a separate `_blob` DEK and are out of scope here.
|
|
9663
|
+
*
|
|
9664
|
+
* @throws ForgetStrategyNotConfiguredError when no `withForgetCascade` was set.
|
|
9665
|
+
*/
|
|
9666
|
+
forget(subjectId: string): Promise<ForgetResult>;
|
|
9667
|
+
/**
|
|
9668
|
+
* Seal ONE record's content-encryption key (CEK) to an `at-*` host so that
|
|
9669
|
+
* host — and only that host — can decrypt exactly that record, with no
|
|
9670
|
+
* access to the vault DEK and no ability to read any other record.
|
|
9671
|
+
*
|
|
9672
|
+
* The grantor (this caller, who holds the collection DEK) reads the record's
|
|
9673
|
+
* live `_cek`, unwraps it under the collection DEK, exports the raw CEK
|
|
9674
|
+
* bytes, builds a {@link SealedCekBinding} `{collection, id, cek, expiresAt}`,
|
|
9675
|
+
* seals that binding for the recipient host via the host's published hint,
|
|
9676
|
+
* and persists a thin {@link SealedCekDeliveryEnvelope} at
|
|
9677
|
+
* `_sealed_cek/<collection>/<id>/<pid>`. The binding (not the delivery
|
|
9678
|
+
* envelope) is the security boundary: the host re-verifies `{collection, id}`
|
|
9679
|
+
* and `expiresAt` from inside the sealed payload.
|
|
9680
|
+
*
|
|
9681
|
+
* Only works on a `perRecordKeys` record — a legacy record has no `_cek` to
|
|
9682
|
+
* seal (its body is under the shared collection DEK, which is never exposed
|
|
9683
|
+
* by sealing) → {@link RecordCekNotFoundError}.
|
|
9684
|
+
*
|
|
9685
|
+
* @param collection Collection holding the record.
|
|
9686
|
+
* @param id Record id.
|
|
9687
|
+
* @param hostSealer The recipient host's {@link RecipientSealer}.
|
|
9688
|
+
* @param opts.expiresAt REQUIRED authoritative expiry (ISO 8601), sealed into
|
|
9689
|
+
* the binding the host verifies.
|
|
9690
|
+
* @returns `{ pid, envelopeKey }` — the host provider id and the
|
|
9691
|
+
* `<collection>/<id>/<pid>` key the delivery envelope was written under.
|
|
9692
|
+
*/
|
|
9693
|
+
sealRecordToHost(collection: string, id: string, hostSealer: RecipientSealer, opts: {
|
|
9694
|
+
expiresAt: string;
|
|
9695
|
+
}): Promise<{
|
|
9696
|
+
pid: string;
|
|
9697
|
+
envelopeKey: string;
|
|
9698
|
+
}>;
|
|
9699
|
+
/**
|
|
9700
|
+
* Revoke a single sealed-CEK delivery envelope by deleting it from the store.
|
|
9701
|
+
* A soft revocation: it removes the host's copy of the sealed CEK, but a host
|
|
9702
|
+
* that already fetched the envelope keeps whatever it cached. For a hard
|
|
9703
|
+
* revocation that makes the live record undecryptable to every prior grant,
|
|
9704
|
+
* use {@link rotateRecordCek}.
|
|
9705
|
+
*/
|
|
9706
|
+
revokeSealedRecord(collection: string, id: string, pid: string): Promise<void>;
|
|
9707
|
+
/**
|
|
9708
|
+
* HARD-rotate a record's CEK: decrypt the live body under the old CEK,
|
|
9709
|
+
* re-encrypt it under a freshly-minted CEK, write the new live envelope, evict
|
|
9710
|
+
* the in-memory caches, and delete EVERY sealed-CEK delivery envelope for the
|
|
9711
|
+
* record. After this, any host holding a previously-sealed CEK can still
|
|
9712
|
+
* decrypt PRE-rotation history versions (they keep their old `_cek`) but NOT
|
|
9713
|
+
* the rotated live record (its body is under the new CEK → the old CEK fails
|
|
9714
|
+
* the AES-GCM auth tag → `TamperedError`). That asymmetry IS the revocation:
|
|
9715
|
+
* old grants lose the live record.
|
|
9716
|
+
*
|
|
9717
|
+
* Administrative path — bypasses `Collection.put` deliberately (no guards, no
|
|
9718
|
+
* history snapshot, no materialized-view refresh): rotation is a key-rotation
|
|
9719
|
+
* operation, not a business write, and must not version-bump history (which
|
|
9720
|
+
* would re-encrypt the prior version under the NEW CEK and defeat the point).
|
|
9721
|
+
*
|
|
9722
|
+
* @throws {@link RecordCekNotFoundError} if the record is missing or has no `_cek`.
|
|
9723
|
+
*/
|
|
9724
|
+
rotateRecordCek(collection: string, id: string): Promise<void>;
|
|
9725
|
+
/**
|
|
9726
|
+
* Build the {@link SealingContext} the record-keys grantor functions need:
|
|
9727
|
+
* the vault-bound adapter, DEK resolver, actor, and the dual-cache eviction
|
|
9728
|
+
* `rotateRecordCek` performs (per-record CEK cache + decrypted-record cache).
|
|
9729
|
+
*/
|
|
9730
|
+
private sealingContext;
|
|
9122
9731
|
/**
|
|
9123
9732
|
* @internal — called by `Noydb.openVault` after construction.
|
|
9124
9733
|
* Dynamic-imports `GuardRegistry` + `ReadOnlyVaultFacade` and seeds
|
|
@@ -10111,6 +10720,25 @@ declare class Collection<T> {
|
|
|
10111
10720
|
* is inactive for this collection; a frozen `Set` otherwise.
|
|
10112
10721
|
*/
|
|
10113
10722
|
private readonly deterministicFields;
|
|
10723
|
+
/**
|
|
10724
|
+
* Per-record CEK opt-in (`perRecordKeys: true`). When set, writes mint /
|
|
10725
|
+
* reuse a per-record content-encryption key and stamp `_cek` on the
|
|
10726
|
+
* envelope (see {@link EncryptedEnvelope._cek}). OFF by default — a
|
|
10727
|
+
* non-adopting collection takes the byte-identical legacy path. The READ
|
|
10728
|
+
* path does not consult this flag: `_cek` presence on the envelope is the
|
|
10729
|
+
* format discriminant, so a mixed vault (and a recipient that never set the
|
|
10730
|
+
* flag) still decrypts CEK records.
|
|
10731
|
+
*/
|
|
10732
|
+
private readonly perRecordCek;
|
|
10733
|
+
/**
|
|
10734
|
+
* Session-scoped `(id) → CEK` cache for this collection. Lets updates
|
|
10735
|
+
* reuse a record's stable CEK and lets repeated reads skip the AES-KW
|
|
10736
|
+
* unwrap. Bounded by LRU; never persisted. Dropped when the owning
|
|
10737
|
+
* collection instance is discarded — `vault.load()` clears the
|
|
10738
|
+
* collectionCache, so a keyring refresh drops every CEK alongside the
|
|
10739
|
+
* DEK cache. `null` unless `perRecordCek` is set.
|
|
10740
|
+
*/
|
|
10741
|
+
private readonly cekCache;
|
|
10114
10742
|
/**
|
|
10115
10743
|
* declared tiers for this collection. `null` when
|
|
10116
10744
|
* tier-aware methods are disabled. Tier 0 is implicit and never
|
|
@@ -10331,7 +10959,7 @@ declare class Collection<T> {
|
|
|
10331
10959
|
/** — i18nText field descriptors for locale-aware reads. */
|
|
10332
10960
|
i18nFields?: Record<string, I18nTextDescriptor> | undefined;
|
|
10333
10961
|
/** — dictKey field descriptors for label resolution on reads. */
|
|
10334
|
-
dictKeyFields?: Record<string, DictKeyDescriptor> | undefined;
|
|
10962
|
+
dictKeyFields?: Record<string, DictKeyDescriptor | StaticDictDescriptor> | undefined;
|
|
10335
10963
|
moneyFields?: Record<string, MoneyDescriptor> | undefined;
|
|
10336
10964
|
computed?: ComputedFields | undefined;
|
|
10337
10965
|
/**
|
|
@@ -10414,6 +11042,15 @@ declare class Collection<T> {
|
|
|
10414
11042
|
* any deterministic field is declared. Any other value throws.
|
|
10415
11043
|
*/
|
|
10416
11044
|
acknowledgeDeterministicRisk?: boolean | undefined;
|
|
11045
|
+
/**
|
|
11046
|
+
* Per-record content-encryption keys. When `true`, every record body
|
|
11047
|
+
* (and every history version of it) is encrypted under a fresh
|
|
11048
|
+
* per-record CEK, AES-KW-wrapped under the collection DEK and stored
|
|
11049
|
+
* on the envelope's `_cek`. Off by default. Foundation for per-record
|
|
11050
|
+
* erasure (#304) and record-scoped sealing (#306). `_det` slots stay
|
|
11051
|
+
* keyed to the collection DEK regardless.
|
|
11052
|
+
*/
|
|
11053
|
+
perRecordKeys?: boolean | undefined;
|
|
10417
11054
|
/**
|
|
10418
11055
|
* declared tiers this collection supports. An
|
|
10419
11056
|
* undefined or empty list disables the hierarchical-tier surface
|
|
@@ -10633,6 +11270,37 @@ declare class Collection<T> {
|
|
|
10633
11270
|
*/
|
|
10634
11271
|
_internalDelete(id: string, txCtx?: TxContext | null): Promise<void>;
|
|
10635
11272
|
private _doDelete;
|
|
11273
|
+
/**
|
|
11274
|
+
* @internal — GDPR crypto-shred a LIVE record to a tombstone (#304).
|
|
11275
|
+
*
|
|
11276
|
+
* Rewrites the on-disk envelope to `{ _noydb, _v, _ts, _by, _iv:'', _data:'' }`,
|
|
11277
|
+
* dropping `_iv`/`_data`/`_cek`/`_det`. The wrapped per-record CEK is gone, so
|
|
11278
|
+
* the body — and (via {@link tombstoneHistory}) every history version under
|
|
11279
|
+
* the same CEK — is permanently undecryptable; the collection DEK and every
|
|
11280
|
+
* other record are untouched. `_det` is stripped too, so `findByDet` no
|
|
11281
|
+
* longer matches the shredded record (avoiding a post-shred TamperedError).
|
|
11282
|
+
*
|
|
11283
|
+
* Unlike `delete()`/`_internalDelete`, this:
|
|
11284
|
+
* - does NOT fire onDelete guards / MV / derivation dispatch (a shred is an
|
|
11285
|
+
* erasure, not a domain delete — re-running those would be wrong),
|
|
11286
|
+
* - does NOT append a per-record ledger entry (`vault.forget()` appends a
|
|
11287
|
+
* single `op:'forget'` summary for the whole subject),
|
|
11288
|
+
* - keeps the record KEY present (it's an overwrite, not an adapter delete)
|
|
11289
|
+
* so the version counter + "record existed" survive for audit.
|
|
11290
|
+
*
|
|
11291
|
+
* Idempotent: returns `null` when the record is absent or already a tombstone.
|
|
11292
|
+
* Otherwise returns `{ previousVersion }`. Invalidates the eager cache, the
|
|
11293
|
+
* lazy LRU, and the per-record CEK cache for this id.
|
|
11294
|
+
*/
|
|
11295
|
+
/**
|
|
11296
|
+
* @internal — decrypt an envelope to a plain record for subject-index
|
|
11297
|
+
* rebuild (#304). Returns `null` for a tombstone or unreadable envelope.
|
|
11298
|
+
* Skips schema validation — the rebuild only reads the subject field.
|
|
11299
|
+
*/
|
|
11300
|
+
_decodeEnvelope(envelope: EncryptedEnvelope, id: string): Promise<Record<string, unknown> | null>;
|
|
11301
|
+
_writeTombstone(id: string, actor: string): Promise<{
|
|
11302
|
+
previousVersion: number;
|
|
11303
|
+
} | null>;
|
|
10636
11304
|
/**
|
|
10637
11305
|
* Cascade deletes of array-shape derived rows when a source row is
|
|
10638
11306
|
* deleted. Reads each registered strategy's fanout sidecar
|
|
@@ -10934,6 +11602,16 @@ declare class Collection<T> {
|
|
|
10934
11602
|
* the cache entry (record still present) or deletes it (record was
|
|
10935
11603
|
* gone before the tx and the revert deleted it again).
|
|
10936
11604
|
*/
|
|
11605
|
+
/**
|
|
11606
|
+
* @internal — evict ONLY the per-record CEK cache entry for `id`. Used by
|
|
11607
|
+
* `vault.rotateRecordCek()`: after a hard CEK rotation the cached unwrapped
|
|
11608
|
+
* CEK is stale (it would decrypt the pre-rotation body and fail GCM auth on
|
|
11609
|
+
* the post-rotation body). Eviction must be synchronous with the live-envelope
|
|
11610
|
+
* rewrite so no concurrent read observes the old CEK. Paired with
|
|
11611
|
+
* {@link _invalidateCacheEntry} (which refreshes the decrypted-record cache).
|
|
11612
|
+
* No-op when the collection is not `perRecordKeys`.
|
|
11613
|
+
*/
|
|
11614
|
+
_invalidateCekCacheEntry(id: string): void;
|
|
10937
11615
|
_invalidateCacheEntry(id: string): Promise<void>;
|
|
10938
11616
|
/**
|
|
10939
11617
|
* Apply a peer tab's committed write to THIS tab's in-memory view:
|
|
@@ -11145,7 +11823,22 @@ declare class Collection<T> {
|
|
|
11145
11823
|
* `query()` there. Throws if no index is declared, because a lazy
|
|
11146
11824
|
* query with no index would need to enumerate the whole collection.
|
|
11147
11825
|
*/
|
|
11148
|
-
lazyQuery(): LazyQuery<T>;
|
|
11826
|
+
lazyQuery(): LazyQuery<T>;
|
|
11827
|
+
/**
|
|
11828
|
+
* Resolve the stable CEK for a record on the WRITE path — see
|
|
11829
|
+
* {@link resolveStableCek}. Thin delegate that supplies the collection's
|
|
11830
|
+
* CEK cache, live-envelope reader, and DEK resolver.
|
|
11831
|
+
*/
|
|
11832
|
+
private resolveRecordCek;
|
|
11833
|
+
/**
|
|
11834
|
+
* Encrypt a JSON body into an envelope.
|
|
11835
|
+
*
|
|
11836
|
+
* When `cek` is supplied (per-record CEK collections), the body is
|
|
11837
|
+
* encrypted under the CEK and the CEK is AES-KW-wrapped under the
|
|
11838
|
+
* collection DEK and stamped on `_cek`. When `cek` is omitted, the legacy
|
|
11839
|
+
* path encrypts the body directly under the collection DEK — byte-identical
|
|
11840
|
+
* to pre-CEK behaviour, so non-adopting collections pay nothing.
|
|
11841
|
+
*/
|
|
11149
11842
|
private encryptJsonString;
|
|
11150
11843
|
private encryptRecord;
|
|
11151
11844
|
/**
|
|
@@ -11227,7 +11920,20 @@ declare class Collection<T> {
|
|
|
11227
11920
|
demote(id: string, toTier: number): Promise<void>;
|
|
11228
11921
|
private isElevatorOrOwner;
|
|
11229
11922
|
private emitCrossTierEvent;
|
|
11230
|
-
/**
|
|
11923
|
+
/**
|
|
11924
|
+
* Low-level: decrypt an envelope and return the raw JSON string.
|
|
11925
|
+
*
|
|
11926
|
+
* `_cek` presence is the format discriminant (NOT `this.perRecordCek`),
|
|
11927
|
+
* so a mixed vault — and a recipient that never opted into
|
|
11928
|
+
* `perRecordKeys` — decrypts both legacy and CEK records:
|
|
11929
|
+
* - `_cek` present → unwrap the CEK under the collection DEK, decrypt the
|
|
11930
|
+
* body under the CEK (cache the unwrapped CEK so repeated reads skip it).
|
|
11931
|
+
* - `_cek` absent → legacy path, body decrypts directly under the
|
|
11932
|
+
* collection DEK.
|
|
11933
|
+
*
|
|
11934
|
+
* The optional `id` lets reads populate the CEK cache; it is omitted by
|
|
11935
|
+
* callers (history, conflict merge) that have only the envelope.
|
|
11936
|
+
*/
|
|
11231
11937
|
private decryptJsonString;
|
|
11232
11938
|
/**
|
|
11233
11939
|
* Decrypt an envelope into a record of type `T`.
|
|
@@ -11373,7 +12079,7 @@ interface ShadowStrategy {
|
|
|
11373
12079
|
* @internal
|
|
11374
12080
|
*/
|
|
11375
12081
|
interface TxStrategy {
|
|
11376
|
-
runTransaction<T>(db: Noydb, fn: (tx: TxContext) => Promise<T> | T, options?: AmendmentTxOptions): Promise<T>;
|
|
12082
|
+
runTransaction<T>(db: Noydb, fn: (tx: TxContext) => Promise<T> | T, options?: AmendmentTxOptions, txInvariants?: ReadonlyArray<TransactionInvariant>): Promise<T>;
|
|
11377
12083
|
runDryRun(db: Noydb, fn: (tx: TxContext) => unknown): Promise<DryRunResult>;
|
|
11378
12084
|
}
|
|
11379
12085
|
|
|
@@ -11504,244 +12210,6 @@ interface SessionStrategy {
|
|
|
11504
12210
|
revokeAllSessions(): void;
|
|
11505
12211
|
}
|
|
11506
12212
|
|
|
11507
|
-
/**
|
|
11508
|
-
* Managed-passphrase mode — rubber-hose-resistant vaults.
|
|
11509
|
-
*
|
|
11510
|
-
* A vault mode where the passphrase is machine-generated and never
|
|
11511
|
-
* exposed to the user, sealed under a developer-provided
|
|
11512
|
-
* {@link SealingKeyProvider} (macOS Keychain, Windows Credential
|
|
11513
|
-
* Manager, libsecret, AWS KMS, …). The user has no secret to give
|
|
11514
|
-
* up to coercion — they can't reveal what they don't know.
|
|
11515
|
-
*
|
|
11516
|
-
* ## Components in this file
|
|
11517
|
-
*
|
|
11518
|
-
* - {@link SealingKeyProvider} — the interface concrete providers
|
|
11519
|
-
* implement. Provider implementations live OUTSIDE hub (per-
|
|
11520
|
-
* platform packages).
|
|
11521
|
-
* - {@link MemorySealingKeyProvider} — in-memory test provider; uses
|
|
11522
|
-
* a deterministic per-instance "key" so two providers with
|
|
11523
|
-
* different ids cannot unseal each other's outputs.
|
|
11524
|
-
* - {@link RecipientHint} — public material a sender uses to seal
|
|
11525
|
-
* plaintext for a specific recipient; published by
|
|
11526
|
-
* {@link RecipientSealer.publishRecipientHint} and transported
|
|
11527
|
-
* out-of-band to the sender before bundle writes.
|
|
11528
|
-
* - {@link RecipientSealer} — interface for asymmetric/granted
|
|
11529
|
-
* providers that support recipient-target sealing (RSA-OAEP,
|
|
11530
|
-
* cloud-KMS asymmetric, etc.); distinct from self-only
|
|
11531
|
-
* {@link SealingKeyProvider} (macOS Keychain, WebAuthn-PRF).
|
|
11532
|
-
* - {@link MemoryRecipientSealer} — in-process reference
|
|
11533
|
-
* implementation of both `RecipientSealer` and
|
|
11534
|
-
* `SealingKeyProvider` using real WebCrypto RSA-OAEP + AES-GCM;
|
|
11535
|
-
* safe for tests and same-process sender/recipient scenarios.
|
|
11536
|
-
* - {@link loadSealedPassphrase} / {@link saveSealedPassphrase} —
|
|
11537
|
-
* plaintext envelope storage at `_meta/sealed-passphrase`.
|
|
11538
|
-
* Mirrors the `_meta/handle` and `_meta/public-envelope` AES-
|
|
11539
|
-
* GCM-bypassed patterns. The sealing layer (provider's job)
|
|
11540
|
-
* is the security boundary; hub doesn't have a key to encrypt
|
|
11541
|
-
* with at this layer — that's the whole point of the design.
|
|
11542
|
-
* - {@link resolveManagedSecret} — orchestrates the "generate +
|
|
11543
|
-
* seal + persist on first open; unseal on reopen" flow.
|
|
11544
|
-
* Returns the plaintext passphrase string that the rest of the
|
|
11545
|
-
* `createNoydb` keyring path consumes.
|
|
11546
|
-
*
|
|
11547
|
-
* Deferred to follow-ups:
|
|
11548
|
-
* - Block `rotate-passphrase` policy gate under managed mode.
|
|
11549
|
-
* - Mandatory strong-recovery enforcement.
|
|
11550
|
-
* - Recovery flow under managed mode (generates fresh sealed phrase).
|
|
11551
|
-
*
|
|
11552
|
-
* @see docs/subsystems/session-tiers.md → Managed-passphrase mode
|
|
11553
|
-
*
|
|
11554
|
-
* @module
|
|
11555
|
-
*/
|
|
11556
|
-
|
|
11557
|
-
/**
|
|
11558
|
-
* The contract concrete providers (per-platform key stores) implement
|
|
11559
|
-
* to seal and unseal a hub-generated random passphrase. The plaintext
|
|
11560
|
-
* passphrase NEVER leaves hub-controlled memory in unsealed form —
|
|
11561
|
-
* the provider receives the bytes, returns opaque sealed bytes, and
|
|
11562
|
-
* later reverses the operation. Hub treats the sealed bytes as
|
|
11563
|
-
* fully opaque.
|
|
11564
|
-
*
|
|
11565
|
-
* Implementations live OUTSIDE `@noy-db/hub` (separate packages
|
|
11566
|
-
* per the issue's "Concrete providers (live outside hub)" note):
|
|
11567
|
-
*
|
|
11568
|
-
* | Platform | Package (TBD) | Backing |
|
|
11569
|
-
* |---|---|---|
|
|
11570
|
-
* | macOS | `@noy-db/seal-macos-keychain` | Security.framework |
|
|
11571
|
-
* | Windows | `@noy-db/seal-wincred` | Credential Manager |
|
|
11572
|
-
* | Linux | `@noy-db/seal-libsecret` | libsecret / secret-service |
|
|
11573
|
-
* | Cloud / server | `@noy-db/seal-aws-kms` | AWS KMS Decrypt |
|
|
11574
|
-
*/
|
|
11575
|
-
interface SealingKeyProvider {
|
|
11576
|
-
/**
|
|
11577
|
-
* Non-sensitive identifier disclosed in the persisted envelope.
|
|
11578
|
-
* Surfaced to consumers via `loadSealedPassphrase().providerId` so
|
|
11579
|
-
* a vault opened with the wrong provider class can detect the
|
|
11580
|
-
* mismatch and surface a clear error. NOT secret — fine to log.
|
|
11581
|
-
*
|
|
11582
|
-
* Suggested format: `<family>:<scope>` — e.g. `macos-keychain:com.acme.app`,
|
|
11583
|
-
* `aws-kms:arn:aws:kms:us-east-1:123:key/abc`. The hub never
|
|
11584
|
-
* parses this; it's purely audit metadata.
|
|
11585
|
-
*/
|
|
11586
|
-
readonly id: string;
|
|
11587
|
-
/** Seal raw passphrase bytes. Output bytes are opaque to hub. */
|
|
11588
|
-
seal(passphrase: Uint8Array): Promise<Uint8Array>;
|
|
11589
|
-
/**
|
|
11590
|
-
* Reverse {@link seal}. MUST throw on tamper, wrong-provider, or
|
|
11591
|
-
* any other failure — hub treats a thrown error as "this provider
|
|
11592
|
-
* cannot unlock this vault" and surfaces it to the caller.
|
|
11593
|
-
*/
|
|
11594
|
-
unseal(sealed: Uint8Array): Promise<Uint8Array>;
|
|
11595
|
-
}
|
|
11596
|
-
/**
|
|
11597
|
-
* In-memory test provider. NOT secure — uses a deterministic
|
|
11598
|
-
* per-instance "key" (16-byte SHA-256 of `id`) XOR'd over the
|
|
11599
|
-
* passphrase plus a 4-byte provider-id fingerprint prefix. The XOR is
|
|
11600
|
-
* sufficient to make different `id` values produce mutually-unsealable
|
|
11601
|
-
* outputs (the contract tests for that), but offers ZERO real
|
|
11602
|
-
* confidentiality — never use outside tests.
|
|
11603
|
-
*
|
|
11604
|
-
* Replace with a real platform provider in production.
|
|
11605
|
-
*/
|
|
11606
|
-
declare class MemorySealingKeyProvider implements SealingKeyProvider {
|
|
11607
|
-
readonly id: string;
|
|
11608
|
-
private readonly fingerprint;
|
|
11609
|
-
private readonly keyBytes;
|
|
11610
|
-
constructor(opts: {
|
|
11611
|
-
id: string;
|
|
11612
|
-
});
|
|
11613
|
-
seal(passphrase: Uint8Array): Promise<Uint8Array>;
|
|
11614
|
-
unseal(sealed: Uint8Array): Promise<Uint8Array>;
|
|
11615
|
-
}
|
|
11616
|
-
/**
|
|
11617
|
-
* Public material a sender uses to seal-for-this-recipient. Published by
|
|
11618
|
-
* a recipient's RecipientSealer; transported to the sender out-of-band
|
|
11619
|
-
* (email, S3, in-app message). The sender obtains the hint, supplies it
|
|
11620
|
-
* to writeNoydbBundle's sealedCredentials.perUser[userId].hint, and the
|
|
11621
|
-
* hub seals each user's credential against it. Per foundation §11.4.
|
|
11622
|
-
*/
|
|
11623
|
-
type RecipientHint = {
|
|
11624
|
-
readonly v: 1;
|
|
11625
|
-
/** Recipient's provider id; matches the SealedAutoUnlockEntry.pid they'll unseal under. */
|
|
11626
|
-
readonly pid: string;
|
|
11627
|
-
/** Algorithm the sender uses to produce the seal. Slice 1 ships RSA-OAEP-SHA256 only. */
|
|
11628
|
-
readonly alg: 'rsa-oaep-sha256';
|
|
11629
|
-
/** Public material — alg-specific. For 'rsa-oaep-sha256': { publicKeyPem: string }. */
|
|
11630
|
-
readonly material: Readonly<Record<string, unknown>>;
|
|
11631
|
-
};
|
|
11632
|
-
/**
|
|
11633
|
-
* Handover-capable provider. Implemented additionally by asymmetric/granted
|
|
11634
|
-
* providers (cloud-KMS asymmetric, Azure RSA Key Vault, AWS KMS with grant).
|
|
11635
|
-
* Self-only providers (macOS Keychain, env-var, WebAuthn-PRF) do NOT
|
|
11636
|
-
* implement this — the §11.2 capability matrix lives in the type system.
|
|
11637
|
-
*
|
|
11638
|
-
* Per foundation §11.4. A function that requires recipient-target sealing
|
|
11639
|
-
* takes `RecipientSealer`, not `SealingKeyProvider` — the compiler rejects
|
|
11640
|
-
* passing a self-only provider at the spec site.
|
|
11641
|
-
*/
|
|
11642
|
-
interface RecipientSealer {
|
|
11643
|
-
readonly id: string;
|
|
11644
|
-
/** Produce hint material a sender uses to seal-for-this-recipient. */
|
|
11645
|
-
publishRecipientHint(): Promise<RecipientHint>;
|
|
11646
|
-
/**
|
|
11647
|
-
* Seal plaintext for the recipient described by `hint`. Returns opaque
|
|
11648
|
-
* bytes — same contract as `SealingKeyProvider.seal()`. The bundle
|
|
11649
|
-
* layer base64-encodes the bytes into `SealedAutoUnlockEntry.sealed`
|
|
11650
|
-
* without inspecting them.
|
|
11651
|
-
*/
|
|
11652
|
-
sealForRecipient(plaintext: Uint8Array, hint: RecipientHint): Promise<Uint8Array>;
|
|
11653
|
-
}
|
|
11654
|
-
/**
|
|
11655
|
-
* Reference implementation of `RecipientSealer` + `SealingKeyProvider`.
|
|
11656
|
-
* Uses WebCrypto RSA-OAEP-SHA256 (2048-bit) to wrap a fresh 32-byte
|
|
11657
|
-
* AES-GCM CEK, AES-GCM-encrypts plaintext under it, and packs the
|
|
11658
|
-
* result into a self-describing TLV:
|
|
11659
|
-
*
|
|
11660
|
-
* byte 0 : version (0x01)
|
|
11661
|
-
* bytes 1..256 : RSA-OAEP-wrapped CEK (fixed 256 bytes at RSA-2048)
|
|
11662
|
-
* bytes 257..268: AES-GCM IV (12 bytes)
|
|
11663
|
-
* bytes 269.. : AES-GCM ciphertext ‖ 16-byte tag
|
|
11664
|
-
*
|
|
11665
|
-
* Implements BOTH interfaces. `seal(plaintext)` (self-target) is just
|
|
11666
|
-
* `sealForRecipient(plaintext, this own hint)` — same TLV. Convenient
|
|
11667
|
-
* for tests where one provider plays both ends. Real cloud providers
|
|
11668
|
-
* (`at-aws-kms`, etc.) will pick their own internal layouts; the only
|
|
11669
|
-
* contract is round-trip identity.
|
|
11670
|
-
*
|
|
11671
|
-
* SAFE for production within its scope — the cryptography is real
|
|
11672
|
-
* (RSA-OAEP + AES-GCM via WebCrypto), but the keypair lives in-process
|
|
11673
|
-
* and is regenerated on every construction. Not suitable as a managed
|
|
11674
|
-
* keychain; use it for tests and for shipping bundles where the
|
|
11675
|
-
* recipient instance lives in the same process as the sender (rare).
|
|
11676
|
-
*/
|
|
11677
|
-
declare class MemoryRecipientSealer implements SealingKeyProvider, RecipientSealer {
|
|
11678
|
-
readonly id: string;
|
|
11679
|
-
private readonly keypair;
|
|
11680
|
-
constructor(opts: {
|
|
11681
|
-
id: string;
|
|
11682
|
-
});
|
|
11683
|
-
publishRecipientHint(): Promise<RecipientHint>;
|
|
11684
|
-
sealForRecipient(plaintext: Uint8Array, hint: RecipientHint): Promise<Uint8Array>;
|
|
11685
|
-
seal(plaintext: Uint8Array): Promise<Uint8Array>;
|
|
11686
|
-
unseal(bytes: Uint8Array): Promise<Uint8Array>;
|
|
11687
|
-
}
|
|
11688
|
-
/** Reserved id for the managed-passphrase envelope under `_meta`. */
|
|
11689
|
-
declare const SEALED_PASSPHRASE_RECORD_ID: "sealed-passphrase";
|
|
11690
|
-
/** Plaintext payload stored inside the `_meta/sealed-passphrase` envelope. */
|
|
11691
|
-
interface SealedPassphrase {
|
|
11692
|
-
readonly _noydb_sealed: 1;
|
|
11693
|
-
readonly providerId: string;
|
|
11694
|
-
/** Sealed bytes. Base64-encoded on the wire; decoded on load. */
|
|
11695
|
-
readonly sealed: Uint8Array;
|
|
11696
|
-
}
|
|
11697
|
-
/**
|
|
11698
|
-
* Wire-format envelope persisted at `_meta/sealed-passphrase` for
|
|
11699
|
-
* managed-mode vaults. The provider produces raw sealed bytes via
|
|
11700
|
-
* {@link SealingKeyProvider.seal}; this wrapper carries the dispatch
|
|
11701
|
-
* metadata hub needs to pick the right provider on the unseal path.
|
|
11702
|
-
*
|
|
11703
|
-
* Stability boundary: once shipped, the wire format only grows by
|
|
11704
|
-
* adding optional fields. See the at-* sealing dimension foundation
|
|
11705
|
-
* doc, §11.9.1.
|
|
11706
|
-
*
|
|
11707
|
-
* v1 shape (this release): `{ v: 1, _noydb_sealed: 1, pid, payload }`.
|
|
11708
|
-
*
|
|
11709
|
-
* Legacy shape (earlier releases): `{ _noydb_sealed: 1, providerId, sealed }`
|
|
11710
|
-
* — accepted on read for backwards compatibility; never produced on
|
|
11711
|
-
* write going forward.
|
|
11712
|
-
*/
|
|
11713
|
-
interface SealedEnvelope {
|
|
11714
|
-
/** Envelope schema version. v1 is the current shape. */
|
|
11715
|
-
readonly v: 1;
|
|
11716
|
-
/** Magic marker for forensics + legacy-shape detection. */
|
|
11717
|
-
readonly _noydb_sealed: 1;
|
|
11718
|
-
/** Matches the producing provider's `.id`. Dispatch key on unseal. */
|
|
11719
|
-
readonly pid: string;
|
|
11720
|
-
/** Sealed bytes from the provider, base64-encoded on the wire. */
|
|
11721
|
-
readonly payload: string;
|
|
11722
|
-
}
|
|
11723
|
-
/**
|
|
11724
|
-
* Parse a `_meta/sealed-passphrase` `_data` JSON string into the
|
|
11725
|
-
* in-memory {@link SealedPassphrase} representation. Accepts both:
|
|
11726
|
-
*
|
|
11727
|
-
* 1. v1 wire format `{ v: 1, _noydb_sealed: 1, pid, payload }` —
|
|
11728
|
-
* the current shape.
|
|
11729
|
-
* 2. Legacy wire format `{ _noydb_sealed: 1, providerId, sealed }` —
|
|
11730
|
-
* read-only; never written
|
|
11731
|
-
* going forward.
|
|
11732
|
-
*
|
|
11733
|
-
* Returns `undefined` for any input that doesn't match either shape,
|
|
11734
|
-
* so callers can fall back to "no managed-mode envelope present."
|
|
11735
|
-
*
|
|
11736
|
-
* @internal — exported only for the migration safety-net test suite.
|
|
11737
|
-
*/
|
|
11738
|
-
declare function parseSealedEnvelope(raw: unknown): SealedPassphrase | undefined;
|
|
11739
|
-
declare function saveSealedPassphrase(store: NoydbStore, vault: string, payload: {
|
|
11740
|
-
readonly providerId: string;
|
|
11741
|
-
readonly sealed: Uint8Array;
|
|
11742
|
-
}): Promise<void>;
|
|
11743
|
-
declare function loadSealedPassphrase(store: NoydbStore, vault: string): Promise<SealedPassphrase | undefined>;
|
|
11744
|
-
|
|
11745
12213
|
/**
|
|
11746
12214
|
* Core types — the {@link NoydbStore} interface, envelope format, roles, and
|
|
11747
12215
|
* all configuration shapes consumed by {@link createNoydb}.
|
|
@@ -11838,6 +12306,27 @@ interface EncryptedEnvelope {
|
|
|
11838
12306
|
* side channel.
|
|
11839
12307
|
*/
|
|
11840
12308
|
readonly _det?: Record<string, string>;
|
|
12309
|
+
/**
|
|
12310
|
+
* Per-record content-encryption key (CEK), base64 AES-KW-wrapped under
|
|
12311
|
+
* the collection (or tier) DEK. Present only on records written by a
|
|
12312
|
+
* collection opened with `perRecordKeys: true`. When present, the body
|
|
12313
|
+
* (`_iv`/`_data`) is encrypted under the unwrapped CEK rather than the
|
|
12314
|
+
* collection DEK directly.
|
|
12315
|
+
*
|
|
12316
|
+
* Presence is the format discriminant: `_cek` absent → legacy body
|
|
12317
|
+
* keyed off the collection DEK (read unchanged); `_cek` present →
|
|
12318
|
+
* unwrap under the collection DEK, then decrypt the body under the CEK.
|
|
12319
|
+
*
|
|
12320
|
+
* The CEK is stable across every version of a record (insert mints it;
|
|
12321
|
+
* updates and history snapshots reuse it), so all `_history` envelopes
|
|
12322
|
+
* for a record carry the same `_cek`. This is the foundation for
|
|
12323
|
+
* per-record erasure (#304) and record-scoped sealing (#306).
|
|
12324
|
+
*
|
|
12325
|
+
* `_det` slots are deliberately NOT keyed off the CEK — they remain
|
|
12326
|
+
* keyed to the collection DEK so blind-equality search keeps working
|
|
12327
|
+
* across records.
|
|
12328
|
+
*/
|
|
12329
|
+
readonly _cek?: string;
|
|
11841
12330
|
}
|
|
11842
12331
|
/**
|
|
11843
12332
|
* Placeholder returned by `getAtTier()` in `'ghost'` mode when a
|
|
@@ -13056,6 +13545,25 @@ interface BlobObject {
|
|
|
13056
13545
|
readonly createdAt: string;
|
|
13057
13546
|
/** Live reference count — slots + published versions pointing to this blob. */
|
|
13058
13547
|
readonly refCount: number;
|
|
13548
|
+
/**
|
|
13549
|
+
* Base64 AES-KW-wrapped per-blob **content CEK** (wrapped under the `_blob`
|
|
13550
|
+
* DEK). Present on erasable-collection blobs (`perRecordKeys`): the chunks
|
|
13551
|
+
* are encrypted under this content CEK rather than directly under the `_blob`
|
|
13552
|
+
* DEK, so deleting this BlobObject at `refCount → 0` crypto-shreds the chunks
|
|
13553
|
+
* (they become permanently undecryptable). Absent → legacy blob, chunks
|
|
13554
|
+
* decrypt directly under the `_blob` DEK (read unchanged). See
|
|
13555
|
+
* docs/superpowers/specs/2026-06-13-per-blob-cek-design.md.
|
|
13556
|
+
*/
|
|
13557
|
+
readonly _cek?: string;
|
|
13558
|
+
/**
|
|
13559
|
+
* Transient migration marker (#365 slice 3). Present only while a legacy
|
|
13560
|
+
* blob is being migrated to a content CEK: it holds the wrapped content CEK
|
|
13561
|
+
* BEFORE the chunks have been re-encrypted under it. Readers **ignore**
|
|
13562
|
+
* `_cekPending` (they key off `_cek`), so the blob stays readable under the
|
|
13563
|
+
* `_blob` DEK during migration AND the content CEK survives a crash → a
|
|
13564
|
+
* re-run resumes and promotes `_cekPending` → `_cek`. Never set on a settled blob.
|
|
13565
|
+
*/
|
|
13566
|
+
readonly _cekPending?: string;
|
|
13059
13567
|
/**
|
|
13060
13568
|
* Hint indicating which store holds the chunk data.
|
|
13061
13569
|
* Used by `routeStore` size-tiered routing: `'default'` for small blobs
|
|
@@ -13287,6 +13795,19 @@ interface NoydbOptions {
|
|
|
13287
13795
|
* @internal
|
|
13288
13796
|
*/
|
|
13289
13797
|
readonly historyStrategy?: HistoryStrategy;
|
|
13798
|
+
/**
|
|
13799
|
+
* GDPR right-to-erasure (#304). Pass `withForgetCascade({ subjects })`
|
|
13800
|
+
* from `@noy-db/hub/forget` to declare which collections carry erasable
|
|
13801
|
+
* subject data and the record field naming the data subject. Enables
|
|
13802
|
+
* `vault.forget(subjectId)` crypto-shred (rewrite-to-tombstone of the live
|
|
13803
|
+
* record + every history version → body permanently undecryptable, single
|
|
13804
|
+
* `op:'forget'` ledger entry, chain still verifies). Each declared
|
|
13805
|
+
* collection is forced to `perRecordKeys: true`. When omitted (the
|
|
13806
|
+
* `NO_FORGET` default), `vault.forget()` throws
|
|
13807
|
+
* `ForgetStrategyNotConfiguredError` and no subject-index write hooks run.
|
|
13808
|
+
* Requires `historyStrategy` (the ledger) for the erasure-proof entry.
|
|
13809
|
+
*/
|
|
13810
|
+
readonly forgetStrategy?: ForgetStrategy;
|
|
13290
13811
|
/**
|
|
13291
13812
|
* tree-shake seam — optional i18n strategy. Pass `withI18n()`
|
|
13292
13813
|
* from `@noy-db/hub/i18n` to enable `i18nText`/`dictKey` field
|
|
@@ -13642,4 +14163,4 @@ interface DeleteManyResult {
|
|
|
13642
14163
|
}>;
|
|
13643
14164
|
}
|
|
13644
14165
|
|
|
13645
|
-
export { type ExportBlobsAuditEntry as $, BLOB_COLLECTION as A, type BlobStrategy as B, BLOB_EVICTION_AUDIT_COLLECTION as C, DICT_COLLECTION_PREFIX as D, BLOB_INDEX_COLLECTION as E, BLOB_SLOTS_PREFIX as F, BLOB_VERSIONS_PREFIX as G, type BlobEvictionEntry as H, type I18nStrategy as I, type BlobFieldPolicy as J, type BlobFieldsConfig as K, type Layer as L, type BlobObject as M, type BlobPutOptions as N, type OnMissing as O, PolicyEnforcer as P, type BlobResponseOptions as Q, type ResolveI18nOptions as R, type ScriptWarning as S, BlobSet as T, type BlobStrategyOpenArgs as U, type CompactRunOptions as V, type CompactionContext as W, type CompactionResult as X, DEFAULT_CHUNK_SIZE as Y, EXPORT_AUDIT_COLLECTION as Z, ExportBlobsAbortedError as _, type DictEntry as a, type SyncStrategy as a$, type ExportBlobsHandle as a0, type ExportBlobsOptions as a1, type ExportedBlob as a2, type SlotInfo as a3, type SlotRecord as a4, type VersionRecord as a5, createExportBlobsHandle as a6, runCompaction as a7, type ConsentStrategy as a8, CONSENT_AUDIT_COLLECTION as a9, VaultFrame as aA, type NoydbBundleStore as aB, type RetentionPolicy as aC, type SnapshotPolicy as aD, type SnapshotStrategy as aE, type SnapshotMeta as aF, type SnapshotMode as aG, type TxStrategy as aH, type AmendmentTxOptions as aI, TxCollection as aJ, TxContext as aK, TxVault as aL, runTransaction as aM, type DerivationStrategy as aN, type DerivationContext as aO, type ArrayOutputSpec as aP, DerivationRegistry as aQ, type DerivationStrategyHandle as aR, type DerivedFromMeta as aS, type OutputSpec as aT, type RecordOutputSpec as aU, type MaterializedViewStrategy as aV, type MaterializedViewStrategyHandle as aW, type OverlayedViewStrategy as aX, Collection as aY, OverlayedViewRegistry as aZ, type OverlayedViewStrategyHandle as a_, type ConsentAuditEntry as aa, type ConsentAuditFilter as ab, type ConsentContext as ac, type ConsentOp as ad, loadConsentEntries as ae, writeConsentEntry as af, type PeriodsStrategy as ag, type CarryForwardContext as ah, type ClosePeriodOptions as ai, type OpenPeriodOptions as aj, PERIODS_COLLECTION as ak, type PeriodRecord as al, type ReadOnlyCollection as am, appendPeriodLedgerEntry as an, assertTsWritable as ao, chainAnchor as ap, loadPeriods as aq, validatePeriodName as ar, type GuardStrategy as as, type GuardChange as at, type GuardContext as au, GuardRegistry as av, type GuardStrategyHandle as aw, ReadOnlyVaultFacade as ax, type ShadowStrategy as ay, CollectionFrame as az, type DictKeyDescriptor as b, type CapturedBlueprint as b$, type Role as b0, type UnlockedKeyring as b1, type HistoryStrategy as b2, type NoydbStore as b3, type HistoryOptions as b4, type EncryptedEnvelope as b5, type PruneOptions as b6, type AppendInput as b7, type ChangeType as b8, CollectionInstant as b9, type RegisteredMV as bA, MaterializedViewRegistry as bB, type MaterializedFromMeta as bC, type MaterializedViewOutput as bD, type UnionSource as bE, type UserEnvelope as bF, type GateName as bG, type GatePolicy as bH, type VaultPolicy as bI, type ActiveTier as bJ, type FactorProof as bK, type SchemaUpdateStrategy as bL, type TransformFn as bM, type PersistedSchemaEnvelope as bN, type UpdateDecision as bO, type DirectoryConfig as bP, type UserVisibility as bQ, type AccessibleVault as bR, type AffectedDocument as bS, type ArchivePolicy as bT, type ArchiveResult as bU, type ArchiveRunOptions as bV, type ArchiveStrategy as bW, BUNDLE_STORE_POLICY as bX, type BuiltInGateName as bY, type CacheOptions as bZ, type CacheStats as b_, type DiffEntry as ba, type JsonPatch as bb, type JsonPatchOp as bc, type LedgerEntry as bd, LedgerStore as be, type VaultEngine as bf, VaultInstant as bg, type VerifyResult as bh, applyPatch as bi, canonicalJson as bj, computePatch as bk, diff as bl, formatDiff as bm, hashEntry as bn, paddedIndex as bo, parseIndex as bp, sha256Hex as bq, type PublicEnvelope as br, type SealingKeyProvider as bs, type BundleRecipient as bt, type RecipientSealer as bu, type RecipientHint as bv, Vault as bw, type RecoveryEnrollmentInput as bx, type ShamirRecoveryProvider as by, type MVQueryContext as bz, DictionaryHandle as c, type KeyringFile as c$, type ChangeEvent as c0, type CollectionChangeEvent as c1, type CollectionConflictResolver as c2, type CollectionDescriptor as c3, type CollectionStats as c4, ComputedFieldError as c5, type ComputedFields as c6, type ComputedFn as c7, type Conflict as c8, type ConflictPolicy as c9, type ExportChunk as cA, type ExportFormat as cB, type ExportStreamOptions as cC, type FactorKind as cD, type FactorProofBundle as cE, type FactorRequirement as cF, type FanoutQueryOptions as cG, type FanoutResult as cH, type FenceDoc as cI, type FenceState as cJ, type FieldChange as cK, type FieldDescriptor as cL, type FieldSource as cM, type GhostRecord as cN, type GrantOptions as cO, type GuardViolation as cP, type HistoryConfig as cQ, type HistoryEntry as cR, INDEXED_STORE_POLICY as cS, type ImportCapability as cT, type InferOutput as cU, type InternalCollectionStats as cV, type IssueDelegationOptions as cW, type IssueMagicLinkGrantOptions as cX, type KeyringAuthenticator as cY, type KeyringAuthenticatorWrappingDEKs as cZ, type KeyringAuthenticatorWrappingKEK as c_, type ConflictStrategy as ca, type CrossTierAccessEvent as cb, CrossVaultAggregation as cc, CrossVaultGroupedAggregation as cd, type GroupedRow as ce, type CrossVaultLiveAggregation as cf, type CrossVaultLiveQuery as cg, DEFAULT_PUBLIC_ENVELOPE_SCHEMA as ch, DELEGATIONS_COLLECTION as ci, type DeepPartial as cj, type DeepPartialOrNull as ck, type DeferredNumberingConfig as cl, type DelegationToken as cm, type DeleteManyResult as cn, type DeploymentEvent as co, type DerivationDescriptor as cp, type DirtyEntry as cq, type DryRunResult as cr, type DumpSchemaOptions as cs, ELEVATION_AUDIT_COLLECTION as ct, ElevatedHandle as cu, type EnrollAuthenticatorOptions as cv, type EnrollAuthenticatorWrappingDEKsOptions as cw, type EnrollAuthenticatorWrappingKEKOptions as cx, type EnrollRecoveryResult as cy, type ExportCapability as cz, type DictionaryOptions as d, type ResolvedPublicEnvelopeSchema as d$, type ListAccessibleVaultsOptions as d0, type ListPageResult as d1, type ListUsersOptions as d2, type LiveQueryOptions as d3, type LiveUserEnvelope as d4, type LocaleReadOptions as d5, Lru as d6, type LruOptions as d7, type LruStats as d8, MAGIC_LINK_CONTENT_INFO_PREFIX as d9, type PlaintextTranslatorContext as dA, type PlaintextTranslatorFn as dB, PresenceHandle as dC, type PresencePeer as dD, type PublicEnvelopeField as dE, type PublicEnvelopeSchema as dF, type PublicEnvelopeText as dG, type PullMode as dH, type PullOptions as dI, type PullPolicy as dJ, type PullResult as dK, type PushMode as dL, type PushOptions as dM, type PushPolicy as dN, type PushResult as dO, type PutManyItemOptions as dP, type PutManyOptions as dQ, type PutManyResult as dR, type QueryAcrossOptions as dS, type QueryAcrossResult as dT, type QuickUnlockState as dU, QuickUnlockStore as dV, type ReAuthOperation as dW, type RecoverPassphraseInput as dX, type RecoverPassphraseResult as dY, type RecoverUserOptions as dZ, type RecoveryProof as d_, MAGIC_LINK_GRANTS_COLLECTION as da, MAGIC_LINK_KEK_INFO_PREFIX as db, type MagicLinkGrantPayload as dc, type MagicLinkGrantRecord as dd, type MaterializedViewDescriptor as de, MemoryRecipientSealer as df, MemorySealingKeyProvider as dg, NOYDB_BACKUP_VERSION as dh, NOYDB_FORMAT_VERSION as di, NOYDB_KEYRING_VERSION as dj, NOYDB_SYNC_VERSION as dk, type NextOptions as dl, Noydb as dm, type NoydbEventMap as dn, type NoydbOptions as dp, type Assignment as dq, type OverlayViewDescriptor as dr, PUBLIC_ENVELOPE_FIELDS as ds, type PaperRecoveryDoc as dt, type PaperRecoveryEntry as du, type PassphrasePolicy as dv, type PassphraseValidationResult as dw, type Permission as dx, type Permissions as dy, type PersistedSchemaKind as dz, type I18nMap as e, type VaultPolicyOnDisk as e$, type RevokeOptions as e0, type RotatePassphraseInput as e1, type RotateRecoveryOptions as e2, type RotateRecoveryResult as e3, SEALED_PASSPHRASE_RECORD_ID as e4, type SchemaDelta as e5, type SchemaIntrospection as e6, type SchemaManifestRow as e7, type SealedEnvelope as e8, type SealedPassphrase as e9, type SyncStatus as eA, type SyncTarget as eB, type SyncTargetRole as eC, SyncTransaction as eD, type SyncTransactionResult as eE, type TabChannel as eF, type TabCoordinationOptions as eG, type TabLockManager as eH, type TabPresence as eI, type TabRole as eJ, type TierMode as eK, type TranslatorAuditEntry as eL, type TxOp as eM, USER_ENVELOPE_COLLECTION as eN, USER_ENVELOPE_MAX_BYTES as eO, type Unsubscribe as eP, type UpdateAuthenticatorOptions as eQ, type UpdateContext as eR, type UpdateUserOptions as eS, UserApi as eT, type UserEnvelopeCheckGate as eU, UserEnvelopeOversizedError as eV, type UserEnvelopePresented as eW, type UserInfo as eX, type VaultBackup as eY, VaultGroup as eZ, type VaultGroupOptions as e_, type SequenceHandle as ea, SequenceStore as eb, type SessionPolicy as ec, type SetPublicEnvelopeInput as ed, type ShamirRecoveryDoc as ee, type ShamirRecoveryEntry as ef, ShardedCollection as eg, ShardedGroupedQuery as eh, ShardedQuery as ei, type ShardingConfig as ej, type SkippedVault as ek, type SlotRewrapCeremony as el, type SlotRewrapContext as em, type StandardSchemaV1 as en, type StandardSchemaV1Issue as eo, type StandardSchemaV1SyncResult as ep, StateManagementVault as eq, type StoreAuth as er, type StoreAuthKind as es, type StoreCapabilities as et, type StoreTime as eu, SyncEngine as ev, type SyncMetadata as ew, type SyncPolicy as ex, SyncScheduler as ey, type SyncSchedulerStatus as ez, type I18nTextDescriptor as f, withDeferredNumbering as f$, type VaultRegistryRow as f0, type VaultSchemaSnapshot as f1, type VaultSnapshot as f2, type VaultTemplate as f3, type WarningRules as f4, WeakPassphraseError as f5, type WeakPassphraseReason as f6, type WithArchiveOptions as f7, type WrappedDeksBlob as f8, type WriteConflict as f9, loadActiveDelegations as fA, loadPaperRecoveryEntries as fB, loadSealedPassphrase as fC, loadShamirRecoveryEntries as fD, magicLinkGrantRecordId as fE, mintPaperRecoveryEntry as fF, mintShamirRecoveryEntry as fG, mintWrappedDeksBlob as fH, parseSealedEnvelope as fI, readMagicLinkGrantRecord as fJ, recoverUser as fK, removeAuthenticator as fL, resolveSchema as fM, revokeDelegation as fN, revokeMagicLinkGrant as fO, savePaperRecoveryEntries as fP, saveSealedPassphrase as fQ, saveShamirRecoveryEntries as fR, unwrapDeksFromBlob as fS, unwrapDeksFromPaperEntry as fT, unwrapDeksFromShamirEntry as fU, unwrapMagicLinkGrant as fV, validatePassphrase as fW, validatePublicEnvelopeInput as fX, validateSchemaInput as fY, validateSchemaOutput as fZ, withArchive as f_, type WriteEvent as fa, type WriteHook as fb, type WriteQueue as fc, assertStrongPassphrase as fd, buildRecipientKeyringFile as fe, burnPaperRecoveryEntry as ff, createNoydb as fg, createStore as fh, deriveMagicLinkContentKey as fi, enrollAuthenticator as fj, estimateEntropy as fk, evalComputedFields as fl, evaluateExportCapability as fm, evaluateImportCapability as fn, findAuthenticator as fo, hasExportCapability as fp, hasImportCapability as fq, hasRecoveryEnrolled as fr, isMagicLinkGrantExpired as fs, isPublicEnvelope as ft, issueDelegation as fu, recoverPassphrase as fv, rotatePassphrase as fw, listMagicLinkGrants as fx, listUsers as fy, listUsersWithEnvelopes as fz, type I18nTextOptions as g, writeMagicLinkGrant as g0, changeSecret as g1, createOwnerKeyring as g2, ensureCollectionDEK as g3, grant as g4, loadKeyring as g5, persistKeyring as g6, revoke as g7, updateAuthenticator as g8, updateKeyringIdentity as g9, type OnMissingPolicy as h, applyI18nLocale as i, dictCollectionName as j, dictKey as k, enforceScript as l, getAtPath as m, i18nText as n, inferScripts as o, isDictCollectionName as p, isDictKeyDescriptor as q, isI18nTextDescriptor as r, resolveI18nText as s, resolvePolicy as t, setAtPathInPlace as u, validateI18nTextValue as v, type SessionStrategy as w, createEnforcer as x, validateSessionPolicy as y, BLOB_CHUNKS_COLLECTION as z };
|
|
14166
|
+
export { DEFAULT_CHUNK_SIZE as $, createEnforcer as A, validateSessionPolicy as B, type BlobStrategy as C, DICT_COLLECTION_PREFIX as D, BLOB_CHUNKS_COLLECTION as E, BLOB_COLLECTION as F, BLOB_EVICTION_AUDIT_COLLECTION as G, BLOB_INDEX_COLLECTION as H, type I18nStrategy as I, BLOB_SLOTS_PREFIX as J, BLOB_VERSIONS_PREFIX as K, type Layer as L, type BlobEvictionEntry as M, type BlobFieldPolicy as N, type OnMissing as O, PolicyEnforcer as P, type BlobFieldsConfig as Q, type ResolveI18nOptions as R, type ScriptWarning as S, type BlobObject as T, type BlobPutOptions as U, type BlobResponseOptions as V, BlobSet as W, type BlobStrategyOpenArgs as X, type CompactRunOptions as Y, type CompactionContext as Z, type CompactionResult as _, type DictEntry as a, type Role as a$, EXPORT_AUDIT_COLLECTION as a0, ExportBlobsAbortedError as a1, type ExportBlobsAuditEntry as a2, type ExportBlobsHandle as a3, type ExportBlobsOptions as a4, type ExportedBlob as a5, type SlotInfo as a6, type SlotRecord as a7, type VersionRecord as a8, createExportBlobsHandle as a9, ReadOnlyVaultFacade as aA, type ShadowStrategy as aB, CollectionFrame as aC, VaultFrame as aD, type NoydbBundleStore as aE, type RetentionPolicy as aF, type SnapshotPolicy as aG, type SnapshotStrategy as aH, type SnapshotMeta as aI, type SnapshotMode as aJ, type DerivationStrategy as aK, type DerivationContext as aL, type ArrayOutputSpec as aM, DerivationRegistry as aN, type DerivationStrategyHandle as aO, type DerivedFromMeta as aP, type OutputSpec as aQ, type RecordOutputSpec as aR, type MaterializedViewStrategy as aS, type MaterializedViewStrategyHandle as aT, type OverlayedViewStrategy as aU, Collection as aV, type OverlayFieldMergeMode as aW, type OverlayFieldMergeRule as aX, OverlayedViewRegistry as aY, type OverlayedViewStrategyHandle as aZ, type SyncStrategy as a_, runCompaction as aa, type ConsentStrategy as ab, CONSENT_AUDIT_COLLECTION as ac, type ConsentAuditEntry as ad, type ConsentAuditFilter as ae, type ConsentContext as af, type ConsentOp as ag, loadConsentEntries as ah, writeConsentEntry as ai, type PeriodsStrategy as aj, type CarryForwardContext as ak, type ClosePeriodOptions as al, type OpenPeriodOptions as am, PERIODS_COLLECTION as an, type PeriodRecord as ao, type ReadOnlyCollection as ap, appendPeriodLedgerEntry as aq, assertTsWritable as ar, chainAnchor as as, loadPeriods as at, validatePeriodName as au, type GuardStrategy as av, type GuardChange as aw, type GuardContext as ax, GuardRegistry as ay, type GuardStrategyHandle as az, type DictKeyDescriptor as b, type CollectionStats as b$, type UnlockedKeyring as b0, type HistoryStrategy as b1, type NoydbStore as b2, type HistoryOptions as b3, type EncryptedEnvelope as b4, type PruneOptions as b5, type AppendInput as b6, type ChangeType as b7, CollectionInstant as b8, type DiffEntry as b9, type UserEnvelope as bA, type GateName as bB, type GatePolicy as bC, type VaultPolicy as bD, type ActiveTier as bE, type FactorProof as bF, type SchemaUpdateStrategy as bG, type TransformFn as bH, type PersistedSchemaEnvelope as bI, type UpdateDecision as bJ, type DirectoryConfig as bK, type UserVisibility as bL, type AccessibleVault as bM, type AffectedDocument as bN, type ArchivePolicy as bO, type ArchiveResult as bP, type ArchiveRunOptions as bQ, type ArchiveStrategy as bR, BUNDLE_STORE_POLICY as bS, type BuiltInGateName as bT, type CacheOptions as bU, type CacheStats as bV, type CapturedBlueprint as bW, type ChangeEvent as bX, type CollectionChangeEvent as bY, type CollectionConflictResolver as bZ, type CollectionDescriptor as b_, type JsonPatch as ba, type JsonPatchOp as bb, LedgerStore as bc, type VaultEngine as bd, VaultInstant as be, type VerifyResult as bf, applyPatch as bg, computePatch as bh, diff as bi, formatDiff as bj, type PublicEnvelope as bk, type SealingKeyProvider as bl, type BundleRecipient as bm, type RecipientSealer as bn, type RecipientHint as bo, Vault as bp, type RecoveryEnrollmentInput as bq, type ShamirRecoveryProvider as br, TxContext as bs, type MVQueryContext as bt, type RegisteredMV as bu, MaterializedViewRegistry as bv, type MaterializedFromMeta as bw, type MaterializedViewOutput as bx, type UnionArmJoin as by, type UnionSource as bz, DictionaryHandle as c, type LiveUserEnvelope as c$, ComputedFieldError as c0, type ComputedFields as c1, type ComputedFn as c2, type Conflict as c3, type ConflictPolicy as c4, type ConflictStrategy as c5, type CrossTierAccessEvent as c6, CrossVaultAggregation as c7, CrossVaultGroupedAggregation as c8, type GroupedRow as c9, type FactorRequirement as cA, type FanoutQueryOptions as cB, type FanoutResult as cC, type FenceDoc as cD, type FenceState as cE, type FieldChange as cF, type FieldDescriptor as cG, type FieldSource as cH, type GhostRecord as cI, type GrantOptions as cJ, type GuardViolation as cK, type HistoryConfig as cL, type HistoryEntry as cM, INDEXED_STORE_POLICY as cN, type ImportCapability as cO, type InferOutput as cP, type InternalCollectionStats as cQ, type IssueDelegationOptions as cR, type IssueMagicLinkGrantOptions as cS, type KeyringAuthenticator as cT, type KeyringAuthenticatorWrappingDEKs as cU, type KeyringAuthenticatorWrappingKEK as cV, type KeyringFile as cW, type ListAccessibleVaultsOptions as cX, type ListPageResult as cY, type ListUsersOptions as cZ, type LiveQueryOptions as c_, type CrossVaultLiveAggregation as ca, type CrossVaultLiveQuery as cb, DEFAULT_PUBLIC_ENVELOPE_SCHEMA as cc, DELEGATIONS_COLLECTION as cd, type DeepPartial as ce, type DeepPartialOrNull as cf, type DeferredNumberingConfig as cg, type DelegationToken as ch, type DeleteManyResult as ci, type DeploymentEvent as cj, type DerivationDescriptor as ck, type DirtyEntry as cl, type DryRunResult as cm, type DumpSchemaOptions as cn, ELEVATION_AUDIT_COLLECTION as co, ElevatedHandle as cp, type EnrollAuthenticatorOptions as cq, type EnrollAuthenticatorWrappingDEKsOptions as cr, type EnrollAuthenticatorWrappingKEKOptions as cs, type EnrollRecoveryResult as ct, type ExportCapability as cu, type ExportChunk as cv, type ExportFormat as cw, type ExportStreamOptions as cx, type FactorKind as cy, type FactorProofBundle as cz, type DictionaryOptions as d, SEALED_PASSPHRASE_RECORD_ID as d$, type LocaleReadOptions as d0, Lru as d1, type LruOptions as d2, type LruStats as d3, MAGIC_LINK_CONTENT_INFO_PREFIX as d4, MAGIC_LINK_GRANTS_COLLECTION as d5, MAGIC_LINK_KEK_INFO_PREFIX as d6, type MagicLinkGrantPayload as d7, type MagicLinkGrantRecord as d8, type MaterializedViewDescriptor as d9, type PublicEnvelopeSchema as dA, type PublicEnvelopeText as dB, type PullMode as dC, type PullOptions as dD, type PullPolicy as dE, type PullResult as dF, type PushMode as dG, type PushOptions as dH, type PushPolicy as dI, type PushResult as dJ, type PutManyItemOptions as dK, type PutManyOptions as dL, type PutManyResult as dM, type QueryAcrossOptions as dN, type QueryAcrossResult as dO, type QuickUnlockState as dP, QuickUnlockStore as dQ, type ReAuthOperation as dR, type RecoverPassphraseInput as dS, type RecoverPassphraseResult as dT, type RecoverUserOptions as dU, type RecoveryProof as dV, type ResolvedPublicEnvelopeSchema as dW, type RevokeOptions as dX, type RotatePassphraseInput as dY, type RotateRecoveryOptions as dZ, type RotateRecoveryResult as d_, MemoryRecipientSealer as da, MemorySealingKeyProvider as db, NOYDB_BACKUP_VERSION as dc, NOYDB_FORMAT_VERSION as dd, NOYDB_KEYRING_VERSION as de, NOYDB_SYNC_VERSION as df, type NextOptions as dg, Noydb as dh, type NoydbEventMap as di, type NoydbOptions as dj, type Assignment as dk, type OverlayViewDescriptor as dl, PUBLIC_ENVELOPE_FIELDS as dm, type PaperRecoveryDoc as dn, type PaperRecoveryEntry as dp, type PassphrasePolicy as dq, type PassphraseValidationResult as dr, type Permission as ds, type Permissions as dt, type PersistedSchemaKind as du, type PlaintextTranslatorContext as dv, type PlaintextTranslatorFn as dw, PresenceHandle as dx, type PresencePeer as dy, type PublicEnvelopeField as dz, type I18nMap as e, type VaultRegistryRow as e$, type SchemaDelta as e0, type SchemaIntrospection as e1, type SchemaManifestRow as e2, type SealedEnvelope as e3, type SealedPassphrase as e4, type SequenceHandle as e5, type SequenceOptions as e6, SequenceStore as e7, type SessionPolicy as e8, type SetPublicEnvelopeInput as e9, type SyncTransactionResult as eA, type TabChannel as eB, type TabCoordinationOptions as eC, type TabLockManager as eD, type TabPresence as eE, type TabRole as eF, type TierMode as eG, type TransactionInvariant as eH, type TranslatorAuditEntry as eI, TxCollection as eJ, type TxOp as eK, TxVault as eL, USER_ENVELOPE_COLLECTION as eM, USER_ENVELOPE_MAX_BYTES as eN, type Unsubscribe as eO, type UpdateAuthenticatorOptions as eP, type UpdateContext as eQ, type UpdateUserOptions as eR, UserApi as eS, type UserEnvelopeCheckGate as eT, UserEnvelopeOversizedError as eU, type UserEnvelopePresented as eV, type UserInfo as eW, type VaultBackup as eX, VaultGroup as eY, type VaultGroupOptions as eZ, type VaultPolicyOnDisk as e_, type ShamirRecoveryDoc as ea, type ShamirRecoveryEntry as eb, ShardedCollection as ec, ShardedGroupedQuery as ed, ShardedQuery as ee, type ShardingConfig as ef, type SkippedVault as eg, type SlotRewrapCeremony as eh, type SlotRewrapContext as ei, type StandardSchemaV1 as ej, type StandardSchemaV1Issue as ek, type StandardSchemaV1SyncResult as el, StateManagementVault as em, type StoreAuth as en, type StoreAuthKind as eo, type StoreCapabilities as ep, type StoreTime as eq, SyncEngine as er, type SyncMetadata as es, type SyncPolicy as et, SyncScheduler as eu, type SyncSchedulerStatus as ev, type SyncStatus as ew, type SyncTarget as ex, type SyncTargetRole as ey, SyncTransaction as ez, type I18nTextDescriptor as f, validatePublicEnvelopeInput as f$, type VaultSchemaSnapshot as f0, type VaultSnapshot as f1, type VaultTemplate as f2, type WarningRules as f3, WeakPassphraseError as f4, type WeakPassphraseReason as f5, type WithArchiveOptions as f6, type WrappedDeksBlob as f7, type WriteConflict as f8, type WriteEvent as f9, loadActiveDelegations as fA, loadPaperRecoveryEntries as fB, loadSealedPassphrase as fC, loadShamirRecoveryEntries as fD, magicLinkGrantRecordId as fE, mintPaperRecoveryEntry as fF, mintShamirRecoveryEntry as fG, mintWrappedDeksBlob as fH, parseRsaOaepTlv as fI, parseSealedEnvelope as fJ, readMagicLinkGrantRecord as fK, recoverUser as fL, removeAuthenticator as fM, resolveSchema as fN, resolveSequenceKey as fO, revokeDelegation as fP, revokeMagicLinkGrant as fQ, runTransaction as fR, savePaperRecoveryEntries as fS, saveSealedPassphrase as fT, saveShamirRecoveryEntries as fU, sealRsaOaepTlv as fV, unwrapDeksFromBlob as fW, unwrapDeksFromPaperEntry as fX, unwrapDeksFromShamirEntry as fY, unwrapMagicLinkGrant as fZ, validatePassphrase as f_, type WriteHook as fa, type WriteQueue as fb, aesGcmOpen as fc, assertStrongPassphrase as fd, buildRecipientKeyringFile as fe, burnPaperRecoveryEntry as ff, createNoydb as fg, createStore as fh, deriveMagicLinkContentKey as fi, enrollAuthenticator as fj, estimateEntropy as fk, evalComputedFields as fl, evaluateExportCapability as fm, evaluateImportCapability as fn, findAuthenticator as fo, hasExportCapability as fp, hasImportCapability as fq, hasRecoveryEnrolled as fr, isMagicLinkGrantExpired as fs, isPublicEnvelope as ft, issueDelegation as fu, recoverPassphrase as fv, rotatePassphrase as fw, listMagicLinkGrants as fx, listUsers as fy, listUsersWithEnvelopes as fz, type I18nTextOptions as g, validateSchemaInput as g0, validateSchemaOutput as g1, withArchive as g2, withDeferredNumbering as g3, writeMagicLinkGrant as g4, changeSecret as g5, createOwnerKeyring as g6, ensureCollectionDEK as g7, grant as g8, loadKeyring as g9, persistKeyring as ga, revoke as gb, updateAuthenticator as gc, updateKeyringIdentity as gd, type TxStrategy as ge, type AmendmentTxOptions as gf, type OnMissingPolicy as h, type StaticDictDescriptor as i, applyI18nLocale as j, dictCollectionName as k, dictKey as l, enforceScript as m, getAtPath as n, i18nText as o, inferScripts as p, isDictCollectionName as q, isDictKeyDescriptor as r, isI18nTextDescriptor as s, isStaticDictDescriptor as t, resolveI18nText as u, resolvePolicy as v, setAtPathInPlace as w, staticDict as x, validateI18nTextValue as y, type SessionStrategy as z };
|