@noy-db/hub 0.3.0-pre.7 → 0.3.0-pre.9
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/adapter/index.d.ts +2 -3
- package/dist/adapter/index.js +1 -1
- package/dist/aggregate/index.d.ts +3 -4
- package/dist/aggregate/index.js +9 -8
- package/dist/aggregate/index.js.map +1 -1
- package/dist/api-HV4PDUWF.js +20 -0
- package/dist/as/index.d.ts +4 -5
- package/dist/as/index.js +11 -11
- package/dist/at/index.d.ts +2 -3
- package/dist/attestation/index.d.ts +3 -4
- package/dist/attestation/index.js +18 -18
- package/dist/{backup-7PX6NDKT.js → backup-ODXO7OGA.js} +12 -12
- package/dist/blobs/index.d.ts +4 -5
- package/dist/blobs/index.js +11 -11
- package/dist/blobs/index.js.map +1 -1
- package/dist/bundle/index.d.ts +5 -6
- package/dist/bundle/index.js +20 -20
- package/dist/{bundle-BN_VSq5L.d.ts → bundle-DlSxo31R.d.ts} +1 -1
- package/dist/by/index.js +2 -2
- package/dist/cargo/index.d.ts +4 -5
- package/dist/cargo/index.js +23 -22
- package/dist/{chunk-QYNBPDGO.js → chunk-2644KEGW.js} +18 -10
- package/dist/chunk-2644KEGW.js.map +1 -0
- package/dist/{chunk-XW5LZYV4.js → chunk-2HVUKRNE.js} +2 -2
- package/dist/{chunk-VFQOE76I.js → chunk-2KOSJTMU.js} +2 -2
- package/dist/{chunk-7AERMJP6.js → chunk-2UQ4RMHV.js} +4 -4
- package/dist/{chunk-5GQE5IDB.js → chunk-2XE6LIXT.js} +13 -11
- package/dist/chunk-2XE6LIXT.js.map +1 -0
- package/dist/{chunk-VIWOGJ2O.js → chunk-34RRRIC3.js} +2 -2
- package/dist/{chunk-BASCPHNM.js → chunk-3C5CKXFI.js} +289 -48
- package/dist/chunk-3C5CKXFI.js.map +1 -0
- package/dist/{chunk-LFGJG4XZ.js → chunk-3I4KYWNP.js} +2 -2
- package/dist/{chunk-RUWEHFRB.js → chunk-42KVY3OC.js} +4 -4
- package/dist/{chunk-5K7KZMXG.js → chunk-4C4WBS4V.js} +2048 -948
- package/dist/chunk-4C4WBS4V.js.map +1 -0
- package/dist/{chunk-J54DC5IC.js → chunk-4G3IT7J7.js} +7 -7
- package/dist/{chunk-WJEMUO32.js → chunk-4RQ62VSR.js} +2 -2
- package/dist/{chunk-QAVFI23O.js → chunk-4ZSGSRBI.js} +8 -8
- package/dist/{chunk-C4NOLA5X.js → chunk-5GAX2QOH.js} +2 -2
- package/dist/{chunk-IEKK2XI4.js → chunk-5SAZMBW7.js} +7 -7
- package/dist/{chunk-II6DLDZM.js → chunk-5TZZUL5X.js} +2 -2
- package/dist/{chunk-GMZFHDSG.js → chunk-6CU3FN74.js} +8 -8
- package/dist/chunk-6G2SCQYS.js +29 -0
- package/dist/chunk-6G2SCQYS.js.map +1 -0
- package/dist/{chunk-UWZ7O4NR.js → chunk-6MWUANRF.js} +2 -2
- package/dist/{chunk-U7LV2DUN.js → chunk-6ULCYCMU.js} +2 -2
- package/dist/{chunk-6Z435B6N.js → chunk-7HAPHVRJ.js} +131 -92
- package/dist/chunk-7HAPHVRJ.js.map +1 -0
- package/dist/{chunk-VFG5YXWM.js → chunk-7IZQTCTR.js} +3 -3
- package/dist/chunk-7ZANJ6JZ.js +265 -0
- package/dist/chunk-7ZANJ6JZ.js.map +1 -0
- package/dist/{chunk-VMRXI3IF.js → chunk-A2APWS32.js} +6 -6
- package/dist/chunk-A2APWS32.js.map +1 -0
- package/dist/{chunk-U5IJHUHZ.js → chunk-A4MAD6PO.js} +5 -5
- package/dist/chunk-ANDBBA2P.js +36 -0
- package/dist/chunk-ANDBBA2P.js.map +1 -0
- package/dist/{chunk-5HZULLHE.js → chunk-AUR7MRHA.js} +2 -2
- package/dist/{chunk-YSJG4L4A.js → chunk-AYGZMQP5.js} +3 -3
- package/dist/chunk-AYGZMQP5.js.map +1 -0
- package/dist/{chunk-H7WQMGXH.js → chunk-CJEMA2Z6.js} +2 -2
- package/dist/{chunk-WRLPV6SI.js → chunk-CTSFKDGA.js} +2 -2
- package/dist/{chunk-OJ64XMPJ.js → chunk-CU62BBRB.js} +32 -1
- package/dist/chunk-CU62BBRB.js.map +1 -0
- package/dist/{chunk-OFYY5Q4R.js → chunk-CWCCYH34.js} +2 -2
- package/dist/{chunk-WGH3YV6Y.js → chunk-DEYAO3N6.js} +10 -10
- package/dist/{chunk-7L6EH6S7.js → chunk-DFIBOSOQ.js} +8 -8
- package/dist/{chunk-3CHNKEFI.js → chunk-E5AHO65E.js} +7 -7
- package/dist/{chunk-AG2PLGQ2.js → chunk-FC3XK5JT.js} +8 -8
- package/dist/{chunk-CP3GUIAN.js → chunk-FGOGS7CE.js} +10 -12
- package/dist/chunk-FGOGS7CE.js.map +1 -0
- package/dist/{chunk-DJ4TNF4N.js → chunk-GX2BYNCT.js} +7 -7
- package/dist/{chunk-YTCCC6NG.js → chunk-GZ2MOPVX.js} +34 -63
- package/dist/chunk-GZ2MOPVX.js.map +1 -0
- package/dist/chunk-H57P2SJB.js +126 -0
- package/dist/chunk-H57P2SJB.js.map +1 -0
- package/dist/{chunk-55LSDABW.js → chunk-HBTT7YQX.js} +2 -2
- package/dist/{chunk-PYIUDUGS.js → chunk-HOCEGVGQ.js} +2 -2
- package/dist/{chunk-5JQMX2EH.js → chunk-ICV32FPK.js} +2 -2
- package/dist/{chunk-WFBZRXS5.js → chunk-IIP5WFVT.js} +138 -92
- package/dist/chunk-IIP5WFVT.js.map +1 -0
- package/dist/{chunk-YSU2E4GZ.js → chunk-IUKGIB2Q.js} +19 -6
- package/dist/chunk-IUKGIB2Q.js.map +1 -0
- package/dist/{chunk-OWGAXPRE.js → chunk-IWTYFS3D.js} +2 -2
- package/dist/{chunk-FCOZ7DEC.js → chunk-JC5R7P44.js} +3 -3
- package/dist/{chunk-K6WHZBCO.js → chunk-KBBWY46Y.js} +2 -2
- package/dist/{chunk-RNYDHIQL.js → chunk-KBF5GSGX.js} +3 -3
- package/dist/{chunk-6P5AJ2ZS.js → chunk-KCG3BHFG.js} +2 -23
- package/dist/chunk-KCG3BHFG.js.map +1 -0
- package/dist/{chunk-T4NQMPBN.js → chunk-KNXN47MO.js} +3 -3
- package/dist/{chunk-VK6OC22R.js → chunk-KWLD2D75.js} +4 -3
- package/dist/chunk-KWLD2D75.js.map +1 -0
- package/dist/{chunk-7F2P2NHX.js → chunk-KXKLKJW4.js} +6 -6
- package/dist/{chunk-PAJCRV7O.js → chunk-KYC4LIEU.js} +2 -2
- package/dist/{chunk-AZPGB4OD.js → chunk-LEOXZY36.js} +8 -8
- package/dist/{chunk-2L7PPF2C.js → chunk-M5I354EE.js} +9 -9
- package/dist/{chunk-26FSVNBL.js → chunk-MK2BSS44.js} +2 -2
- package/dist/{chunk-G5CFMITA.js → chunk-MSVY7ULZ.js} +3 -3
- package/dist/{chunk-PE25C4QL.js → chunk-NNWK3PSY.js} +9 -9
- package/dist/{chunk-JT4A6OAJ.js → chunk-NSZ6J5DF.js} +7 -7
- package/dist/{chunk-RHB7BOEO.js → chunk-NUEFXLZU.js} +7 -7
- package/dist/{chunk-I6Q7YECW.js → chunk-NYVQCZ54.js} +5 -5
- package/dist/chunk-NYVQCZ54.js.map +1 -0
- package/dist/{chunk-DKGZIX6S.js → chunk-OQAODTYR.js} +2 -2
- package/dist/{chunk-HV3VNAYI.js → chunk-OV4CMJGR.js} +5 -5
- package/dist/{chunk-V76PHWLE.js → chunk-PEDZFFKW.js} +2 -2
- package/dist/chunk-PF3FQCCF.js +164 -0
- package/dist/chunk-PF3FQCCF.js.map +1 -0
- package/dist/{chunk-CND5WQLO.js → chunk-Q2KVKTC2.js} +5 -5
- package/dist/{chunk-RUIMKQTA.js → chunk-QJR3W3MF.js} +1 -1
- package/dist/chunk-QJR3W3MF.js.map +1 -0
- package/dist/{chunk-HBVEGUON.js → chunk-QTXQ6RNZ.js} +3 -3
- package/dist/{chunk-J5YDMJHG.js → chunk-RJ5522XX.js} +12 -12
- package/dist/{chunk-H3MPSYCH.js → chunk-RMUXPHIH.js} +56 -7
- package/dist/chunk-RMUXPHIH.js.map +1 -0
- package/dist/{chunk-CHK5VVMI.js → chunk-S6AQXDN4.js} +2 -2
- package/dist/{chunk-E3LURVGQ.js → chunk-SSFC3IYA.js} +9 -9
- package/dist/chunk-SSFC3IYA.js.map +1 -0
- package/dist/{chunk-MOQ5M46U.js → chunk-SZCHEDML.js} +2 -2
- package/dist/{chunk-UB3XHGFG.js → chunk-TDPEJINP.js} +14 -7
- package/dist/chunk-TDPEJINP.js.map +1 -0
- package/dist/{chunk-HCK2DFEK.js → chunk-UNJU345Q.js} +1 -1
- package/dist/chunk-UNJU345Q.js.map +1 -0
- package/dist/chunk-V7RNIND6.js +265 -0
- package/dist/chunk-V7RNIND6.js.map +1 -0
- package/dist/{chunk-VUHXLL32.js → chunk-VJ3NQK4K.js} +5 -5
- package/dist/{chunk-62TTFKU4.js → chunk-WNA6T3MA.js} +8 -8
- package/dist/{chunk-7CZ6YXMO.js → chunk-WVCQ75OK.js} +2 -2
- package/dist/{chunk-NO53W6O5.js → chunk-X66MXMR5.js} +2 -2
- package/dist/{chunk-JIW3Q5JN.js → chunk-XHD3L3JO.js} +199 -35
- package/dist/chunk-XHD3L3JO.js.map +1 -0
- package/dist/{chunk-6PALJTC2.js → chunk-XJK2WIYN.js} +3 -3
- package/dist/{chunk-5UOPIPZC.js → chunk-XQ7UYPVN.js} +3 -3
- package/dist/{chunk-HU55NPHZ.js → chunk-Y2OCKFZ3.js} +2 -2
- package/dist/{chunk-UJJJPKJO.js → chunk-YBFOBRTO.js} +3 -3
- package/dist/{chunk-I6JJ6VJS.js → chunk-YLN33ADH.js} +2 -2
- package/dist/{chunk-HGQ6UNHE.js → chunk-Z5YFRMG5.js} +2 -2
- package/dist/{chunk-EU7HDMT3.js → chunk-ZWQFOXBR.js} +2 -2
- package/dist/classified/index.d.ts +4 -5
- package/dist/classified/index.js +5 -3
- package/dist/{config-drift-LRKCC3VZ.js → classified-marker-IO5EBW7Q.js} +4 -4
- package/dist/classified-marker-IO5EBW7Q.js.map +1 -0
- package/dist/collection-facade-3RAXG2OU.js +38 -0
- package/dist/{computed-6FOFXQPE.js → computed-R62HGCO4.js} +3 -3
- package/dist/consent/index.d.ts +3 -4
- package/dist/consent/index.js +9 -9
- package/dist/crdt/index.d.ts +3 -4
- package/dist/{dead-filter-NZORV45S.js → dead-filter-VNA4M3GP.js} +2 -2
- package/dist/delegation-ZKII5UBI.js +25 -0
- package/dist/derivations/index.d.ts +4 -5
- package/dist/derivations/index.js +12 -11
- package/dist/derive-5D2C3WG4.js +21 -0
- package/dist/describe/index.d.ts +4 -1
- package/dist/{dev-unlock-CsOqX7O2.d.ts → dev-unlock-DDTafVO6.d.ts} +1 -1
- package/dist/{enclave-7X5BUAMS.js → enclave-EUBEJGPG.js} +11 -9
- package/dist/executor-IL7SRNUZ.js +23 -0
- package/dist/executor-OUA2Y6YC.js +9 -0
- package/dist/executor-WAEI6PDV.js +9 -0
- package/dist/export-accessible-O55RKAGC.js +23 -0
- package/dist/extract-partition-VBGCBA6Q.js +36 -0
- package/dist/{fanout-sidecar-BDKLP5GG.js → fanout-sidecar-CI5BCMQM.js} +11 -11
- package/dist/find-FISGUWSK.js +11 -0
- package/dist/forget/index.d.ts +2 -2
- package/dist/forget/index.js +9 -9
- package/dist/guards/index.d.ts +4 -5
- package/dist/guards/index.js +4 -4
- package/dist/{hash-BPzm02Za.d.ts → hash-DBkZfIBW.d.ts} +1 -1
- package/dist/history/index.d.ts +4 -5
- package/dist/history/index.js +11 -11
- package/dist/i18n/index.d.ts +24 -5
- package/dist/i18n/index.js +45 -34
- package/dist/i18n/index.js.map +1 -1
- package/dist/in/index.d.ts +2 -3
- package/dist/{index-CLucFLeN.d.ts → index-CyclmyDr.d.ts} +2 -3
- package/dist/{index-BJ3L-XcV.d.ts → index-DzhNI2vE.d.ts} +6378 -5022
- package/dist/index.d.ts +64 -23
- package/dist/index.js +411 -132
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.d.ts +3 -4
- package/dist/indexing/index.js +4 -4
- package/dist/issue-EQAWIYFQ.js +19 -0
- package/dist/kernel/index.d.ts +3 -4
- package/dist/kernel/index.js +13 -12
- package/dist/lazy/index.d.ts +3 -4
- package/dist/{ledger-EMDHRJJN.js → ledger-AJA2SR3C.js} +11 -11
- package/dist/liberate-SEGR4JLY.js +24 -0
- package/dist/{link-set-62Y36TOG.js → link-set-YBAC2DQT.js} +11 -11
- package/dist/materialized-views/index.d.ts +14 -5
- package/dist/materialized-views/index.js +17 -15
- package/dist/{mime-magic-BlE9fzn2.d.ts → mime-magic-CbV1quyp.d.ts} +1 -1
- package/dist/noydb-AMEOU456.js +65 -0
- package/dist/on/index.d.ts +2 -3
- package/dist/on/index.js +9 -9
- package/dist/overlay-views/index.d.ts +4 -5
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.d.ts +3 -4
- package/dist/periods/index.js +20 -12
- package/dist/pod/index.d.ts +3 -4
- package/dist/pod/index.js +11 -11
- package/dist/{policy-SXPLTQS4.js → policy-SDGRWVIL.js} +5 -5
- package/dist/portability/index.d.ts +3 -4
- package/dist/portability/index.js +8 -8
- package/dist/{post-register-DCJ2SRVG.js → post-register-MROIUA5O.js} +6 -6
- package/dist/{public-envelope-CBRIJORQ.js → public-envelope-J5PLQOW2.js} +4 -4
- package/dist/query/index.d.ts +2 -3
- package/dist/query/index.js +9 -6
- package/dist/{read-only-facade-5ADRJVTM.js → read-only-facade-UCYNRE56.js} +2 -2
- package/dist/register-OVJKW7BS.js +23 -0
- package/dist/registry-5RJSK5AJ.js +9 -0
- package/dist/registry-DQRNTWRL.js +18 -0
- package/dist/registry-LTGLERS7.js +19 -0
- package/dist/request-withdrawal-27O77D2V.js +31 -0
- package/dist/{reveal-R5N7JKA5.js → reveal-LT3I2K4P.js} +6 -6
- package/dist/revoke-4N45G7D2.js +24 -0
- package/dist/satellites/index.d.ts +2 -3
- package/dist/satellites/index.js +1 -1
- package/dist/sealed-record/index.d.ts +3 -4
- package/dist/sealed-record/index.js +12 -12
- package/dist/session/index.d.ts +4 -5
- package/dist/session/index.js +9 -9
- package/dist/shadow/index.d.ts +3 -4
- package/dist/shadow/index.js +2 -2
- package/dist/signer-XJCOV7JD.js +25 -0
- package/dist/snapshots/index.d.ts +3 -4
- package/dist/snapshots/index.js +10 -10
- package/dist/{stale-DP66HFT3.js → stale-IYYXEH3U.js} +2 -2
- package/dist/storage-Y44CAPE6.js +23 -0
- package/dist/{store-coordination-provider-F5N5ASDF.js → store-coordination-provider-WKGAE2WI.js} +3 -3
- package/dist/{subject-index-O75wKshW.d.ts → subject-index-CyOgGMJL.d.ts} +18 -0
- package/dist/sync/index.d.ts +2 -3
- package/dist/sync/index.js +9 -9
- package/dist/team/index.d.ts +3 -4
- package/dist/team/index.js +15 -15
- package/dist/tiers/index.d.ts +2 -3
- package/dist/tiers/index.js +10 -10
- package/dist/to/index.d.ts +2 -3
- package/dist/to/index.js +1 -1
- package/dist/{transition-guard-BzsAQEd-.d.ts → transition-guard-BOUkzQU4.d.ts} +1 -1
- package/dist/tx/index.d.ts +3 -4
- package/dist/tx/index.js +3 -3
- package/dist/ui/index.d.ts +4 -1
- package/dist/util/index.js +1 -1
- package/dist/{validators-DGmYFP51.d.ts → validators-B3SalxlJ.d.ts} +1 -1
- package/dist/{vault-diff-DmBBatkx.d.ts → vault-diff-Dt9NkLzw.d.ts} +1 -1
- package/dist/{verify-CAYGXYNO.js → verify-GH6CWVSW.js} +5 -5
- package/dist/{walk-3SB25HF2.js → walk-76UCFXYV.js} +11 -11
- package/dist/with/index.d.ts +3 -4
- package/dist/with/index.js +11 -11
- package/dist/{with-materialized-view-DPEVoGoB.d.ts → with-materialized-view-BP33v1Z6.d.ts} +1 -1
- package/dist/{with-overlayed-view-DGpKFSv1.d.ts → with-overlayed-view-DntFpOKN.d.ts} +1 -1
- package/dist/{with-rollup-B2RwnTq0.d.ts → with-rollup-ClCHO6-4.d.ts} +1 -1
- package/dist/withdraw-accessible-6J2UH3IN.js +28 -0
- package/package.json +3 -3
- package/dist/api-Z66JMGOU.js +0 -20
- package/dist/chunk-47C4CI7R.js +0 -108
- package/dist/chunk-47C4CI7R.js.map +0 -1
- package/dist/chunk-5GQE5IDB.js.map +0 -1
- package/dist/chunk-5K7KZMXG.js.map +0 -1
- package/dist/chunk-6P5AJ2ZS.js.map +0 -1
- package/dist/chunk-6Z435B6N.js.map +0 -1
- package/dist/chunk-7KTL4RHZ.js +0 -120
- package/dist/chunk-7KTL4RHZ.js.map +0 -1
- package/dist/chunk-BASCPHNM.js.map +0 -1
- package/dist/chunk-CP3GUIAN.js.map +0 -1
- package/dist/chunk-E3LURVGQ.js.map +0 -1
- package/dist/chunk-H3MPSYCH.js.map +0 -1
- package/dist/chunk-HCK2DFEK.js.map +0 -1
- package/dist/chunk-I6Q7YECW.js.map +0 -1
- package/dist/chunk-JIW3Q5JN.js.map +0 -1
- package/dist/chunk-OJ64XMPJ.js.map +0 -1
- package/dist/chunk-QYNBPDGO.js.map +0 -1
- package/dist/chunk-RUIMKQTA.js.map +0 -1
- package/dist/chunk-UB3XHGFG.js.map +0 -1
- package/dist/chunk-VK6OC22R.js.map +0 -1
- package/dist/chunk-VMRXI3IF.js.map +0 -1
- package/dist/chunk-WFBZRXS5.js.map +0 -1
- package/dist/chunk-YSJG4L4A.js.map +0 -1
- package/dist/chunk-YSU2E4GZ.js.map +0 -1
- package/dist/chunk-YTCCC6NG.js.map +0 -1
- package/dist/collection-facade-JP6N7USE.js +0 -39
- package/dist/config-drift-LRKCC3VZ.js.map +0 -1
- package/dist/delegation-FDOXXHNG.js +0 -25
- package/dist/derive-JR2C5JFG.js +0 -21
- package/dist/describe-C6iHNlqJ.d.ts +0 -162
- package/dist/executor-6LHOV7MN.js +0 -21
- package/dist/executor-EUPYHCLU.js +0 -9
- package/dist/executor-KZ4B3PRT.js +0 -9
- package/dist/export-accessible-7XLSZBGJ.js +0 -23
- package/dist/extract-partition-RM6EROS5.js +0 -36
- package/dist/find-OH4BEULF.js +0 -11
- package/dist/issue-FJE2IHUI.js +0 -19
- package/dist/liberate-HFIPHJGR.js +0 -24
- package/dist/noydb-4ZASGII5.js +0 -62
- package/dist/register-5NRE7HBZ.js +0 -23
- package/dist/registry-5QLETK3Q.js +0 -17
- package/dist/registry-DYTNLOJC.js +0 -19
- package/dist/registry-HSNQ4NBP.js +0 -9
- package/dist/request-withdrawal-5APB7RKN.js +0 -31
- package/dist/revoke-VELFCK7S.js +0 -24
- package/dist/signer-IVU3B6DP.js +0 -25
- package/dist/storage-RH5VBLLL.js +0 -23
- package/dist/withdraw-accessible-LN4AOJDD.js +0 -28
- /package/dist/{api-Z66JMGOU.js.map → api-HV4PDUWF.js.map} +0 -0
- /package/dist/{backup-7PX6NDKT.js.map → backup-ODXO7OGA.js.map} +0 -0
- /package/dist/{chunk-XW5LZYV4.js.map → chunk-2HVUKRNE.js.map} +0 -0
- /package/dist/{chunk-VFQOE76I.js.map → chunk-2KOSJTMU.js.map} +0 -0
- /package/dist/{chunk-7AERMJP6.js.map → chunk-2UQ4RMHV.js.map} +0 -0
- /package/dist/{chunk-VIWOGJ2O.js.map → chunk-34RRRIC3.js.map} +0 -0
- /package/dist/{chunk-LFGJG4XZ.js.map → chunk-3I4KYWNP.js.map} +0 -0
- /package/dist/{chunk-RUWEHFRB.js.map → chunk-42KVY3OC.js.map} +0 -0
- /package/dist/{chunk-J54DC5IC.js.map → chunk-4G3IT7J7.js.map} +0 -0
- /package/dist/{chunk-WJEMUO32.js.map → chunk-4RQ62VSR.js.map} +0 -0
- /package/dist/{chunk-QAVFI23O.js.map → chunk-4ZSGSRBI.js.map} +0 -0
- /package/dist/{chunk-C4NOLA5X.js.map → chunk-5GAX2QOH.js.map} +0 -0
- /package/dist/{chunk-IEKK2XI4.js.map → chunk-5SAZMBW7.js.map} +0 -0
- /package/dist/{chunk-II6DLDZM.js.map → chunk-5TZZUL5X.js.map} +0 -0
- /package/dist/{chunk-GMZFHDSG.js.map → chunk-6CU3FN74.js.map} +0 -0
- /package/dist/{chunk-UWZ7O4NR.js.map → chunk-6MWUANRF.js.map} +0 -0
- /package/dist/{chunk-U7LV2DUN.js.map → chunk-6ULCYCMU.js.map} +0 -0
- /package/dist/{chunk-VFG5YXWM.js.map → chunk-7IZQTCTR.js.map} +0 -0
- /package/dist/{chunk-U5IJHUHZ.js.map → chunk-A4MAD6PO.js.map} +0 -0
- /package/dist/{chunk-5HZULLHE.js.map → chunk-AUR7MRHA.js.map} +0 -0
- /package/dist/{chunk-H7WQMGXH.js.map → chunk-CJEMA2Z6.js.map} +0 -0
- /package/dist/{chunk-WRLPV6SI.js.map → chunk-CTSFKDGA.js.map} +0 -0
- /package/dist/{chunk-OFYY5Q4R.js.map → chunk-CWCCYH34.js.map} +0 -0
- /package/dist/{chunk-WGH3YV6Y.js.map → chunk-DEYAO3N6.js.map} +0 -0
- /package/dist/{chunk-7L6EH6S7.js.map → chunk-DFIBOSOQ.js.map} +0 -0
- /package/dist/{chunk-3CHNKEFI.js.map → chunk-E5AHO65E.js.map} +0 -0
- /package/dist/{chunk-AG2PLGQ2.js.map → chunk-FC3XK5JT.js.map} +0 -0
- /package/dist/{chunk-DJ4TNF4N.js.map → chunk-GX2BYNCT.js.map} +0 -0
- /package/dist/{chunk-55LSDABW.js.map → chunk-HBTT7YQX.js.map} +0 -0
- /package/dist/{chunk-PYIUDUGS.js.map → chunk-HOCEGVGQ.js.map} +0 -0
- /package/dist/{chunk-5JQMX2EH.js.map → chunk-ICV32FPK.js.map} +0 -0
- /package/dist/{chunk-OWGAXPRE.js.map → chunk-IWTYFS3D.js.map} +0 -0
- /package/dist/{chunk-FCOZ7DEC.js.map → chunk-JC5R7P44.js.map} +0 -0
- /package/dist/{chunk-K6WHZBCO.js.map → chunk-KBBWY46Y.js.map} +0 -0
- /package/dist/{chunk-RNYDHIQL.js.map → chunk-KBF5GSGX.js.map} +0 -0
- /package/dist/{chunk-T4NQMPBN.js.map → chunk-KNXN47MO.js.map} +0 -0
- /package/dist/{chunk-7F2P2NHX.js.map → chunk-KXKLKJW4.js.map} +0 -0
- /package/dist/{chunk-PAJCRV7O.js.map → chunk-KYC4LIEU.js.map} +0 -0
- /package/dist/{chunk-AZPGB4OD.js.map → chunk-LEOXZY36.js.map} +0 -0
- /package/dist/{chunk-2L7PPF2C.js.map → chunk-M5I354EE.js.map} +0 -0
- /package/dist/{chunk-26FSVNBL.js.map → chunk-MK2BSS44.js.map} +0 -0
- /package/dist/{chunk-G5CFMITA.js.map → chunk-MSVY7ULZ.js.map} +0 -0
- /package/dist/{chunk-PE25C4QL.js.map → chunk-NNWK3PSY.js.map} +0 -0
- /package/dist/{chunk-JT4A6OAJ.js.map → chunk-NSZ6J5DF.js.map} +0 -0
- /package/dist/{chunk-RHB7BOEO.js.map → chunk-NUEFXLZU.js.map} +0 -0
- /package/dist/{chunk-DKGZIX6S.js.map → chunk-OQAODTYR.js.map} +0 -0
- /package/dist/{chunk-HV3VNAYI.js.map → chunk-OV4CMJGR.js.map} +0 -0
- /package/dist/{chunk-V76PHWLE.js.map → chunk-PEDZFFKW.js.map} +0 -0
- /package/dist/{chunk-CND5WQLO.js.map → chunk-Q2KVKTC2.js.map} +0 -0
- /package/dist/{chunk-HBVEGUON.js.map → chunk-QTXQ6RNZ.js.map} +0 -0
- /package/dist/{chunk-J5YDMJHG.js.map → chunk-RJ5522XX.js.map} +0 -0
- /package/dist/{chunk-CHK5VVMI.js.map → chunk-S6AQXDN4.js.map} +0 -0
- /package/dist/{chunk-MOQ5M46U.js.map → chunk-SZCHEDML.js.map} +0 -0
- /package/dist/{chunk-VUHXLL32.js.map → chunk-VJ3NQK4K.js.map} +0 -0
- /package/dist/{chunk-62TTFKU4.js.map → chunk-WNA6T3MA.js.map} +0 -0
- /package/dist/{chunk-7CZ6YXMO.js.map → chunk-WVCQ75OK.js.map} +0 -0
- /package/dist/{chunk-NO53W6O5.js.map → chunk-X66MXMR5.js.map} +0 -0
- /package/dist/{chunk-6PALJTC2.js.map → chunk-XJK2WIYN.js.map} +0 -0
- /package/dist/{chunk-5UOPIPZC.js.map → chunk-XQ7UYPVN.js.map} +0 -0
- /package/dist/{chunk-HU55NPHZ.js.map → chunk-Y2OCKFZ3.js.map} +0 -0
- /package/dist/{chunk-UJJJPKJO.js.map → chunk-YBFOBRTO.js.map} +0 -0
- /package/dist/{chunk-I6JJ6VJS.js.map → chunk-YLN33ADH.js.map} +0 -0
- /package/dist/{chunk-HGQ6UNHE.js.map → chunk-Z5YFRMG5.js.map} +0 -0
- /package/dist/{chunk-EU7HDMT3.js.map → chunk-ZWQFOXBR.js.map} +0 -0
- /package/dist/{collection-facade-JP6N7USE.js.map → collection-facade-3RAXG2OU.js.map} +0 -0
- /package/dist/{computed-6FOFXQPE.js.map → computed-R62HGCO4.js.map} +0 -0
- /package/dist/{dead-filter-NZORV45S.js.map → dead-filter-VNA4M3GP.js.map} +0 -0
- /package/dist/{delegation-FDOXXHNG.js.map → delegation-ZKII5UBI.js.map} +0 -0
- /package/dist/{derive-JR2C5JFG.js.map → derive-5D2C3WG4.js.map} +0 -0
- /package/dist/{enclave-7X5BUAMS.js.map → enclave-EUBEJGPG.js.map} +0 -0
- /package/dist/{executor-6LHOV7MN.js.map → executor-IL7SRNUZ.js.map} +0 -0
- /package/dist/{executor-EUPYHCLU.js.map → executor-OUA2Y6YC.js.map} +0 -0
- /package/dist/{executor-KZ4B3PRT.js.map → executor-WAEI6PDV.js.map} +0 -0
- /package/dist/{export-accessible-7XLSZBGJ.js.map → export-accessible-O55RKAGC.js.map} +0 -0
- /package/dist/{extract-partition-RM6EROS5.js.map → extract-partition-VBGCBA6Q.js.map} +0 -0
- /package/dist/{fanout-sidecar-BDKLP5GG.js.map → fanout-sidecar-CI5BCMQM.js.map} +0 -0
- /package/dist/{find-OH4BEULF.js.map → find-FISGUWSK.js.map} +0 -0
- /package/dist/{issue-FJE2IHUI.js.map → issue-EQAWIYFQ.js.map} +0 -0
- /package/dist/{ledger-EMDHRJJN.js.map → ledger-AJA2SR3C.js.map} +0 -0
- /package/dist/{liberate-HFIPHJGR.js.map → liberate-SEGR4JLY.js.map} +0 -0
- /package/dist/{link-set-62Y36TOG.js.map → link-set-YBAC2DQT.js.map} +0 -0
- /package/dist/{noydb-4ZASGII5.js.map → noydb-AMEOU456.js.map} +0 -0
- /package/dist/{policy-SXPLTQS4.js.map → policy-SDGRWVIL.js.map} +0 -0
- /package/dist/{post-register-DCJ2SRVG.js.map → post-register-MROIUA5O.js.map} +0 -0
- /package/dist/{public-envelope-CBRIJORQ.js.map → public-envelope-J5PLQOW2.js.map} +0 -0
- /package/dist/{read-only-facade-5ADRJVTM.js.map → read-only-facade-UCYNRE56.js.map} +0 -0
- /package/dist/{register-5NRE7HBZ.js.map → register-OVJKW7BS.js.map} +0 -0
- /package/dist/{registry-5QLETK3Q.js.map → registry-5RJSK5AJ.js.map} +0 -0
- /package/dist/{registry-DYTNLOJC.js.map → registry-DQRNTWRL.js.map} +0 -0
- /package/dist/{registry-HSNQ4NBP.js.map → registry-LTGLERS7.js.map} +0 -0
- /package/dist/{request-withdrawal-5APB7RKN.js.map → request-withdrawal-27O77D2V.js.map} +0 -0
- /package/dist/{reveal-R5N7JKA5.js.map → reveal-LT3I2K4P.js.map} +0 -0
- /package/dist/{revoke-VELFCK7S.js.map → revoke-4N45G7D2.js.map} +0 -0
- /package/dist/{signer-IVU3B6DP.js.map → signer-XJCOV7JD.js.map} +0 -0
- /package/dist/{stale-DP66HFT3.js.map → stale-IYYXEH3U.js.map} +0 -0
- /package/dist/{storage-RH5VBLLL.js.map → storage-Y44CAPE6.js.map} +0 -0
- /package/dist/{store-coordination-provider-F5N5ASDF.js.map → store-coordination-provider-WKGAE2WI.js.map} +0 -0
- /package/dist/{verify-CAYGXYNO.js.map → verify-GH6CWVSW.js.map} +0 -0
- /package/dist/{walk-3SB25HF2.js.map → walk-76UCFXYV.js.map} +0 -0
- /package/dist/{withdraw-accessible-LN4AOJDD.js.map → withdraw-accessible-6J2UH3IN.js.map} +0 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DerivationCycleError,
|
|
3
|
+
MaterializedViewCycleError
|
|
4
|
+
} from "./chunk-CU62BBRB.js";
|
|
5
|
+
|
|
6
|
+
// src/kernel/via-graph.ts
|
|
7
|
+
var DEFAULT_POSTURE = { encryptedAtRest: "envelope", queryable: "full", exportable: true, forgettable: false };
|
|
8
|
+
var QUERYABLE_RANK = {
|
|
9
|
+
none: 0,
|
|
10
|
+
"det-exact": 1,
|
|
11
|
+
ordered: 2,
|
|
12
|
+
full: 3
|
|
13
|
+
};
|
|
14
|
+
function foldPosture(a, b) {
|
|
15
|
+
return {
|
|
16
|
+
encryptedAtRest: a.encryptedAtRest === "sealed" || b.encryptedAtRest === "sealed" ? "sealed" : "envelope",
|
|
17
|
+
queryable: QUERYABLE_RANK[a.queryable] <= QUERYABLE_RANK[b.queryable] ? a.queryable : b.queryable,
|
|
18
|
+
exportable: a.exportable && b.exportable,
|
|
19
|
+
forgettable: a.forgettable || b.forgettable
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function isDefaultPosture(p) {
|
|
23
|
+
return p.encryptedAtRest === DEFAULT_POSTURE.encryptedAtRest && p.queryable === DEFAULT_POSTURE.queryable && p.exportable === DEFAULT_POSTURE.exportable && p.forgettable === DEFAULT_POSTURE.forgettable;
|
|
24
|
+
}
|
|
25
|
+
var SEP = "\0";
|
|
26
|
+
var EMPTY_SET = /* @__PURE__ */ new Set();
|
|
27
|
+
function nodeId(ref) {
|
|
28
|
+
return `${ref.collection}${SEP}${ref.field}`;
|
|
29
|
+
}
|
|
30
|
+
function displayNodeId(id) {
|
|
31
|
+
const sep = id.indexOf(SEP);
|
|
32
|
+
return `${id.slice(0, sep)}.${id.slice(sep + 1)}`;
|
|
33
|
+
}
|
|
34
|
+
var ViaGraph = class {
|
|
35
|
+
/** Declared source-field postures (registerField), keyed by node id. */
|
|
36
|
+
_posture = /* @__PURE__ */ new Map();
|
|
37
|
+
/** Every derived target's registration, keyed by the target's node id. */
|
|
38
|
+
_in = /* @__PURE__ */ new Map();
|
|
39
|
+
/** source node id -> derived targets that depend on it (dispatch/erasure). */
|
|
40
|
+
_out = /* @__PURE__ */ new Map();
|
|
41
|
+
/** Memoized effective posture per derived target node id. */
|
|
42
|
+
_effectiveCache = /* @__PURE__ */ new Map();
|
|
43
|
+
/** Collections with at least one classified field (#638 Task 2 fix wave 2,
|
|
44
|
+
* Finding I1 — the reconcile path's combined-state leak guard memory). */
|
|
45
|
+
_classifiedCollections = /* @__PURE__ */ new Set();
|
|
46
|
+
/** Computed field names declared with no `deps` entry, per collection
|
|
47
|
+
* (Finding I1 — such a field registers no edge when no classified field is
|
|
48
|
+
* present yet, so a LATER, separate reconcile call attaching classifiedFields
|
|
49
|
+
* still needs to see it regardless of declaration order). */
|
|
50
|
+
_depslessComputed = /* @__PURE__ */ new Map();
|
|
51
|
+
/** Declare a source field's posture (money/i18n/classified/plain). Plain fields
|
|
52
|
+
* default to `DEFAULT_POSTURE`; a later declaration for the same node wins-first (idempotent). */
|
|
53
|
+
registerField(collection, field, posture) {
|
|
54
|
+
const id = nodeId({ collection, field });
|
|
55
|
+
if (this._posture.has(id)) return;
|
|
56
|
+
this._posture.set(id, posture);
|
|
57
|
+
this._effectiveCache.clear();
|
|
58
|
+
}
|
|
59
|
+
/** A derived target depends on `sources` (may be cross-collection). `kind`/`grain`
|
|
60
|
+
* drive dispatch + erasure semantics; sources drive taint. Re-registering the same
|
|
61
|
+
* target replaces `_in` but does not prune stale `_out` edges — callers must
|
|
62
|
+
* register each target at most once per graph lifetime. `onDelete`/`keyField` (#650 Task 5)
|
|
63
|
+
* are meaningful only for `kind === 'ref'` edges — {@link referencingEdgesOf} reads them back. */
|
|
64
|
+
registerDerived(target, sources, kind, grain, onDelete, keyField) {
|
|
65
|
+
const id = nodeId(target);
|
|
66
|
+
this._in.set(id, {
|
|
67
|
+
target,
|
|
68
|
+
sources,
|
|
69
|
+
kind,
|
|
70
|
+
grain,
|
|
71
|
+
...onDelete !== void 0 ? { onDelete } : {},
|
|
72
|
+
...keyField !== void 0 ? { keyField } : {}
|
|
73
|
+
});
|
|
74
|
+
for (const source of sources) {
|
|
75
|
+
const sourceId = nodeId(source);
|
|
76
|
+
const dependents = this._out.get(sourceId);
|
|
77
|
+
if (dependents) dependents.push(target);
|
|
78
|
+
else this._out.set(sourceId, [target]);
|
|
79
|
+
}
|
|
80
|
+
this._effectiveCache.clear();
|
|
81
|
+
}
|
|
82
|
+
/** Whether `target` already has a registered in-edge — lets a caller skip
|
|
83
|
+
* re-registering the same target (#638 Task 2 fix wave 2, Finding I2i:
|
|
84
|
+
* `registerDerived`'s at-most-once contract must hold across repeated
|
|
85
|
+
* identical `vault.collection()` calls, not just within a single one). */
|
|
86
|
+
hasDerived(target) {
|
|
87
|
+
return this._in.has(nodeId(target));
|
|
88
|
+
}
|
|
89
|
+
/** Mark/query a collection as having declared at least one classified field. */
|
|
90
|
+
markClassified(collection) {
|
|
91
|
+
this._classifiedCollections.add(collection);
|
|
92
|
+
}
|
|
93
|
+
isClassified(collection) {
|
|
94
|
+
return this._classifiedCollections.has(collection);
|
|
95
|
+
}
|
|
96
|
+
/** Mark/query a collection's depsless (no declared `deps`) computed
|
|
97
|
+
* field names — see `_depslessComputed`'s doc comment above. */
|
|
98
|
+
markDepslessComputed(collection, field) {
|
|
99
|
+
let set = this._depslessComputed.get(collection);
|
|
100
|
+
if (!set) {
|
|
101
|
+
set = /* @__PURE__ */ new Set();
|
|
102
|
+
this._depslessComputed.set(collection, set);
|
|
103
|
+
}
|
|
104
|
+
set.add(field);
|
|
105
|
+
}
|
|
106
|
+
depslessComputedFields(collection) {
|
|
107
|
+
return this._depslessComputed.get(collection) ?? EMPTY_SET;
|
|
108
|
+
}
|
|
109
|
+
/** Reject cycles at declare time (vault open). Throws `DerivationCycleError` for a
|
|
110
|
+
* derivation/rollup/computed cycle, `MaterializedViewCycleError` for an MV cycle —
|
|
111
|
+
* same classes + message shape the registries throw today (behavior lock). */
|
|
112
|
+
assertAcyclic() {
|
|
113
|
+
const visited = /* @__PURE__ */ new Set();
|
|
114
|
+
const stack = [];
|
|
115
|
+
const visit = (id) => {
|
|
116
|
+
const idx = stack.indexOf(id);
|
|
117
|
+
if (idx !== -1) {
|
|
118
|
+
const cycle = stack.slice(idx).concat(id);
|
|
119
|
+
const isMv = cycle.some((cycleId) => this._in.get(cycleId)?.kind === "mv");
|
|
120
|
+
const path = cycle.map(displayNodeId);
|
|
121
|
+
if (isMv) throw new MaterializedViewCycleError(path);
|
|
122
|
+
throw new DerivationCycleError(path);
|
|
123
|
+
}
|
|
124
|
+
if (visited.has(id)) return;
|
|
125
|
+
stack.push(id);
|
|
126
|
+
const dependents = this._out.get(id);
|
|
127
|
+
if (dependents) {
|
|
128
|
+
for (const dependent of dependents) visit(nodeId(dependent));
|
|
129
|
+
}
|
|
130
|
+
stack.pop();
|
|
131
|
+
visited.add(id);
|
|
132
|
+
};
|
|
133
|
+
for (const id of this._out.keys()) visit(id);
|
|
134
|
+
}
|
|
135
|
+
/** A leaf (non-derived) node's own posture contribution: its declared posture,
|
|
136
|
+
* or `DEFAULT_POSTURE` if never declared via `registerField`. */
|
|
137
|
+
_declaredPosture(id) {
|
|
138
|
+
return this._posture.get(id) ?? DEFAULT_POSTURE;
|
|
139
|
+
}
|
|
140
|
+
/** A node's posture contribution when folded into a dependent: recurse if it is
|
|
141
|
+
* itself derived, else its declared/default posture. */
|
|
142
|
+
_contribution(id) {
|
|
143
|
+
const edge = this._in.get(id);
|
|
144
|
+
return edge ? this._computeEffective(id, edge) : this._declaredPosture(id);
|
|
145
|
+
}
|
|
146
|
+
_computeEffective(id, edge) {
|
|
147
|
+
const cached = this._effectiveCache.get(id);
|
|
148
|
+
if (cached) return cached;
|
|
149
|
+
let result = DEFAULT_POSTURE;
|
|
150
|
+
for (const source of edge.sources) {
|
|
151
|
+
result = foldPosture(result, this._contribution(nodeId(source)));
|
|
152
|
+
}
|
|
153
|
+
if (edge.grain === "virtual" && result.queryable !== "none") {
|
|
154
|
+
result = { ...result, queryable: "none" };
|
|
155
|
+
}
|
|
156
|
+
this._effectiveCache.set(id, result);
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
/** Strictest source posture on every axis, per §2. `undefined` when `target` has no
|
|
160
|
+
* in-edges (not a derived field). Transitive: folds through chained derivations. */
|
|
161
|
+
effectivePosture(target) {
|
|
162
|
+
const id = nodeId(target);
|
|
163
|
+
const edge = this._in.get(id);
|
|
164
|
+
if (!edge) return void 0;
|
|
165
|
+
return this._computeEffective(id, edge);
|
|
166
|
+
}
|
|
167
|
+
/** Per-collection { field → effectivePosture } overlay the pipeline consumes (Task 3). */
|
|
168
|
+
taintedPostures(collection) {
|
|
169
|
+
const out = /* @__PURE__ */ new Map();
|
|
170
|
+
for (const edge of this._in.values()) {
|
|
171
|
+
if (edge.target.collection !== collection) continue;
|
|
172
|
+
out.set(edge.target.field, this._computeEffective(nodeId(edge.target), edge));
|
|
173
|
+
}
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
176
|
+
/** Materialized (`grain !== 'virtual'`) derived fields on `collection` whose effective
|
|
177
|
+
* encryptedAtRest resolves to 'sealed' — the taint-seal set (Task 3). #638 Task 7
|
|
178
|
+
* makes the `grain !== 'virtual'` filter meaningful: a `computed(fn, { mode: 'virtual' })`
|
|
179
|
+
* field is never stored (rides the `present` phase, seam map Part 4's money-Formatted/
|
|
180
|
+
* i18n-Label precedent), so it is EXCLUDED here even when its effective `encryptedAtRest`
|
|
181
|
+
* folds to `'sealed'` — there is no envelope slot to seal. It still surfaces in
|
|
182
|
+
* {@link taintedPostures} (query refusal via `postureFor`'s `queryable` clamp above, and
|
|
183
|
+
* export refusal via `exportable`) — only the AT-REST SEALING ACTION is skipped. */
|
|
184
|
+
taintSealedFields(collection) {
|
|
185
|
+
const out = /* @__PURE__ */ new Set();
|
|
186
|
+
for (const edge of this._in.values()) {
|
|
187
|
+
if (edge.target.collection !== collection || edge.grain === "virtual") continue;
|
|
188
|
+
const posture = this._computeEffective(nodeId(edge.target), edge);
|
|
189
|
+
if (posture.encryptedAtRest === "sealed") out.add(edge.target.field);
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
/** Fields on `collection` whose registered edge is `grain === 'virtual'` (#638 Task 7) —
|
|
194
|
+
* `via-graph-wiring.ts#applyTaintOverlay` intersects this with `exportable === false`
|
|
195
|
+
* postures to know which virtual fields need PRESENT-TIME (not just export-time)
|
|
196
|
+
* redaction, since a virtual field's value is only ever materialized inside `present()`. */
|
|
197
|
+
virtualFields(collection) {
|
|
198
|
+
const out = /* @__PURE__ */ new Set();
|
|
199
|
+
for (const edge of this._in.values()) {
|
|
200
|
+
if (edge.target.collection === collection && edge.grain === "virtual") out.add(edge.target.field);
|
|
201
|
+
}
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
/** Per-collection { field → immediate source field names that forced its
|
|
205
|
+
* effective posture away from `DEFAULT_POSTURE` } — `describe()`'s
|
|
206
|
+
* provenance (Task 3). Only the DIRECT declared sources are named (not the
|
|
207
|
+
* ultimate origin several hops up a transitive chain); a source absent
|
|
208
|
+
* from the result contributed nothing restrictive. */
|
|
209
|
+
taintProvenance(collection) {
|
|
210
|
+
const out = /* @__PURE__ */ new Map();
|
|
211
|
+
for (const edge of this._in.values()) {
|
|
212
|
+
if (edge.target.collection !== collection) continue;
|
|
213
|
+
const forcedBy = edge.sources.filter((source) => !isDefaultPosture(this._contribution(nodeId(source)))).map((source) => source.field);
|
|
214
|
+
if (forcedBy.length > 0) out.set(edge.target.field, forcedBy);
|
|
215
|
+
}
|
|
216
|
+
return out;
|
|
217
|
+
}
|
|
218
|
+
_targetsDependingOn(collection) {
|
|
219
|
+
const out = [];
|
|
220
|
+
for (const edge of this._in.values()) {
|
|
221
|
+
if (edge.sources.some((source) => source.collection === collection)) {
|
|
222
|
+
out.push({ target: edge.target, kind: edge.kind, grain: edge.grain });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
/** Dispatch (Task 4): every derived target triggered by a write to `collection`. Excludes
|
|
228
|
+
* `'ref'` edges (#650 Task 5 review, folded Minor) — a `'ref'` edge is consulted only by the
|
|
229
|
+
* delete/forget-time restrict/cascade/nullify path ({@link referencingEdgesOf}), never by the
|
|
230
|
+
* sync/cutover/restore dispatch wave (`runGraphDispatchWave`); counting it here defeated that
|
|
231
|
+
* wave's zero-cost early-continue, costing every write to a referenced backing collection an
|
|
232
|
+
* unconditional decrypt + two no-op dispatch passes. {@link derivedArtifactsOf} (erasure
|
|
233
|
+
* fanout) still includes `'ref'` edges — the forget path genuinely needs them. */
|
|
234
|
+
dependentsOf(collection) {
|
|
235
|
+
return this._targetsDependingOn(collection).filter((edge) => edge.kind !== "ref");
|
|
236
|
+
}
|
|
237
|
+
/** Erasure (Task 6): derived artifacts of a forgotten record whose source is `collection`. */
|
|
238
|
+
derivedArtifactsOf(collection) {
|
|
239
|
+
return this._targetsDependingOn(collection);
|
|
240
|
+
}
|
|
241
|
+
/** Referencing edges pointing AT a backing dimension (delete/forget-time restrict/cascade/
|
|
242
|
+
* nullify, #650 Task 5) — an O(1) reverse lookup via the existing `_out` map (keyed at the
|
|
243
|
+
* dimension's wildcard `field:'*'` node, the same convention every whole-collection source
|
|
244
|
+
* already uses), NEVER a scan across collections or records. `backing` is the collection name
|
|
245
|
+
* a lookup field's edge was registered against (a reserved dimension's `_dict_<name>`/
|
|
246
|
+
* `_lookup_<name>` collection name, or a matrix dimension's own collection name). */
|
|
247
|
+
referencingEdgesOf(backing) {
|
|
248
|
+
const targets = this._out.get(nodeId({ collection: backing, field: "*" }));
|
|
249
|
+
if (!targets) return [];
|
|
250
|
+
const out = [];
|
|
251
|
+
for (const target of targets) {
|
|
252
|
+
const edge = this._in.get(nodeId(target));
|
|
253
|
+
if (edge?.kind === "ref" && edge.onDelete !== void 0) {
|
|
254
|
+
out.push({ referencing: target, onDelete: edge.onDelete, keyField: edge.keyField ?? "id" });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export {
|
|
262
|
+
DEFAULT_POSTURE,
|
|
263
|
+
ViaGraph
|
|
264
|
+
};
|
|
265
|
+
//# sourceMappingURL=chunk-7ZANJ6JZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/kernel/via-graph.ts"],"sourcesContent":["// kernel/via-graph.ts — the per-vault ViaGraph dependency model (Via port phase C, #638).\n//\n// Metadata-only dependency graph over (collection, field) nodes: source-field\n// postures, derived-field edges, and the taint (effective-posture) algebra.\n// NEVER stores record values or key material — see\n// docs/superpowers/specs/2026-07-11-via-phase-c-design.md §1/§2.\n\nimport { DerivationCycleError, MaterializedViewCycleError } from './errors.js'\nimport type { ViaPosture } from './via.js'\n\n/** A (collection, field) node. Artifact-grain targets (rollup field, MV row-class,\n * overlay output) are modelled as a field node whose `field` is the artifact key. */\nexport interface FieldRef { readonly collection: string; readonly field: string }\n\nexport type EdgeKind = 'computed' | 'derivation' | 'rollup' | 'mv' | 'overlay' | 'ref'\n/** `'virtual'` (#638 Task 7) marks a `computed(fn, { mode: 'virtual' })` field's edge —\n * never stored (rides the `present` phase), so it can never be sealed at rest\n * ({@link ViaGraph.taintSealedFields} excludes it) and is always non-queryable\n * regardless of source posture ({@link ViaGraph.effectivePosture} clamps it). */\nexport type Grain = 'record' | 'aggregate' | 'virtual'\n\n/** Delete/forget-time referential policy for a `'ref'` edge (#650 Task 5, spec §4) — duplicated\n * (not imported) from `shape/via-lookup/descriptor.ts`'s `OnDelete`: the kernel spine may not\n * statically import `shape/**` (Check 14, `via-layering`). Keep the two in sync by hand. */\nexport type OnDelete = 'restrict' | 'cascade' | 'nullify'\n\n/** Plain (non-via) field baseline — max-permissive; taint only ever tightens. */\nexport const DEFAULT_POSTURE: ViaPosture =\n { encryptedAtRest: 'envelope', queryable: 'full', exportable: true, forgettable: false }\n\nconst QUERYABLE_RANK: Record<ViaPosture['queryable'], number> = {\n none: 0,\n 'det-exact': 1,\n ordered: 2,\n full: 3,\n}\n\n/** The per-axis \"most restrictive\" fold — pure, exported for direct unit testing. */\nexport function foldPosture(a: ViaPosture, b: ViaPosture): ViaPosture {\n return {\n encryptedAtRest: a.encryptedAtRest === 'sealed' || b.encryptedAtRest === 'sealed' ? 'sealed' : 'envelope',\n queryable: QUERYABLE_RANK[a.queryable] <= QUERYABLE_RANK[b.queryable] ? a.queryable : b.queryable,\n exportable: a.exportable && b.exportable,\n forgettable: a.forgettable || b.forgettable,\n }\n}\n\n/** Whether `p` is exactly the plain, non-taint baseline (Task 3 — `taintProvenance`). */\nfunction isDefaultPosture(p: ViaPosture): boolean {\n return p.encryptedAtRest === DEFAULT_POSTURE.encryptedAtRest && p.queryable === DEFAULT_POSTURE.queryable &&\n p.exportable === DEFAULT_POSTURE.exportable && p.forgettable === DEFAULT_POSTURE.forgettable\n}\n\n/** One registered `registerDerived` call — a target's whole in-edge set. */\ninterface DerivedEdge {\n readonly target: FieldRef\n readonly sources: readonly FieldRef[]\n readonly kind: EdgeKind\n readonly grain: Grain\n /** `'ref'` edges only — the referencing field's delete/forget policy (#650 Task 5). */\n readonly onDelete?: OnDelete\n /** `'ref'` edges only — the backing dimension's canonical-key FIELD NAME on its own row (matrix\n * tier's `lookup(dim, {key})`; always `'id'` for reserved/static tiers). Defaults to `'id'`\n * when absent — a referencing field always stores this field's VALUE, never the backing row's\n * PUT-id when the two differ (#650 Task 5, mirrors Task 3's membership review fix). */\n readonly keyField?: string\n}\n\nconst SEP = '\\0'\nconst EMPTY_SET: ReadonlySet<string> = new Set()\n\nfunction nodeId(ref: FieldRef): string {\n return `${ref.collection}${SEP}${ref.field}`\n}\n\n/** Inverse of `nodeId`, for building a human-readable cycle path. */\nfunction displayNodeId(id: string): string {\n const sep = id.indexOf(SEP)\n return `${id.slice(0, sep)}.${id.slice(sep + 1)}`\n}\n\n/** Metadata-only dependency graph, ONE per vault. Never stores values or key material. */\nexport class ViaGraph {\n /** Declared source-field postures (registerField), keyed by node id. */\n private readonly _posture = new Map<string, ViaPosture>()\n /** Every derived target's registration, keyed by the target's node id. */\n private readonly _in = new Map<string, DerivedEdge>()\n /** source node id -> derived targets that depend on it (dispatch/erasure). */\n private readonly _out = new Map<string, FieldRef[]>()\n /** Memoized effective posture per derived target node id. */\n private readonly _effectiveCache = new Map<string, ViaPosture>()\n /** Collections with at least one classified field (#638 Task 2 fix wave 2,\n * Finding I1 — the reconcile path's combined-state leak guard memory). */\n private readonly _classifiedCollections = new Set<string>()\n /** Computed field names declared with no `deps` entry, per collection\n * (Finding I1 — such a field registers no edge when no classified field is\n * present yet, so a LATER, separate reconcile call attaching classifiedFields\n * still needs to see it regardless of declaration order). */\n private readonly _depslessComputed = new Map<string, Set<string>>()\n\n /** Declare a source field's posture (money/i18n/classified/plain). Plain fields\n * default to `DEFAULT_POSTURE`; a later declaration for the same node wins-first (idempotent). */\n registerField(collection: string, field: string, posture: ViaPosture): void {\n const id = nodeId({ collection, field })\n if (this._posture.has(id)) return\n this._posture.set(id, posture)\n this._effectiveCache.clear()\n }\n\n /** A derived target depends on `sources` (may be cross-collection). `kind`/`grain`\n * drive dispatch + erasure semantics; sources drive taint. Re-registering the same\n * target replaces `_in` but does not prune stale `_out` edges — callers must\n * register each target at most once per graph lifetime. `onDelete`/`keyField` (#650 Task 5)\n * are meaningful only for `kind === 'ref'` edges — {@link referencingEdgesOf} reads them back. */\n registerDerived(target: FieldRef, sources: readonly FieldRef[], kind: EdgeKind, grain: Grain, onDelete?: OnDelete, keyField?: string): void {\n const id = nodeId(target)\n this._in.set(id, {\n target, sources, kind, grain,\n ...(onDelete !== undefined ? { onDelete } : {}),\n ...(keyField !== undefined ? { keyField } : {}),\n })\n for (const source of sources) {\n const sourceId = nodeId(source)\n const dependents = this._out.get(sourceId)\n if (dependents) dependents.push(target)\n else this._out.set(sourceId, [target])\n }\n this._effectiveCache.clear()\n }\n\n /** Whether `target` already has a registered in-edge — lets a caller skip\n * re-registering the same target (#638 Task 2 fix wave 2, Finding I2i:\n * `registerDerived`'s at-most-once contract must hold across repeated\n * identical `vault.collection()` calls, not just within a single one). */\n hasDerived(target: FieldRef): boolean {\n return this._in.has(nodeId(target))\n }\n\n /** Mark/query a collection as having declared at least one classified field. */\n markClassified(collection: string): void {\n this._classifiedCollections.add(collection)\n }\n\n isClassified(collection: string): boolean {\n return this._classifiedCollections.has(collection)\n }\n\n /** Mark/query a collection's depsless (no declared `deps`) computed\n * field names — see `_depslessComputed`'s doc comment above. */\n markDepslessComputed(collection: string, field: string): void {\n let set = this._depslessComputed.get(collection)\n if (!set) { set = new Set(); this._depslessComputed.set(collection, set) }\n set.add(field)\n }\n\n depslessComputedFields(collection: string): ReadonlySet<string> {\n return this._depslessComputed.get(collection) ?? EMPTY_SET\n }\n\n /** Reject cycles at declare time (vault open). Throws `DerivationCycleError` for a\n * derivation/rollup/computed cycle, `MaterializedViewCycleError` for an MV cycle —\n * same classes + message shape the registries throw today (behavior lock). */\n assertAcyclic(): void {\n const visited = new Set<string>()\n const stack: string[] = []\n\n const visit = (id: string): void => {\n const idx = stack.indexOf(id)\n if (idx !== -1) {\n const cycle = stack.slice(idx).concat(id)\n const isMv = cycle.some(cycleId => this._in.get(cycleId)?.kind === 'mv')\n const path = cycle.map(displayNodeId)\n if (isMv) throw new MaterializedViewCycleError(path)\n throw new DerivationCycleError(path)\n }\n if (visited.has(id)) return\n stack.push(id)\n const dependents = this._out.get(id)\n if (dependents) {\n for (const dependent of dependents) visit(nodeId(dependent))\n }\n stack.pop()\n visited.add(id)\n }\n\n for (const id of this._out.keys()) visit(id)\n }\n\n /** A leaf (non-derived) node's own posture contribution: its declared posture,\n * or `DEFAULT_POSTURE` if never declared via `registerField`. */\n private _declaredPosture(id: string): ViaPosture {\n return this._posture.get(id) ?? DEFAULT_POSTURE\n }\n\n /** A node's posture contribution when folded into a dependent: recurse if it is\n * itself derived, else its declared/default posture. */\n private _contribution(id: string): ViaPosture {\n const edge = this._in.get(id)\n return edge ? this._computeEffective(id, edge) : this._declaredPosture(id)\n }\n\n private _computeEffective(id: string, edge: DerivedEdge): ViaPosture {\n const cached = this._effectiveCache.get(id)\n if (cached) return cached\n let result = DEFAULT_POSTURE\n for (const source of edge.sources) {\n result = foldPosture(result, this._contribution(nodeId(source)))\n }\n // #638 Task 7 — a virtual field is computed fresh on every read and never\n // stored, so it is structurally unqueryable regardless of what its\n // sources' fold would otherwise permit (e.g. a money-sourced virtual\n // field must NOT inherit money's 'ordered' queryability — there is no\n // stored/indexed form to query against). Every OTHER axis (exportable/\n // forgettable/encryptedAtRest) still folds normally — the taint rule is\n // identical to a materialized field; only queryability is a fixed,\n // grain-level property, not a source-derived one.\n if (edge.grain === 'virtual' && result.queryable !== 'none') {\n result = { ...result, queryable: 'none' }\n }\n this._effectiveCache.set(id, result)\n return result\n }\n\n /** Strictest source posture on every axis, per §2. `undefined` when `target` has no\n * in-edges (not a derived field). Transitive: folds through chained derivations. */\n effectivePosture(target: FieldRef): ViaPosture | undefined {\n const id = nodeId(target)\n const edge = this._in.get(id)\n if (!edge) return undefined\n return this._computeEffective(id, edge)\n }\n\n /** Per-collection { field → effectivePosture } overlay the pipeline consumes (Task 3). */\n taintedPostures(collection: string): ReadonlyMap<string, ViaPosture> {\n const out = new Map<string, ViaPosture>()\n for (const edge of this._in.values()) {\n if (edge.target.collection !== collection) continue\n out.set(edge.target.field, this._computeEffective(nodeId(edge.target), edge))\n }\n return out\n }\n\n /** Materialized (`grain !== 'virtual'`) derived fields on `collection` whose effective\n * encryptedAtRest resolves to 'sealed' — the taint-seal set (Task 3). #638 Task 7\n * makes the `grain !== 'virtual'` filter meaningful: a `computed(fn, { mode: 'virtual' })`\n * field is never stored (rides the `present` phase, seam map Part 4's money-Formatted/\n * i18n-Label precedent), so it is EXCLUDED here even when its effective `encryptedAtRest`\n * folds to `'sealed'` — there is no envelope slot to seal. It still surfaces in\n * {@link taintedPostures} (query refusal via `postureFor`'s `queryable` clamp above, and\n * export refusal via `exportable`) — only the AT-REST SEALING ACTION is skipped. */\n taintSealedFields(collection: string): ReadonlySet<string> {\n const out = new Set<string>()\n for (const edge of this._in.values()) {\n if (edge.target.collection !== collection || edge.grain === 'virtual') continue\n const posture = this._computeEffective(nodeId(edge.target), edge)\n if (posture.encryptedAtRest === 'sealed') out.add(edge.target.field)\n }\n return out\n }\n\n /** Fields on `collection` whose registered edge is `grain === 'virtual'` (#638 Task 7) —\n * `via-graph-wiring.ts#applyTaintOverlay` intersects this with `exportable === false`\n * postures to know which virtual fields need PRESENT-TIME (not just export-time)\n * redaction, since a virtual field's value is only ever materialized inside `present()`. */\n virtualFields(collection: string): ReadonlySet<string> {\n const out = new Set<string>()\n for (const edge of this._in.values()) {\n if (edge.target.collection === collection && edge.grain === 'virtual') out.add(edge.target.field)\n }\n return out\n }\n\n /** Per-collection { field → immediate source field names that forced its\n * effective posture away from `DEFAULT_POSTURE` } — `describe()`'s\n * provenance (Task 3). Only the DIRECT declared sources are named (not the\n * ultimate origin several hops up a transitive chain); a source absent\n * from the result contributed nothing restrictive. */\n taintProvenance(collection: string): ReadonlyMap<string, readonly string[]> {\n const out = new Map<string, readonly string[]>()\n for (const edge of this._in.values()) {\n if (edge.target.collection !== collection) continue\n const forcedBy = edge.sources\n .filter((source) => !isDefaultPosture(this._contribution(nodeId(source))))\n .map((source) => source.field)\n if (forcedBy.length > 0) out.set(edge.target.field, forcedBy)\n }\n return out\n }\n\n private _targetsDependingOn(collection: string): ReadonlyArray<{ readonly target: FieldRef; readonly kind: EdgeKind; readonly grain: Grain }> {\n const out: Array<{ target: FieldRef; kind: EdgeKind; grain: Grain }> = []\n for (const edge of this._in.values()) {\n if (edge.sources.some(source => source.collection === collection)) {\n out.push({ target: edge.target, kind: edge.kind, grain: edge.grain })\n }\n }\n return out\n }\n\n /** Dispatch (Task 4): every derived target triggered by a write to `collection`. Excludes\n * `'ref'` edges (#650 Task 5 review, folded Minor) — a `'ref'` edge is consulted only by the\n * delete/forget-time restrict/cascade/nullify path ({@link referencingEdgesOf}), never by the\n * sync/cutover/restore dispatch wave (`runGraphDispatchWave`); counting it here defeated that\n * wave's zero-cost early-continue, costing every write to a referenced backing collection an\n * unconditional decrypt + two no-op dispatch passes. {@link derivedArtifactsOf} (erasure\n * fanout) still includes `'ref'` edges — the forget path genuinely needs them. */\n dependentsOf(collection: string): ReadonlyArray<{ readonly target: FieldRef; readonly kind: EdgeKind; readonly grain: Grain }> {\n return this._targetsDependingOn(collection).filter((edge) => edge.kind !== 'ref')\n }\n\n /** Erasure (Task 6): derived artifacts of a forgotten record whose source is `collection`. */\n derivedArtifactsOf(collection: string): ReadonlyArray<{ readonly target: FieldRef; readonly kind: EdgeKind; readonly grain: Grain }> {\n return this._targetsDependingOn(collection)\n }\n\n /** Referencing edges pointing AT a backing dimension (delete/forget-time restrict/cascade/\n * nullify, #650 Task 5) — an O(1) reverse lookup via the existing `_out` map (keyed at the\n * dimension's wildcard `field:'*'` node, the same convention every whole-collection source\n * already uses), NEVER a scan across collections or records. `backing` is the collection name\n * a lookup field's edge was registered against (a reserved dimension's `_dict_<name>`/\n * `_lookup_<name>` collection name, or a matrix dimension's own collection name). */\n referencingEdgesOf(backing: string): ReadonlyArray<{ readonly referencing: FieldRef; readonly onDelete: OnDelete; readonly keyField: string }> {\n const targets = this._out.get(nodeId({ collection: backing, field: '*' }))\n if (!targets) return []\n const out: Array<{ referencing: FieldRef; onDelete: OnDelete; keyField: string }> = []\n for (const target of targets) {\n const edge = this._in.get(nodeId(target))\n if (edge?.kind === 'ref' && edge.onDelete !== undefined) {\n out.push({ referencing: target, onDelete: edge.onDelete, keyField: edge.keyField ?? 'id' })\n }\n }\n return out\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
openEnvelopeJson
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import {
|
|
5
|
-
NOYDB_FORMAT_VERSION
|
|
6
|
-
} from "./chunk-HCK2DFEK.js";
|
|
3
|
+
} from "./chunk-XHD3L3JO.js";
|
|
7
4
|
import {
|
|
8
5
|
encrypt,
|
|
9
6
|
hmacSha256Hex,
|
|
10
7
|
sha256Hex
|
|
11
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-AUR7MRHA.js";
|
|
9
|
+
import {
|
|
10
|
+
NOYDB_FORMAT_VERSION
|
|
11
|
+
} from "./chunk-UNJU345Q.js";
|
|
12
12
|
|
|
13
13
|
// src/with-audit/forget/strategy.ts
|
|
14
14
|
var NO_FORGET = { subjects: {} };
|
|
@@ -147,4 +147,4 @@ export {
|
|
|
147
147
|
coerceSubjectId,
|
|
148
148
|
readDottedPath
|
|
149
149
|
};
|
|
150
|
-
//# sourceMappingURL=chunk-
|
|
150
|
+
//# sourceMappingURL=chunk-A2APWS32.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-audit/forget/strategy.ts","../src/with-audit/forget/subject-index.ts"],"sourcesContent":["/**\n * `withForgetCascade` — declaration surface for GDPR right-to-erasure via\n * per-record CEK crypto-shred.\n *\n * This file holds only the *declaration* shape and the disabled sentinel.\n * The actual erasure machinery lives in:\n * - `subject-index.ts` — the encrypted `_subject_index` reserved collection\n * - `vault.ts` `forget()` — the per-record tombstone + ledger flow\n * - `collection.ts` `_writeTombstone` — the envelope rewrite\n *\n * A `ForgetStrategy` declares which collections carry erasable subject data\n * and the (dotted-path) field on each record that names the data subject.\n * Declaring a collection here ALSO forces `perRecordKeys: true` for it (a\n * shred can only erase a record whose body is keyed off a per-record CEK),\n * so adopters opt into the CEK foundation transitively.\n *\n * @module\n */\nimport type { LedgerEntry } from '../../with-commit/history/ledger/entry.js'\n\n/**\n * User-supplied declaration passed to {@link withForgetCascade}. Maps a\n * collection name to the record field (dotted path supported, e.g.\n * `'billing.buyerId'`) that identifies the data subject for erasure.\n *\n * ```ts\n * withForgetCascade({ subjects: { invoices: 'buyerId', contacts: 'id' } })\n * ```\n */\nexport interface SubjectDeclaration {\n readonly subjects: Record<string, string>\n}\n\n/**\n * Resolved forget strategy threaded through Noydb → every Vault. Carries\n * the same `subjects` map the user declared. `NO_FORGET` (empty map) is the\n * off-by-default sentinel; `vault.forget()` throws\n * `ForgetStrategyNotConfiguredError` when the map is empty.\n */\nexport interface ForgetStrategy {\n /** Collection → subject-field (dotted path). Empty under `NO_FORGET`. */\n readonly subjects: Readonly<Record<string, string>>\n}\n\n/**\n * Disabled sentinel — no collections declare a subject field. `vault.forget()`\n * refuses with `ForgetStrategyNotConfiguredError`; no write hooks register; no\n * collection is forced into `perRecordKeys`. Non-adopters pay nothing.\n */\nexport const NO_FORGET: ForgetStrategy = { subjects: {} }\n\n// The `withForgetCascade` factory lives in `active.ts` (canonical\n// strategy.ts/active.ts/index.ts split); this file holds only the\n// declaration shape + disabled sentinel.\n\n/**\n * The outcome of a `vault.forget(subjectId)` call.\n *\n * `unmigratedRecords` lists `collection:id` pairs that were tombstoned but\n * whose body had NOT been migrated to a per-record CEK at shred time (legacy\n * body still under the shared collection DEK). Those records are tombstoned\n * (live envelope + history stripped) but their pre-shred ciphertext, if it\n * leaked into a backup before migration, remains decryptable under the\n * collection DEK — so erasure-completeness is NOT guaranteed for them. Run\n * the per-record-CEK migration pass, then re-forget, to close the gap.\n *\n * Blob attachments: a shredded record's **erasable** blobs (on a\n * `perRecordKeys` collection) are crypto-shredded inline — `blobsShredded`\n * counts those taken to refCount 0 (BlobObject deleted → chunks permanently\n * undecryptable), `blobsRetainedShared` counts those still referenced by\n * another record (shared content legitimately persists for its other owner).\n * `blobResidueCollections` now lists only collections with blobs that could\n * NOT be crypto-shredded: **legacy** blobs (no per-blob `_cek`, chunks under\n * the shared `_blob` DEK — migrate them), or a session without the blob\n * service loaded. An all-erasable subject yields an empty residue list.\n */\nexport interface ForgetResult {\n /** The subject id passed to `forget()`. Echoed for caller convenience. */\n readonly subject: string\n /** Count of live records rewritten to a tombstone. */\n readonly recordsShredded: number\n /** Count of `_history` envelopes tombstoned across all shredded records. */\n readonly historyVersionsShredded: number\n /** Distinct collections that had at least one record shredded. */\n readonly collections: readonly string[]\n /** `collection:id` pairs shredded while still un-migrated (see type docs). */\n readonly unmigratedRecords: readonly string[]\n /** Count of erasable blobs crypto-shredded (refCount → 0, BlobObject deleted). */\n readonly blobsShredded: number\n /** Count of erasable blobs retained because still referenced elsewhere (shared). */\n readonly blobsRetainedShared: number\n /** Collections with blobs that could NOT be crypto-shredded — legacy (no `_cek`) or blobs disabled (see type docs). */\n readonly blobResidueCollections: readonly string[]\n /**\n * Count of persisted `_idx/<field>/<recordId>` index side-cars hard-deleted\n * across the shredded records. These live under the retained\n * collection DEK, so crypto-shred alone would leave the indexed field VALUES\n * readable — `forget()` must delete them.\n */\n readonly indexPostingsPurged: number\n /**\n * `collection:id:field` entries whose persisted `_idx` side-car could NOT be\n * deleted — index residue that still leaks the indexed value under the\n * retained collection DEK. Non-empty means erasure is INCOMPLETE: retry, or\n * purge the side-car out of band.\n */\n readonly indexResidue: readonly string[]\n /**\n * Count of `_sealed[field]` slots dropped from the live store across the\n * shredded records. For slots written under `sensitive` +\n * `perRecordKeys` (the current path), the key derives off the per-record CEK,\n * so tombstoning the record — which drops `_cek` and `_sealed` — also\n * crypto-shreds the value. A legacy slot (written before per-record CEKs) keys\n * off the collection DEK instead, so dropping it removes it from the live store but a\n * pre-forget backup remains recoverable by a DEK holder (same caveat `_data`\n * carries); migrate by re-`put`ting before forgetting for full crypto-shred.\n */\n readonly sealedFieldsShredded: number\n /** Count of `_sealed_cek` host-delivery envelopes deleted (#H-1). A record sealed to an\n * at-* host via sealRecordToHost persists its raw CEK there; forget() must destroy them. */\n readonly sealedCekEnvelopesPurged: number\n /** `collection:id` whose `_sealed_cek` purge failed (residue — the host-recoverable CEK may survive). */\n readonly sealedCekResidue: readonly string[]\n /** `collection:id:field` sealed slots that were DEK-derived (legacy, written before per-record CEKs) and thus NOT crypto-shredded\n * by dropping `_cek` — the collection DEK is retained, so synced/backup copies stay decryptable (#M-1). */\n readonly sealedResidue: readonly string[]\n /** The single `op:'forget'` ledger entry appended for this erasure. */\n readonly ledgerEntry: LedgerEntry\n /** #622 — record-grain derived artifacts (MV rows, per-record derived copies) erased\n * because their source subject was forgotten. Overlay outputs are intentionally out of\n * scope here — an overlay is always sourced from an MV, never directly from a subject\n * collection, so the personal-data-bearing MV rows it reads are already erased above. */\n readonly derivedRecordsErased: number\n /** #622 — aggregate-grain targets (rollups) recomputed without the forgotten contribution. */\n readonly derivedAggregatesRecomputed: number\n /** #622 — `collection:id` derived writes SKIPPED because the target period is frozen\n * (recompute deferred; the aggregate retains the forgotten contribution — audited). */\n readonly derivedResidueFrozen: readonly string[]\n /** #650 Task 5 (#648) — referencing records tombstoned via a `'ref'` edge's `cascade` policy. */\n readonly lookupReferencesCascaded: number\n /** #650 Task 5 (#648) — referencing fields cleared via a `'ref'` edge's `nullify` policy. */\n readonly lookupReferencesNullified: number\n /** #650 Task 5 review (Important fix) — `'ref'` edges whose compare-key could NOT be resolved,\n * even from the LIVE pre-shred backing row — cascade/nullify propagation was SKIPPED for these.\n * Always empty in the ordinary case; non-empty means the skip is reported, never silent. */\n readonly lookupReferencesResidue: readonly string[]\n}\n","/**\n * The encrypted subject index.\n *\n * GDPR crypto-shred needs to answer \"which records belong to data subject\n * X?\" portably (the index must travel with the vault/bundle) WITHOUT leaking\n * subject-equivalence to the store. The rejected alternative — an unencrypted\n * subject tag in envelope metadata — would let anyone with store access see\n * which records share a subject. Instead we keep a reserved `_subject_index`\n * collection, encrypted under its OWN DEK (`getDEK('_subject_index')`):\n *\n * - record id = `HMAC-SHA256(indexDEK, subjectId)` (M-2). A bare\n * `sha256Hex(subjectId)` would be offline-computable: an attacker with\n * store access and a candidate list (emails / customer ids are low-entropy)\n * could dictionary the hash to confirm \"subject X is present here.\" Keying\n * the id with the vault-only index DEK removes that capability — without the\n * DEK the id cannot be derived. (Legacy entries written before M-2 used the\n * bare sha256 id; the read/remove paths dual-look-up both forms.)\n * - record body = AES-GCM(JSON `{ r: [{ collection, id }], p }`) under the\n * index DEK, where `p` pads the plaintext to a bucketed length so the\n * ciphertext `_data` length does not leak the approximate record count.\n * Legacy bodies were a bare `[{ collection, id }]` array; reads accept both.\n *\n * ## Concurrency (RISK #3 — known v1 limitation)\n *\n * `addSubjectRef` / `removeSubjectRef` are read-modify-write with no CAS. The\n * design assumes a SINGLE WRITER (the noy-db single-process write model). Two\n * concurrent writers racing on the SAME subject can lose an entry (last-write\n * wins on the ref list). This is documented, not fixed in v1:\n * `rebuildSubjectIndex` performs a full scan to recover a correct index from\n * the canonical records, so a lost ref is recoverable. A CAS-backed index is\n * deferred to a later slice.\n *\n * @module\n */\nimport { encrypt, openEnvelopeJson, hmacSha256Hex, sha256Hex, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\n\n/** Reserved collection holding the encrypted subject → records index. */\nexport const SUBJECT_INDEX_COLLECTION = '_subject_index'\n\n/**\n * Bucket (bytes) the encrypted ref-list plaintext is padded up to, so the\n * ciphertext `_data` length leaks only `count` rounded up to a bucket — not the\n * exact record count. 256 keeps small subjects (the common case) indistinguishable.\n */\nconst REF_LIST_BUCKET = 256\n\n/** A single record reference held in a subject's index entry. */\nexport interface SubjectRef {\n readonly collection: string\n readonly id: string\n}\n\ntype GetDEK = (collectionName: string) => Promise<EnclaveKey>\n\n/** SHA-256 hex of a UTF-8 string. The LEGACY (pre-M-2) subject-index key. */\nasync function sha256HexString(input: string): Promise<string> {\n return sha256Hex(new TextEncoder().encode(input))\n}\n\n/**\n * The subject-index record id(s) to consult for a subject, most-current first.\n *\n * - Encrypted vault: the PRIMARY id is `HMAC-SHA256(indexDEK, subjectId)` (M-2)\n * — not offline-computable. The LEGACY `sha256Hex(subjectId)` id is also\n * returned so reads/removes still find entries written before M-2 (dual-lookup).\n * - Plaintext/debug vault: no DEK to key with, so the only id is the legacy\n * sha256 form (unchanged behaviour — plaintext mode is not zero-knowledge anyway).\n */\nasync function subjectKeys(getDEK: GetDEK, encrypted: boolean, subjectId: string): Promise<string[]> {\n const legacy = await sha256HexString(subjectId)\n if (!encrypted) return [legacy]\n const dek = await getDEK(SUBJECT_INDEX_COLLECTION)\n const keyed = await hmacSha256Hex(dek, new TextEncoder().encode(subjectId))\n return keyed === legacy ? [keyed] : [keyed, legacy]\n}\n\n/** The id new writes land under (keyed when encrypted, else legacy sha256). */\nasync function primarySubjectKey(getDEK: GetDEK, encrypted: boolean, subjectId: string): Promise<string> {\n return (await subjectKeys(getDEK, encrypted, subjectId))[0]!\n}\n\n/** Parse a stored ref-list body: new padded `{ r, p }` wrapper OR legacy bare array. */\nfunction parseRefs(json: string): SubjectRef[] {\n const parsed = JSON.parse(json) as SubjectRef[] | { r: SubjectRef[] }\n return Array.isArray(parsed) ? parsed : parsed.r\n}\n\n/** Serialize + pad the ref list to a bucket boundary (encrypted vaults only). */\nfunction serializeRefs(refs: SubjectRef[]): string {\n const base = JSON.stringify({ r: refs, p: '' })\n const pad = Math.ceil(base.length / REF_LIST_BUCKET) * REF_LIST_BUCKET - base.length\n return JSON.stringify({ r: refs, p: ' '.repeat(pad) })\n}\n\n/** Read + decrypt the ref list at a SINGLE index key. Returns `[]` when absent. */\nasync function readRefs(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n key: string,\n): Promise<SubjectRef[]> {\n const env = await adapter.get(vault, SUBJECT_INDEX_COLLECTION, key)\n if (!env || !env._data) return []\n if (!encrypted) return parseRefs(env._data)\n const dek = await getDEK(SUBJECT_INDEX_COLLECTION)\n const json = await openEnvelopeJson(env, dek)\n return parseRefs(json)\n}\n\n/** Encrypt + write a ref list for a subject under its derived key. */\nasync function writeRefs(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n key: string,\n refs: SubjectRef[],\n): Promise<void> {\n let env: EncryptedEnvelope\n if (!encrypted) {\n // Plaintext/debug vault: keep the legacy bare-array form (no padding needed).\n env = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: new Date().toISOString(), _iv: '', _data: JSON.stringify(refs) }\n } else {\n const dek = await getDEK(SUBJECT_INDEX_COLLECTION)\n const { iv, data } = await encrypt(serializeRefs(refs), dek)\n env = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: new Date().toISOString(), _iv: iv, _data: data }\n }\n await adapter.put(vault, SUBJECT_INDEX_COLLECTION, key, env)\n}\n\n/**\n * Add a `{ collection, id }` ref to a subject's index entry (idempotent —\n * a duplicate ref is not appended). Read-modify-write; see the concurrency\n * note in the module docstring.\n */\nexport async function addSubjectRef(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjectId: string,\n ref: SubjectRef,\n): Promise<void> {\n const key = await primarySubjectKey(getDEK, encrypted, subjectId)\n const refs = await readRefs(adapter, vault, getDEK, encrypted, key)\n if (refs.some((r) => r.collection === ref.collection && r.id === ref.id)) return\n refs.push(ref)\n await writeRefs(adapter, vault, getDEK, encrypted, key, refs)\n}\n\n/**\n * Remove a `{ collection, id }` ref from a subject's index entry. When the\n * last ref is removed the (now empty) entry is deleted so the store holds no\n * residual key for an erased subject.\n */\nexport async function removeSubjectRef(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjectId: string,\n ref: SubjectRef,\n): Promise<void> {\n // Dual-lookup: drop the ref from BOTH the keyed (M-2) and the legacy sha256\n // entry, so a pre-M-2 subject is still fully erased.\n for (const key of await subjectKeys(getDEK, encrypted, subjectId)) {\n const refs = await readRefs(adapter, vault, getDEK, encrypted, key)\n const next = refs.filter((r) => !(r.collection === ref.collection && r.id === ref.id))\n if (next.length === refs.length) continue\n if (next.length === 0) {\n await adapter.delete(vault, SUBJECT_INDEX_COLLECTION, key)\n } else {\n await writeRefs(adapter, vault, getDEK, encrypted, key, next)\n }\n }\n}\n\n/**\n * Look up every record ref for a subject. Returns `[]` when none exist. Unions\n * the keyed (M-2) and legacy sha256 entries (dual-lookup), deduplicated, so a\n * subject indexed before M-2 is still fully found.\n */\nexport async function lookupSubject(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjectId: string,\n): Promise<SubjectRef[]> {\n const keys = await subjectKeys(getDEK, encrypted, subjectId)\n const seen = new Set<string>()\n const out: SubjectRef[] = []\n for (const key of keys) {\n for (const ref of await readRefs(adapter, vault, getDEK, encrypted, key)) {\n const dedup = `${ref.collection}\\u0000${ref.id}`\n if (seen.has(dedup)) continue\n seen.add(dedup)\n out.push(ref)\n }\n }\n return out\n}\n\n/**\n * Rebuild the entire subject index from the canonical records (the recovery\n * path for the documented read-modify-write race). Scans each declared\n * collection, reads `record[subjectField]` (dotted path), and rewrites the\n * index from scratch. Tombstoned (already-shredded) records contribute no\n * ref — their body is gone, so they cannot be re-indexed.\n *\n * `decodeRecord` decrypts an envelope to a plain object (or returns null for\n * a tombstone / unreadable record); supplied by the caller so this module\n * stays free of Collection internals.\n */\nexport async function rebuildSubjectIndex(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjects: Readonly<Record<string, string>>,\n decodeRecord: (collection: string, id: string, env: EncryptedEnvelope) => Promise<Record<string, unknown> | null>,\n): Promise<number> {\n // Drop every existing index entry first so removed refs don't linger.\n const existing = await adapter.list(vault, SUBJECT_INDEX_COLLECTION)\n for (const k of existing) {\n await adapter.delete(vault, SUBJECT_INDEX_COLLECTION, k)\n }\n\n // subjectId → refs, accumulated across all declared collections.\n const bySubject = new Map<string, SubjectRef[]>()\n for (const [collection, field] of Object.entries(subjects)) {\n const ids = await adapter.list(vault, collection)\n for (const id of ids) {\n if (id.startsWith('_')) continue\n const env = await adapter.get(vault, collection, id)\n if (!env || !env._data) continue // missing or tombstone\n const record = await decodeRecord(collection, id, env)\n if (record === null) continue\n const subjectValue = readDottedPath(record, field)\n if (subjectValue === undefined || subjectValue === null) continue\n const subjectId = coerceSubjectId(subjectValue)\n const list = bySubject.get(subjectId) ?? []\n list.push({ collection, id })\n bySubject.set(subjectId, list)\n }\n }\n\n let entries = 0\n for (const [subjectId, refs] of bySubject) {\n const key = await primarySubjectKey(getDEK, encrypted, subjectId)\n await writeRefs(adapter, vault, getDEK, encrypted, key, refs)\n entries++\n }\n return entries\n}\n\n/**\n * Coerce a read subject-field value to a stable string id. Primitives use\n * their natural string form; objects/arrays are JSON-stringified so structural\n * subjects still get a deterministic key (avoids the `[object Object]` trap).\n */\nexport function coerceSubjectId(value: unknown): string {\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value)\n }\n return JSON.stringify(value)\n}\n\n/** Read a (possibly dotted) field path from a plain record. */\nexport function readDottedPath(record: Record<string, unknown>, field: string): unknown {\n if (!field.includes('.')) return record[field]\n let cursor: unknown = record\n for (const segment of field.split('.')) {\n if (cursor === null || cursor === undefined) return undefined\n cursor = (cursor as Record<string, unknown>)[segment]\n }\n return cursor\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dekKey
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MK2BSS44.js";
|
|
4
4
|
import {
|
|
5
5
|
generateULID
|
|
6
6
|
} from "./chunk-ZJADGNYF.js";
|
|
7
7
|
import {
|
|
8
8
|
openEnvelopeJson
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XHD3L3JO.js";
|
|
10
10
|
import {
|
|
11
11
|
encrypt,
|
|
12
12
|
unwrapKey,
|
|
13
13
|
wrapKey
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-AUR7MRHA.js";
|
|
15
15
|
import {
|
|
16
16
|
DelegationTargetMissingError
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-CU62BBRB.js";
|
|
18
18
|
|
|
19
19
|
// src/with-party/team/delegation.ts
|
|
20
20
|
var DELEGATIONS_COLLECTION = "_delegations";
|
|
@@ -96,4 +96,4 @@ export {
|
|
|
96
96
|
loadActiveDelegations,
|
|
97
97
|
revokeDelegation
|
|
98
98
|
};
|
|
99
|
-
//# sourceMappingURL=chunk-
|
|
99
|
+
//# sourceMappingURL=chunk-A4MAD6PO.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LookupHandle
|
|
3
|
+
} from "./chunk-IIP5WFVT.js";
|
|
4
|
+
|
|
5
|
+
// src/shape/via-lookup/active.ts
|
|
6
|
+
function withLookup() {
|
|
7
|
+
return {
|
|
8
|
+
buildLookupHandle(opts) {
|
|
9
|
+
return new LookupHandle(
|
|
10
|
+
opts.adapter,
|
|
11
|
+
opts.compartmentName,
|
|
12
|
+
opts.dimensionName,
|
|
13
|
+
opts.keyring,
|
|
14
|
+
opts.reservedEnvelopes,
|
|
15
|
+
opts.encrypted,
|
|
16
|
+
opts.ledger,
|
|
17
|
+
opts.options,
|
|
18
|
+
opts.findAndUpdateReferences,
|
|
19
|
+
opts.emitter,
|
|
20
|
+
opts.buildDeleteMarker,
|
|
21
|
+
// #647 fix wave 1
|
|
22
|
+
opts.onDirty,
|
|
23
|
+
// #650 Task 4
|
|
24
|
+
opts.onRecordMutated,
|
|
25
|
+
// #650 Task 4
|
|
26
|
+
opts.checkReferencesOnDelete
|
|
27
|
+
// #650 Task 5
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
withLookup
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=chunk-ANDBBA2P.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shape/via-lookup/active.ts"],"sourcesContent":["/**\n * Active lookup strategy — `withLookup()` returns the real implementation\n * that constructs `LookupHandle` instances (the `_dict_*` / future\n * `_lookup_*` reserved-collection engine).\n *\n * #650 Task 1 (via-lookup extraction, phase D): this is the new home for\n * the handle-construction logic that used to live inline in\n * `shape/via-i18n/active.ts`'s `withI18n().buildDictionaryHandle`, which\n * now delegates here (same handle, new home — see that file). Splitting\n * the import chain through this file is the same `#553` tree-shake seam\n * `shape/via-i18n/active.ts` already used: the `LookupHandle` engine only\n * ships in a consumer's bundle once something reaches `withLookup()` (today\n * that's transitively via `withI18n()`; later phase-D tasks may call it\n * directly).\n *\n * @public\n */\n\nimport type { LookupStrategy, BuildLookupHandleOptions } from '../../port/with/lookup-strategy.js'\nimport { LookupHandle } from './handle.js'\n\nexport function withLookup(): LookupStrategy {\n return {\n buildLookupHandle<Keys extends string = string>(\n opts: BuildLookupHandleOptions<Keys>,\n ): LookupHandle<Keys> {\n return new LookupHandle<Keys>(\n opts.adapter,\n opts.compartmentName,\n opts.dimensionName,\n opts.keyring,\n opts.reservedEnvelopes,\n opts.encrypted,\n opts.ledger,\n opts.options,\n opts.findAndUpdateReferences,\n opts.emitter,\n opts.buildDeleteMarker, // #647 fix wave 1\n opts.onDirty, // #650 Task 4\n opts.onRecordMutated, // #650 Task 4\n opts.checkReferencesOnDelete, // #650 Task 5\n )\n },\n }\n}\n"],"mappings":";;;;;AAqBO,SAAS,aAA6B;AAC3C,SAAO;AAAA,IACL,kBACE,MACoB;AACpB,aAAO,IAAI;AAAA,QACT,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA;AAAA,QACL,KAAK;AAAA;AAAA,QACL,KAAK;AAAA;AAAA,QACL,KAAK;AAAA;AAAA,MACP;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
DecryptionError,
|
|
3
3
|
InvalidKeyError,
|
|
4
4
|
TamperedError
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-CU62BBRB.js";
|
|
6
6
|
|
|
7
7
|
// src/kernel/enclave/crypto.ts
|
|
8
8
|
var PBKDF2_ITERATIONS = 6e5;
|
|
@@ -401,4 +401,4 @@ export {
|
|
|
401
401
|
bufferToBase64,
|
|
402
402
|
base64ToBuffer
|
|
403
403
|
};
|
|
404
|
-
//# sourceMappingURL=chunk-
|
|
404
|
+
//# sourceMappingURL=chunk-AUR7MRHA.js.map
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
} from "./chunk-ZJADGNYF.js";
|
|
4
4
|
import {
|
|
5
5
|
openEnvelopeJson
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-XHD3L3JO.js";
|
|
7
7
|
import {
|
|
8
8
|
encrypt
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-AUR7MRHA.js";
|
|
10
10
|
|
|
11
11
|
// src/with-audit/consent/consent.ts
|
|
12
12
|
var CONSENT_AUDIT_COLLECTION = "_consent_audit";
|
|
@@ -72,4 +72,4 @@ export {
|
|
|
72
72
|
writeConsentEntry,
|
|
73
73
|
loadConsentEntries
|
|
74
74
|
};
|
|
75
|
-
//# sourceMappingURL=chunk-
|
|
75
|
+
//# sourceMappingURL=chunk-AYGZMQP5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-audit/consent/consent.ts"],"sourcesContent":["/**\n * Consent boundaries — per-access audit log.\n *\n * ```ts\n * const audit = await vault.withConsent(\n * { purpose: 'quarterly-review', consentHash: '7f3a...' },\n * async () => {\n * const invoices = await vault.collection<Invoice>('invoices').list()\n * return invoices\n * },\n * )\n *\n * const log = await vault.consentAudit({ since: '2026-01-01T00:00:00Z' })\n * // → entries: { actor, purpose, consentHash, ts, op, collection, id }\n * ```\n *\n * ## Contract\n *\n * Every `get` / `put` / `delete` that happens inside a `withConsent`\n * callback writes one entry to the reserved `_consent_audit`\n * collection. Entries are encrypted with the vault's consent-audit\n * DEK (separate from per-user-collection DEKs so access-log queries\n * don't require unwrapping individual collection keys). Outside a\n * `withConsent` scope, no entries are written — consent is\n * opt-in by design (GDPR Art. 7: *demonstrable*, *specific*\n * consent).\n *\n * ## Why store the hash, not the consent text?\n *\n * The `consentHash` is the sha256 of whatever consent receipt the\n * actor presented (a signed GDPR banner click, a HIPAA authorisation\n * PDF, an API-level `X-Consent-Hash` header). Storing only the hash:\n *\n * 1. Keeps the audit log small and indexable.\n * 2. Preserves zero-knowledge at the adapter — adapters see\n * ciphertext envelopes of `{ actor, purpose, consentHash, ts,\n * op, collection, id }`, never the consent record itself.\n * 3. Lets the regulator verify a presented consent doc matches\n * the logged hash at audit time without the system ever\n * possessing the doc.\n *\n * ## Concurrency\n *\n * The consent context lives on the {@link Vault} instance. Two\n * concurrent `withConsent` calls on the same Vault would stomp each\n * other — documented limitation; adopters needing per-flight scope\n * should use separate Vault instances or an AsyncLocalStorage shim.\n *\n * @module\n */\nimport type { EncryptedEnvelope, NoydbStore } from '../../kernel/types.js'\nimport { encrypt, openEnvelopeJson, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { generateULID } from '../../with-pod/ulid.js'\n\n/** Reserved collection for consent-audit entries. */\nexport const CONSENT_AUDIT_COLLECTION = '_consent_audit'\n\n/**\n * The consent scope active for a block of work. Set via\n * `vault.withConsent()`; observed by the collection's access hooks.\n */\nexport interface ConsentContext {\n /**\n * What this access is for. Used by the audit query (`consentAudit\n * ({ purpose })`) and carried in the stored entry. Free-form; the\n * regulator or compliance tooling decides the vocabulary.\n */\n readonly purpose: string\n /**\n * Hex-encoded sha256 of whatever consent artefact the actor\n * presented. Stored as-is in each entry.\n */\n readonly consentHash: string\n}\n\n/**\n * Access operation recorded in an audit entry.\n *\n * Union-widening checklist (I-2) — these three sites widen together or a\n * downstream exhaustive switch breaks at its next in-range minor:\n * 1. `ConsentOp` here (public, re-exported at `index.ts`).\n * 2. `onAccess` op union on `kernel/collection-config.ts`.\n * 3. classified ctx `onAccess` op union on `port/with/classified-strategy.ts`\n * (the `ClassifiedVerifyCtx` one — the `ClassifiedRevealCtx` one stays `'reveal'`-only).\n */\nexport type ConsentOp = 'get' | 'put' | 'delete' | 'reveal' | 'verify' | 'find'\n\n/** One consent-audit record, as decrypted for the caller. */\nexport interface ConsentAuditEntry {\n /** ULID — stable insertion-order key. */\n readonly id: string\n readonly timestamp: string\n readonly actor: string\n readonly purpose: string\n readonly consentHash: string\n readonly op: ConsentOp\n readonly collection: string\n readonly recordId: string\n}\n\n/** Filter passed to `vault.consentAudit()`. */\nexport interface ConsentAuditFilter {\n /** Only entries at or after this ISO timestamp. */\n readonly since?: string\n /** Only entries at or before this ISO timestamp. */\n readonly until?: string\n /** Match entries targeting this collection. */\n readonly collection?: string\n /** Match entries written by this actor. */\n readonly actor?: string\n /** Match entries with this purpose. */\n readonly purpose?: string\n}\n\n/**\n * Write one audit entry. Called by Vault's onAccess hook when a\n * consent context is active.\n */\nexport async function writeConsentEntry(\n adapter: NoydbStore,\n vault: string,\n encrypted: boolean,\n entry: Omit<ConsentAuditEntry, 'id' | 'timestamp'>,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n): Promise<void> {\n const id = generateULID()\n const full: ConsentAuditEntry = {\n id,\n timestamp: new Date().toISOString(),\n ...entry,\n }\n const envelope = await buildEnvelope(full, encrypted, getDEK)\n await adapter.put(vault, CONSENT_AUDIT_COLLECTION, id, envelope)\n}\n\n/** Load + decrypt + filter all entries. */\nexport async function loadConsentEntries(\n adapter: NoydbStore,\n vault: string,\n encrypted: boolean,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n filter: ConsentAuditFilter = {},\n): Promise<ConsentAuditEntry[]> {\n const ids = await adapter.list(vault, CONSENT_AUDIT_COLLECTION)\n const entries: ConsentAuditEntry[] = []\n\n for (const id of ids.sort()) {\n const envelope = await adapter.get(vault, CONSENT_AUDIT_COLLECTION, id)\n if (!envelope) continue\n const entry = await decryptEntry(envelope, encrypted, getDEK)\n if (!matchesFilter(entry, filter)) continue\n entries.push(entry)\n }\n return entries\n}\n\n// ── internals ──────────────────────────────────────────────────────\n\nasync function buildEnvelope(\n entry: ConsentAuditEntry,\n encrypted: boolean,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n): Promise<EncryptedEnvelope> {\n const json = JSON.stringify(entry)\n if (!encrypted) {\n return {\n _noydb: 1,\n _v: 1,\n _ts: entry.timestamp,\n _iv: '',\n _data: json,\n }\n }\n const dek = await getDEK(CONSENT_AUDIT_COLLECTION)\n const { iv, data } = await encrypt(json, dek)\n return {\n _noydb: 1,\n _v: 1,\n _ts: entry.timestamp,\n _iv: iv,\n _data: data,\n }\n}\n\nasync function decryptEntry(\n envelope: EncryptedEnvelope,\n encrypted: boolean,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n): Promise<ConsentAuditEntry> {\n if (!encrypted) return JSON.parse(envelope._data) as ConsentAuditEntry\n const json = await openEnvelopeJson(envelope, await getDEK(CONSENT_AUDIT_COLLECTION))\n return JSON.parse(json) as ConsentAuditEntry\n}\n\nfunction matchesFilter(entry: ConsentAuditEntry, f: ConsentAuditFilter): boolean {\n if (f.since && entry.timestamp < f.since) return false\n if (f.until && entry.timestamp > f.until) return false\n if (f.collection && entry.collection !== f.collection) return false\n if (f.actor && entry.actor !== f.actor) return false\n if (f.purpose && entry.purpose !== f.purpose) return false\n return true\n}\n"],"mappings":";;;;;;;;;;;AAuDO,IAAM,2BAA2B;AA+DxC,eAAsB,kBACpB,SACA,OACA,WACA,OACA,QACe;AACf,QAAM,KAAK,aAAa;AACxB,QAAM,OAA0B;AAAA,IAC9B;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,GAAG;AAAA,EACL;AACA,QAAM,WAAW,MAAM,cAAc,MAAM,WAAW,MAAM;AAC5D,QAAM,QAAQ,IAAI,OAAO,0BAA0B,IAAI,QAAQ;AACjE;AAGA,eAAsB,mBACpB,SACA,OACA,WACA,QACA,SAA6B,CAAC,GACA;AAC9B,QAAM,MAAM,MAAM,QAAQ,KAAK,OAAO,wBAAwB;AAC9D,QAAM,UAA+B,CAAC;AAEtC,aAAW,MAAM,IAAI,KAAK,GAAG;AAC3B,UAAM,WAAW,MAAM,QAAQ,IAAI,OAAO,0BAA0B,EAAE;AACtE,QAAI,CAAC,SAAU;AACf,UAAM,QAAQ,MAAM,aAAa,UAAU,WAAW,MAAM;AAC5D,QAAI,CAAC,cAAc,OAAO,MAAM,EAAG;AACnC,YAAQ,KAAK,KAAK;AAAA,EACpB;AACA,SAAO;AACT;AAIA,eAAe,cACb,OACA,WACA,QAC4B;AAC5B,QAAM,OAAO,KAAK,UAAU,KAAK;AACjC,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,IAAI;AAAA,MACJ,KAAK,MAAM;AAAA,MACX,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,MAAM,MAAM,OAAO,wBAAwB;AACjD,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,QAAQ,MAAM,GAAG;AAC5C,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,KAAK,MAAM;AAAA,IACX,KAAK;AAAA,IACL,OAAO;AAAA,EACT;AACF;AAEA,eAAe,aACb,UACA,WACA,QAC4B;AAC5B,MAAI,CAAC,UAAW,QAAO,KAAK,MAAM,SAAS,KAAK;AAChD,QAAM,OAAO,MAAM,iBAAiB,UAAU,MAAM,OAAO,wBAAwB,CAAC;AACpF,SAAO,KAAK,MAAM,IAAI;AACxB;AAEA,SAAS,cAAc,OAA0B,GAAgC;AAC/E,MAAI,EAAE,SAAS,MAAM,YAAY,EAAE,MAAO,QAAO;AACjD,MAAI,EAAE,SAAS,MAAM,YAAY,EAAE,MAAO,QAAO;AACjD,MAAI,EAAE,cAAc,MAAM,eAAe,EAAE,WAAY,QAAO;AAC9D,MAAI,EAAE,SAAS,MAAM,UAAU,EAAE,MAAO,QAAO;AAC/C,MAAI,EAAE,WAAW,MAAM,YAAY,EAAE,QAAS,QAAO;AACrD,SAAO;AACT;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NOYDB_FORMAT_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UNJU345Q.js";
|
|
4
4
|
|
|
5
5
|
// src/with-party/team/managed-passphrase.ts
|
|
6
6
|
var MemorySealingKeyProvider = class {
|
|
@@ -227,4 +227,4 @@ export {
|
|
|
227
227
|
loadSealedPassphrase,
|
|
228
228
|
resolveManagedSecret
|
|
229
229
|
};
|
|
230
|
-
//# sourceMappingURL=chunk-
|
|
230
|
+
//# sourceMappingURL=chunk-CJEMA2Z6.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NOYDB_FORMAT_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UNJU345Q.js";
|
|
4
4
|
|
|
5
5
|
// src/with-shape/schema-update/fence.ts
|
|
6
6
|
var FENCE_RECORD_ID = "schema-fence";
|
|
@@ -37,4 +37,4 @@ export {
|
|
|
37
37
|
loadFence,
|
|
38
38
|
saveFence
|
|
39
39
|
};
|
|
40
|
-
//# sourceMappingURL=chunk-
|
|
40
|
+
//# sourceMappingURL=chunk-CTSFKDGA.js.map
|
|
@@ -526,6 +526,17 @@ var IndexWriteFailureError = class extends NoydbError {
|
|
|
526
526
|
this.cause = args.cause;
|
|
527
527
|
}
|
|
528
528
|
};
|
|
529
|
+
var FieldNotQueryableError = class extends NoydbError {
|
|
530
|
+
field;
|
|
531
|
+
constructor(field) {
|
|
532
|
+
super(
|
|
533
|
+
"FIELD_NOT_QUERYABLE",
|
|
534
|
+
`Field "${field}" is not queryable \u2014 its Via feature declares queryable: 'none'.`
|
|
535
|
+
);
|
|
536
|
+
this.name = "FieldNotQueryableError";
|
|
537
|
+
this.field = field;
|
|
538
|
+
}
|
|
539
|
+
};
|
|
529
540
|
var BundleIntegrityError = class extends NoydbError {
|
|
530
541
|
constructor(message) {
|
|
531
542
|
super("BUNDLE_INTEGRITY", `.noydb bundle integrity check failed: ${message}`);
|
|
@@ -678,6 +689,24 @@ var UnknownDictCodeError = class extends NoydbError {
|
|
|
678
689
|
this.code = code;
|
|
679
690
|
}
|
|
680
691
|
};
|
|
692
|
+
var UnknownLookupKeyError = class extends NoydbError {
|
|
693
|
+
/** The dimension (dictionary/target collection) name. */
|
|
694
|
+
dimension;
|
|
695
|
+
/** The field that carried the unknown key. */
|
|
696
|
+
field;
|
|
697
|
+
/** The offending key value. */
|
|
698
|
+
key;
|
|
699
|
+
constructor(dimension, field, key) {
|
|
700
|
+
super(
|
|
701
|
+
"UNKNOWN_LOOKUP_KEY",
|
|
702
|
+
`Field "${field}": key "${key}" is not a known member of the "${dimension}" lookup vocabulary (closed). Use a declared/existing key, or declare { vocabulary: 'open' } to allow unknown keys.`
|
|
703
|
+
);
|
|
704
|
+
this.name = "UnknownLookupKeyError";
|
|
705
|
+
this.dimension = dimension;
|
|
706
|
+
this.field = field;
|
|
707
|
+
this.key = key;
|
|
708
|
+
}
|
|
709
|
+
};
|
|
681
710
|
var TranslatorNotConfiguredError = class extends NoydbError {
|
|
682
711
|
/** The field that requested auto-translation. */
|
|
683
712
|
field;
|
|
@@ -1359,6 +1388,7 @@ export {
|
|
|
1359
1388
|
UniqueConstraintError,
|
|
1360
1389
|
UnsupportedIndexOptionError,
|
|
1361
1390
|
IndexWriteFailureError,
|
|
1391
|
+
FieldNotQueryableError,
|
|
1362
1392
|
BundleIntegrityError,
|
|
1363
1393
|
BundleSealMismatchError,
|
|
1364
1394
|
ReservedCollectionNameError,
|
|
@@ -1369,6 +1399,7 @@ export {
|
|
|
1369
1399
|
ScriptViolationError,
|
|
1370
1400
|
StaticDictReadonlyError,
|
|
1371
1401
|
UnknownDictCodeError,
|
|
1402
|
+
UnknownLookupKeyError,
|
|
1372
1403
|
TranslatorNotConfiguredError,
|
|
1373
1404
|
BackupLedgerError,
|
|
1374
1405
|
BackupCorruptedError,
|
|
@@ -1431,4 +1462,4 @@ export {
|
|
|
1431
1462
|
ClassifiedRotationError,
|
|
1432
1463
|
SatelliteConfigError
|
|
1433
1464
|
};
|
|
1434
|
-
//# sourceMappingURL=chunk-
|
|
1465
|
+
//# sourceMappingURL=chunk-CU62BBRB.js.map
|