@noy-db/hub 0.3.0-pre.8 → 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-WRGNTGJF.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-DzWtVw7w.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-X6VNIRHS.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-2PJB6H3H.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-723XB5NX.js → chunk-42KVY3OC.js} +4 -4
- package/dist/{chunk-56JB2BGI.js → chunk-4C4WBS4V.js} +1739 -920
- package/dist/chunk-4C4WBS4V.js.map +1 -0
- package/dist/{chunk-62APEMF3.js → chunk-4G3IT7J7.js} +7 -7
- package/dist/{chunk-W3JABOVB.js → chunk-4RQ62VSR.js} +2 -2
- package/dist/{chunk-WPNP3NF5.js → chunk-4ZSGSRBI.js} +8 -8
- package/dist/{chunk-C4NOLA5X.js → chunk-5GAX2QOH.js} +2 -2
- package/dist/{chunk-5HRJQSKN.js → chunk-5SAZMBW7.js} +7 -7
- package/dist/{chunk-II6DLDZM.js → chunk-5TZZUL5X.js} +2 -2
- package/dist/{chunk-TUL5YQWF.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-GDOOG5YQ.js → chunk-7IZQTCTR.js} +3 -3
- package/dist/chunk-7ZANJ6JZ.js +265 -0
- package/dist/chunk-7ZANJ6JZ.js.map +1 -0
- package/dist/{chunk-VVLOWY74.js → chunk-A2APWS32.js} +6 -6
- package/dist/chunk-A2APWS32.js.map +1 -0
- package/dist/{chunk-TIAYXOEI.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-44DV37ZV.js → chunk-AYGZMQP5.js} +3 -3
- package/dist/chunk-AYGZMQP5.js.map +1 -0
- package/dist/{chunk-P2PEPSPK.js → chunk-CJEMA2Z6.js} +2 -2
- package/dist/{chunk-L6Q5NA22.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-J5YUA6JB.js → chunk-CWCCYH34.js} +2 -2
- package/dist/{chunk-NTPWQH4S.js → chunk-DEYAO3N6.js} +10 -10
- package/dist/{chunk-M3OYTM5Z.js → chunk-DFIBOSOQ.js} +8 -8
- package/dist/{chunk-FZK3JJJK.js → chunk-E5AHO65E.js} +7 -7
- package/dist/{chunk-F3EKFGUS.js → chunk-FC3XK5JT.js} +8 -8
- package/dist/{chunk-XBPUVQDO.js → chunk-FGOGS7CE.js} +10 -12
- package/dist/chunk-FGOGS7CE.js.map +1 -0
- package/dist/{chunk-3KBK5LU4.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-PMMV5SQC.js → chunk-IIP5WFVT.js} +138 -92
- package/dist/chunk-IIP5WFVT.js.map +1 -0
- package/dist/{chunk-37JZR7CY.js → chunk-IUKGIB2Q.js} +4 -4
- package/dist/{chunk-OWGAXPRE.js → chunk-IWTYFS3D.js} +2 -2
- package/dist/{chunk-FCOZ7DEC.js → chunk-JC5R7P44.js} +3 -3
- package/dist/{chunk-IARAK3PK.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-GLVXQU3E.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-J7OH6R4K.js → chunk-KXKLKJW4.js} +6 -6
- package/dist/{chunk-PAJCRV7O.js → chunk-KYC4LIEU.js} +2 -2
- package/dist/{chunk-R4IVPJHN.js → chunk-LEOXZY36.js} +8 -8
- package/dist/{chunk-25G7PUTA.js → chunk-M5I354EE.js} +9 -9
- package/dist/{chunk-26FSVNBL.js → chunk-MK2BSS44.js} +2 -2
- package/dist/{chunk-UMH35AQV.js → chunk-MSVY7ULZ.js} +3 -3
- package/dist/{chunk-5YBUFCFF.js → chunk-NNWK3PSY.js} +9 -9
- package/dist/{chunk-X5QZAJDB.js → chunk-NSZ6J5DF.js} +7 -7
- package/dist/{chunk-I6URUNTD.js → chunk-NUEFXLZU.js} +7 -7
- package/dist/{chunk-PPPZIBKY.js → chunk-NYVQCZ54.js} +5 -5
- package/dist/chunk-NYVQCZ54.js.map +1 -0
- package/dist/{chunk-DCVVCJVB.js → chunk-OQAODTYR.js} +2 -2
- package/dist/{chunk-QYMMMKOO.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-BJ2XF2RC.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-QABECTNN.js → chunk-QTXQ6RNZ.js} +3 -3
- package/dist/{chunk-P6TZ3IVN.js → chunk-RJ5522XX.js} +12 -12
- package/dist/{chunk-BM6E4MUT.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-4VDFUBTS.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-REWTMYSF.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-FWART3VA.js → chunk-VJ3NQK4K.js} +5 -5
- package/dist/{chunk-3YKBGS3I.js → chunk-WNA6T3MA.js} +8 -8
- package/dist/{chunk-7CZ6YXMO.js → chunk-WVCQ75OK.js} +2 -2
- package/dist/{chunk-BTFTHF6Q.js → chunk-X66MXMR5.js} +2 -2
- package/dist/{chunk-7TAV3BFQ.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-TCGVRKOS.js → chunk-YBFOBRTO.js} +3 -3
- package/dist/{chunk-I6JJ6VJS.js → chunk-YLN33ADH.js} +2 -2
- package/dist/{chunk-PVC2FBCQ.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-RFEVGBKR.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-RHMNYOXP.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-wgQ7Ci5I.d.ts → dev-unlock-DDTafVO6.d.ts} +1 -1
- package/dist/{enclave-KGEUW2GL.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-NBDKKCHT.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-CwHnoKyW.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-CTvUp94-.d.ts → index-CyclmyDr.d.ts} +2 -3
- package/dist/{index-Dn5GzPg_.d.ts → index-DzhNI2vE.d.ts} +7114 -5992
- package/dist/index.d.ts +51 -17
- package/dist/index.js +403 -125
- 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-3LR3YO2W.js → ledger-AJA2SR3C.js} +11 -11
- package/dist/liberate-SEGR4JLY.js +24 -0
- package/dist/{link-set-NSUIX6BR.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-CYbFNQ4h.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 +12 -12
- package/dist/pod/index.d.ts +3 -4
- package/dist/pod/index.js +11 -11
- package/dist/{policy-Y7HL2KGA.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-KOFRTCDD.js → post-register-MROIUA5O.js} +6 -6
- package/dist/{public-envelope-NC7SPNMV.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-5Y777N5Q.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-77LP2EEF.js → stale-IYYXEH3U.js} +2 -2
- package/dist/storage-Y44CAPE6.js +23 -0
- package/dist/{store-coordination-provider-NJDDZ264.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-D5RLJv8S.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-C3lrVF-c.d.ts → validators-B3SalxlJ.d.ts} +1 -1
- package/dist/{vault-diff-_LSIcOMv.d.ts → vault-diff-Dt9NkLzw.d.ts} +1 -1
- package/dist/{verify-CAYGXYNO.js → verify-GH6CWVSW.js} +5 -5
- package/dist/{walk-SMAE4IEI.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-Xv1-9Bh0.d.ts → with-materialized-view-BP33v1Z6.d.ts} +1 -1
- package/dist/{with-overlayed-view-CuWhq_-C.d.ts → with-overlayed-view-DntFpOKN.d.ts} +1 -1
- package/dist/{with-rollup-K_iwal4b.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-JDWL67WL.js +0 -20
- package/dist/chunk-44DV37ZV.js.map +0 -1
- package/dist/chunk-47C4CI7R.js +0 -108
- package/dist/chunk-47C4CI7R.js.map +0 -1
- package/dist/chunk-4VDFUBTS.js.map +0 -1
- package/dist/chunk-56JB2BGI.js.map +0 -1
- package/dist/chunk-5GQE5IDB.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-7TAV3BFQ.js.map +0 -1
- package/dist/chunk-BASCPHNM.js.map +0 -1
- package/dist/chunk-BM6E4MUT.js.map +0 -1
- package/dist/chunk-OJ64XMPJ.js.map +0 -1
- package/dist/chunk-PMMV5SQC.js.map +0 -1
- package/dist/chunk-PPPZIBKY.js.map +0 -1
- package/dist/chunk-REWTMYSF.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-VVLOWY74.js.map +0 -1
- package/dist/chunk-X6VNIRHS.js.map +0 -1
- package/dist/chunk-XBPUVQDO.js.map +0 -1
- package/dist/chunk-YTCCC6NG.js.map +0 -1
- package/dist/collection-facade-EJVIN2BY.js +0 -39
- package/dist/config-drift-RFEVGBKR.js.map +0 -1
- package/dist/delegation-IHXOASHV.js +0 -25
- package/dist/derive-CD3ZVIXK.js +0 -21
- package/dist/describe-C6iHNlqJ.d.ts +0 -162
- package/dist/executor-EUPYHCLU.js +0 -9
- package/dist/executor-KZ4B3PRT.js +0 -9
- package/dist/executor-MHUM2VZK.js +0 -21
- package/dist/export-accessible-Z3NSZZHN.js +0 -23
- package/dist/extract-partition-VSKT35JG.js +0 -36
- package/dist/find-OH4BEULF.js +0 -11
- package/dist/issue-AEOL6RXA.js +0 -19
- package/dist/liberate-XSEPRWEA.js +0 -24
- package/dist/noydb-POVTIVQ6.js +0 -62
- package/dist/register-3D6R2MKN.js +0 -23
- package/dist/registry-AI4E46PD.js +0 -19
- package/dist/registry-HSNQ4NBP.js +0 -9
- package/dist/registry-WIX6QCV5.js +0 -17
- package/dist/request-withdrawal-GU4XS2R5.js +0 -31
- package/dist/revoke-ZFETCLYV.js +0 -24
- package/dist/signer-GAIMQQ2J.js +0 -25
- package/dist/storage-KEXIXLGM.js +0 -23
- package/dist/withdraw-accessible-Q7BULAN5.js +0 -28
- /package/dist/{api-JDWL67WL.js.map → api-HV4PDUWF.js.map} +0 -0
- /package/dist/{backup-WRGNTGJF.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-2PJB6H3H.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-723XB5NX.js.map → chunk-42KVY3OC.js.map} +0 -0
- /package/dist/{chunk-62APEMF3.js.map → chunk-4G3IT7J7.js.map} +0 -0
- /package/dist/{chunk-W3JABOVB.js.map → chunk-4RQ62VSR.js.map} +0 -0
- /package/dist/{chunk-WPNP3NF5.js.map → chunk-4ZSGSRBI.js.map} +0 -0
- /package/dist/{chunk-C4NOLA5X.js.map → chunk-5GAX2QOH.js.map} +0 -0
- /package/dist/{chunk-5HRJQSKN.js.map → chunk-5SAZMBW7.js.map} +0 -0
- /package/dist/{chunk-II6DLDZM.js.map → chunk-5TZZUL5X.js.map} +0 -0
- /package/dist/{chunk-TUL5YQWF.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-GDOOG5YQ.js.map → chunk-7IZQTCTR.js.map} +0 -0
- /package/dist/{chunk-TIAYXOEI.js.map → chunk-A4MAD6PO.js.map} +0 -0
- /package/dist/{chunk-5HZULLHE.js.map → chunk-AUR7MRHA.js.map} +0 -0
- /package/dist/{chunk-P2PEPSPK.js.map → chunk-CJEMA2Z6.js.map} +0 -0
- /package/dist/{chunk-L6Q5NA22.js.map → chunk-CTSFKDGA.js.map} +0 -0
- /package/dist/{chunk-J5YUA6JB.js.map → chunk-CWCCYH34.js.map} +0 -0
- /package/dist/{chunk-NTPWQH4S.js.map → chunk-DEYAO3N6.js.map} +0 -0
- /package/dist/{chunk-M3OYTM5Z.js.map → chunk-DFIBOSOQ.js.map} +0 -0
- /package/dist/{chunk-FZK3JJJK.js.map → chunk-E5AHO65E.js.map} +0 -0
- /package/dist/{chunk-F3EKFGUS.js.map → chunk-FC3XK5JT.js.map} +0 -0
- /package/dist/{chunk-3KBK5LU4.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-37JZR7CY.js.map → chunk-IUKGIB2Q.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-IARAK3PK.js.map → chunk-KBBWY46Y.js.map} +0 -0
- /package/dist/{chunk-RNYDHIQL.js.map → chunk-KBF5GSGX.js.map} +0 -0
- /package/dist/{chunk-GLVXQU3E.js.map → chunk-KNXN47MO.js.map} +0 -0
- /package/dist/{chunk-J7OH6R4K.js.map → chunk-KXKLKJW4.js.map} +0 -0
- /package/dist/{chunk-PAJCRV7O.js.map → chunk-KYC4LIEU.js.map} +0 -0
- /package/dist/{chunk-R4IVPJHN.js.map → chunk-LEOXZY36.js.map} +0 -0
- /package/dist/{chunk-25G7PUTA.js.map → chunk-M5I354EE.js.map} +0 -0
- /package/dist/{chunk-26FSVNBL.js.map → chunk-MK2BSS44.js.map} +0 -0
- /package/dist/{chunk-UMH35AQV.js.map → chunk-MSVY7ULZ.js.map} +0 -0
- /package/dist/{chunk-5YBUFCFF.js.map → chunk-NNWK3PSY.js.map} +0 -0
- /package/dist/{chunk-X5QZAJDB.js.map → chunk-NSZ6J5DF.js.map} +0 -0
- /package/dist/{chunk-I6URUNTD.js.map → chunk-NUEFXLZU.js.map} +0 -0
- /package/dist/{chunk-DCVVCJVB.js.map → chunk-OQAODTYR.js.map} +0 -0
- /package/dist/{chunk-QYMMMKOO.js.map → chunk-OV4CMJGR.js.map} +0 -0
- /package/dist/{chunk-V76PHWLE.js.map → chunk-PEDZFFKW.js.map} +0 -0
- /package/dist/{chunk-BJ2XF2RC.js.map → chunk-Q2KVKTC2.js.map} +0 -0
- /package/dist/{chunk-QABECTNN.js.map → chunk-QTXQ6RNZ.js.map} +0 -0
- /package/dist/{chunk-P6TZ3IVN.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-FWART3VA.js.map → chunk-VJ3NQK4K.js.map} +0 -0
- /package/dist/{chunk-3YKBGS3I.js.map → chunk-WNA6T3MA.js.map} +0 -0
- /package/dist/{chunk-7CZ6YXMO.js.map → chunk-WVCQ75OK.js.map} +0 -0
- /package/dist/{chunk-BTFTHF6Q.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-TCGVRKOS.js.map → chunk-YBFOBRTO.js.map} +0 -0
- /package/dist/{chunk-I6JJ6VJS.js.map → chunk-YLN33ADH.js.map} +0 -0
- /package/dist/{chunk-PVC2FBCQ.js.map → chunk-Z5YFRMG5.js.map} +0 -0
- /package/dist/{chunk-EU7HDMT3.js.map → chunk-ZWQFOXBR.js.map} +0 -0
- /package/dist/{collection-facade-EJVIN2BY.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-RHMNYOXP.js.map → dead-filter-VNA4M3GP.js.map} +0 -0
- /package/dist/{delegation-IHXOASHV.js.map → delegation-ZKII5UBI.js.map} +0 -0
- /package/dist/{derive-CD3ZVIXK.js.map → derive-5D2C3WG4.js.map} +0 -0
- /package/dist/{enclave-KGEUW2GL.js.map → enclave-EUBEJGPG.js.map} +0 -0
- /package/dist/{executor-EUPYHCLU.js.map → executor-IL7SRNUZ.js.map} +0 -0
- /package/dist/{executor-KZ4B3PRT.js.map → executor-OUA2Y6YC.js.map} +0 -0
- /package/dist/{executor-MHUM2VZK.js.map → executor-WAEI6PDV.js.map} +0 -0
- /package/dist/{export-accessible-Z3NSZZHN.js.map → export-accessible-O55RKAGC.js.map} +0 -0
- /package/dist/{extract-partition-VSKT35JG.js.map → extract-partition-VBGCBA6Q.js.map} +0 -0
- /package/dist/{fanout-sidecar-NBDKKCHT.js.map → fanout-sidecar-CI5BCMQM.js.map} +0 -0
- /package/dist/{find-OH4BEULF.js.map → find-FISGUWSK.js.map} +0 -0
- /package/dist/{issue-AEOL6RXA.js.map → issue-EQAWIYFQ.js.map} +0 -0
- /package/dist/{ledger-3LR3YO2W.js.map → ledger-AJA2SR3C.js.map} +0 -0
- /package/dist/{liberate-XSEPRWEA.js.map → liberate-SEGR4JLY.js.map} +0 -0
- /package/dist/{link-set-NSUIX6BR.js.map → link-set-YBAC2DQT.js.map} +0 -0
- /package/dist/{noydb-POVTIVQ6.js.map → noydb-AMEOU456.js.map} +0 -0
- /package/dist/{policy-Y7HL2KGA.js.map → policy-SDGRWVIL.js.map} +0 -0
- /package/dist/{post-register-KOFRTCDD.js.map → post-register-MROIUA5O.js.map} +0 -0
- /package/dist/{public-envelope-NC7SPNMV.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-3D6R2MKN.js.map → register-OVJKW7BS.js.map} +0 -0
- /package/dist/{registry-AI4E46PD.js.map → registry-5RJSK5AJ.js.map} +0 -0
- /package/dist/{registry-HSNQ4NBP.js.map → registry-DQRNTWRL.js.map} +0 -0
- /package/dist/{registry-WIX6QCV5.js.map → registry-LTGLERS7.js.map} +0 -0
- /package/dist/{request-withdrawal-GU4XS2R5.js.map → request-withdrawal-27O77D2V.js.map} +0 -0
- /package/dist/{reveal-5Y777N5Q.js.map → reveal-LT3I2K4P.js.map} +0 -0
- /package/dist/{revoke-ZFETCLYV.js.map → revoke-4N45G7D2.js.map} +0 -0
- /package/dist/{signer-GAIMQQ2J.js.map → signer-XJCOV7JD.js.map} +0 -0
- /package/dist/{stale-77LP2EEF.js.map → stale-IYYXEH3U.js.map} +0 -0
- /package/dist/{storage-KEXIXLGM.js.map → storage-Y44CAPE6.js.map} +0 -0
- /package/dist/{store-coordination-provider-NJDDZ264.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-SMAE4IEI.js.map → walk-76UCFXYV.js.map} +0 -0
- /package/dist/{withdraw-accessible-Q7BULAN5.js.map → withdraw-accessible-6J2UH3IN.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadVaultPolicy
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-X66MXMR5.js";
|
|
4
4
|
import {
|
|
5
5
|
jsonSchemaToFields
|
|
6
6
|
} from "./chunk-TQ3REHVF.js";
|
|
@@ -9,31 +9,31 @@ import {
|
|
|
9
9
|
SEQUENCE_COLLECTION,
|
|
10
10
|
compileSequenceFormat,
|
|
11
11
|
resolveSequenceKey
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-5SAZMBW7.js";
|
|
13
13
|
import {
|
|
14
14
|
LinkIntegrityError,
|
|
15
15
|
isLinkCollectionName,
|
|
16
16
|
linkCollectionName,
|
|
17
17
|
linkRowKey
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-PEDZFFKW.js";
|
|
19
19
|
import {
|
|
20
20
|
hashFields,
|
|
21
21
|
validateSatelliteDeclaration
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-ICV32FPK.js";
|
|
23
23
|
import {
|
|
24
24
|
loadFence
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-CTSFKDGA.js";
|
|
26
26
|
import {
|
|
27
27
|
InvertedIndex,
|
|
28
28
|
MemoryIndexStore,
|
|
29
29
|
NO_SEARCH,
|
|
30
30
|
VectorSet,
|
|
31
31
|
buildPersistedIndexCallbacks
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-FGOGS7CE.js";
|
|
33
33
|
import {
|
|
34
34
|
derivePersistedSchema,
|
|
35
35
|
isZod4Schema
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-HBTT7YQX.js";
|
|
37
37
|
import {
|
|
38
38
|
isBaseLive,
|
|
39
39
|
liveBaseIdSet
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
CustodyApi,
|
|
43
43
|
NO_CARGO,
|
|
44
44
|
NO_CUSTODY
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-42KVY3OC.js";
|
|
46
46
|
import {
|
|
47
47
|
ServiceBus,
|
|
48
48
|
WriteHookRegistry,
|
|
@@ -50,49 +50,59 @@ import {
|
|
|
50
50
|
assertCanImport,
|
|
51
51
|
canExport,
|
|
52
52
|
canImport
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-QTXQ6RNZ.js";
|
|
54
54
|
import {
|
|
55
55
|
NO_ATTESTATION
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-3I4KYWNP.js";
|
|
57
57
|
import {
|
|
58
58
|
NO_CLASSIFIED,
|
|
59
|
-
|
|
59
|
+
guardClassifiedCompat,
|
|
60
60
|
resolveClassifiedFields
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-V7RNIND6.js";
|
|
62
62
|
import {
|
|
63
63
|
NO_TIERS,
|
|
64
64
|
classifySealedShred
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-SSFC3IYA.js";
|
|
66
66
|
import {
|
|
67
67
|
NO_PORTABILITY
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-5TZZUL5X.js";
|
|
69
69
|
import {
|
|
70
70
|
runDrainBarrier
|
|
71
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-ZWQFOXBR.js";
|
|
72
72
|
import {
|
|
73
73
|
PERIODS_COLLECTION,
|
|
74
74
|
PERIOD_ARCHIVES_COLLECTION,
|
|
75
75
|
PERIOD_FREEZES_COLLECTION,
|
|
76
76
|
PERIOD_TARGET_PURGES_COLLECTION,
|
|
77
77
|
periodExclusiveUpperBound
|
|
78
|
-
} from "./chunk-
|
|
78
|
+
} from "./chunk-IUKGIB2Q.js";
|
|
79
79
|
import {
|
|
80
80
|
TxContext,
|
|
81
81
|
revertExecuted
|
|
82
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-JC5R7P44.js";
|
|
83
|
+
import {
|
|
84
|
+
DEFAULT_POSTURE,
|
|
85
|
+
ViaGraph
|
|
86
|
+
} from "./chunk-7ZANJ6JZ.js";
|
|
87
|
+
import {
|
|
88
|
+
forgetDerivedFanout,
|
|
89
|
+
ledgerAuditHook,
|
|
90
|
+
putDerivedOutput,
|
|
91
|
+
runGraphDispatchWave
|
|
92
|
+
} from "./chunk-H57P2SJB.js";
|
|
83
93
|
import {
|
|
84
94
|
OverlayedCollection
|
|
85
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-SZCHEDML.js";
|
|
86
96
|
import {
|
|
87
97
|
EXPORT_AUDIT_COLLECTION,
|
|
88
98
|
createExportBlobsHandle,
|
|
89
99
|
runCompaction
|
|
90
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-NYVQCZ54.js";
|
|
91
101
|
import {
|
|
92
102
|
LazyQuery,
|
|
93
103
|
decodeIdxId,
|
|
94
104
|
encodeIdxId
|
|
95
|
-
} from "./chunk-
|
|
105
|
+
} from "./chunk-XJK2WIYN.js";
|
|
96
106
|
import {
|
|
97
107
|
IMPLICIT_LAZY,
|
|
98
108
|
Lru,
|
|
@@ -101,11 +111,10 @@ import {
|
|
|
101
111
|
import {
|
|
102
112
|
resolveManagedSecret,
|
|
103
113
|
saveSealedPassphrase
|
|
104
|
-
} from "./chunk-
|
|
114
|
+
} from "./chunk-CJEMA2Z6.js";
|
|
105
115
|
import {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
} from "./chunk-PMMV5SQC.js";
|
|
116
|
+
dictCollectionName
|
|
117
|
+
} from "./chunk-IIP5WFVT.js";
|
|
109
118
|
import {
|
|
110
119
|
NO_TEAM,
|
|
111
120
|
enrollAuthenticator,
|
|
@@ -116,7 +125,7 @@ import {
|
|
|
116
125
|
rotatePassphrase,
|
|
117
126
|
updateAuthenticator,
|
|
118
127
|
writeMagicLinkGrant
|
|
119
|
-
} from "./chunk-
|
|
128
|
+
} from "./chunk-DEYAO3N6.js";
|
|
120
129
|
import {
|
|
121
130
|
hasRecoveryEnrolled,
|
|
122
131
|
hasStrongRecoveryEnrolled,
|
|
@@ -126,7 +135,7 @@ import {
|
|
|
126
135
|
mintShamirRecoveryEntry,
|
|
127
136
|
savePaperRecoveryEntries,
|
|
128
137
|
saveShamirRecoveryEntries
|
|
129
|
-
} from "./chunk-
|
|
138
|
+
} from "./chunk-VJ3NQK4K.js";
|
|
130
139
|
import {
|
|
131
140
|
INDEXED_STORE_POLICY
|
|
132
141
|
} from "./chunk-VQNJ7UZL.js";
|
|
@@ -142,17 +151,17 @@ import {
|
|
|
142
151
|
listUsers,
|
|
143
152
|
loadKeyring,
|
|
144
153
|
updateKeyringIdentity
|
|
145
|
-
} from "./chunk-
|
|
154
|
+
} from "./chunk-6CU3FN74.js";
|
|
146
155
|
import {
|
|
147
156
|
persistDirectoryConfig,
|
|
148
157
|
readDirectoryConfig
|
|
149
|
-
} from "./chunk-
|
|
158
|
+
} from "./chunk-E5AHO65E.js";
|
|
150
159
|
import {
|
|
151
160
|
generateULID
|
|
152
161
|
} from "./chunk-ZJADGNYF.js";
|
|
153
162
|
import {
|
|
154
163
|
sha256Hex
|
|
155
|
-
} from "./chunk-
|
|
164
|
+
} from "./chunk-MSVY7ULZ.js";
|
|
156
165
|
import {
|
|
157
166
|
NO_FORGET,
|
|
158
167
|
addSubjectRef,
|
|
@@ -161,50 +170,59 @@ import {
|
|
|
161
170
|
readDottedPath,
|
|
162
171
|
rebuildSubjectIndex,
|
|
163
172
|
removeSubjectRef
|
|
164
|
-
} from "./chunk-
|
|
173
|
+
} from "./chunk-A2APWS32.js";
|
|
165
174
|
import {
|
|
166
175
|
NO_SEALED_RECORD
|
|
167
|
-
} from "./chunk-
|
|
176
|
+
} from "./chunk-2HVUKRNE.js";
|
|
168
177
|
import {
|
|
169
178
|
RecordCodec,
|
|
170
179
|
SEALED_CEK_NS,
|
|
171
180
|
findByDet,
|
|
172
181
|
hasPerRecordKey,
|
|
182
|
+
makeReservedEnvelopes,
|
|
173
183
|
openEnvelopeJson,
|
|
174
184
|
queryByDet,
|
|
175
185
|
resolveStableCek,
|
|
176
186
|
validateSchemaInput
|
|
177
|
-
} from "./chunk-
|
|
187
|
+
} from "./chunk-XHD3L3JO.js";
|
|
178
188
|
import {
|
|
179
189
|
buildDeleteMarker,
|
|
180
190
|
buildTombstone,
|
|
181
191
|
isDeleteMarker,
|
|
182
192
|
isTombstone
|
|
183
|
-
} from "./chunk-
|
|
184
|
-
import {
|
|
185
|
-
NOYDB_FORMAT_VERSION
|
|
186
|
-
} from "./chunk-REWTMYSF.js";
|
|
193
|
+
} from "./chunk-Z5YFRMG5.js";
|
|
187
194
|
import {
|
|
188
195
|
encrypt
|
|
189
|
-
} from "./chunk-
|
|
196
|
+
} from "./chunk-AUR7MRHA.js";
|
|
197
|
+
import {
|
|
198
|
+
NOYDB_FORMAT_VERSION,
|
|
199
|
+
SealedHandle
|
|
200
|
+
} from "./chunk-UNJU345Q.js";
|
|
190
201
|
import {
|
|
191
202
|
NO_AGGREGATE,
|
|
192
203
|
Query,
|
|
193
204
|
ScanBuilder
|
|
194
|
-
} from "./chunk-
|
|
205
|
+
} from "./chunk-7HAPHVRJ.js";
|
|
195
206
|
import {
|
|
196
207
|
canonicalGroupKey
|
|
197
|
-
} from "./chunk-
|
|
208
|
+
} from "./chunk-2XE6LIXT.js";
|
|
198
209
|
import {
|
|
199
|
-
moneyRuntime,
|
|
200
210
|
readPath
|
|
201
|
-
} from "./chunk-
|
|
211
|
+
} from "./chunk-KCG3BHFG.js";
|
|
202
212
|
import {
|
|
213
|
+
NO_I18N,
|
|
203
214
|
getAtPath,
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
215
|
+
isDictCollectionName,
|
|
216
|
+
isDictKeyDescriptor,
|
|
217
|
+
isI18nTextDescriptor,
|
|
218
|
+
isStaticDictDescriptor,
|
|
219
|
+
presentI18nForJoin
|
|
220
|
+
} from "./chunk-3C5CKXFI.js";
|
|
221
|
+
import {
|
|
222
|
+
installViaBinder,
|
|
223
|
+
isViaInstalled,
|
|
224
|
+
viaBinder
|
|
225
|
+
} from "./chunk-6G2SCQYS.js";
|
|
208
226
|
import {
|
|
209
227
|
AlreadyElevatedError,
|
|
210
228
|
AttestationError,
|
|
@@ -214,12 +232,13 @@ import {
|
|
|
214
232
|
ConflictError,
|
|
215
233
|
DebugPlaintextError,
|
|
216
234
|
DerivationCapExceededError,
|
|
235
|
+
DictKeyInUseError,
|
|
217
236
|
ElevationExpiredError,
|
|
237
|
+
FieldNotQueryableError,
|
|
218
238
|
ForgetStrategyNotConfiguredError,
|
|
219
239
|
IndexWriteFailureError,
|
|
220
240
|
InvalidKeyError,
|
|
221
241
|
KeyringCorruptError,
|
|
222
|
-
LocaleNotSpecifiedError,
|
|
223
242
|
ManagedRecoveryNotEnrolledError,
|
|
224
243
|
MigrationRequiredError,
|
|
225
244
|
NoAccessError,
|
|
@@ -236,85 +255,281 @@ import {
|
|
|
236
255
|
StaticDictReadonlyError,
|
|
237
256
|
StoreCapabilityError,
|
|
238
257
|
TierNotGrantedError,
|
|
239
|
-
TranslatorNotConfiguredError,
|
|
240
258
|
USER_ENVELOPE_COLLECTION,
|
|
241
259
|
UniqueConstraintError,
|
|
242
260
|
UnknownDictCodeError,
|
|
243
261
|
UnsupportedIndexOptionError,
|
|
244
262
|
ValidationError
|
|
245
|
-
} from "./chunk-
|
|
263
|
+
} from "./chunk-CU62BBRB.js";
|
|
246
264
|
|
|
247
265
|
// src/port/by/default-provider.ts
|
|
248
266
|
async function createDefaultCoordinationProvider(store) {
|
|
249
|
-
const { StoreCoordinationProvider } = await import("./store-coordination-provider-
|
|
267
|
+
const { StoreCoordinationProvider } = await import("./store-coordination-provider-WKGAE2WI.js");
|
|
250
268
|
return new StoreCoordinationProvider(store);
|
|
251
269
|
}
|
|
252
270
|
|
|
253
|
-
// src/
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if (
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
271
|
+
// src/kernel/via-pipeline.ts
|
|
272
|
+
var ViaPipeline = class _ViaPipeline {
|
|
273
|
+
constructor(bindings, taint) {
|
|
274
|
+
this.bindings = bindings;
|
|
275
|
+
this.taint = taint;
|
|
276
|
+
}
|
|
277
|
+
bindings;
|
|
278
|
+
taint;
|
|
279
|
+
/** undefined when there is nothing to enforce — the zero-via fast path is
|
|
280
|
+
* `this.via === undefined` (#553: keeps an all-plain collection sync). */
|
|
281
|
+
static build(bindings, taint) {
|
|
282
|
+
if (bindings.length === 0 && (!taint || taint.postures.size === 0)) return void 0;
|
|
283
|
+
return new _ViaPipeline(bindings, taint);
|
|
284
|
+
}
|
|
285
|
+
/** Refuse a write before crypto runs: awaits each binding's `enforceWrite` in order — first throw wins. */
|
|
286
|
+
async enforceWrite(record, ctx) {
|
|
287
|
+
for (const b of this.bindings) if (b.enforceWrite) await b.enforceWrite(record, ctx);
|
|
288
|
+
}
|
|
289
|
+
ingest(record) {
|
|
290
|
+
let r = record;
|
|
291
|
+
for (const b of this.bindings) if (b.ingest) r = b.ingest(r);
|
|
292
|
+
return r;
|
|
293
|
+
}
|
|
294
|
+
canonicalizeStored(record) {
|
|
295
|
+
let r = record;
|
|
296
|
+
for (const b of this.bindings) if (b.canonicalizeStored) r = b.canonicalizeStored(r);
|
|
297
|
+
return r;
|
|
298
|
+
}
|
|
299
|
+
async encodeWrite(record, ctx) {
|
|
300
|
+
let r = record;
|
|
301
|
+
for (const b of this.bindings) if (b.encodeWrite) r = await b.encodeWrite(r, ctx);
|
|
302
|
+
return r;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Final write-pipeline stage: folds each binding's `encodeAtRest` in
|
|
306
|
+
* order, threading the record and accumulating the sealed-slot map. A
|
|
307
|
+
* field belongs to exactly one via feature — two bindings sealing the
|
|
308
|
+
* same field is a brand-keyed collision and throws.
|
|
309
|
+
*/
|
|
310
|
+
async encodeAtRest(record, crypto) {
|
|
311
|
+
let r = record;
|
|
312
|
+
let sealed;
|
|
313
|
+
const sealedBy = /* @__PURE__ */ new Map();
|
|
314
|
+
for (const b of this.bindings) {
|
|
315
|
+
if (!b.encodeAtRest) continue;
|
|
316
|
+
const result = await b.encodeAtRest(r, crypto);
|
|
317
|
+
r = result.record;
|
|
318
|
+
if (!result.sealed) continue;
|
|
319
|
+
for (const [field, ref2] of Object.entries(result.sealed)) {
|
|
320
|
+
const priorBrand = sealedBy.get(field);
|
|
321
|
+
if (priorBrand !== void 0) {
|
|
322
|
+
throw new ValidationError(
|
|
323
|
+
`via encodeAtRest: field "${field}" sealed by binding "${priorBrand}" is also sealed by binding "${b.brand}"`
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
sealedBy.set(field, b.brand);
|
|
327
|
+
sealed ??= {};
|
|
328
|
+
sealed[field] = ref2;
|
|
329
|
+
}
|
|
269
330
|
}
|
|
331
|
+
return sealed ? { record: r, sealed } : { record: r };
|
|
270
332
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
);
|
|
333
|
+
/** First read-pipeline stage: folds each binding's `decodeAtRest` in order. */
|
|
334
|
+
async decodeAtRest(record, sealed, crypto, opts) {
|
|
335
|
+
let r = record;
|
|
336
|
+
for (const b of this.bindings) if (b.decodeAtRest) r = await b.decodeAtRest(r, sealed, crypto, opts);
|
|
337
|
+
return r;
|
|
277
338
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
339
|
+
async present(record, ctx) {
|
|
340
|
+
let r = record;
|
|
341
|
+
for (const b of this.bindings) if (b.present) r = await b.present(r, ctx);
|
|
342
|
+
return r;
|
|
343
|
+
}
|
|
344
|
+
buildClause(field, op, value) {
|
|
345
|
+
for (const b of this.bindings) {
|
|
346
|
+
const payload = b.buildClause?.(field, op, value);
|
|
347
|
+
if (payload !== void 0) return { brand: b.brand, payload };
|
|
348
|
+
}
|
|
349
|
+
return void 0;
|
|
350
|
+
}
|
|
351
|
+
evaluateClause(clause, actual, op) {
|
|
352
|
+
const b = this.bindings.find((x) => x.brand === clause.brand);
|
|
353
|
+
return b?.evaluateClause ? b.evaluateClause(actual, op, clause.payload) : false;
|
|
354
|
+
}
|
|
355
|
+
decodeResults(record) {
|
|
356
|
+
let r = record;
|
|
357
|
+
for (const b of this.bindings) if (b.decodeResults) r = b.decodeResults(r);
|
|
358
|
+
return r;
|
|
284
359
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
360
|
+
/** undefined = no binding covers the field → caller falls back to generic compare. */
|
|
361
|
+
compareForOrder(field, a, b) {
|
|
362
|
+
for (const bind of this.bindings) {
|
|
363
|
+
const c = bind.compareForOrder?.(field, a, b);
|
|
364
|
+
if (c !== void 0) return c;
|
|
365
|
+
}
|
|
366
|
+
return void 0;
|
|
367
|
+
}
|
|
368
|
+
/** undefined = no binding resolves an order-sort label for `key` at `field`/`locale` (#650 Task 7 —
|
|
369
|
+
* the `orderBy(..., {by:'label'})` per-call-locale channel `compareForOrder` above can't serve). */
|
|
370
|
+
resolveOrderLabel(field, key, locale) {
|
|
371
|
+
for (const bind of this.bindings) {
|
|
372
|
+
const label = bind.resolveOrderLabel?.(field, key, locale);
|
|
373
|
+
if (label !== void 0) return label;
|
|
374
|
+
}
|
|
375
|
+
return void 0;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Which posture governs `field`, if any binding covers it — `undefined`
|
|
379
|
+
* means no binding declares `field` (the generic, non-via query path
|
|
380
|
+
* applies). #629 Task 8's posture consumer: `.where()`/`.orderBy()`
|
|
381
|
+
* consult this before building/evaluating a clause, refusing fields whose
|
|
382
|
+
* posture is `queryable: 'none'` (e.g. blob); every other posture
|
|
383
|
+
* ('det-exact'/'ordered'/'full') is left to the existing per-binding
|
|
384
|
+
* buildClause/evaluateClause/compareForOrder machinery, unchanged.
|
|
385
|
+
*
|
|
386
|
+
* #638 Task 3: the graph-computed taint overlay is consulted FIRST — a
|
|
387
|
+
* derived field's assigned (most-restrictive-of-sources) posture wins over
|
|
388
|
+
* whatever a binding would otherwise report for that field name (no
|
|
389
|
+
* binding covers a computed/derived field today, so this never actually
|
|
390
|
+
* shadows one — see `via-taint-binding.ts`'s `taintBinding.covers`, which
|
|
391
|
+
* only claims the sealed subset for `encodeAtRest`/`decodeAtRest`, not for
|
|
392
|
+
* this lookup).
|
|
393
|
+
*/
|
|
394
|
+
postureFor(field) {
|
|
395
|
+
const t = this.taint?.postures.get(field);
|
|
396
|
+
if (t) return t;
|
|
397
|
+
for (const b of this.bindings) {
|
|
398
|
+
if (b.covers?.(field)) return b.posture;
|
|
399
|
+
}
|
|
400
|
+
return void 0;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Refuse any field-based reducer over a `queryable: 'none'` field (blob) —
|
|
404
|
+
* metadata-only: walks each reducer's `.field` and checks posture, no
|
|
405
|
+
* rewriting. Reducers with no `.field` (e.g. `count()`) are skipped.
|
|
406
|
+
*
|
|
407
|
+
* Shared by `wrapReducers` below (which ALSO applies each binding's
|
|
408
|
+
* rewrite, e.g. money's exact-BigInt reducer swap) and by
|
|
409
|
+
* `ScanBuilder.aggregate()` (#629 Task 8 review fix wave 1), which must
|
|
410
|
+
* call THIS, not `wrapReducers` — wiring full `wrapReducers` into
|
|
411
|
+
* `ScanBuilder.aggregate()` would newly activate money/i18n reducer
|
|
412
|
+
* wrapping on a path that has never run it, a parity-breaking behavior
|
|
413
|
+
* change for existing brands.
|
|
414
|
+
*/
|
|
415
|
+
refuseUnqueryableReducers(spec) {
|
|
416
|
+
for (const reducer of Object.values(spec)) {
|
|
417
|
+
const field = reducer?.field;
|
|
418
|
+
if (field !== void 0 && this.postureFor(field)?.queryable === "none") {
|
|
419
|
+
throw new FieldNotQueryableError(field);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Rewrite an aggregate spec (money exact reducers), then refuse any
|
|
425
|
+
* field-based reducer over a `queryable: 'none'` field (blob) — the same
|
|
426
|
+
* posture gate `.where()`/`.orderBy()` apply, extended to `.aggregate()`
|
|
427
|
+
* (both the bare-spec and builder forms funnel through here).
|
|
428
|
+
*/
|
|
429
|
+
wrapReducers(spec) {
|
|
430
|
+
this.refuseUnqueryableReducers(spec);
|
|
431
|
+
let s = spec;
|
|
432
|
+
for (const b of this.bindings) if (b.wrapReducers) s = b.wrapReducers(s);
|
|
433
|
+
return s;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Deliberate export-layer redaction (#629 Task 9's posture consumer):
|
|
437
|
+
* for every OWN field the decoded record carries, if the covering
|
|
438
|
+
* binding's posture declares `exportable: false` (classified today;
|
|
439
|
+
* money/i18n/blob are all `true`), replace the value with
|
|
440
|
+
* {@link EXPORT_REDACTION_MARKER} — the same `'[sealed]'` string
|
|
441
|
+
* `SealedHandle.toJSON()` (`kernel/types.ts`) already emits, so this is
|
|
442
|
+
* byte-parity with today's accidental redaction, now independent of the
|
|
443
|
+
* field's runtime shape. `SealedHandle.toJSON()` stays untouched as
|
|
444
|
+
* defense-in-depth: a record read via `collection.get()` and
|
|
445
|
+
* JSON.stringify'd outside `exportStream()` still gets the accident's
|
|
446
|
+
* redaction on its own. Never mutates the input; returns a new object
|
|
447
|
+
* only when a field was actually redacted.
|
|
448
|
+
*/
|
|
449
|
+
redactForExport(record) {
|
|
450
|
+
let out;
|
|
451
|
+
for (const field of Object.keys(record)) {
|
|
452
|
+
if (this.postureFor(field)?.exportable === false) {
|
|
453
|
+
out ??= { ...record };
|
|
454
|
+
out[field] = EXPORT_REDACTION_MARKER;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return out ?? record;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Shared fold over a subset of `this.bindings`: runs each binding's
|
|
461
|
+
* `erase`, concatenating shredded/retainedShared counts and residue.
|
|
462
|
+
* Consults `posture.forgettable` (#629 Task 10) — a binding declaring
|
|
463
|
+
* `forgettable: false` is skipped even if it defines `erase` (none does
|
|
464
|
+
* today; a future non-forgettable binding is supported without a brand check).
|
|
465
|
+
*/
|
|
466
|
+
async foldErase(ctx, bindings) {
|
|
467
|
+
let shredded = 0;
|
|
468
|
+
let retainedShared = 0;
|
|
469
|
+
const residue = [];
|
|
470
|
+
for (const b of bindings) {
|
|
471
|
+
if (!b.erase || b.posture.forgettable === false) continue;
|
|
472
|
+
const report = await b.erase(ctx);
|
|
473
|
+
shredded += report.shredded;
|
|
474
|
+
retainedShared += report.retainedShared ?? 0;
|
|
475
|
+
residue.push(...report.residue);
|
|
476
|
+
}
|
|
477
|
+
return retainedShared > 0 ? { shredded, residue, retainedShared } : { shredded, residue };
|
|
478
|
+
}
|
|
479
|
+
/** `forget()`'s per-ref erasure door: folds every binding's `erase`. */
|
|
480
|
+
async erase(ctx) {
|
|
481
|
+
return this.foldErase(ctx, this.bindings);
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* `forget()`'s SEALED-posture-only erase fold (#629 Task 10, `Collection._onViaErase`'s
|
|
485
|
+
* one caller) — classified today; metadata-filtered on
|
|
486
|
+
* `posture.encryptedAtRest`, not brand-checked. `undefined` when no
|
|
487
|
+
* sealed-posture binding is compiled in — `vault.ts` then falls back to
|
|
488
|
+
* its own bare-`sensitive` classification, which no via binding covers.
|
|
489
|
+
*/
|
|
490
|
+
async eraseSealed(ctx) {
|
|
491
|
+
const sealed = this.bindings.filter((b) => b.posture.encryptedAtRest === "sealed");
|
|
492
|
+
return sealed.length === 0 ? void 0 : this.foldErase(ctx, sealed);
|
|
290
493
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
494
|
+
/** True iff any binding implements decodeResults (query fast-path gating). */
|
|
495
|
+
get hasResultDecode() {
|
|
496
|
+
return this.bindings.some((b) => b.decodeResults !== void 0);
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* True iff any binding declares an at-rest hook (`encodeAtRest`/
|
|
500
|
+
* `decodeAtRest`) — the codec boundary's async-stack gate (#629 Task 3
|
|
501
|
+
* consults this to choose between the hook path and today's inline
|
|
502
|
+
* sealed-slot path; a stack with only sync hooks, e.g. money-only, stays
|
|
503
|
+
* `false`).
|
|
504
|
+
*/
|
|
505
|
+
get hasAtRestHooks() {
|
|
506
|
+
return this.bindings.some((b) => b.encodeAtRest !== void 0 || b.decodeAtRest !== void 0);
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Fold every binding's `describeFragment()` into one `brand -> fragment`
|
|
510
|
+
* map (#650 Task 7 — the first-ever consumer; `describeFragment` was
|
|
511
|
+
* declared at `via.ts:136` since #623 with zero callers until this task).
|
|
512
|
+
* `undefined` when no compiled binding implements it. Consumed by
|
|
513
|
+
* `Collection.describe()`/`describeAsync()`, threaded to `buildDescription`
|
|
514
|
+
* as `BuildDescriptionInput.viaFragments`.
|
|
515
|
+
*/
|
|
516
|
+
describeFragments() {
|
|
517
|
+
let out;
|
|
518
|
+
for (const b of this.bindings) {
|
|
519
|
+
const f = b.describeFragment?.();
|
|
520
|
+
if (f !== void 0) {
|
|
521
|
+
out ??= {};
|
|
522
|
+
out[b.brand] = f;
|
|
523
|
+
}
|
|
316
524
|
}
|
|
525
|
+
return out;
|
|
317
526
|
}
|
|
527
|
+
};
|
|
528
|
+
var EXPORT_REDACTION_MARKER = "[sealed]";
|
|
529
|
+
function exportRedact(coll, record) {
|
|
530
|
+
if (record === null || typeof record !== "object") return record;
|
|
531
|
+
const via2 = coll.via;
|
|
532
|
+
return via2 ? via2.redactForExport(record) : record;
|
|
318
533
|
}
|
|
319
534
|
|
|
320
535
|
// src/with-lookup/indexing/collection-facade.ts
|
|
@@ -402,7 +617,7 @@ async function rebuildIndexes(ctx) {
|
|
|
402
617
|
for (const recordId of canonicalIds) {
|
|
403
618
|
const envelope = await ctx.adapter.get(ctx.vault, ctx.name, recordId);
|
|
404
619
|
if (!envelope) continue;
|
|
405
|
-
const record = await ctx.codec.decryptRecord(envelope, { skipValidation: true });
|
|
620
|
+
const record = await ctx.codec.decryptRecord(envelope, { skipValidation: true, id: recordId });
|
|
406
621
|
if (record === null) continue;
|
|
407
622
|
await maintainPersistedIndexesOnPut(ctx, recordId, record, null, envelope._v);
|
|
408
623
|
}
|
|
@@ -455,7 +670,7 @@ async function reconcileIndex(ctx, field, opts = {}) {
|
|
|
455
670
|
if (id.startsWith("_")) continue;
|
|
456
671
|
const env = await ctx.adapter.get(ctx.vault, ctx.name, id);
|
|
457
672
|
if (!env) continue;
|
|
458
|
-
const record = await ctx.codec.decryptRecord(env, { skipValidation: true });
|
|
673
|
+
const record = await ctx.codec.decryptRecord(env, { skipValidation: true, id });
|
|
459
674
|
if (record === null) continue;
|
|
460
675
|
const live = readPersistedValue(record, field);
|
|
461
676
|
const stored = sidecar.get(id);
|
|
@@ -879,13 +1094,14 @@ function deriveWidget(opts) {
|
|
|
879
1094
|
case "percent":
|
|
880
1095
|
return "number";
|
|
881
1096
|
}
|
|
882
|
-
if (opts.dict !== void 0) return "select";
|
|
1097
|
+
if (opts.dict !== void 0 || opts.lookup !== void 0) return "select";
|
|
883
1098
|
if (opts.type === "boolean") return "checkbox";
|
|
884
1099
|
if (opts.type === "number") return "number";
|
|
885
1100
|
return "text";
|
|
886
1101
|
}
|
|
887
1102
|
function buildDescription(input) {
|
|
888
|
-
const { collection, fieldMeta, moneyFields, dictKeyFields, computed, refs, zodFields, dictLabels, meta, i18nFields, classified } = input;
|
|
1103
|
+
const { collection, fieldMeta, moneyFields, dictKeyFields, lookupFields, computed, refs, zodFields, dictLabels, meta, i18nFields, classified, taint } = input;
|
|
1104
|
+
const lookupFragments = input.viaFragments?.["lookup"]?.lookupFields;
|
|
889
1105
|
if (zodFields !== void 0 && Object.keys(zodFields).length > 0 && fieldMeta !== void 0) {
|
|
890
1106
|
const knownFields = /* @__PURE__ */ new Set([
|
|
891
1107
|
...Object.keys(moneyFields ?? {}),
|
|
@@ -893,13 +1109,15 @@ function buildDescription(input) {
|
|
|
893
1109
|
...Object.keys(refs),
|
|
894
1110
|
...Object.keys(computed ?? {}),
|
|
895
1111
|
...Object.keys(zodFields),
|
|
896
|
-
...Object.keys(i18nFields ?? {})
|
|
1112
|
+
...Object.keys(i18nFields ?? {}),
|
|
1113
|
+
...Object.keys(lookupFields ?? {})
|
|
897
1114
|
]);
|
|
898
1115
|
validateFieldMetaKeys(collection, fieldMeta, knownFields);
|
|
899
1116
|
}
|
|
900
1117
|
const allKeys = /* @__PURE__ */ new Set([
|
|
901
1118
|
...Object.keys(moneyFields ?? {}),
|
|
902
1119
|
...Object.keys(dictKeyFields ?? {}),
|
|
1120
|
+
...Object.keys(lookupFields ?? {}),
|
|
903
1121
|
...Object.keys(refs),
|
|
904
1122
|
...Object.keys(computed ?? {}),
|
|
905
1123
|
...Object.keys(fieldMeta ?? {}),
|
|
@@ -912,15 +1130,18 @@ function buildDescription(input) {
|
|
|
912
1130
|
const zod = zodFields?.[key];
|
|
913
1131
|
const money = moneyFields?.[key];
|
|
914
1132
|
const dict = dictKeyFields?.[key];
|
|
1133
|
+
const lookupDesc = lookupFields?.[key];
|
|
915
1134
|
const refDesc = refs[key];
|
|
916
1135
|
const isComputed = computed !== void 0 && key in computed;
|
|
917
1136
|
const i18nDesc = i18nFields?.[key];
|
|
918
1137
|
const cls = classified?.[key];
|
|
1138
|
+
const taintPosture = taint?.postures.get(key);
|
|
919
1139
|
let type = zod?.type ?? "unknown";
|
|
920
1140
|
const inferred = {};
|
|
921
1141
|
let moneyBlock;
|
|
922
1142
|
let dictBlock;
|
|
923
1143
|
let refBlock;
|
|
1144
|
+
let lookupBlock;
|
|
924
1145
|
if (money) {
|
|
925
1146
|
type = "number";
|
|
926
1147
|
inferred.semanticType = "currency";
|
|
@@ -967,10 +1188,49 @@ function buildDescription(input) {
|
|
|
967
1188
|
dictBlock = { name: dict.name, static: false };
|
|
968
1189
|
}
|
|
969
1190
|
}
|
|
1191
|
+
} else if (lookupDesc) {
|
|
1192
|
+
type = "enum";
|
|
1193
|
+
const lookupLabelMap = dictLabels?.[lookupDesc.dimension];
|
|
1194
|
+
const lookupLabelMapHasEntries = lookupLabelMap !== void 0 && Object.keys(lookupLabelMap).length > 0;
|
|
1195
|
+
if (lookupDesc.table !== void 0) {
|
|
1196
|
+
const displayLocale = lookupDesc.displayLocale;
|
|
1197
|
+
const table = lookupDesc.table;
|
|
1198
|
+
const values = (lookupDesc.keys ?? Object.keys(table)).map((k) => {
|
|
1199
|
+
const localeMap = table[k];
|
|
1200
|
+
const label = displayLocale !== void 0 ? localeMap?.[displayLocale] ?? void 0 : void 0;
|
|
1201
|
+
return label !== void 0 ? { value: k, label } : { value: k };
|
|
1202
|
+
});
|
|
1203
|
+
dictBlock = { name: lookupDesc.dimension, static: true, values };
|
|
1204
|
+
} else if (lookupLabelMapHasEntries) {
|
|
1205
|
+
const values = Object.entries(lookupLabelMap).map(([value, label]) => ({ value, label }));
|
|
1206
|
+
dictBlock = { name: lookupDesc.dimension, static: false, values };
|
|
1207
|
+
} else if (lookupDesc.keys !== void 0) {
|
|
1208
|
+
const values = lookupDesc.keys.map((k) => {
|
|
1209
|
+
const label = lookupDesc.labels?.[k];
|
|
1210
|
+
return label !== void 0 ? { value: k, label } : { value: k };
|
|
1211
|
+
});
|
|
1212
|
+
dictBlock = { name: lookupDesc.dimension, static: false, values };
|
|
1213
|
+
} else {
|
|
1214
|
+
dictBlock = { name: lookupDesc.dimension, static: false };
|
|
1215
|
+
}
|
|
970
1216
|
} else if (i18nDesc) {
|
|
971
1217
|
type = "string";
|
|
972
1218
|
} else if (isComputed) {
|
|
973
1219
|
}
|
|
1220
|
+
const lookupFragmentEntry = lookupFragments?.[key];
|
|
1221
|
+
if (lookupFragmentEntry !== void 0) {
|
|
1222
|
+
lookupBlock = {
|
|
1223
|
+
...lookupFragmentEntry.dimension !== void 0 ? { dimension: lookupFragmentEntry.dimension } : {},
|
|
1224
|
+
backing: lookupFragmentEntry.backing,
|
|
1225
|
+
vocabulary: lookupFragmentEntry.vocabulary,
|
|
1226
|
+
key: lookupFragmentEntry.key,
|
|
1227
|
+
onDelete: lookupFragmentEntry.onDelete,
|
|
1228
|
+
...lookupFragmentEntry.altKeys !== void 0 ? { altKeys: lookupFragmentEntry.altKeys } : {},
|
|
1229
|
+
...lookupFragmentEntry.present !== void 0 ? { present: lookupFragmentEntry.present } : {},
|
|
1230
|
+
...lookupFragmentEntry.sortBy !== void 0 ? { sortBy: lookupFragmentEntry.sortBy } : {},
|
|
1231
|
+
...lookupFragmentEntry.keys !== void 0 ? { keys: lookupFragmentEntry.keys } : {}
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
974
1234
|
if (cls !== void 0) inferred.sensitivity = cls.sensitivity;
|
|
975
1235
|
const channelEntry = fieldMeta?.[key];
|
|
976
1236
|
const zodMeta = zod?.meta;
|
|
@@ -992,6 +1252,7 @@ function buildDescription(input) {
|
|
|
992
1252
|
type,
|
|
993
1253
|
...resolved.semanticType !== void 0 ? { semanticType: resolved.semanticType } : {},
|
|
994
1254
|
...dictBlock !== void 0 ? { dict: dictBlock } : {},
|
|
1255
|
+
...lookupBlock !== void 0 ? { lookup: lookupBlock } : {},
|
|
995
1256
|
...resolved.widget !== void 0 ? { resolvedWidget: resolved.widget } : {}
|
|
996
1257
|
});
|
|
997
1258
|
const editable = !isComputed && key !== "id";
|
|
@@ -1013,6 +1274,7 @@ function buildDescription(input) {
|
|
|
1013
1274
|
...refBlock !== void 0 ? { ref: refBlock } : {},
|
|
1014
1275
|
...moneyBlock !== void 0 ? { money: moneyBlock } : {},
|
|
1015
1276
|
...dictBlock !== void 0 ? { dict: dictBlock } : {},
|
|
1277
|
+
...lookupBlock !== void 0 ? { lookup: lookupBlock } : {},
|
|
1016
1278
|
...isComputed ? { computed: true } : {},
|
|
1017
1279
|
...i18nBlock !== void 0 ? { i18n: i18nBlock } : {},
|
|
1018
1280
|
widget,
|
|
@@ -1024,7 +1286,8 @@ function buildDescription(input) {
|
|
|
1024
1286
|
list: cls.list.kind === "omit" ? "omit" : cls.list.kind === "mask" ? { mask: cls.list.pattern } : { rider: cls.list.rider },
|
|
1025
1287
|
...cls.equatable === true ? { equatable: true } : {}
|
|
1026
1288
|
}
|
|
1027
|
-
} : {}
|
|
1289
|
+
} : {},
|
|
1290
|
+
...taintPosture !== void 0 ? { taint: { posture: taintPosture, forcedBy: taint?.provenance?.get(key) ?? [] } } : {}
|
|
1028
1291
|
};
|
|
1029
1292
|
fields.push(field);
|
|
1030
1293
|
}
|
|
@@ -1075,7 +1338,7 @@ async function resolveStaleOnRead(accessor, outputCollection, id) {
|
|
|
1075
1338
|
}
|
|
1076
1339
|
const sourceWithId = { ...source, id };
|
|
1077
1340
|
if (DerivationExecutor === null) {
|
|
1078
|
-
({ DerivationExecutor } = await import("./executor-
|
|
1341
|
+
({ DerivationExecutor } = await import("./executor-WAEI6PDV.js"));
|
|
1079
1342
|
}
|
|
1080
1343
|
const ctx = { vault: accessor.getReadOnlyFacade() };
|
|
1081
1344
|
const result = await DerivationExecutor.run(spec, sourceWithId, 0, strategyHash, ctx);
|
|
@@ -1172,51 +1435,64 @@ var NO_HISTORY = {
|
|
|
1172
1435
|
}
|
|
1173
1436
|
};
|
|
1174
1437
|
|
|
1175
|
-
// src/with-shape/i18n/strategy.ts
|
|
1176
|
-
function notEnabled2(op) {
|
|
1177
|
-
return new Error(
|
|
1178
|
-
`${op} requires the i18n strategy. Import \`{ withI18n }\` from "@noy-db/hub/i18n" and pass it to \`createNoydb({ i18nStrategy: withI18n() })\`.`
|
|
1179
|
-
);
|
|
1180
|
-
}
|
|
1181
|
-
var NO_I18N = {
|
|
1182
|
-
applyI18nLocale(record) {
|
|
1183
|
-
return record;
|
|
1184
|
-
},
|
|
1185
|
-
validateI18nTextValue() {
|
|
1186
|
-
throw notEnabled2("i18nText field validation");
|
|
1187
|
-
},
|
|
1188
|
-
enforceScript(value) {
|
|
1189
|
-
return { value, warnings: [] };
|
|
1190
|
-
},
|
|
1191
|
-
computeExemptFills() {
|
|
1192
|
-
return /* @__PURE__ */ new Map();
|
|
1193
|
-
},
|
|
1194
|
-
densify() {
|
|
1195
|
-
},
|
|
1196
|
-
buildDictionaryHandle() {
|
|
1197
|
-
throw notEnabled2("vault.dictionary()");
|
|
1198
|
-
}
|
|
1199
|
-
};
|
|
1200
|
-
|
|
1201
1438
|
// src/with-party/team/sync-strategy.ts
|
|
1202
|
-
function
|
|
1439
|
+
function notEnabled2(op) {
|
|
1203
1440
|
return new Error(
|
|
1204
1441
|
`${op} requires the sync strategy. Import \`{ withSync }\` from "@noy-db/hub/sync" and pass it to \`createNoydb({ syncStrategy: withSync() })\`.`
|
|
1205
1442
|
);
|
|
1206
1443
|
}
|
|
1207
1444
|
var NO_SYNC = {
|
|
1208
1445
|
buildSyncEngine() {
|
|
1209
|
-
throw
|
|
1446
|
+
throw notEnabled2("SyncEngine");
|
|
1210
1447
|
},
|
|
1211
1448
|
buildSyncTransaction() {
|
|
1212
|
-
throw
|
|
1449
|
+
throw notEnabled2("SyncTransaction");
|
|
1213
1450
|
},
|
|
1214
1451
|
buildPresence() {
|
|
1215
|
-
throw
|
|
1452
|
+
throw notEnabled2("collection.presence()");
|
|
1216
1453
|
}
|
|
1217
1454
|
};
|
|
1218
1455
|
|
|
1219
|
-
// src/
|
|
1456
|
+
// src/shape/via-blob/binding.ts
|
|
1457
|
+
async function eraseBlobs(ctx, cfg) {
|
|
1458
|
+
if (cfg.purgeBlobsForRecord) return cfg.purgeBlobsForRecord(ctx.id);
|
|
1459
|
+
return { shredded: 0, residue: [] };
|
|
1460
|
+
}
|
|
1461
|
+
function buildBlobDescribeFragment(fields) {
|
|
1462
|
+
return {
|
|
1463
|
+
blobFields: Object.fromEntries(
|
|
1464
|
+
Object.entries(fields).map(([field, p]) => [field, {
|
|
1465
|
+
...p.retainDays !== void 0 ? { retainDays: p.retainDays } : {},
|
|
1466
|
+
...p.evictWhen !== void 0 ? { evictWhen: true } : {},
|
|
1467
|
+
...p.legalHold !== void 0 ? { legalHold: true } : {},
|
|
1468
|
+
...p.retainUntil !== void 0 ? { retainUntil: true } : {},
|
|
1469
|
+
...p.external === true ? { external: true } : {},
|
|
1470
|
+
...p.public === true ? { public: true } : {},
|
|
1471
|
+
...p.backlink !== void 0 ? { backlink: p.backlink } : {}
|
|
1472
|
+
}])
|
|
1473
|
+
)
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
function blobBinding(cfg) {
|
|
1477
|
+
return {
|
|
1478
|
+
brand: "blob",
|
|
1479
|
+
// encryptedAtRest: 'envelope' — blob chunks are AEAD-encrypted envelopes
|
|
1480
|
+
// in `_blob_chunks` (service-side), not sealed record fields; queryable:
|
|
1481
|
+
// 'none' — nothing indexes blob content; exportable — vault.exportBlobs()
|
|
1482
|
+
// is a first-class door; forgettable — shredAllForRecord participates in
|
|
1483
|
+
// vault.forget().
|
|
1484
|
+
posture: { encryptedAtRest: "envelope", queryable: "none", exportable: true, forgettable: true },
|
|
1485
|
+
covers: (field) => field in cfg.fields,
|
|
1486
|
+
erase: (ctx) => eraseBlobs(ctx, cfg),
|
|
1487
|
+
describeFragment: () => buildBlobDescribeFragment(cfg.fields)
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
function linkBlobVia() {
|
|
1491
|
+
installViaBinder("blob", (c) => blobBinding(c));
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
// src/port/with/blob-strategy.ts
|
|
1495
|
+
linkBlobVia();
|
|
1220
1496
|
var NO_BLOBS = {
|
|
1221
1497
|
openSlot() {
|
|
1222
1498
|
throw new Error(
|
|
@@ -1225,23 +1501,502 @@ var NO_BLOBS = {
|
|
|
1225
1501
|
}
|
|
1226
1502
|
};
|
|
1227
1503
|
|
|
1504
|
+
// src/shape/via-lookup/registry.ts
|
|
1505
|
+
function enforceStaticDictOnPut(staticFields, record) {
|
|
1506
|
+
if (!staticFields || Object.keys(staticFields).length === 0) return;
|
|
1507
|
+
if (!record || typeof record !== "object") return;
|
|
1508
|
+
const obj = record;
|
|
1509
|
+
for (const [field, desc] of Object.entries(staticFields)) {
|
|
1510
|
+
if (desc.validateCodes === false) continue;
|
|
1511
|
+
const known = new Set(desc.keys);
|
|
1512
|
+
const values = getAtPath(obj, field);
|
|
1513
|
+
for (const value of values) {
|
|
1514
|
+
if (value === void 0 || value === null) continue;
|
|
1515
|
+
const codes = Array.isArray(value) ? value : [value];
|
|
1516
|
+
for (const code of codes) {
|
|
1517
|
+
if (typeof code !== "string") continue;
|
|
1518
|
+
if (!known.has(code)) {
|
|
1519
|
+
throw new UnknownDictCodeError(desc.name, field, code);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
function resolveDictSource(leftCollection, field, staticDescriptorByField, dictKeyFieldRegistry, getDictionaryHandle) {
|
|
1526
|
+
const staticFields = staticDescriptorByField.get(leftCollection);
|
|
1527
|
+
if (staticFields && field in staticFields) {
|
|
1528
|
+
const desc = staticFields[field];
|
|
1529
|
+
const rows = Object.entries(desc.table).map(
|
|
1530
|
+
([key, labels]) => ({ key, labels, ...labels })
|
|
1531
|
+
);
|
|
1532
|
+
const source = {
|
|
1533
|
+
snapshot() {
|
|
1534
|
+
return rows;
|
|
1535
|
+
},
|
|
1536
|
+
lookupById(id) {
|
|
1537
|
+
return rows.find((e) => e["key"] === id);
|
|
1538
|
+
}
|
|
1539
|
+
};
|
|
1540
|
+
if (desc.displayLocale !== void 0) {
|
|
1541
|
+
;
|
|
1542
|
+
source.displayLocale = desc.displayLocale;
|
|
1543
|
+
}
|
|
1544
|
+
return source;
|
|
1545
|
+
}
|
|
1546
|
+
const dictFields = dictKeyFieldRegistry.get(leftCollection);
|
|
1547
|
+
if (!dictFields || !(field in dictFields)) return null;
|
|
1548
|
+
const dictName = dictFields[field];
|
|
1549
|
+
if (!dictName) return null;
|
|
1550
|
+
const handle = getDictionaryHandle(dictName);
|
|
1551
|
+
return {
|
|
1552
|
+
snapshot() {
|
|
1553
|
+
return handle.snapshotEntries();
|
|
1554
|
+
},
|
|
1555
|
+
lookupById(id) {
|
|
1556
|
+
const entries = handle.snapshotEntries();
|
|
1557
|
+
return entries.find((e) => e["key"] === id);
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
async function updateReferencingRecords(registry, getCollection, name, oldKey, newKey) {
|
|
1562
|
+
for (const [collectionName, dictFields] of registry) {
|
|
1563
|
+
const fields = Object.entries(dictFields).filter(([, dn]) => dn === name).map(([field]) => field);
|
|
1564
|
+
if (fields.length === 0) continue;
|
|
1565
|
+
const coll = getCollection(collectionName);
|
|
1566
|
+
const records = await coll.list();
|
|
1567
|
+
for (const record of records) {
|
|
1568
|
+
let changed = false;
|
|
1569
|
+
const updated = { ...record };
|
|
1570
|
+
for (const field of fields) {
|
|
1571
|
+
if (updated[field] === oldKey) {
|
|
1572
|
+
updated[field] = newKey;
|
|
1573
|
+
changed = true;
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
if (changed) {
|
|
1577
|
+
const id = record["id"];
|
|
1578
|
+
if (id !== void 0) {
|
|
1579
|
+
await coll.put(id, updated);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
function resolveLabelFromMap(labels, locale, fallback) {
|
|
1586
|
+
if (labels[locale] !== void 0) return labels[locale];
|
|
1587
|
+
const chain = Array.isArray(fallback) ? fallback : fallback ? [fallback] : [];
|
|
1588
|
+
for (const fb of chain) {
|
|
1589
|
+
if (fb === "any") {
|
|
1590
|
+
const any = Object.values(labels)[0];
|
|
1591
|
+
if (any !== void 0) return any;
|
|
1592
|
+
} else if (labels[fb] !== void 0) {
|
|
1593
|
+
return labels[fb];
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
return void 0;
|
|
1597
|
+
}
|
|
1598
|
+
function lookupToStaticDictCompat(desc) {
|
|
1599
|
+
if (desc.backing !== "static" || desc.table === void 0) return void 0;
|
|
1600
|
+
return {
|
|
1601
|
+
_noydbStaticDict: true,
|
|
1602
|
+
_viaBrand: "i18n",
|
|
1603
|
+
name: desc.dimension,
|
|
1604
|
+
table: desc.table,
|
|
1605
|
+
keys: desc.keys ?? Object.keys(desc.table),
|
|
1606
|
+
...desc.displayLocale !== void 0 ? { displayLocale: desc.displayLocale } : {},
|
|
1607
|
+
...desc.onMissing !== void 0 ? { onMissing: desc.onMissing } : {},
|
|
1608
|
+
...desc.substitute !== void 0 ? { substitute: desc.substitute } : {},
|
|
1609
|
+
validateCodes: desc.vocabulary === "closed"
|
|
1610
|
+
};
|
|
1611
|
+
}
|
|
1612
|
+
function collectLookupDictCompat(lookupFields) {
|
|
1613
|
+
const dictFieldMap = {};
|
|
1614
|
+
const staticEntries = [];
|
|
1615
|
+
for (const [field, desc] of Object.entries(lookupFields ?? {})) {
|
|
1616
|
+
if (desc.backing === "reserved") {
|
|
1617
|
+
dictFieldMap[field] = desc.dimension;
|
|
1618
|
+
} else {
|
|
1619
|
+
const compat = lookupToStaticDictCompat(desc);
|
|
1620
|
+
if (compat) staticEntries.push([field, compat]);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
return { dictFieldMap, staticEntries };
|
|
1624
|
+
}
|
|
1625
|
+
function materializeBackingTable(descriptor, rows) {
|
|
1626
|
+
const keys = new Set(rows.keys());
|
|
1627
|
+
const owner = /* @__PURE__ */ new Map();
|
|
1628
|
+
for (const key of keys) owner.set(key, key);
|
|
1629
|
+
const altIndex = /* @__PURE__ */ new Map();
|
|
1630
|
+
const altFields = descriptor.altKeys ?? [];
|
|
1631
|
+
for (const [canonicalKey, row] of rows) {
|
|
1632
|
+
for (const altField of altFields) {
|
|
1633
|
+
const value = row[altField];
|
|
1634
|
+
if (typeof value !== "string" || value === "") continue;
|
|
1635
|
+
const existingOwner = owner.get(value);
|
|
1636
|
+
if (existingOwner !== void 0 && existingOwner !== canonicalKey) {
|
|
1637
|
+
throw new ValidationError(
|
|
1638
|
+
`lookup "${descriptor.dimension}": altKey field "${altField}" value "${value}" is claimed by both "${existingOwner}" and "${canonicalKey}" \u2014 key/altKey values must be unique across the dimension.`
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
owner.set(value, canonicalKey);
|
|
1642
|
+
altIndex.set(value, canonicalKey);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
return { keys, altIndex };
|
|
1646
|
+
}
|
|
1647
|
+
function checkLookupMembership(descriptor, key, getDictionary, getCollection) {
|
|
1648
|
+
if (descriptor.backing === "static") {
|
|
1649
|
+
const known = descriptor.keys ?? (descriptor.table ? Object.keys(descriptor.table) : []);
|
|
1650
|
+
return known.includes(key);
|
|
1651
|
+
}
|
|
1652
|
+
if (descriptor.backing === "reserved") {
|
|
1653
|
+
if ((descriptor.keys ?? []).includes(key)) return true;
|
|
1654
|
+
return getDictionary(descriptor.dimension).snapshotEntries().some((e) => e["key"] === key);
|
|
1655
|
+
}
|
|
1656
|
+
return buildLookupAltIndex(descriptor, getDictionary, getCollection).keys.has(key);
|
|
1657
|
+
}
|
|
1658
|
+
function buildLookupAltIndex(descriptor, getDictionary, getCollection) {
|
|
1659
|
+
if (descriptor.backing === "static") {
|
|
1660
|
+
return materializeBackingTable(descriptor, new Map(Object.entries(descriptor.table ?? {})));
|
|
1661
|
+
}
|
|
1662
|
+
if (descriptor.backing === "reserved") {
|
|
1663
|
+
const entries = getDictionary(descriptor.dimension).snapshotEntries();
|
|
1664
|
+
return materializeBackingTable(descriptor, new Map(entries.map((e) => [String(e["key"]), e])));
|
|
1665
|
+
}
|
|
1666
|
+
const rows = getCollection(descriptor.dimension).querySourceForJoin().snapshot();
|
|
1667
|
+
return materializeBackingTable(
|
|
1668
|
+
descriptor,
|
|
1669
|
+
new Map(
|
|
1670
|
+
rows.map((r) => {
|
|
1671
|
+
const row = r;
|
|
1672
|
+
return [String(row[descriptor.key]), row];
|
|
1673
|
+
})
|
|
1674
|
+
)
|
|
1675
|
+
);
|
|
1676
|
+
}
|
|
1677
|
+
function collectLookupRefEdges(collectionName, lookupFields) {
|
|
1678
|
+
const edges = [];
|
|
1679
|
+
for (const [field, desc] of Object.entries(lookupFields ?? {})) {
|
|
1680
|
+
if (desc.backing === "static") continue;
|
|
1681
|
+
const backing = desc.backing === "reserved" ? dictCollectionName(desc.dimension) : desc.dimension;
|
|
1682
|
+
edges.push({
|
|
1683
|
+
referencing: { collection: collectionName, field },
|
|
1684
|
+
sources: [
|
|
1685
|
+
{ collection: backing, field: "*" },
|
|
1686
|
+
...desc.present?.label !== void 0 ? [{ collection: backing, field: desc.present.label }] : []
|
|
1687
|
+
],
|
|
1688
|
+
onDelete: desc.onDelete,
|
|
1689
|
+
keyField: desc.key
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
return edges;
|
|
1693
|
+
}
|
|
1694
|
+
function registerLookupRefEdges(graph, collectionName, lookupFields) {
|
|
1695
|
+
for (const e of collectLookupRefEdges(collectionName, lookupFields)) {
|
|
1696
|
+
graph.registerDerived(e.referencing, e.sources, "ref", "record", e.onDelete, e.keyField);
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
function buildLookupSnapshotRows(descriptor, isReservedDimension, getDictionary, getCollection) {
|
|
1700
|
+
const dimension = descriptor.dimension;
|
|
1701
|
+
if (isReservedDimension(dimension)) {
|
|
1702
|
+
const rows = /* @__PURE__ */ new Map();
|
|
1703
|
+
for (const entry of getDictionary(dimension).snapshotEntries()) {
|
|
1704
|
+
const key = entry["key"];
|
|
1705
|
+
if (typeof key === "string") rows.set(key, entry);
|
|
1706
|
+
}
|
|
1707
|
+
return rows;
|
|
1708
|
+
}
|
|
1709
|
+
if (descriptor.backing === "collection") {
|
|
1710
|
+
const rawRows = getCollection(dimension).querySourceForJoin().snapshot();
|
|
1711
|
+
const rows = /* @__PURE__ */ new Map();
|
|
1712
|
+
for (const r of rawRows) {
|
|
1713
|
+
const row = r;
|
|
1714
|
+
rows.set(String(row[descriptor.key]), row);
|
|
1715
|
+
}
|
|
1716
|
+
return rows;
|
|
1717
|
+
}
|
|
1718
|
+
return void 0;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
// src/shape/via-lookup/snapshot.ts
|
|
1722
|
+
function readRowField(row, field, by, locale, fallback) {
|
|
1723
|
+
if (!row || field === void 0) return void 0;
|
|
1724
|
+
const raw = row[field];
|
|
1725
|
+
if (by === void 0) return typeof raw === "string" ? raw : void 0;
|
|
1726
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
1727
|
+
const map = raw;
|
|
1728
|
+
const val = map[locale];
|
|
1729
|
+
if (typeof val === "string") return val;
|
|
1730
|
+
if (typeof fallback === "string" && typeof map[fallback] === "string") return map[fallback];
|
|
1731
|
+
return void 0;
|
|
1732
|
+
}
|
|
1733
|
+
function buildLookupSnapshot(dimension, rows, descriptor) {
|
|
1734
|
+
void dimension;
|
|
1735
|
+
return {
|
|
1736
|
+
row: (key) => rows.get(key),
|
|
1737
|
+
label: (key, locale, fallback) => readRowField(rows.get(key), descriptor.present?.label, descriptor.present?.by, locale, fallback),
|
|
1738
|
+
compareKeys: (a, b, locale) => {
|
|
1739
|
+
const sortField = descriptor.sortBy ?? descriptor.present?.label;
|
|
1740
|
+
const av = readRowField(rows.get(a), sortField, descriptor.present?.by, locale) ?? a;
|
|
1741
|
+
const bv = readRowField(rows.get(b), sortField, descriptor.present?.by, locale) ?? b;
|
|
1742
|
+
return av < bv ? -1 : av > bv ? 1 : 0;
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
function presentLookupForJoin(record, locale, lookupFields, getSnapshotRows) {
|
|
1747
|
+
let result = record;
|
|
1748
|
+
for (const [field, desc] of Object.entries(lookupFields)) {
|
|
1749
|
+
if (desc.present === void 0) continue;
|
|
1750
|
+
const raw = record[field];
|
|
1751
|
+
if (typeof raw !== "string") continue;
|
|
1752
|
+
const rows = desc.backing === "static" ? desc.table ? new Map(Object.entries(desc.table)) : void 0 : getSnapshotRows(desc);
|
|
1753
|
+
if (!rows) continue;
|
|
1754
|
+
const label = buildLookupSnapshot(desc.dimension, rows, desc).label(raw, locale);
|
|
1755
|
+
if (label === void 0) continue;
|
|
1756
|
+
if (result === record) result = { ...record };
|
|
1757
|
+
result[`${field}Label`] = label;
|
|
1758
|
+
}
|
|
1759
|
+
return result;
|
|
1760
|
+
}
|
|
1761
|
+
function buildPresentForJoin(i18nFields, lookupFields, getSnapshotRows) {
|
|
1762
|
+
const hasI18n = i18nFields !== void 0 && Object.keys(i18nFields).length > 0;
|
|
1763
|
+
const hasLookup = lookupFields !== void 0 && Object.keys(lookupFields).length > 0;
|
|
1764
|
+
if (!hasI18n && !hasLookup) return void 0;
|
|
1765
|
+
const resolveRows = getSnapshotRows ?? (() => void 0);
|
|
1766
|
+
return (record, locale) => {
|
|
1767
|
+
if (record === null || typeof record !== "object") return record;
|
|
1768
|
+
let result = record;
|
|
1769
|
+
if (hasI18n) result = presentI18nForJoin(result, i18nFields, locale);
|
|
1770
|
+
if (hasLookup) result = presentLookupForJoin(result, locale, lookupFields, resolveRows);
|
|
1771
|
+
return result;
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
// src/port/with/lookup-strategy.ts
|
|
1776
|
+
function isLookupDescriptor(x) {
|
|
1777
|
+
return typeof x === "object" && x !== null && x._viaBrand === "lookup";
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
// src/kernel/via-compose.ts
|
|
1781
|
+
function via(...descriptors) {
|
|
1782
|
+
if (descriptors.length === 0) {
|
|
1783
|
+
throw new ValidationError("via(): at least one descriptor is required, e.g. via(money({ currency: 'EUR' }))");
|
|
1784
|
+
}
|
|
1785
|
+
return { _noydbVia: true, descriptors };
|
|
1786
|
+
}
|
|
1787
|
+
function isViaFieldSpec(x) {
|
|
1788
|
+
return typeof x === "object" && x !== null && x._noydbVia === true;
|
|
1789
|
+
}
|
|
1790
|
+
function mergeViaFields(sources) {
|
|
1791
|
+
for (const field of Object.keys(sources.dictKeyFields ?? {})) {
|
|
1792
|
+
if (sources.lookupFields && field in sources.lookupFields) {
|
|
1793
|
+
throw new ValidationError(
|
|
1794
|
+
`mergeViaFields(): field "${field}" is declared in both \`dictKeyFields\` and \`lookupFields\` \u2014 declare it in one place only.`
|
|
1795
|
+
);
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
if (!sources.viaFields || Object.keys(sources.viaFields).length === 0) {
|
|
1799
|
+
return {
|
|
1800
|
+
moneyFields: sources.moneyFields,
|
|
1801
|
+
i18nFields: sources.i18nFields,
|
|
1802
|
+
dictKeyFields: sources.dictKeyFields,
|
|
1803
|
+
computedFields: void 0,
|
|
1804
|
+
lookupFields: sources.lookupFields
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
const sugarFieldNames = /* @__PURE__ */ new Set([
|
|
1808
|
+
...Object.keys(sources.moneyFields ?? {}),
|
|
1809
|
+
...Object.keys(sources.i18nFields ?? {}),
|
|
1810
|
+
...Object.keys(sources.dictKeyFields ?? {}),
|
|
1811
|
+
...Object.keys(sources.lookupFields ?? {})
|
|
1812
|
+
]);
|
|
1813
|
+
const viaMoney = {};
|
|
1814
|
+
const viaI18nText = {};
|
|
1815
|
+
const viaDictKey = {};
|
|
1816
|
+
const viaComputed = {};
|
|
1817
|
+
const viaLookup = {};
|
|
1818
|
+
for (const [field, spec] of Object.entries(sources.viaFields)) {
|
|
1819
|
+
if (sugarFieldNames.has(field)) {
|
|
1820
|
+
throw new ValidationError(
|
|
1821
|
+
`via(): field "${field}" is declared via both a sugar key and \`viaFields\` \u2014 declare it in one place only.`
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1824
|
+
for (const descriptor of spec.descriptors) {
|
|
1825
|
+
if (descriptor._viaBrand === "money") {
|
|
1826
|
+
viaMoney[field] = descriptor;
|
|
1827
|
+
} else if (descriptor._viaBrand === "i18n") {
|
|
1828
|
+
if (isI18nTextDescriptor(descriptor)) viaI18nText[field] = descriptor;
|
|
1829
|
+
else if (isDictKeyDescriptor(descriptor) || isStaticDictDescriptor(descriptor)) viaDictKey[field] = descriptor;
|
|
1830
|
+
} else if (descriptor._viaBrand === "computed") {
|
|
1831
|
+
viaComputed[field] = descriptor;
|
|
1832
|
+
} else if (descriptor._viaBrand === "lookup" && isLookupDescriptor(descriptor)) {
|
|
1833
|
+
viaLookup[field] = descriptor;
|
|
1834
|
+
} else {
|
|
1835
|
+
throw new ValidationError(`via(): field "${field}" has a descriptor with unrecognized _viaBrand "${descriptor._viaBrand}" \u2014 via() only supports money/i18n/computed/lookup descriptors today.`);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
return {
|
|
1840
|
+
moneyFields: Object.keys(viaMoney).length > 0 ? { ...sources.moneyFields ?? {}, ...viaMoney } : sources.moneyFields,
|
|
1841
|
+
i18nFields: Object.keys(viaI18nText).length > 0 ? { ...sources.i18nFields ?? {}, ...viaI18nText } : sources.i18nFields,
|
|
1842
|
+
dictKeyFields: Object.keys(viaDictKey).length > 0 ? { ...sources.dictKeyFields ?? {}, ...viaDictKey } : sources.dictKeyFields,
|
|
1843
|
+
computedFields: Object.keys(viaComputed).length > 0 ? viaComputed : void 0,
|
|
1844
|
+
lookupFields: Object.keys(viaLookup).length > 0 ? { ...sources.lookupFields ?? {}, ...viaLookup } : sources.lookupFields
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1228
1848
|
// src/kernel/collection-config.ts
|
|
1849
|
+
function computedEntryParts(entry) {
|
|
1850
|
+
if (typeof entry === "function") return { fn: entry, mode: "materialized" };
|
|
1851
|
+
return { fn: entry.fn, ...entry.deps !== void 0 ? { deps: entry.deps } : {}, mode: entry.mode ?? "materialized" };
|
|
1852
|
+
}
|
|
1853
|
+
function unifyComputedFields(opts, viaComputedFields) {
|
|
1854
|
+
return { ...opts.computed ?? {}, ...viaComputedFields ?? {} };
|
|
1855
|
+
}
|
|
1856
|
+
function compileViaBindings(opts, classifiedGuardCtx, eraseCfgOut) {
|
|
1857
|
+
const { moneyFields, i18nFields, dictKeyFields, computedFields, lookupFields } = mergeViaFields(opts);
|
|
1858
|
+
const bindings = [];
|
|
1859
|
+
if (moneyFields) bindings.push(viaBinder("money")(moneyFields));
|
|
1860
|
+
if (i18nFields || dictKeyFields) {
|
|
1861
|
+
const densify = i18nFields ? Object.fromEntries(
|
|
1862
|
+
Object.entries(i18nFields).filter(([, d]) => d.options.densifyOnWrite === true)
|
|
1863
|
+
) : {};
|
|
1864
|
+
const i18nDensifyFields = Object.keys(densify).length > 0 ? densify : void 0;
|
|
1865
|
+
bindings.push(viaBinder("i18n")({
|
|
1866
|
+
...i18nFields !== void 0 ? { i18nFields } : {},
|
|
1867
|
+
...dictKeyFields !== void 0 ? { dictKeyFields } : {},
|
|
1868
|
+
...i18nDensifyFields !== void 0 ? { i18nDensifyFields } : {},
|
|
1869
|
+
strategy: opts.i18nStrategy ?? NO_I18N,
|
|
1870
|
+
...opts.defaultLocale !== void 0 ? { defaultLocale: opts.defaultLocale } : {},
|
|
1871
|
+
...opts.autoTranslateHook !== void 0 ? { autoTranslateHook: opts.autoTranslateHook } : {},
|
|
1872
|
+
...opts.dictLabelResolver !== void 0 ? { dictLabelResolver: opts.dictLabelResolver } : {},
|
|
1873
|
+
...opts.i18nPutValidator !== void 0 ? { i18nPutValidator: opts.i18nPutValidator } : {},
|
|
1874
|
+
collectionName: opts.name
|
|
1875
|
+
}));
|
|
1876
|
+
}
|
|
1877
|
+
if (lookupFields !== void 0) {
|
|
1878
|
+
bindings.push(viaBinder("lookup")({
|
|
1879
|
+
lookupFields,
|
|
1880
|
+
...opts.lookupLabelResolver !== void 0 ? { lookupLabelResolver: opts.lookupLabelResolver } : {},
|
|
1881
|
+
...opts.getLookupBacking !== void 0 ? { getLookupBacking: opts.getLookupBacking } : {},
|
|
1882
|
+
...opts.membership !== void 0 ? { membership: opts.membership } : {},
|
|
1883
|
+
...opts.getAltIndex !== void 0 ? { getAltIndex: opts.getAltIndex } : {},
|
|
1884
|
+
...opts.snapshotFor !== void 0 ? { snapshotFor: opts.snapshotFor } : {},
|
|
1885
|
+
collectionName: opts.name
|
|
1886
|
+
}));
|
|
1887
|
+
}
|
|
1888
|
+
if (opts.classifiedFields !== void 0) {
|
|
1889
|
+
const classifiedCfg = {
|
|
1890
|
+
entries: opts.classifiedFields,
|
|
1891
|
+
collectionName: opts.name,
|
|
1892
|
+
guardCtx: classifiedGuardCtx
|
|
1893
|
+
};
|
|
1894
|
+
if (eraseCfgOut) eraseCfgOut.classified = classifiedCfg;
|
|
1895
|
+
bindings.push(viaBinder("classified")(classifiedCfg));
|
|
1896
|
+
}
|
|
1897
|
+
if (opts.blobFields !== void 0) {
|
|
1898
|
+
bindings.push(viaBinder("blob")({
|
|
1899
|
+
fields: opts.blobFields,
|
|
1900
|
+
collectionName: opts.name
|
|
1901
|
+
}));
|
|
1902
|
+
}
|
|
1903
|
+
const virtualFields = /* @__PURE__ */ new Map();
|
|
1904
|
+
for (const [field, entry] of Object.entries(unifyComputedFields(opts, computedFields))) {
|
|
1905
|
+
const parts = computedEntryParts(entry);
|
|
1906
|
+
if (parts.mode === "virtual") {
|
|
1907
|
+
virtualFields.set(field, { _viaBrand: "computed", fn: parts.fn, mode: "virtual", ...parts.deps !== void 0 ? { deps: parts.deps } : {} });
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
if (virtualFields.size > 0) {
|
|
1911
|
+
bindings.push(viaBinder("computed")({ virtualFields }));
|
|
1912
|
+
}
|
|
1913
|
+
return bindings;
|
|
1914
|
+
}
|
|
1915
|
+
function collectKnownFieldNames(parts) {
|
|
1916
|
+
return /* @__PURE__ */ new Set([
|
|
1917
|
+
...Object.keys(parts.moneyFields ?? {}),
|
|
1918
|
+
...Object.keys(parts.i18nFields ?? {}),
|
|
1919
|
+
...Object.keys(parts.dictKeyFields ?? {}),
|
|
1920
|
+
...Object.keys(parts.classifiedFields ?? {}),
|
|
1921
|
+
...Object.keys(parts.computed ?? {}),
|
|
1922
|
+
...Object.keys(parts.lookupFields ?? {})
|
|
1923
|
+
]);
|
|
1924
|
+
}
|
|
1925
|
+
var EMPTY_KNOWN_FIELDS = /* @__PURE__ */ new Set();
|
|
1926
|
+
function resolveComputedEdges(collectionName, computed, hasClassifiedFields, knownFields = EMPTY_KNOWN_FIELDS) {
|
|
1927
|
+
if (!computed) return [];
|
|
1928
|
+
const edges = [];
|
|
1929
|
+
for (const [field, entry] of Object.entries(computed)) {
|
|
1930
|
+
const parts = computedEntryParts(entry);
|
|
1931
|
+
const deps = parts.deps;
|
|
1932
|
+
if (deps === void 0) {
|
|
1933
|
+
if (hasClassifiedFields) {
|
|
1934
|
+
throw new ValidationError(
|
|
1935
|
+
`Collection "${collectionName}": computed field "${field}" has no declared \`deps\` and the collection declares classified fields \u2014 an opaque computed function could silently copy a classified field's plaintext into an ordinary, unredacted field. Declare \`deps\` naming the source fields it reads, e.g. computed: { ${field}: { fn, deps: [...] } } or via(computed(fn, { deps: [...] })).`
|
|
1936
|
+
);
|
|
1937
|
+
}
|
|
1938
|
+
continue;
|
|
1939
|
+
}
|
|
1940
|
+
if (deps.length === 0) {
|
|
1941
|
+
throw new ValidationError(`Collection "${collectionName}": computed field "${field}"'s \`deps\` must be non-empty.`);
|
|
1942
|
+
}
|
|
1943
|
+
for (const dep of deps) {
|
|
1944
|
+
if (typeof dep !== "string" || dep.length === 0) {
|
|
1945
|
+
throw new ValidationError(`Collection "${collectionName}": computed field "${field}"'s \`deps\` entries must be non-empty strings.`);
|
|
1946
|
+
}
|
|
1947
|
+
if (hasClassifiedFields && !knownFields.has(dep)) {
|
|
1948
|
+
throw new ValidationError(
|
|
1949
|
+
`Collection "${collectionName}": computed field "${field}"'s \`deps\` entry "${dep}" does not name a declared field, and the collection declares classified fields \u2014 an opaque computed function could silently copy a classified field's plaintext into an ordinary, unredacted field via a mistyped/unknown dep name. Declare \`deps\` naming only known fields (money/i18n/dictKey/classified/computed) \u2014 check "${dep}" for a typo.`
|
|
1950
|
+
);
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
edges.push({
|
|
1954
|
+
target: { collection: collectionName, field },
|
|
1955
|
+
sources: deps.map((d) => ({ collection: collectionName, field: d })),
|
|
1956
|
+
grain: parts.mode === "virtual" ? "virtual" : "record"
|
|
1957
|
+
});
|
|
1958
|
+
}
|
|
1959
|
+
return edges;
|
|
1960
|
+
}
|
|
1961
|
+
function resolveViaBindingDepsEdges(collectionName, bindings, knownFields) {
|
|
1962
|
+
const edges = [];
|
|
1963
|
+
for (const binding of bindings) {
|
|
1964
|
+
if (!binding.deps || binding.deps.length === 0) continue;
|
|
1965
|
+
for (const dep of binding.deps) {
|
|
1966
|
+
if (!knownFields.has(dep)) {
|
|
1967
|
+
throw new ValidationError(
|
|
1968
|
+
`Collection "${collectionName}": via binding "${binding.brand}" deps references undeclared field "${dep}".`
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
const sources = binding.deps.map((d) => ({ collection: collectionName, field: d }));
|
|
1973
|
+
for (const field of knownFields) {
|
|
1974
|
+
if (binding.covers?.(field)) edges.push({ target: { collection: collectionName, field }, sources });
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
return edges;
|
|
1978
|
+
}
|
|
1229
1979
|
function resolveCollectionConfig(opts) {
|
|
1230
1980
|
if (opts.embeddings && opts.crdt) {
|
|
1231
1981
|
throw new Error(
|
|
1232
1982
|
`Collection "${opts.name}": embeddings are not supported on CRDT collections (L2). Use a non-CRDT collection for semantic search.`
|
|
1233
1983
|
);
|
|
1234
1984
|
}
|
|
1235
|
-
|
|
1985
|
+
const effectiveViaFields = mergeViaFields(opts);
|
|
1236
1986
|
const resolvedClassified = opts.classifiedFields !== void 0 ? resolveClassifiedFields(opts.name, opts.classifiedFields) : void 0;
|
|
1237
|
-
|
|
1987
|
+
const allComputed = unifyComputedFields(opts, effectiveViaFields.computedFields);
|
|
1988
|
+
const materializedComputed = {};
|
|
1989
|
+
for (const [field, entry] of Object.entries(allComputed)) {
|
|
1990
|
+
if (computedEntryParts(entry).mode !== "virtual") materializedComputed[field] = entry;
|
|
1991
|
+
}
|
|
1992
|
+
let mergedComputed = Object.keys(materializedComputed).length > 0 ? materializedComputed : void 0;
|
|
1238
1993
|
if (resolvedClassified !== void 0) {
|
|
1239
|
-
for (const key of Object.keys(
|
|
1994
|
+
for (const key of Object.keys(allComputed)) {
|
|
1240
1995
|
if (resolvedClassified.riderComputed[key] !== void 0) {
|
|
1241
1996
|
throw new ClassifiedConfigError(opts.name, `computed field "${key}" collides with a rider companion`);
|
|
1242
1997
|
}
|
|
1243
1998
|
}
|
|
1244
|
-
mergedComputed = { ...resolvedClassified.riderComputed, ...
|
|
1999
|
+
mergedComputed = { ...resolvedClassified.riderComputed, ...materializedComputed };
|
|
1245
2000
|
}
|
|
1246
2001
|
let deterministicFields;
|
|
1247
2002
|
if (opts.deterministicFields && opts.deterministicFields.length > 0) {
|
|
@@ -1254,10 +2009,6 @@ function resolveCollectionConfig(opts) {
|
|
|
1254
2009
|
} else {
|
|
1255
2010
|
deterministicFields = null;
|
|
1256
2011
|
}
|
|
1257
|
-
const densifyFields = opts.i18nFields ? Object.fromEntries(
|
|
1258
|
-
Object.entries(opts.i18nFields).filter(([, d]) => d.options.densifyOnWrite === true)
|
|
1259
|
-
) : {};
|
|
1260
|
-
const i18nDensifyFields = Object.keys(densifyFields).length > 0 ? densifyFields : void 0;
|
|
1261
2012
|
const guardIndexedFields = /* @__PURE__ */ new Set();
|
|
1262
2013
|
for (const ix of opts.indexes ?? []) {
|
|
1263
2014
|
if (typeof ix === "string") guardIndexedFields.add(ix);
|
|
@@ -1293,6 +2044,18 @@ function resolveCollectionConfig(opts) {
|
|
|
1293
2044
|
const vdigFields = vdigEntries.length > 0 ? new Map(vdigEntries) : null;
|
|
1294
2045
|
const perRecordCek = opts.perRecordKeys === true;
|
|
1295
2046
|
const cekCache = perRecordCek ? new Lru({ maxRecords: 4096 }) : null;
|
|
2047
|
+
const viaEraseCfgOut = {};
|
|
2048
|
+
const via2 = ViaPipeline.build(compileViaBindings(opts, classifiedGuardCtx, viaEraseCfgOut));
|
|
2049
|
+
const knownFields = collectKnownFieldNames({
|
|
2050
|
+
moneyFields: effectiveViaFields.moneyFields,
|
|
2051
|
+
i18nFields: effectiveViaFields.i18nFields,
|
|
2052
|
+
dictKeyFields: effectiveViaFields.dictKeyFields,
|
|
2053
|
+
classifiedFields: resolvedClassified?.byField,
|
|
2054
|
+
computed: allComputed,
|
|
2055
|
+
lookupFields: effectiveViaFields.lookupFields
|
|
2056
|
+
});
|
|
2057
|
+
const computedEdges = resolveComputedEdges(opts.name, allComputed, resolvedClassified !== void 0, knownFields);
|
|
2058
|
+
const viaDepsEdges = resolveViaBindingDepsEdges(opts.name, via2?.bindings ?? [], knownFields);
|
|
1296
2059
|
return {
|
|
1297
2060
|
adapter: opts.adapter,
|
|
1298
2061
|
vault: opts.vault,
|
|
@@ -1324,20 +2087,34 @@ function resolveCollectionConfig(opts) {
|
|
|
1324
2087
|
ledger: opts.ledger,
|
|
1325
2088
|
refEnforcer: opts.refEnforcer,
|
|
1326
2089
|
joinResolver: opts.joinResolver,
|
|
1327
|
-
i18nFields:
|
|
2090
|
+
i18nFields: effectiveViaFields.i18nFields,
|
|
1328
2091
|
textIndexes: opts.textIndexes,
|
|
1329
|
-
i18nDensifyFields,
|
|
1330
2092
|
embeddings: opts.embeddings,
|
|
1331
2093
|
vectorSet: opts.embeddings ? new VectorSet() : void 0,
|
|
1332
|
-
dictKeyFields:
|
|
2094
|
+
dictKeyFields: effectiveViaFields.dictKeyFields,
|
|
2095
|
+
lookupFields: effectiveViaFields.lookupFields,
|
|
2096
|
+
// #650 Task 6 — the sync join-dressing hook (#626 retirement, spec §5);
|
|
2097
|
+
// `undefined` when this collection declares neither i18nText nor lookup
|
|
2098
|
+
// fields, matching today's `i18nFields`-absent JoinableSource shape.
|
|
2099
|
+
presentForJoin: buildPresentForJoin(effectiveViaFields.i18nFields, effectiveViaFields.lookupFields, opts.snapshotFor),
|
|
1333
2100
|
fieldMeta: opts.fieldMeta,
|
|
1334
2101
|
meta: opts.meta,
|
|
1335
2102
|
_refs: opts.declaredRefs ?? {},
|
|
1336
|
-
|
|
2103
|
+
via: via2,
|
|
2104
|
+
classifiedEraseCfg: viaEraseCfgOut.classified,
|
|
2105
|
+
moneyFields: effectiveViaFields.moneyFields,
|
|
1337
2106
|
classified: resolvedClassified,
|
|
1338
2107
|
classifiedGuardCtx,
|
|
1339
2108
|
classifiedStrategy: opts.classifiedStrategy ?? NO_CLASSIFIED,
|
|
1340
2109
|
computed: mergedComputed,
|
|
2110
|
+
computedEdges,
|
|
2111
|
+
viaDepsEdges,
|
|
2112
|
+
// #638 Task 7 — every declared computed field name (both modes, both surfaces); the
|
|
2113
|
+
// reconcile path's cross-call depsless-computed leak-guard memory
|
|
2114
|
+
// (`via-graph-wiring.ts#registerCollectionGraphSources`) reads this instead of
|
|
2115
|
+
// `Object.keys(opts.computed ?? {})` alone, so a via(computed(...))-declared field
|
|
2116
|
+
// participates in that guard identically to a sugar-declared one.
|
|
2117
|
+
computedFieldNames: Object.keys(allComputed),
|
|
1341
2118
|
dictLabelResolver: opts.dictLabelResolver,
|
|
1342
2119
|
getDictionary: opts.getDictionary,
|
|
1343
2120
|
i18nPutValidator: opts.i18nPutValidator,
|
|
@@ -1348,6 +2125,7 @@ function resolveCollectionConfig(opts) {
|
|
|
1348
2125
|
onAccess: opts.onAccess,
|
|
1349
2126
|
derivationSource: opts.derivationSource,
|
|
1350
2127
|
materializedViewSource: opts.materializedViewSource,
|
|
2128
|
+
graphDispatch: opts.graphDispatch,
|
|
1351
2129
|
tiers: opts.tiers && opts.tiers.length > 0 ? new Set(opts.tiers) : null,
|
|
1352
2130
|
tiersStrategy: opts.tiersStrategy ?? NO_TIERS,
|
|
1353
2131
|
searchStrategy: opts.searchStrategy ?? NO_SEARCH,
|
|
@@ -1365,7 +2143,7 @@ function resolveCollectionConfig(opts) {
|
|
|
1365
2143
|
// src/with-formula/computed/lazy.ts
|
|
1366
2144
|
var fn = null;
|
|
1367
2145
|
async function loadEvalComputedFields() {
|
|
1368
|
-
fn ??= (await import("./computed-
|
|
2146
|
+
fn ??= (await import("./computed-R62HGCO4.js")).evalComputedFields;
|
|
1369
2147
|
return fn;
|
|
1370
2148
|
}
|
|
1371
2149
|
|
|
@@ -1424,7 +2202,6 @@ var Collection = class {
|
|
|
1424
2202
|
tiersStrategy;
|
|
1425
2203
|
searchStrategy;
|
|
1426
2204
|
historyStrategy;
|
|
1427
|
-
i18nStrategy;
|
|
1428
2205
|
syncStrategy;
|
|
1429
2206
|
// In-memory cache of decrypted records (eager mode only). Lazy mode
|
|
1430
2207
|
// uses `lru` instead. Both fields exist so a single Collection instance
|
|
@@ -1526,70 +2303,38 @@ var Collection = class {
|
|
|
1526
2303
|
* throw `LocaleNotSpecifiedError` unless a per-call locale is passed.
|
|
1527
2304
|
*/
|
|
1528
2305
|
defaultLocale;
|
|
1529
|
-
/**
|
|
1530
|
-
*
|
|
1531
|
-
*
|
|
1532
|
-
* - `put()` via `i18nPutValidator` to enforce required translations
|
|
1533
|
-
* - `get()`/`list()` to apply locale resolution after decryption
|
|
1534
|
-
*
|
|
1535
|
-
* Declared via the `i18nFields` collection option.
|
|
1536
|
-
*/
|
|
2306
|
+
/** Field name → `I18nTextDescriptor` for `i18nText()` fields (`i18nFields` option); write/read
|
|
2307
|
+
* runs through the compiled `via` i18n binding — this remains for `describe()`
|
|
2308
|
+
* and the search-index build path. */
|
|
1537
2309
|
i18nFields;
|
|
1538
|
-
/**
|
|
1539
|
-
* The configured string fields exposed to `retrieve()`. `undefined`
|
|
1540
|
-
* for ordinary collections, so the search path costs nothing when unused.
|
|
1541
|
-
*/
|
|
2310
|
+
/** The configured string fields exposed to `retrieve()`; `undefined` for ordinary collections (zero-cost). */
|
|
1542
2311
|
textIndexes;
|
|
1543
|
-
/**
|
|
1544
|
-
* The session-scoped lexical index store. `undefined` (so the dirty
|
|
1545
|
-
* poke + retrieve are zero-cost) unless `textIndexes` is non-empty.
|
|
1546
|
-
*/
|
|
2312
|
+
/** Session-scoped lexical index store; `undefined` (zero-cost) unless `textIndexes` is non-empty. */
|
|
1547
2313
|
searchIndexStore;
|
|
1548
|
-
/**
|
|
1549
|
-
*
|
|
1550
|
-
* descriptor opts in via `densifyOnWrite: true`). `undefined` when none opt
|
|
1551
|
-
* in, so the write path skips all densify work for ordinary collections.
|
|
1552
|
-
*/
|
|
1553
|
-
i18nDensifyFields;
|
|
1554
|
-
/**
|
|
1555
|
-
* Embedding config for write-time vector derivation. `undefined`
|
|
1556
|
-
* for ordinary collections (zero cost). When set, `put()` encodes the
|
|
1557
|
-
* source field(s) and stores an encrypted `_vec` sidecar.
|
|
1558
|
-
*/
|
|
2314
|
+
/** Embedding config for write-time vector derivation; `undefined` (zero-cost) for ordinary
|
|
2315
|
+
* collections. When set, `put()` encodes the source field(s) and stores an encrypted `_vec` sidecar. */
|
|
1559
2316
|
embeddings;
|
|
1560
|
-
/**
|
|
1561
|
-
* In-memory vector set, populated lazily from `_vec` sidecars.
|
|
1562
|
-
* `undefined` when no embedding config is declared.
|
|
1563
|
-
*/
|
|
2317
|
+
/** In-memory vector set, populated lazily from `_vec` sidecars; `undefined` when no embedding config is declared. */
|
|
1564
2318
|
vectorSet;
|
|
1565
|
-
/**
|
|
1566
|
-
*
|
|
1567
|
-
* `dictKey()`. Used by `get()`/`list()` to add `<field>Label` virtual
|
|
1568
|
-
* fields when a locale is requested.
|
|
1569
|
-
*/
|
|
2319
|
+
/** Field name → `DictKeyDescriptor` for `dictKey()` fields; used by `get()`/`list()` to add
|
|
2320
|
+
* `<field>Label` virtual fields when a locale is requested. */
|
|
1570
2321
|
dictKeyFields;
|
|
1571
|
-
/**
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
2322
|
+
/** Field name → `LookupDescriptor` for native `lookup()`/`enumOf()`/`dict()` fields (#650 Task 2) — describe()-only in this task. */
|
|
2323
|
+
lookupFields;
|
|
2324
|
+
/** Sync join-dressing hook (#650 Task 6, #626 retirement) — `querySourceForJoin()`'s `presentForJoin`. */
|
|
2325
|
+
presentForJoin;
|
|
2326
|
+
/** Consumer-neutral per-field descriptors declared via `fieldMeta`; read by `getFieldMeta()`, merged by `describe()`. */
|
|
1575
2327
|
fieldMeta;
|
|
1576
|
-
/**
|
|
1577
|
-
* Collection-level descriptive metadata declared via the `meta` collection
|
|
1578
|
-
* option. Read by `getMeta()`; surfaced in `collection.describe()`.
|
|
1579
|
-
*/
|
|
2328
|
+
/** Collection-level descriptive metadata declared via `meta`; read by `getMeta()`, surfaced in `describe()`. */
|
|
1580
2329
|
meta;
|
|
1581
|
-
/**
|
|
1582
|
-
* Outbound ref declarations for this collection (snapshot from vault
|
|
1583
|
-
* refRegistry at construction time). Used by `describe()` (sync, config-only).
|
|
1584
|
-
*/
|
|
2330
|
+
/** Outbound ref declarations (snapshot from vault refRegistry at construction time); used by `describe()`. */
|
|
1585
2331
|
_refs;
|
|
1586
|
-
/**
|
|
1587
|
-
*
|
|
1588
|
-
*
|
|
1589
|
-
* string, `get()`/`list()` decode back. Mutable so {@link _applyMoneyFields}
|
|
1590
|
-
* can attach descriptors to a collection MV-analysis pre-created.
|
|
1591
|
-
*/
|
|
2332
|
+
/** Money field descriptors keyed by field path, typed as the opaque {@link ViaDescriptor} marker
|
|
2333
|
+
* (the kernel never inspects the concrete shape); `put()` quantizes to a scaled-int string,
|
|
2334
|
+
* `get()`/`list()` decode back. Mutable so {@link _applyMoneyFields} can attach. */
|
|
1592
2335
|
moneyFields;
|
|
2336
|
+
via;
|
|
2337
|
+
// compiled Via pipeline (money, i18n); rebuilt by {@link _applyMoneyFields}
|
|
1593
2338
|
/**
|
|
1594
2339
|
* Computed scalar fields, evaluated first on every `put()`. Mutable for
|
|
1595
2340
|
* the same MV-pre-creation reconcile as {@link moneyFields}.
|
|
@@ -1624,27 +2369,12 @@ var Collection = class {
|
|
|
1624
2369
|
* `createNoydb()` option (opt in with `withClassified()`).
|
|
1625
2370
|
*/
|
|
1626
2371
|
classifiedStrategy;
|
|
1627
|
-
/**
|
|
1628
|
-
* Async callback provided by the Vault that resolves a dict key
|
|
1629
|
-
* to its label for a given locale. Used by the locale-read path for
|
|
1630
|
-
* dictKey fields.
|
|
1631
|
-
*
|
|
1632
|
-
* Signature: `(dictName, key, locale, fallback?) => Promise<string | undefined>`
|
|
1633
|
-
*/
|
|
1634
|
-
dictLabelResolver;
|
|
1635
2372
|
/**
|
|
1636
2373
|
* Async callback provided by the Vault to open a dynamic
|
|
1637
2374
|
* dictionary handle (for label-map pre-computation in the search index).
|
|
1638
2375
|
* Only used in `resolveDictLabelMaps()`; static dicts bypass this entirely.
|
|
1639
2376
|
*/
|
|
1640
2377
|
getDictionary;
|
|
1641
|
-
/**
|
|
1642
|
-
* Synchronous callback provided by the Vault that validates
|
|
1643
|
-
* i18nText fields on `put()`. Throws `MissingTranslationError` when
|
|
1644
|
-
* a required translation is absent. Called after schema validation,
|
|
1645
|
-
* before encryption.
|
|
1646
|
-
*/
|
|
1647
|
-
i18nPutValidator;
|
|
1648
2378
|
/**
|
|
1649
2379
|
* declared deterministic fields. `null` when the feature
|
|
1650
2380
|
* is inactive for this collection; a frozen `Set` otherwise.
|
|
@@ -1699,14 +2429,6 @@ var Collection = class {
|
|
|
1699
2429
|
tiers;
|
|
1700
2430
|
tierMode;
|
|
1701
2431
|
onCrossTierAccess;
|
|
1702
|
-
/**
|
|
1703
|
-
* Async translator callback provided by Noydb via Vault for
|
|
1704
|
-
* `i18nText` fields with `autoTranslate: true`. Called
|
|
1705
|
-
* before i18n validation so translated values are present when the
|
|
1706
|
-
* validator runs. `undefined` when no `plaintextTranslator` was
|
|
1707
|
-
* configured on `createNoydb()`.
|
|
1708
|
-
*/
|
|
1709
|
-
autoTranslateHook;
|
|
1710
2432
|
/**
|
|
1711
2433
|
* Optional reference to the vault-level hash-chained audit
|
|
1712
2434
|
* log. When present, every successful `put()` and `delete()` appends
|
|
@@ -1744,6 +2466,8 @@ var Collection = class {
|
|
|
1744
2466
|
* commits + after `dispatchDerivations` has run.
|
|
1745
2467
|
*/
|
|
1746
2468
|
materializedViewSource;
|
|
2469
|
+
/** #638 Task 4 — `Vault._collectGraphTouch`; a no-op absent an open sync/cutover/restore batch. */
|
|
2470
|
+
graphDispatch;
|
|
1747
2471
|
/**
|
|
1748
2472
|
* Optional back-reference to the owning compartment's ref
|
|
1749
2473
|
* enforcer. When present, `Collection.put` calls
|
|
@@ -1806,7 +2530,6 @@ var Collection = class {
|
|
|
1806
2530
|
this.tiersStrategy = cfg.tiersStrategy;
|
|
1807
2531
|
this.searchStrategy = cfg.searchStrategy;
|
|
1808
2532
|
this.historyStrategy = cfg.historyStrategy;
|
|
1809
|
-
this.i18nStrategy = cfg.i18nStrategy;
|
|
1810
2533
|
this.syncStrategy = cfg.syncStrategy;
|
|
1811
2534
|
this.reconcileOnOpen = cfg.reconcileOnOpen;
|
|
1812
2535
|
this.getDEK = cfg.getDEK;
|
|
@@ -1820,29 +2543,29 @@ var Collection = class {
|
|
|
1820
2543
|
this.i18nFields = cfg.i18nFields;
|
|
1821
2544
|
this.textIndexes = cfg.textIndexes;
|
|
1822
2545
|
this.searchIndexStore = opts.textIndexes && opts.textIndexes.length > 0 ? opts.textIndexPersist ? new PersistedIndexStore(buildPersistedIndexCallbacks(() => this.searchContext())) : new MemoryIndexStore() : void 0;
|
|
1823
|
-
this.i18nDensifyFields = cfg.i18nDensifyFields;
|
|
1824
2546
|
this.embeddings = cfg.embeddings;
|
|
1825
2547
|
this.vectorSet = cfg.vectorSet;
|
|
1826
2548
|
this.dictKeyFields = cfg.dictKeyFields;
|
|
2549
|
+
this.lookupFields = cfg.lookupFields;
|
|
2550
|
+
this.presentForJoin = cfg.presentForJoin;
|
|
1827
2551
|
this.fieldMeta = cfg.fieldMeta;
|
|
1828
2552
|
this.meta = cfg.meta;
|
|
1829
2553
|
this._refs = cfg._refs;
|
|
1830
2554
|
this.moneyFields = cfg.moneyFields;
|
|
2555
|
+
this.via = cfg.via;
|
|
1831
2556
|
this.classified = cfg.classified;
|
|
1832
2557
|
this.classifiedGuardCtx = cfg.classifiedGuardCtx;
|
|
1833
2558
|
this.vdigFields = cfg.vdigFields;
|
|
1834
2559
|
this.classifiedStrategy = cfg.classifiedStrategy;
|
|
1835
2560
|
this.computed = cfg.computed;
|
|
1836
|
-
this.dictLabelResolver = cfg.dictLabelResolver;
|
|
1837
2561
|
this.getDictionary = cfg.getDictionary;
|
|
1838
|
-
this.i18nPutValidator = cfg.i18nPutValidator;
|
|
1839
|
-
this.autoTranslateHook = cfg.autoTranslateHook;
|
|
1840
2562
|
this.defaultLocale = cfg.defaultLocale;
|
|
1841
2563
|
this.crdtMode = cfg.crdtMode;
|
|
1842
2564
|
this.syncAdapter = cfg.syncAdapter;
|
|
1843
2565
|
this.onAccess = cfg.onAccess;
|
|
1844
2566
|
this.derivationSource = cfg.derivationSource;
|
|
1845
2567
|
this.materializedViewSource = cfg.materializedViewSource;
|
|
2568
|
+
this.graphDispatch = cfg.graphDispatch;
|
|
1846
2569
|
this.tiers = cfg.tiers;
|
|
1847
2570
|
this.tierMode = cfg.tierMode;
|
|
1848
2571
|
this.onCrossTierAccess = cfg.onCrossTierAccess;
|
|
@@ -1882,10 +2605,12 @@ var Collection = class {
|
|
|
1882
2605
|
crdtMode: this.crdtMode,
|
|
1883
2606
|
crdtStrategy: this.crdtStrategy,
|
|
1884
2607
|
schema: this.schema,
|
|
1885
|
-
getDEK: () => this.getDEK(this.name),
|
|
2608
|
+
getDEK: (collection) => this.getDEK(collection ?? this.name),
|
|
1886
2609
|
cekCache: this.cekCache,
|
|
1887
|
-
classifiedMarkerDigestOnly: () => this._classifiedMarkerDigestOnly()
|
|
2610
|
+
classifiedMarkerDigestOnly: () => this._classifiedMarkerDigestOnly(),
|
|
2611
|
+
via: this.via
|
|
1888
2612
|
});
|
|
2613
|
+
if (cfg.classifiedEraseCfg) cfg.classifiedEraseCfg.classifySealedShred = (live) => this.codec.classifySealedShred(live);
|
|
1889
2614
|
if (opts.crdt && opts.onRegisterConflictResolver) {
|
|
1890
2615
|
const crdtMode = opts.crdt;
|
|
1891
2616
|
const crdtResolver = async (id, local, remote) => {
|
|
@@ -1949,7 +2674,7 @@ var Collection = class {
|
|
|
1949
2674
|
const merged = mergeFn(localRecord, remoteRecord);
|
|
1950
2675
|
const mergedVersion = Math.max(local._v, remote._v) + 1;
|
|
1951
2676
|
const cek = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
|
|
1952
|
-
return this.codec.encryptRecord(merged, mergedVersion, cek);
|
|
2677
|
+
return this.codec.encryptRecord(merged, mergedVersion, cek, void 0, void 0, void 0, id);
|
|
1953
2678
|
};
|
|
1954
2679
|
}
|
|
1955
2680
|
opts.onRegisterConflictResolver(collectionName, resolver);
|
|
@@ -2039,35 +2764,41 @@ var Collection = class {
|
|
|
2039
2764
|
fieldMeta: this.fieldMeta,
|
|
2040
2765
|
moneyFields: this.moneyFields,
|
|
2041
2766
|
dictKeyFields: this.dictKeyFields,
|
|
2767
|
+
...this.lookupFields !== void 0 ? { lookupFields: this.lookupFields } : {},
|
|
2042
2768
|
computed: this.computed,
|
|
2043
2769
|
refs: this._refs,
|
|
2044
2770
|
zodFields: void 0,
|
|
2045
2771
|
...this.meta !== void 0 ? { meta: this.meta } : {},
|
|
2046
2772
|
...this.i18nFields !== void 0 ? { i18nFields: this.i18nFields } : {},
|
|
2047
|
-
...this.classified !== void 0 ? { classified: this.classified.byField } : {}
|
|
2773
|
+
...this.classified !== void 0 ? { classified: this.classified.byField } : {},
|
|
2774
|
+
...this.via?.taint !== void 0 ? { taint: this.via.taint } : {},
|
|
2775
|
+
...this.via ? { viaFragments: this.via.describeFragments() } : {}
|
|
2776
|
+
// #650 Task 7
|
|
2048
2777
|
});
|
|
2049
2778
|
}
|
|
2050
2779
|
/**
|
|
2051
|
-
* Async describe implementation.
|
|
2052
|
-
*
|
|
2053
|
-
*
|
|
2054
|
-
* then delegates to buildDescription which also runs fieldMeta key-validation.
|
|
2780
|
+
* Async describe implementation. Derives validator-exact types via deriveZodFields
|
|
2781
|
+
* (lazy, no static zod import), optionally resolves dynamic-dict labels from vault.dictionary(name).list(),
|
|
2782
|
+
* then delegates to buildDescription (which also runs fieldMeta key-validation).
|
|
2055
2783
|
*/
|
|
2056
2784
|
async describeAsync(opts) {
|
|
2057
2785
|
const zodFields = this.schema !== void 0 ? await deriveZodFields(this.schema) : void 0;
|
|
2058
2786
|
let dictLabels;
|
|
2059
|
-
if (opts.resolveDictLabels === true && this.
|
|
2060
|
-
|
|
2061
|
-
for (const
|
|
2062
|
-
|
|
2063
|
-
|
|
2787
|
+
if (opts.resolveDictLabels === true && this.getDictionary !== void 0) {
|
|
2788
|
+
const names = /* @__PURE__ */ new Set();
|
|
2789
|
+
for (const desc of Object.values(this.dictKeyFields ?? {})) if (!isStaticDictDescriptor(desc)) names.add(desc.name);
|
|
2790
|
+
for (const desc of Object.values(this.lookupFields ?? {})) if (desc.backing === "reserved") names.add(desc.dimension);
|
|
2791
|
+
if (names.size > 0) {
|
|
2792
|
+
dictLabels = {};
|
|
2793
|
+
for (const name of names) {
|
|
2794
|
+
const handle = await this.getDictionary(name);
|
|
2064
2795
|
const entries = await handle.list();
|
|
2065
2796
|
const valueToLabel = {};
|
|
2066
2797
|
for (const entry of entries) {
|
|
2067
2798
|
const label = Object.values(entry.labels)[0];
|
|
2068
2799
|
if (label !== void 0) valueToLabel[entry.key] = label;
|
|
2069
2800
|
}
|
|
2070
|
-
dictLabels[
|
|
2801
|
+
dictLabels[name] = valueToLabel;
|
|
2071
2802
|
}
|
|
2072
2803
|
}
|
|
2073
2804
|
}
|
|
@@ -2076,13 +2807,17 @@ var Collection = class {
|
|
|
2076
2807
|
fieldMeta: this.fieldMeta,
|
|
2077
2808
|
moneyFields: this.moneyFields,
|
|
2078
2809
|
dictKeyFields: this.dictKeyFields,
|
|
2810
|
+
...this.lookupFields !== void 0 ? { lookupFields: this.lookupFields } : {},
|
|
2079
2811
|
computed: this.computed,
|
|
2080
2812
|
refs: this._refs,
|
|
2081
2813
|
zodFields,
|
|
2082
2814
|
...dictLabels !== void 0 ? { dictLabels } : {},
|
|
2083
2815
|
...this.meta !== void 0 ? { meta: this.meta } : {},
|
|
2084
2816
|
...this.i18nFields !== void 0 ? { i18nFields: this.i18nFields } : {},
|
|
2085
|
-
...this.classified !== void 0 ? { classified: this.classified.byField } : {}
|
|
2817
|
+
...this.classified !== void 0 ? { classified: this.classified.byField } : {},
|
|
2818
|
+
...this.via?.taint !== void 0 ? { taint: this.via.taint } : {},
|
|
2819
|
+
...this.via ? { viaFragments: this.via.describeFragments() } : {}
|
|
2820
|
+
// #650 Task 7
|
|
2086
2821
|
});
|
|
2087
2822
|
}
|
|
2088
2823
|
/** JSON Schema for this collection with describe() metadata as x- extensions. */
|
|
@@ -2203,7 +2938,7 @@ var Collection = class {
|
|
|
2203
2938
|
}
|
|
2204
2939
|
await this.onAccess?.("find", "*");
|
|
2205
2940
|
if (hits.length === 0) return [];
|
|
2206
|
-
const { verifyDigestField } = await import("./verify-
|
|
2941
|
+
const { verifyDigestField } = await import("./verify-GH6CWVSW.js");
|
|
2207
2942
|
const policy = {
|
|
2208
2943
|
normalize: spec.verifyNormalize ?? "password",
|
|
2209
2944
|
notLastN: spec.notLastN ?? 0,
|
|
@@ -2290,10 +3025,17 @@ var Collection = class {
|
|
|
2290
3025
|
* analysis auto-creates a source collection (without options) during
|
|
2291
3026
|
* `openVault`, before the user's `collection(name, { moneyFields })`
|
|
2292
3027
|
* declaration; this reconciles that ordering. First-wins. Not public.
|
|
3028
|
+
*
|
|
3029
|
+
* PREPENDS money rather than appending: {@link compileViaBindings} always
|
|
3030
|
+
* compiles money before i18n (money-first pipeline order — see its
|
|
3031
|
+
* docstring), and by the time this reconcile runs an i18n binding may
|
|
3032
|
+
* already be sitting in `this.via.bindings` (declared at construction).
|
|
3033
|
+
* Appending here would yield `[i18n, money]` on this path vs `[money,
|
|
3034
|
+
* i18n]` from compile — prepending keeps both paths money-first.
|
|
2293
3035
|
*/
|
|
2294
3036
|
_applyMoneyFields(moneyFields) {
|
|
2295
3037
|
if (this.moneyFields !== void 0) return;
|
|
2296
|
-
|
|
3038
|
+
this.via = ViaPipeline.build([viaBinder("money")(moneyFields), ...this.via?.bindings ?? []]);
|
|
2297
3039
|
this.moneyFields = moneyFields;
|
|
2298
3040
|
}
|
|
2299
3041
|
/** @internal — attach computed fields post-construction. See {@link _applyMoneyFields}. */
|
|
@@ -2353,6 +3095,13 @@ var Collection = class {
|
|
|
2353
3095
|
}
|
|
2354
3096
|
this.classified = resolved;
|
|
2355
3097
|
this.computed = { ...resolved.riderComputed, ...this.computed ?? {} };
|
|
3098
|
+
this.via = ViaPipeline.build([...this.via?.bindings ?? [], viaBinder("classified")({
|
|
3099
|
+
entries: classifiedFields,
|
|
3100
|
+
collectionName: this.name,
|
|
3101
|
+
guardCtx: this.classifiedGuardCtx,
|
|
3102
|
+
classifySealedShred: (live) => this.codec.classifySealedShred(live)
|
|
3103
|
+
// #629 T10
|
|
3104
|
+
})]);
|
|
2356
3105
|
}
|
|
2357
3106
|
/** @internal — used only in tests; do not read in production code. */
|
|
2358
3107
|
get _ramCiphertext() {
|
|
@@ -2379,7 +3128,7 @@ var Collection = class {
|
|
|
2379
3128
|
}
|
|
2380
3129
|
}
|
|
2381
3130
|
if (this.materializedViewSource !== void 0) {
|
|
2382
|
-
const { resolveStaleMVOnRead } = await import("./stale-
|
|
3131
|
+
const { resolveStaleMVOnRead } = await import("./stale-IYYXEH3U.js");
|
|
2383
3132
|
await resolveStaleMVOnRead(this.materializedViewSource, this.name);
|
|
2384
3133
|
}
|
|
2385
3134
|
let record;
|
|
@@ -2535,7 +3284,7 @@ var Collection = class {
|
|
|
2535
3284
|
if (cached) return cached.record;
|
|
2536
3285
|
const env = await this.adapter.get(this.vault, this.name, id);
|
|
2537
3286
|
if (!env) return void 0;
|
|
2538
|
-
const rec = await this.codec.decryptRecord(env);
|
|
3287
|
+
const rec = await this.codec.decryptRecord(env, { id });
|
|
2539
3288
|
return rec === null ? void 0 : rec;
|
|
2540
3289
|
}
|
|
2541
3290
|
await this.ensureHydrated();
|
|
@@ -2583,7 +3332,7 @@ var Collection = class {
|
|
|
2583
3332
|
if (cached2) return { record: cached2.record, version: cached2.version };
|
|
2584
3333
|
const env = await this.adapter.get(this.vault, this.name, id);
|
|
2585
3334
|
if (!env) return { record: null, version: 0 };
|
|
2586
|
-
return { record: await this.codec.decryptRecord(env, { skipValidation: true }) ?? null, version: env._v };
|
|
3335
|
+
return { record: await this.codec.decryptRecord(env, { skipValidation: true, id }) ?? null, version: env._v };
|
|
2587
3336
|
}
|
|
2588
3337
|
await this.ensureHydrated();
|
|
2589
3338
|
const cached = this.cache.get(id);
|
|
@@ -2617,7 +3366,7 @@ var Collection = class {
|
|
|
2617
3366
|
const env = await this.adapter.get(this.vault, this.name, id);
|
|
2618
3367
|
if (!env) return { env: null, record: null, elided: false };
|
|
2619
3368
|
try {
|
|
2620
|
-
return { env, record: await this.codec.decryptRecord(env, { skipValidation: true }), elided: false };
|
|
3369
|
+
return { env, record: await this.codec.decryptRecord(env, { skipValidation: true, id }), elided: false };
|
|
2621
3370
|
} catch {
|
|
2622
3371
|
return { env, record: null, elided: false };
|
|
2623
3372
|
}
|
|
@@ -2642,7 +3391,7 @@ var Collection = class {
|
|
|
2642
3391
|
if (!hasWritePermission(this.keyring, this.name)) {
|
|
2643
3392
|
throw new ReadOnlyError();
|
|
2644
3393
|
}
|
|
2645
|
-
if (this.
|
|
3394
|
+
if (this.via) record = this.via.ingest(record);
|
|
2646
3395
|
if (this.subsystemBus?.hasGateHandlers("beforePut")) {
|
|
2647
3396
|
const { env: existingEnv, record: existingRecord } = await this.resolveGatePrior("beforePut", id);
|
|
2648
3397
|
const gateEvent = {
|
|
@@ -2651,7 +3400,7 @@ var Collection = class {
|
|
|
2651
3400
|
collection: this.name,
|
|
2652
3401
|
docId: id,
|
|
2653
3402
|
incoming: record,
|
|
2654
|
-
existing: this.
|
|
3403
|
+
existing: this.via ? this.via.canonicalizeStored(existingRecord) : existingRecord,
|
|
2655
3404
|
existingVersion: existingEnv?._v ?? 0,
|
|
2656
3405
|
existingTs: existingEnv?._ts,
|
|
2657
3406
|
userId: this.keyring.userId,
|
|
@@ -2660,99 +3409,21 @@ var Collection = class {
|
|
|
2660
3409
|
};
|
|
2661
3410
|
await this.subsystemBus.dispatchGate("beforePut", gateEvent);
|
|
2662
3411
|
}
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
3412
|
+
const viaWriteCtx = {
|
|
3413
|
+
id,
|
|
3414
|
+
vault: this.vault,
|
|
3415
|
+
prior: async () => await this.resolveDensifyPrior(id) ?? null,
|
|
3416
|
+
emit: (e, p) => this.emitter.emit(e, p)
|
|
3417
|
+
};
|
|
3418
|
+
if (this.via) await this.via.enforceWrite(record, viaWriteCtx);
|
|
2666
3419
|
if (this.computed !== void 0) {
|
|
2667
3420
|
record = (await loadEvalComputedFields())(record, this.computed, id);
|
|
2668
3421
|
}
|
|
2669
3422
|
if (this.schema !== void 0) {
|
|
2670
3423
|
record = await validateSchemaInput(this.schema, record, `put(${id})`);
|
|
2671
3424
|
}
|
|
2672
|
-
if (this.
|
|
2673
|
-
record =
|
|
2674
|
-
}
|
|
2675
|
-
if (this.i18nFields) {
|
|
2676
|
-
const obj = record;
|
|
2677
|
-
for (const [field, descriptor] of Object.entries(this.i18nFields)) {
|
|
2678
|
-
if (!descriptor.options.autoTranslate) continue;
|
|
2679
|
-
const leafValues = getAtPath(obj, field);
|
|
2680
|
-
if (leafValues.length !== 1) continue;
|
|
2681
|
-
const value = leafValues[0];
|
|
2682
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
2683
|
-
const map = value;
|
|
2684
|
-
const { languages, required } = descriptor.options;
|
|
2685
|
-
const missing = languages.filter(
|
|
2686
|
-
(lang) => !(lang in map) || map[lang] === ""
|
|
2687
|
-
);
|
|
2688
|
-
if (missing.length === 0) continue;
|
|
2689
|
-
const sourceLocale = languages.find((l) => l in map && map[l] !== "");
|
|
2690
|
-
if (!sourceLocale) continue;
|
|
2691
|
-
if (!this.autoTranslateHook) {
|
|
2692
|
-
throw new TranslatorNotConfiguredError(field, this.name);
|
|
2693
|
-
}
|
|
2694
|
-
const toTranslate = required === "any" ? [] : required === "all" ? missing : missing.filter((l) => required.includes(l));
|
|
2695
|
-
const translated = { ...map };
|
|
2696
|
-
for (const targetLocale of toTranslate) {
|
|
2697
|
-
translated[targetLocale] = await this.autoTranslateHook(
|
|
2698
|
-
map[sourceLocale],
|
|
2699
|
-
sourceLocale,
|
|
2700
|
-
targetLocale,
|
|
2701
|
-
field,
|
|
2702
|
-
this.name
|
|
2703
|
-
);
|
|
2704
|
-
}
|
|
2705
|
-
setAtPathInPlace(obj, field, translated);
|
|
2706
|
-
}
|
|
2707
|
-
}
|
|
2708
|
-
let densifyPrior;
|
|
2709
|
-
let exemptFills;
|
|
2710
|
-
if (this.i18nDensifyFields) {
|
|
2711
|
-
densifyPrior = await this.resolveDensifyPrior(id);
|
|
2712
|
-
exemptFills = this.i18nStrategy.computeExemptFills(
|
|
2713
|
-
densifyPrior,
|
|
2714
|
-
record,
|
|
2715
|
-
this.i18nDensifyFields
|
|
2716
|
-
);
|
|
2717
|
-
}
|
|
2718
|
-
if (this.i18nFields) {
|
|
2719
|
-
const obj = record;
|
|
2720
|
-
for (const [field, descriptor] of Object.entries(this.i18nFields)) {
|
|
2721
|
-
if (!descriptor.options.script) continue;
|
|
2722
|
-
for (const leaf of getAtPath(obj, field)) {
|
|
2723
|
-
if (!leaf || typeof leaf !== "object" || Array.isArray(leaf)) continue;
|
|
2724
|
-
const leafMap = leaf;
|
|
2725
|
-
const { value: cleaned, warnings } = this.i18nStrategy.enforceScript(
|
|
2726
|
-
leafMap,
|
|
2727
|
-
field,
|
|
2728
|
-
descriptor,
|
|
2729
|
-
exemptFills?.get(field)
|
|
2730
|
-
);
|
|
2731
|
-
if (cleaned !== leafMap) Object.assign(leafMap, cleaned);
|
|
2732
|
-
const mode = descriptor.options.onScriptViolation;
|
|
2733
|
-
if (mode === "warn" || mode === "filter") {
|
|
2734
|
-
for (const w of warnings) {
|
|
2735
|
-
this.emitter.emit("i18n:script-violation", {
|
|
2736
|
-
vault: this.vault,
|
|
2737
|
-
collection: this.name,
|
|
2738
|
-
id,
|
|
2739
|
-
mode,
|
|
2740
|
-
warning: w
|
|
2741
|
-
});
|
|
2742
|
-
}
|
|
2743
|
-
}
|
|
2744
|
-
}
|
|
2745
|
-
}
|
|
2746
|
-
}
|
|
2747
|
-
if (this.i18nPutValidator !== void 0) {
|
|
2748
|
-
this.i18nPutValidator(record);
|
|
2749
|
-
}
|
|
2750
|
-
if (this.i18nDensifyFields) {
|
|
2751
|
-
this.i18nStrategy.densify(
|
|
2752
|
-
record,
|
|
2753
|
-
densifyPrior,
|
|
2754
|
-
this.i18nDensifyFields
|
|
2755
|
-
);
|
|
3425
|
+
if (this.via) {
|
|
3426
|
+
record = await this.via.encodeWrite(record, viaWriteCtx);
|
|
2756
3427
|
}
|
|
2757
3428
|
if (this.refEnforcer !== void 0) {
|
|
2758
3429
|
await this.refEnforcer.enforceRefsOnPut(this.name, record);
|
|
@@ -2799,7 +3470,7 @@ var Collection = class {
|
|
|
2799
3470
|
const existingResolved = existingResolvedRecord !== null ? { record: existingResolvedRecord, version: existingVersion } : void 0;
|
|
2800
3471
|
if (existingResolved && this.historyConfig.enabled !== false) {
|
|
2801
3472
|
const vdigCtx2 = this.vdigFields !== null ? { id, prev: existingEnvelope } : void 0;
|
|
2802
|
-
const histEnvelope = await this.codec.encryptRecord(existingResolved.record, existingResolved.version, cek2, void 0, void 0, vdigCtx2);
|
|
3473
|
+
const histEnvelope = await this.codec.encryptRecord(existingResolved.record, existingResolved.version, cek2, void 0, void 0, vdigCtx2, id);
|
|
2803
3474
|
await this.historyStrategy.saveHistory(this.adapter, this.vault, this.name, id, histEnvelope);
|
|
2804
3475
|
this.emitter.emit("history:save", { vault: this.vault, collection: this.name, id, version: existingResolved.version });
|
|
2805
3476
|
if (this.historyConfig.maxVersions) {
|
|
@@ -2831,12 +3502,7 @@ var Collection = class {
|
|
|
2831
3502
|
this.cache.set(id, { record: resolvedRecord, version: version2 });
|
|
2832
3503
|
this.indexes?.upsert(id, resolvedRecord, existingResolved ? existingResolved.record : null);
|
|
2833
3504
|
}
|
|
2834
|
-
await this.
|
|
2835
|
-
this.emitter.emit("change", { vault: this.vault, collection: this.name, id, action: "put" });
|
|
2836
|
-
this.searchIndexStore?.markDirty();
|
|
2837
|
-
await this.onAccess?.("put", id);
|
|
2838
|
-
await this.dispatchDerivations(id, record, version2);
|
|
2839
|
-
await this.dispatchMaterializedViews(id, record);
|
|
3505
|
+
await this._onRecordMutated(id, "put", "local-write", { record, version: version2 });
|
|
2840
3506
|
return;
|
|
2841
3507
|
}
|
|
2842
3508
|
let existing;
|
|
@@ -2846,7 +3512,7 @@ var Collection = class {
|
|
|
2846
3512
|
if (!existing) {
|
|
2847
3513
|
priorRaw = await this.adapter.get(this.vault, this.name, id);
|
|
2848
3514
|
if (priorRaw) {
|
|
2849
|
-
const previousRecord = await this.codec.decryptRecord(priorRaw);
|
|
3515
|
+
const previousRecord = await this.codec.decryptRecord(priorRaw, { id });
|
|
2850
3516
|
if (previousRecord !== null) {
|
|
2851
3517
|
existing = { record: previousRecord, version: priorRaw._v };
|
|
2852
3518
|
}
|
|
@@ -2865,7 +3531,7 @@ var Collection = class {
|
|
|
2865
3531
|
const cek = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
|
|
2866
3532
|
const vdigCtx = this.vdigFields !== null ? { id, prev: await this.adapter.get(this.vault, this.name, id) } : void 0;
|
|
2867
3533
|
if (existing && this.historyConfig.enabled !== false) {
|
|
2868
|
-
const historyEnvelope = await this.codec.encryptRecord(existing.record, existing.version, cek, void 0, void 0, vdigCtx);
|
|
3534
|
+
const historyEnvelope = await this.codec.encryptRecord(existing.record, existing.version, cek, void 0, void 0, vdigCtx, id);
|
|
2869
3535
|
await this.historyStrategy.saveHistory(this.adapter, this.vault, this.name, id, historyEnvelope);
|
|
2870
3536
|
this.emitter.emit("history:save", {
|
|
2871
3537
|
vault: this.vault,
|
|
@@ -2879,7 +3545,7 @@ var Collection = class {
|
|
|
2879
3545
|
});
|
|
2880
3546
|
}
|
|
2881
3547
|
}
|
|
2882
|
-
const envelope = await this.codec.encryptRecord(record, version, cek, options?.source, options?.sourceTs, vdigCtx);
|
|
3548
|
+
const envelope = await this.codec.encryptRecord(record, version, cek, options?.source, options?.sourceTs, vdigCtx, id);
|
|
2883
3549
|
await this.adapter.put(this.vault, this.name, id, envelope);
|
|
2884
3550
|
await this._ensureClassifiedMarker();
|
|
2885
3551
|
if (this.embeddings) {
|
|
@@ -2908,17 +3574,7 @@ var Collection = class {
|
|
|
2908
3574
|
this.indexes?.upsert(id, record, existing ? existing.record : null);
|
|
2909
3575
|
this.uniqueConstraints?.upsert(id, record, existing?.record);
|
|
2910
3576
|
}
|
|
2911
|
-
await this.
|
|
2912
|
-
this.emitter.emit("change", {
|
|
2913
|
-
vault: this.vault,
|
|
2914
|
-
collection: this.name,
|
|
2915
|
-
id,
|
|
2916
|
-
action: "put"
|
|
2917
|
-
});
|
|
2918
|
-
this.searchIndexStore?.markDirty();
|
|
2919
|
-
await this.onAccess?.("put", id);
|
|
2920
|
-
await this.dispatchDerivations(id, record, version);
|
|
2921
|
-
await this.dispatchMaterializedViews(id, record);
|
|
3577
|
+
await this._onRecordMutated(id, "put", "local-write", { record, version });
|
|
2922
3578
|
}
|
|
2923
3579
|
/**
|
|
2924
3580
|
* Fire registered MV strategies whose dependency set includes this
|
|
@@ -2930,10 +3586,10 @@ var Collection = class {
|
|
|
2930
3586
|
* MV-emitted row (carries `_materializedFrom`) — defensive guard
|
|
2931
3587
|
* against missed cycle detection.
|
|
2932
3588
|
*
|
|
2933
|
-
* @internal
|
|
3589
|
+
* @internal `wave` (#638 Task 4): when present (the sync/cutover/restore dispatch wave),
|
|
3590
|
+
* an eager MV already refreshed this wave is skipped (per-target dedup, keyed on spec name).
|
|
2934
3591
|
*/
|
|
2935
|
-
async dispatchMaterializedViews(id, record) {
|
|
2936
|
-
void id;
|
|
3592
|
+
async dispatchMaterializedViews(id, record, wave) {
|
|
2937
3593
|
if (this.materializedViewSource === void 0) return;
|
|
2938
3594
|
const incoming = record;
|
|
2939
3595
|
if (incoming && typeof incoming === "object" && "_materializedFrom" in incoming) return;
|
|
@@ -2945,18 +3601,20 @@ var Collection = class {
|
|
|
2945
3601
|
for (const reg of mvs) {
|
|
2946
3602
|
const mode = reg.spec.refresh;
|
|
2947
3603
|
if (mode === "eager") {
|
|
3604
|
+
if (wave?.seen(`mv\0${reg.spec.name}`)) continue;
|
|
2948
3605
|
if (executor === null) {
|
|
2949
3606
|
;
|
|
2950
|
-
({ MaterializedViewExecutor: executor } = await import("./executor-
|
|
3607
|
+
({ MaterializedViewExecutor: executor } = await import("./executor-IL7SRNUZ.js"));
|
|
2951
3608
|
}
|
|
2952
3609
|
await executor.refresh(reg, {
|
|
2953
3610
|
getCollection: (name) => this.materializedViewSource.getCollection(name),
|
|
2954
3611
|
getActiveTxContext: () => this.materializedViewSource.getActiveTxContext(),
|
|
2955
|
-
getQueryContext: () => this.materializedViewSource.getQueryContext()
|
|
3612
|
+
getQueryContext: () => this.materializedViewSource.getQueryContext(),
|
|
3613
|
+
dispatchCtx: this.#dispatchCtx({ collection: this.name, id })
|
|
2956
3614
|
});
|
|
2957
3615
|
} else if (mode === "lazy") {
|
|
2958
3616
|
if (staleHelpers === null) {
|
|
2959
|
-
staleHelpers = await import("./stale-
|
|
3617
|
+
staleHelpers = await import("./stale-IYYXEH3U.js");
|
|
2960
3618
|
}
|
|
2961
3619
|
staleHelpers.markMVStale(registry, reg.spec.name);
|
|
2962
3620
|
}
|
|
@@ -3008,7 +3666,15 @@ var Collection = class {
|
|
|
3008
3666
|
raw = (await this.resolvePriorValues(id))?.record ?? null;
|
|
3009
3667
|
}
|
|
3010
3668
|
if (raw === null) return null;
|
|
3011
|
-
return this.
|
|
3669
|
+
return this.via ? this.via.canonicalizeStored(raw) : raw;
|
|
3670
|
+
}
|
|
3671
|
+
/** @internal #638 Task 4 — decrypted STORED-form record + envelope version for the sync/cutover/
|
|
3672
|
+
* restore dispatch wave (id threaded into decrypt, matching `_invalidateCacheEntry`'s contract). */
|
|
3673
|
+
async _getStoredRecordForDispatch(id) {
|
|
3674
|
+
const env = await this.adapter.get(this.vault, this.name, id);
|
|
3675
|
+
if (!env || isTombstone(env, this.storeCiphertext) || isDeleteMarker(env)) return null;
|
|
3676
|
+
const record = await this.codec.decryptRecord(env, { id });
|
|
3677
|
+
return record === null ? null : { record, version: env._v };
|
|
3012
3678
|
}
|
|
3013
3679
|
/**
|
|
3014
3680
|
* @internal Ids of records whose top-level `field` equals `value`.
|
|
@@ -3039,19 +3705,22 @@ var Collection = class {
|
|
|
3039
3705
|
}
|
|
3040
3706
|
return out;
|
|
3041
3707
|
}
|
|
3042
|
-
/**
|
|
3043
|
-
*
|
|
3044
|
-
*
|
|
3045
|
-
*
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3708
|
+
/** @internal Recompute a rollup aggregate onto the parent from `parentId`'s current children
|
|
3709
|
+
* (value-equality guarded; no-op absent parent). `wave` (#638 T4): per-target dedup. Returns
|
|
3710
|
+
* the `putDerivedOutput` outcome, or `'noop'` when nothing needed recomputing (#638 T6 —
|
|
3711
|
+
* `dispatchRollupsOnDelete`'s forget-fanout caller needs this to fill the forget report). */
|
|
3712
|
+
/** @internal — ctx for `putDerivedOutput`'s frozen-period skip+audit (#638 Task 5). */
|
|
3713
|
+
#dispatchCtx(source) {
|
|
3714
|
+
return { emit: (e, p) => this.emitter.emit(e, p), source, audit: ledgerAuditHook(this.ledger, this.keyring.userId) };
|
|
3715
|
+
}
|
|
3716
|
+
async recomputeRollup(spec, parentId, source, wave) {
|
|
3717
|
+
if (this.derivationSource === void 0 || spec.rollup === void 0) return "noop";
|
|
3050
3718
|
const { from, key, field, compute } = spec.rollup;
|
|
3051
3719
|
const into = spec.source;
|
|
3720
|
+
if (wave?.seen(`rollup\0${into}\0${parentId}\0${field}`)) return "noop";
|
|
3052
3721
|
const intoColl = this.derivationSource.getCollection(into);
|
|
3053
3722
|
const base = await intoColl._getStoredRecord(parentId);
|
|
3054
|
-
if (base === null) return;
|
|
3723
|
+
if (base === null) return "noop";
|
|
3055
3724
|
const fromColl = this.derivationSource.getCollection(from);
|
|
3056
3725
|
const childIds = await fromColl._findMatchingIds(key, parentId);
|
|
3057
3726
|
const children = [];
|
|
@@ -3060,7 +3729,7 @@ var Collection = class {
|
|
|
3060
3729
|
if (c !== null && c !== void 0) children.push(c);
|
|
3061
3730
|
}
|
|
3062
3731
|
const newValue = compute(children);
|
|
3063
|
-
if (selfWriteFieldEqual(base[field], newValue)) return;
|
|
3732
|
+
if (selfWriteFieldEqual(base[field], newValue)) return "noop";
|
|
3064
3733
|
const patched = { ...base, [field]: newValue };
|
|
3065
3734
|
const txCtx = this.derivationSource.getActiveTxContext();
|
|
3066
3735
|
if (txCtx !== null) {
|
|
@@ -3070,28 +3739,32 @@ var Collection = class {
|
|
|
3070
3739
|
priorEnvelope: prior
|
|
3071
3740
|
});
|
|
3072
3741
|
}
|
|
3073
|
-
|
|
3742
|
+
return putDerivedOutput(intoColl, parentId, patched, this.#dispatchCtx(source));
|
|
3074
3743
|
}
|
|
3075
3744
|
/**
|
|
3076
|
-
* @internal Fire any rollups for which THIS collection is the
|
|
3077
|
-
*
|
|
3078
|
-
* from
|
|
3079
|
-
*
|
|
3745
|
+
* @internal Fire any rollups for which THIS collection is the child `from`, recomputing the
|
|
3746
|
+
* affected parent after a child delete/forget. Called from the delete path (return discarded)
|
|
3747
|
+
* and from `forgetDerivedFanout` (#638 Task 6), which needs the per-target outcome to fill
|
|
3748
|
+
* `ForgetResult.derivedAggregatesRecomputed`/`derivedResidueFrozen`.
|
|
3080
3749
|
*/
|
|
3081
|
-
async dispatchRollupsOnDelete(deleted) {
|
|
3082
|
-
if (this.derivationSource === void 0) return;
|
|
3750
|
+
async dispatchRollupsOnDelete(id, deleted) {
|
|
3751
|
+
if (this.derivationSource === void 0) return [];
|
|
3083
3752
|
const registry = this.derivationSource.registry();
|
|
3084
3753
|
const rec = deleted;
|
|
3754
|
+
const results = [];
|
|
3085
3755
|
for (const { spec } of registry.strategiesForSource(this.name)) {
|
|
3086
3756
|
if (!spec.rollup || spec.rollup.from !== this.name) continue;
|
|
3087
3757
|
const kv = rec[spec.rollup.key];
|
|
3088
3758
|
if (typeof kv !== "string" && typeof kv !== "number") continue;
|
|
3089
|
-
await this.recomputeRollup(spec, String(kv));
|
|
3759
|
+
results.push({ into: spec.source, parentId: String(kv), outcome: await this.recomputeRollup(spec, String(kv), { collection: this.name, id }) });
|
|
3090
3760
|
}
|
|
3761
|
+
return results;
|
|
3091
3762
|
}
|
|
3092
|
-
|
|
3763
|
+
/** @internal `wave` (#638 Task 4) — threaded to `recomputeRollup` for the sync/cutover/restore
|
|
3764
|
+
* dispatch wave's per-target dedup; `undefined` on the local-write path (byte-identical). */
|
|
3765
|
+
async dispatchDerivations(id, record, version, wave) {
|
|
3093
3766
|
if (this.derivationSource === void 0) return;
|
|
3094
|
-
const incoming = this.
|
|
3767
|
+
const incoming = this.via ? this.via.canonicalizeStored(record) : record;
|
|
3095
3768
|
if (incoming && typeof incoming === "object" && "_derivedFrom" in incoming) return;
|
|
3096
3769
|
const registry = this.derivationSource.registry();
|
|
3097
3770
|
const strategies = registry.strategiesForSource(this.name);
|
|
@@ -3108,7 +3781,7 @@ var Collection = class {
|
|
|
3108
3781
|
} else {
|
|
3109
3782
|
parentId = id;
|
|
3110
3783
|
}
|
|
3111
|
-
if (parentId !== null) await this.recomputeRollup(spec, parentId);
|
|
3784
|
+
if (parentId !== null) await this.recomputeRollup(spec, parentId, { collection: this.name, id }, wave);
|
|
3112
3785
|
continue;
|
|
3113
3786
|
}
|
|
3114
3787
|
const isSource = spec.source === this.name;
|
|
@@ -3141,10 +3814,11 @@ var Collection = class {
|
|
|
3141
3814
|
continue;
|
|
3142
3815
|
}
|
|
3143
3816
|
if (DerivationExecutor === null) {
|
|
3144
|
-
({ DerivationExecutor } = await import("./executor-
|
|
3817
|
+
({ DerivationExecutor } = await import("./executor-WAEI6PDV.js"));
|
|
3145
3818
|
}
|
|
3146
3819
|
for (const run of runs) {
|
|
3147
3820
|
const ctx = { vault: this.derivationSource.getReadOnlyFacade() };
|
|
3821
|
+
const outCtx = this.#dispatchCtx({ collection: spec.source, id: run.runId });
|
|
3148
3822
|
const result = await DerivationExecutor.run(spec, run.input, run.version, strategyHash, ctx);
|
|
3149
3823
|
for (const key of Object.keys(spec.outputs)) {
|
|
3150
3824
|
const out = result.outputs[key];
|
|
@@ -3160,7 +3834,7 @@ var Collection = class {
|
|
|
3160
3834
|
const outputCollection = this.derivationSource.getCollection(outSpec.collection);
|
|
3161
3835
|
const txCtx = this.derivationSource.getActiveTxContext();
|
|
3162
3836
|
if (out.kind === "array") {
|
|
3163
|
-
const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-
|
|
3837
|
+
const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-CI5BCMQM.js");
|
|
3164
3838
|
const prior = await loadFanoutSidecar(this.adapter, this.vault, spec.source, run.runId, key, this.getDEK, this.storeCiphertext);
|
|
3165
3839
|
const prevKeys = new Set(prior?.keys ?? []);
|
|
3166
3840
|
const newKeysList = out.entries.map((e) => e.key);
|
|
@@ -3182,7 +3856,7 @@ var Collection = class {
|
|
|
3182
3856
|
priorEnvelope
|
|
3183
3857
|
});
|
|
3184
3858
|
}
|
|
3185
|
-
await outputCollection
|
|
3859
|
+
await putDerivedOutput(outputCollection, entry.key, entry.value, outCtx, { source: "derived" });
|
|
3186
3860
|
}
|
|
3187
3861
|
await saveFanoutSidecar(this.adapter, this.vault, {
|
|
3188
3862
|
source: spec.source,
|
|
@@ -3215,7 +3889,7 @@ var Collection = class {
|
|
|
3215
3889
|
priorEnvelope: prior
|
|
3216
3890
|
});
|
|
3217
3891
|
}
|
|
3218
|
-
await outputCollection
|
|
3892
|
+
await putDerivedOutput(outputCollection, run.runId, patched, outCtx, { source: "derived" });
|
|
3219
3893
|
continue;
|
|
3220
3894
|
}
|
|
3221
3895
|
if (txCtx !== null) {
|
|
@@ -3230,7 +3904,7 @@ var Collection = class {
|
|
|
3230
3904
|
priorEnvelope: prior
|
|
3231
3905
|
});
|
|
3232
3906
|
}
|
|
3233
|
-
await outputCollection
|
|
3907
|
+
await putDerivedOutput(outputCollection, run.runId, out.value, outCtx, { source: "derived" });
|
|
3234
3908
|
}
|
|
3235
3909
|
}
|
|
3236
3910
|
}
|
|
@@ -3287,9 +3961,9 @@ var Collection = class {
|
|
|
3287
3961
|
const next = transform(record);
|
|
3288
3962
|
const nextVersion = (env._v ?? 0) + 1;
|
|
3289
3963
|
const cek = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
|
|
3290
|
-
const newEnv = await this.codec.encryptRecord(next, nextVersion, cek, void 0, void 0, this.vdigFields !== null ? { id, prev: env } : void 0);
|
|
3964
|
+
const newEnv = await this.codec.encryptRecord(next, nextVersion, cek, void 0, void 0, this.vdigFields !== null ? { id, prev: env } : void 0, id);
|
|
3291
3965
|
await this.adapter.put(this.vault, this.name, id, newEnv);
|
|
3292
|
-
await this.
|
|
3966
|
+
await this._onRecordMutated(id, "put", "cutover");
|
|
3293
3967
|
if (this.ledger) {
|
|
3294
3968
|
await this.ledger.append({
|
|
3295
3969
|
op: "migration",
|
|
@@ -3318,9 +3992,8 @@ var Collection = class {
|
|
|
3318
3992
|
* history snapshot still fire so backup integrity and time-travel
|
|
3319
3993
|
* reconstruction stay consistent.
|
|
3320
3994
|
*
|
|
3321
|
-
* Returns
|
|
3322
|
-
*
|
|
3323
|
-
* envelope and short-circuits before the ledger/event side-effects).
|
|
3995
|
+
* Returns `true` when it erased a live record, `false` for delete-of-absent (idempotent
|
|
3996
|
+
* contract — both txCtx-aware and txCtx===null callers honour this, short-circuiting before any side-effect).
|
|
3324
3997
|
*
|
|
3325
3998
|
* When a `txCtx` is supplied, the prior envelope is captured and
|
|
3326
3999
|
* pushed onto `txCtx._executed` BEFORE the delete fires — mirrors
|
|
@@ -3349,7 +4022,7 @@ var Collection = class {
|
|
|
3349
4022
|
*/
|
|
3350
4023
|
async _internalDelete(id, txCtx = null) {
|
|
3351
4024
|
const prior = await this.adapter.get(this.vault, this.name, id);
|
|
3352
|
-
if (prior === null) return;
|
|
4025
|
+
if (prior === null) return false;
|
|
3353
4026
|
if (txCtx !== null) {
|
|
3354
4027
|
txCtx._executed.push({
|
|
3355
4028
|
op: {
|
|
@@ -3362,6 +4035,7 @@ var Collection = class {
|
|
|
3362
4035
|
});
|
|
3363
4036
|
}
|
|
3364
4037
|
await this._doDelete(id, true);
|
|
4038
|
+
return true;
|
|
3365
4039
|
}
|
|
3366
4040
|
async _doDelete(id, internal) {
|
|
3367
4041
|
if (!hasWritePermission(this.keyring, this.name)) {
|
|
@@ -3374,7 +4048,7 @@ var Collection = class {
|
|
|
3374
4048
|
vault: this.vault,
|
|
3375
4049
|
collection: this.name,
|
|
3376
4050
|
docId: id,
|
|
3377
|
-
existing: this.
|
|
4051
|
+
existing: this.via ? this.via.canonicalizeStored(existingRecord) : existingRecord,
|
|
3378
4052
|
existingVersion: existingEnv?._v ?? 0,
|
|
3379
4053
|
existingTs: existingEnv?._ts,
|
|
3380
4054
|
internal,
|
|
@@ -3392,7 +4066,7 @@ var Collection = class {
|
|
|
3392
4066
|
if (!existing && this.historyConfig.enabled !== false) {
|
|
3393
4067
|
const previousEnvelope2 = await this.adapter.get(this.vault, this.name, id);
|
|
3394
4068
|
if (previousEnvelope2) {
|
|
3395
|
-
const previousRecord = await this.codec.decryptRecord(previousEnvelope2);
|
|
4069
|
+
const previousRecord = await this.codec.decryptRecord(previousEnvelope2, { id });
|
|
3396
4070
|
if (previousRecord !== null) {
|
|
3397
4071
|
existing = { record: previousRecord, version: previousEnvelope2._v };
|
|
3398
4072
|
}
|
|
@@ -3404,7 +4078,7 @@ var Collection = class {
|
|
|
3404
4078
|
if (existing && this.historyConfig.enabled !== false) {
|
|
3405
4079
|
const cek = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
|
|
3406
4080
|
const prevForVdig = this.vdigFields !== null ? await this.adapter.get(this.vault, this.name, id) : null;
|
|
3407
|
-
const historyEnvelope = await this.codec.encryptRecord(existing.record, existing.version, cek, void 0, void 0, this.vdigFields !== null ? { id, prev: prevForVdig } : void 0);
|
|
4081
|
+
const historyEnvelope = await this.codec.encryptRecord(existing.record, existing.version, cek, void 0, void 0, this.vdigFields !== null ? { id, prev: prevForVdig } : void 0, id);
|
|
3408
4082
|
await this.historyStrategy.saveHistory(this.adapter, this.vault, this.name, id, historyEnvelope);
|
|
3409
4083
|
}
|
|
3410
4084
|
const previousEnvelope = await this.adapter.get(this.vault, this.name, id);
|
|
@@ -3440,19 +4114,11 @@ var Collection = class {
|
|
|
3440
4114
|
this.uniqueConstraints?.remove(id, existing.record);
|
|
3441
4115
|
}
|
|
3442
4116
|
}
|
|
3443
|
-
await this.
|
|
3444
|
-
this.emitter.emit("change", {
|
|
3445
|
-
vault: this.vault,
|
|
3446
|
-
collection: this.name,
|
|
3447
|
-
id,
|
|
3448
|
-
action: "delete"
|
|
3449
|
-
});
|
|
3450
|
-
this.searchIndexStore?.markDirty();
|
|
3451
|
-
await this.onAccess?.("delete", id);
|
|
4117
|
+
await this._onRecordMutated(id, "delete", "local-delete", { version: markerVersion ?? (existing?.version ?? 0) + 1 });
|
|
3452
4118
|
if (!internal) {
|
|
3453
4119
|
await this.dispatchMaterializedViewsOnDelete(id);
|
|
3454
4120
|
await this.dispatchArrayDerivationsOnDelete(id);
|
|
3455
|
-
if (existing) await this.dispatchRollupsOnDelete(existing.record);
|
|
4121
|
+
if (existing) await this.dispatchRollupsOnDelete(id, existing.record);
|
|
3456
4122
|
}
|
|
3457
4123
|
}
|
|
3458
4124
|
/**
|
|
@@ -3501,75 +4167,81 @@ var Collection = class {
|
|
|
3501
4167
|
return { previousVersion: live._v };
|
|
3502
4168
|
}
|
|
3503
4169
|
/**
|
|
3504
|
-
* Cascade deletes of array-shape derived rows when a source row is
|
|
3505
|
-
*
|
|
3506
|
-
*
|
|
3507
|
-
* deletes the sidecar itself.
|
|
3508
|
-
*
|
|
3509
|
-
* Record-shape derivations are skipped — see _doDelete's comment
|
|
3510
|
-
* for why the asymmetry is correct.
|
|
4170
|
+
* Cascade deletes of array-shape derived rows when a source row is deleted. Reads each
|
|
4171
|
+
* registered strategy's fanout sidecar for this source id, deletes every listed derived
|
|
4172
|
+
* row, then deletes the sidecar itself. Returns the REAL erased count (#622 review Finding 1).
|
|
3511
4173
|
*
|
|
4174
|
+
* Record-shape derivations are skipped on the ordinary delete path (see _doDelete's comment).
|
|
4175
|
+
* `eraseRecordShapeToo` (#638 T6, default `false`) opts a same-id record-shape copy into
|
|
4176
|
+
* erasure too — forget()'s fanout, GDPR residue. A delete-of-absent contributes 0 either way.
|
|
3512
4177
|
* @internal
|
|
3513
4178
|
*/
|
|
3514
|
-
async dispatchArrayDerivationsOnDelete(id) {
|
|
3515
|
-
if (this.derivationSource === void 0) return;
|
|
4179
|
+
async dispatchArrayDerivationsOnDelete(id, eraseRecordShapeToo = false) {
|
|
4180
|
+
if (this.derivationSource === void 0) return 0;
|
|
3516
4181
|
const registry = this.derivationSource.registry();
|
|
3517
4182
|
const strategies = registry.strategiesForSource(this.name);
|
|
3518
|
-
if (strategies.length === 0) return;
|
|
4183
|
+
if (strategies.length === 0) return 0;
|
|
3519
4184
|
let helpers = null;
|
|
3520
4185
|
const txCtx = this.derivationSource.getActiveTxContext();
|
|
4186
|
+
let erased = 0;
|
|
3521
4187
|
for (const { spec } of strategies) {
|
|
3522
4188
|
for (const [outputKey, outSpec] of Object.entries(spec.outputs)) {
|
|
3523
|
-
if (outSpec.shape
|
|
4189
|
+
if (outSpec.shape === "record") {
|
|
4190
|
+
if (eraseRecordShapeToo && spec.source === this.name && outSpec.collection !== this.name) {
|
|
4191
|
+
if (await this.derivationSource.getCollection(outSpec.collection)._internalDelete(id, txCtx)) erased += 1;
|
|
4192
|
+
}
|
|
4193
|
+
continue;
|
|
4194
|
+
}
|
|
3524
4195
|
if (helpers === null) {
|
|
3525
|
-
helpers = await import("./fanout-sidecar-
|
|
4196
|
+
helpers = await import("./fanout-sidecar-CI5BCMQM.js");
|
|
3526
4197
|
}
|
|
3527
4198
|
const sidecar = await helpers.loadFanoutSidecar(this.adapter, this.vault, spec.source, id, outputKey, this.getDEK, this.storeCiphertext);
|
|
3528
4199
|
if (!sidecar) continue;
|
|
3529
4200
|
const outputCollection = this.derivationSource.getCollection(outSpec.collection);
|
|
3530
4201
|
for (const derivedId of sidecar.keys) {
|
|
3531
|
-
await outputCollection._internalDelete(derivedId, txCtx);
|
|
4202
|
+
if (await outputCollection._internalDelete(derivedId, txCtx)) erased += 1;
|
|
3532
4203
|
}
|
|
3533
4204
|
await helpers.deleteFanoutSidecar(this.adapter, this.vault, spec.source, id, outputKey);
|
|
3534
4205
|
}
|
|
3535
4206
|
}
|
|
4207
|
+
return erased;
|
|
3536
4208
|
}
|
|
3537
4209
|
/**
|
|
3538
|
-
* Mirror of {@link dispatchMaterializedViews} for the delete path
|
|
3539
|
-
*
|
|
3540
|
-
*
|
|
3541
|
-
*
|
|
3542
|
-
* at the `_doDelete` call site above.
|
|
3543
|
-
*
|
|
4210
|
+
* Mirror of {@link dispatchMaterializedViews} for the delete/forget path — no `_materializedFrom`
|
|
4211
|
+
* skip (record's gone); the `internal` gate at `_doDelete` is the recursion guard. Returns the
|
|
4212
|
+
* row count TOMBSTONED across every eager MV sourced here (#638 T6 — `forgetDerivedFanout`'s
|
|
4213
|
+
* `derivedRecordsErased`); a lazy MV only flips its stale bit (0 contribution — resolved on read).
|
|
3544
4214
|
* @internal
|
|
3545
4215
|
*/
|
|
3546
4216
|
async dispatchMaterializedViewsOnDelete(id) {
|
|
3547
|
-
void
|
|
3548
|
-
if (this.materializedViewSource === void 0) return;
|
|
4217
|
+
if (this.materializedViewSource === void 0) return 0;
|
|
3549
4218
|
const registry = this.materializedViewSource.registry();
|
|
3550
4219
|
const mvs = registry.mvsForSource(this.name);
|
|
3551
|
-
if (mvs.length === 0) return;
|
|
4220
|
+
if (mvs.length === 0) return 0;
|
|
3552
4221
|
let executor = null;
|
|
3553
4222
|
let staleHelpers = null;
|
|
4223
|
+
let deleted = 0;
|
|
3554
4224
|
for (const reg of mvs) {
|
|
3555
4225
|
const mode = reg.spec.refresh;
|
|
3556
4226
|
if (mode === "eager") {
|
|
3557
4227
|
if (executor === null) {
|
|
3558
4228
|
;
|
|
3559
|
-
({ MaterializedViewExecutor: executor } = await import("./executor-
|
|
4229
|
+
({ MaterializedViewExecutor: executor } = await import("./executor-IL7SRNUZ.js"));
|
|
3560
4230
|
}
|
|
3561
|
-
await executor.refresh(reg, {
|
|
4231
|
+
deleted += (await executor.refresh(reg, {
|
|
3562
4232
|
getCollection: (name) => this.materializedViewSource.getCollection(name),
|
|
3563
4233
|
getActiveTxContext: () => this.materializedViewSource.getActiveTxContext(),
|
|
3564
|
-
getQueryContext: () => this.materializedViewSource.getQueryContext()
|
|
3565
|
-
|
|
4234
|
+
getQueryContext: () => this.materializedViewSource.getQueryContext(),
|
|
4235
|
+
dispatchCtx: this.#dispatchCtx({ collection: this.name, id })
|
|
4236
|
+
})).deleted;
|
|
3566
4237
|
} else if (mode === "lazy") {
|
|
3567
4238
|
if (staleHelpers === null) {
|
|
3568
|
-
staleHelpers = await import("./stale-
|
|
4239
|
+
staleHelpers = await import("./stale-IYYXEH3U.js");
|
|
3569
4240
|
}
|
|
3570
4241
|
staleHelpers.markMVStale(registry, reg.spec.name);
|
|
3571
4242
|
}
|
|
3572
4243
|
}
|
|
4244
|
+
return deleted;
|
|
3573
4245
|
}
|
|
3574
4246
|
/**
|
|
3575
4247
|
* List all records in the collection.
|
|
@@ -3588,22 +4260,14 @@ var Collection = class {
|
|
|
3588
4260
|
);
|
|
3589
4261
|
}
|
|
3590
4262
|
if (this.materializedViewSource !== void 0) {
|
|
3591
|
-
const { resolveStaleMVOnRead } = await import("./stale-
|
|
4263
|
+
const { resolveStaleMVOnRead } = await import("./stale-IYYXEH3U.js");
|
|
3592
4264
|
await resolveStaleMVOnRead(this.materializedViewSource, this.name);
|
|
3593
4265
|
}
|
|
3594
4266
|
await this.ensureHydrated();
|
|
3595
4267
|
const records = [...this.cache.values()].map((e) => e.record);
|
|
3596
|
-
if (!this.
|
|
4268
|
+
if (!this.via) return records;
|
|
3597
4269
|
return Promise.all(records.map((r) => this.applyLocaleToRecord(r, locale)));
|
|
3598
4270
|
}
|
|
3599
|
-
/**
|
|
3600
|
-
* @internal — whether any read-side record transform is registered
|
|
3601
|
-
* (money decode, i18nText resolution, dictKey labels). Gates the
|
|
3602
|
-
* no-transform fast path in {@link list}.
|
|
3603
|
-
*/
|
|
3604
|
-
hasReadTransforms() {
|
|
3605
|
-
return this.moneyFields !== void 0 && Object.keys(this.moneyFields).length > 0 || this.i18nFields !== void 0 && Object.keys(this.i18nFields).length > 0 || this.dictKeyFields !== void 0 && Object.keys(this.dictKeyFields).length > 0;
|
|
3606
|
-
}
|
|
3607
4271
|
/**
|
|
3608
4272
|
* Scan-mode full-text search over a plain-text `field`. Decrypts the
|
|
3609
4273
|
* collection in memory and ranks records by BM25 against the tokenized query.
|
|
@@ -3830,7 +4494,7 @@ var Collection = class {
|
|
|
3830
4494
|
getIndexes: () => this.getIndexes(),
|
|
3831
4495
|
lookupById: (id) => this.cache.get(id)?.record,
|
|
3832
4496
|
snapshotEntries: () => [...this.cache.entries()].map(([id, e]) => ({ id, record: e.record })),
|
|
3833
|
-
...this.
|
|
4497
|
+
...this.via ? { via: this.via } : {}
|
|
3834
4498
|
};
|
|
3835
4499
|
const resolver = this.joinResolver;
|
|
3836
4500
|
const leftCollection = this.name;
|
|
@@ -3910,8 +4574,7 @@ var Collection = class {
|
|
|
3910
4574
|
* silently miss records — consistent with the `query()` /
|
|
3911
4575
|
* `list()` lazy-mode policy. If this becomes a blocker for a
|
|
3912
4576
|
* real consumer, the fix is to add an async `scan()`-backed
|
|
3913
|
-
* variant of this method, which is exactly what
|
|
3914
|
-
* joins will need anyway.
|
|
4577
|
+
* variant of this method, which is exactly what streaming joins will need anyway.
|
|
3915
4578
|
*/
|
|
3916
4579
|
querySourceForJoin() {
|
|
3917
4580
|
if (this.lazy) {
|
|
@@ -3931,9 +4594,8 @@ var Collection = class {
|
|
|
3931
4594
|
this.emitter.on("change", handler);
|
|
3932
4595
|
return () => this.emitter.off("change", handler);
|
|
3933
4596
|
},
|
|
3934
|
-
//
|
|
3935
|
-
|
|
3936
|
-
...this.i18nFields !== void 0 ? { i18nFields: this.i18nFields } : {}
|
|
4597
|
+
// Sync join dressing (#650 Task 6, #626 retirement) — i18n-text + lookup-label, from this collection's own bindings.
|
|
4598
|
+
...this.presentForJoin !== void 0 ? { presentForJoin: this.presentForJoin } : {}
|
|
3937
4599
|
};
|
|
3938
4600
|
}
|
|
3939
4601
|
/**
|
|
@@ -3969,7 +4631,7 @@ var Collection = class {
|
|
|
3969
4631
|
);
|
|
3970
4632
|
const entries = [];
|
|
3971
4633
|
for (const env of envelopes) {
|
|
3972
|
-
const record = await this.codec.decryptRecord(env, { skipValidation: true });
|
|
4634
|
+
const record = await this.codec.decryptRecord(env, { skipValidation: true, id });
|
|
3973
4635
|
if (record === null) continue;
|
|
3974
4636
|
entries.push({
|
|
3975
4637
|
version: env._v,
|
|
@@ -3998,7 +4660,7 @@ var Collection = class {
|
|
|
3998
4660
|
version
|
|
3999
4661
|
);
|
|
4000
4662
|
if (!envelope) return null;
|
|
4001
|
-
return this.codec.decryptRecord(envelope, { skipValidation: true });
|
|
4663
|
+
return this.codec.decryptRecord(envelope, { skipValidation: true, id });
|
|
4002
4664
|
}
|
|
4003
4665
|
/** Revert a record to a past version. Creates a new version with the old content. */
|
|
4004
4666
|
async revert(id, version) {
|
|
@@ -4106,7 +4768,7 @@ var Collection = class {
|
|
|
4106
4768
|
const id = ids[i];
|
|
4107
4769
|
const envelope = await this.adapter.get(this.vault, this.name, id);
|
|
4108
4770
|
if (envelope) {
|
|
4109
|
-
const record = await this.codec.decryptRecord(envelope, { sealedAsHandles: true });
|
|
4771
|
+
const record = await this.codec.decryptRecord(envelope, { id, sealedAsHandles: true });
|
|
4110
4772
|
if (record === null) continue;
|
|
4111
4773
|
items.push(record);
|
|
4112
4774
|
if (!this.lazy && !this.cache.has(id)) {
|
|
@@ -4180,14 +4842,14 @@ var Collection = class {
|
|
|
4180
4842
|
[],
|
|
4181
4843
|
[],
|
|
4182
4844
|
joinContext,
|
|
4183
|
-
this.
|
|
4845
|
+
this.via
|
|
4184
4846
|
);
|
|
4185
4847
|
}
|
|
4186
4848
|
/** Decrypt a page of envelopes returned by `adapter.listPage`. */
|
|
4187
4849
|
async decryptPage(items) {
|
|
4188
4850
|
const out = [];
|
|
4189
4851
|
for (const { id, envelope } of items) {
|
|
4190
|
-
const record = await this.codec.decryptRecord(envelope, { sealedAsHandles: true });
|
|
4852
|
+
const record = await this.codec.decryptRecord(envelope, { id, sealedAsHandles: true });
|
|
4191
4853
|
if (record === null) continue;
|
|
4192
4854
|
out.push({ id, record, version: envelope._v });
|
|
4193
4855
|
}
|
|
@@ -4247,7 +4909,7 @@ var Collection = class {
|
|
|
4247
4909
|
}
|
|
4248
4910
|
return;
|
|
4249
4911
|
}
|
|
4250
|
-
const record = await this.codec.decryptRecord(envelope, { sealedAsHandles: true });
|
|
4912
|
+
const record = await this.codec.decryptRecord(envelope, { id, sealedAsHandles: true });
|
|
4251
4913
|
if (record === null) {
|
|
4252
4914
|
this.cache.delete(id);
|
|
4253
4915
|
if (previous) {
|
|
@@ -4267,9 +4929,63 @@ var Collection = class {
|
|
|
4267
4929
|
* Never writes to the store and never fires write hooks, so it cannot loop.
|
|
4268
4930
|
*/
|
|
4269
4931
|
async _applyRemoteChange(id, action) {
|
|
4270
|
-
await this.
|
|
4271
|
-
|
|
4272
|
-
|
|
4932
|
+
await this._onRecordMutated(id, action, "tab-mirror");
|
|
4933
|
+
}
|
|
4934
|
+
/**
|
|
4935
|
+
* Origin-tagged mutation choke point (#623 task 10) — the single dispatch
|
|
4936
|
+
* point every put/delete path funnels through, AFTER its own store write,
|
|
4937
|
+
* to fire the side-effects that path performs today. Each case below
|
|
4938
|
+
* performs EXACTLY the side-effect set the seam-map Part-3 table
|
|
4939
|
+
* (`.superpowers/sdd/seam-map-i18n-pipeline.md`) records for `origin` —
|
|
4940
|
+
* this is a pure parity extraction of existing tails, not a behavior
|
|
4941
|
+
* change. Phase C (#621, #622) plugs the dependency-graph dispatch into
|
|
4942
|
+
* this socket, keyed off `origin`, without every call site having to
|
|
4943
|
+
* learn the graph.
|
|
4944
|
+
*
|
|
4945
|
+
* `restore` is never reached: `Vault.load` / `backup.ts#loadVault` drops
|
|
4946
|
+
* the whole `collectionCache` instead of dispatching per record — the
|
|
4947
|
+
* origin is reserved for phase C.
|
|
4948
|
+
*
|
|
4949
|
+
* @internal
|
|
4950
|
+
*/
|
|
4951
|
+
async _onRecordMutated(id, action, origin, ctx) {
|
|
4952
|
+
switch (origin) {
|
|
4953
|
+
case "local-write": {
|
|
4954
|
+
const record = ctx.record;
|
|
4955
|
+
const version = ctx.version;
|
|
4956
|
+
await this.onDirty?.(this.name, id, "put", version);
|
|
4957
|
+
this.emitter.emit("change", { vault: this.vault, collection: this.name, id, action: "put" });
|
|
4958
|
+
this.searchIndexStore?.markDirty();
|
|
4959
|
+
await this.onAccess?.("put", id);
|
|
4960
|
+
await this.dispatchDerivations(id, record, version);
|
|
4961
|
+
await this.dispatchMaterializedViews(id, record);
|
|
4962
|
+
return;
|
|
4963
|
+
}
|
|
4964
|
+
case "local-delete": {
|
|
4965
|
+
await this.onDirty?.(this.name, id, "put", ctx.version);
|
|
4966
|
+
this.emitter.emit("change", { vault: this.vault, collection: this.name, id, action: "delete" });
|
|
4967
|
+
this.searchIndexStore?.markDirty();
|
|
4968
|
+
await this.onAccess?.("delete", id);
|
|
4969
|
+
return;
|
|
4970
|
+
}
|
|
4971
|
+
case "tab-mirror":
|
|
4972
|
+
await this._invalidateCacheEntry(id);
|
|
4973
|
+
this.emitter.emit("change", { vault: this.vault, collection: this.name, id, action });
|
|
4974
|
+
this.searchIndexStore?.markDirty();
|
|
4975
|
+
return;
|
|
4976
|
+
case "sync-apply":
|
|
4977
|
+
this._invalidateCekCacheEntry(id);
|
|
4978
|
+
await this._invalidateCacheEntry(id);
|
|
4979
|
+
this.graphDispatch?.collect(this.name, id);
|
|
4980
|
+
return;
|
|
4981
|
+
case "cutover":
|
|
4982
|
+
await this._invalidateCacheEntry(id);
|
|
4983
|
+
this.graphDispatch?.collect(this.name, id);
|
|
4984
|
+
return;
|
|
4985
|
+
case "restore":
|
|
4986
|
+
this.graphDispatch?.collect(this.name, id);
|
|
4987
|
+
return;
|
|
4988
|
+
}
|
|
4273
4989
|
}
|
|
4274
4990
|
/** @internal — the current in-memory record without a store read (for conflict capture). */
|
|
4275
4991
|
_peekCached(id) {
|
|
@@ -4427,7 +5143,7 @@ var Collection = class {
|
|
|
4427
5143
|
for (const [id, entry] of this.cache) {
|
|
4428
5144
|
const cek = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
|
|
4429
5145
|
const prevForVdig = this.vdigFields !== null ? await this.adapter.get(this.vault, this.name, id) : null;
|
|
4430
|
-
result[id] = await this.codec.encryptRecord(entry.record, entry.version, cek, void 0, void 0, this.vdigFields !== null ? { id, prev: prevForVdig } : void 0);
|
|
5146
|
+
result[id] = await this.codec.encryptRecord(entry.record, entry.version, cek, void 0, void 0, this.vdigFields !== null ? { id, prev: prevForVdig } : void 0, id);
|
|
4431
5147
|
}
|
|
4432
5148
|
return result;
|
|
4433
5149
|
}
|
|
@@ -4442,90 +5158,19 @@ var Collection = class {
|
|
|
4442
5158
|
* map when `locale === 'raw'`).
|
|
4443
5159
|
* - dictKey fields: `<field>Label` virtual fields added.
|
|
4444
5160
|
*
|
|
4445
|
-
* Returns the record unchanged when no
|
|
4446
|
-
*
|
|
5161
|
+
* Returns the record unchanged when no Via pipeline is compiled (#638 Task 7:
|
|
5162
|
+
* was money/i18n/dictKey-flag-gated — missed any OTHER binding's `present`
|
|
5163
|
+
* hook, e.g. the `computed` via-binding's virtual-mode read-time compute;
|
|
5164
|
+
* `this.via` truthiness is the general condition every binding's presence
|
|
5165
|
+
* already implies).
|
|
4447
5166
|
*/
|
|
4448
5167
|
async applyLocaleToRecord(record, localeOpts) {
|
|
4449
|
-
|
|
4450
|
-
const hasDict = this.dictKeyFields && Object.keys(this.dictKeyFields).length > 0;
|
|
4451
|
-
const hasMoney = this.moneyFields && Object.keys(this.moneyFields).length > 0;
|
|
4452
|
-
if (!hasI18n && !hasDict && !hasMoney) return record;
|
|
5168
|
+
if (!this.via) return record;
|
|
4453
5169
|
const locale = localeOpts?.locale ?? this.defaultLocale;
|
|
4454
|
-
let result = record;
|
|
4455
|
-
if (hasMoney && this.moneyFields) {
|
|
4456
|
-
result = moneyRuntime().decodeMoneyFields(result, this.moneyFields, typeof locale === "string" ? locale : void 0);
|
|
4457
|
-
}
|
|
4458
|
-
const hasStaticDisplay = hasDict && this.dictKeyFields !== void 0 && Object.values(this.dictKeyFields).some(
|
|
4459
|
-
(d) => isStaticDictDescriptor(d) && d.displayLocale !== void 0
|
|
4460
|
-
);
|
|
4461
|
-
if (!locale && !hasStaticDisplay) return stripI18nFilled(result);
|
|
4462
5170
|
const layer = localeOpts?._layer ?? "read";
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
if (hasDict && this.dictKeyFields && this.dictLabelResolver && locale !== "raw") {
|
|
4467
|
-
const withLabels = { ...result };
|
|
4468
|
-
const resolver = this.dictLabelResolver;
|
|
4469
|
-
for (const [field, desc] of Object.entries(this.dictKeyFields)) {
|
|
4470
|
-
const policy = desc.onMissing ? resolvePolicy(desc.onMissing, layer) : "null";
|
|
4471
|
-
const fallback = policy === "substitute" ? localeOpts?.fallback ?? desc.substitute : localeOpts?.fallback;
|
|
4472
|
-
const effLocale = locale ?? (isStaticDictDescriptor(desc) ? desc.displayLocale : void 0);
|
|
4473
|
-
const resolveKey = async (key) => {
|
|
4474
|
-
if (!effLocale) {
|
|
4475
|
-
if (policy === "throw") {
|
|
4476
|
-
throw new LocaleNotSpecifiedError(
|
|
4477
|
-
field,
|
|
4478
|
-
`dictKey "${field}": no locale active to resolve key "${key}".`
|
|
4479
|
-
);
|
|
4480
|
-
}
|
|
4481
|
-
return null;
|
|
4482
|
-
}
|
|
4483
|
-
const label = await resolver(desc.name, key, effLocale, fallback);
|
|
4484
|
-
if (label === void 0) {
|
|
4485
|
-
if (policy === "throw") {
|
|
4486
|
-
throw new LocaleNotSpecifiedError(
|
|
4487
|
-
field,
|
|
4488
|
-
`dictKey "${field}": no label for key "${key}" in locale "${effLocale}".`
|
|
4489
|
-
);
|
|
4490
|
-
}
|
|
4491
|
-
return null;
|
|
4492
|
-
}
|
|
4493
|
-
return label;
|
|
4494
|
-
};
|
|
4495
|
-
if (field.includes("[].")) {
|
|
4496
|
-
const parts = field.split("[].");
|
|
4497
|
-
const arrayKey = parts[0];
|
|
4498
|
-
const leaf = parts[1];
|
|
4499
|
-
if (!leaf || leaf.includes(".")) continue;
|
|
4500
|
-
const arr = withLabels[arrayKey];
|
|
4501
|
-
if (!Array.isArray(arr)) continue;
|
|
4502
|
-
const labelKey = `${leaf}Label`;
|
|
4503
|
-
withLabels[arrayKey] = await Promise.all(
|
|
4504
|
-
arr.map(async (el) => {
|
|
4505
|
-
if (!el || typeof el !== "object" || Array.isArray(el)) return el;
|
|
4506
|
-
const k = el[leaf];
|
|
4507
|
-
if (typeof k !== "string") return el;
|
|
4508
|
-
return { ...el, [labelKey]: await resolveKey(k) };
|
|
4509
|
-
})
|
|
4510
|
-
);
|
|
4511
|
-
continue;
|
|
4512
|
-
}
|
|
4513
|
-
const val = result[field];
|
|
4514
|
-
if (Array.isArray(val)) {
|
|
4515
|
-
withLabels[`${field}Label`] = await Promise.all(
|
|
4516
|
-
val.map(async (k) => ({
|
|
4517
|
-
key: k,
|
|
4518
|
-
label: typeof k === "string" ? await resolveKey(k) : null
|
|
4519
|
-
}))
|
|
4520
|
-
);
|
|
4521
|
-
} else if (typeof val === "string") {
|
|
4522
|
-
const label = await resolveKey(val);
|
|
4523
|
-
if (label !== null) withLabels[`${field}Label`] = label;
|
|
4524
|
-
}
|
|
4525
|
-
}
|
|
4526
|
-
result = withLabels;
|
|
4527
|
-
}
|
|
4528
|
-
return stripI18nFilled(result);
|
|
5171
|
+
let result = record;
|
|
5172
|
+
result = await this.via.present(result, { locale, ...localeOpts?.fallback !== void 0 ? { fallback: localeOpts.fallback } : {}, layer });
|
|
5173
|
+
return result;
|
|
4529
5174
|
}
|
|
4530
5175
|
/**
|
|
4531
5176
|
* Low-level: encrypt a pre-serialised JSON string into an EncryptedEnvelope.
|
|
@@ -4742,14 +5387,14 @@ var Collection = class {
|
|
|
4742
5387
|
/** C-A/R10: persist the classified marker once per classified handle (store I/O in config-drift.ts). */
|
|
4743
5388
|
async _ensureClassifiedMarker() {
|
|
4744
5389
|
if (this._markerPersisted || this.vdigFields === null) return;
|
|
4745
|
-
const { persistClassifiedMarkerForFields } = await import("./
|
|
5390
|
+
const { persistClassifiedMarkerForFields } = await import("./classified-marker-IO5EBW7Q.js");
|
|
4746
5391
|
await persistClassifiedMarkerForFields(this.adapter, this.vault, this.name, this.vdigFields, await this.getDEK(this.name));
|
|
4747
5392
|
this._markerPersisted = true;
|
|
4748
5393
|
}
|
|
4749
5394
|
/** C-A/R10 drift signal — the marker's declared digest-only set, memoized to one store read per handle (see config-drift.ts). */
|
|
4750
5395
|
async _classifiedMarkerDigestOnly() {
|
|
4751
5396
|
if (this._markerDigestOnlyCache !== void 0) return this._markerDigestOnlyCache;
|
|
4752
|
-
const { readClassifiedMarkerDigestOnly } = await import("./
|
|
5397
|
+
const { readClassifiedMarkerDigestOnly } = await import("./classified-marker-IO5EBW7Q.js");
|
|
4753
5398
|
this._markerDigestOnlyCache = await readClassifiedMarkerDigestOnly(this.adapter, this.vault, this.name, await this.getDEK(this.name));
|
|
4754
5399
|
return this._markerDigestOnlyCache;
|
|
4755
5400
|
}
|
|
@@ -4828,6 +5473,10 @@ var Collection = class {
|
|
|
4828
5473
|
_classifySealedShred(live) {
|
|
4829
5474
|
return classifySealedShred(this.tiersContext(), live);
|
|
4830
5475
|
}
|
|
5476
|
+
async _onViaErase(id, live) {
|
|
5477
|
+
return this.via ? this.via.eraseSealed({ id, vault: this.vault, live, crypto: await this.codec.eraseCryptoCtx(id, live) }) : void 0;
|
|
5478
|
+
}
|
|
5479
|
+
// @internal forget()'s per-ref via-erase fold (#629 T10)
|
|
4831
5480
|
/**
|
|
4832
5481
|
* Bind the {@link TiersContext} the tier ops need. The `cekCache` is passed
|
|
4833
5482
|
* by reference (the SAME `Lru` the kernel's read/write path owns) so an
|
|
@@ -5777,11 +6426,12 @@ var VaultLinks = class {
|
|
|
5777
6426
|
* for the same (collection, id) returns immediately so two
|
|
5778
6427
|
* mutually-cascading collections don't recurse forever.
|
|
5779
6428
|
*/
|
|
5780
|
-
async enforceRefsOnDelete(collectionName, id) {
|
|
6429
|
+
async enforceRefsOnDelete(graph, collectionName, id) {
|
|
5781
6430
|
const key = `${collectionName}/${id}`;
|
|
5782
6431
|
if (this.cascadeInProgress.has(key)) return;
|
|
5783
6432
|
this.cascadeInProgress.add(key);
|
|
5784
6433
|
try {
|
|
6434
|
+
await this.enforceLookupRefsOnDelete(graph, collectionName, collectionName, id);
|
|
5785
6435
|
const inbound = this.deps.refRegistry.getInbound(collectionName);
|
|
5786
6436
|
for (const rule of inbound) {
|
|
5787
6437
|
const fromCollection = this.deps.collection(rule.collection);
|
|
@@ -5836,6 +6486,109 @@ var VaultLinks = class {
|
|
|
5836
6486
|
this.cascadeInProgress.delete(key);
|
|
5837
6487
|
}
|
|
5838
6488
|
}
|
|
6489
|
+
/**
|
|
6490
|
+
* Find the records in `collection` whose `field` currently equals `key` — the SAME
|
|
6491
|
+
* list()+filter machinery `enforceRefsOnDelete`'s classic ref-restrict/cascade loop above
|
|
6492
|
+
* already uses (bounded by the referencing collection's size, never by the whole vault or by
|
|
6493
|
+
* "which collections reference this" — that part is the graph's O(1) `_out` reverse index).
|
|
6494
|
+
*/
|
|
6495
|
+
async findLookupReferencingRecords(collection, field, key) {
|
|
6496
|
+
const all = await collection.list();
|
|
6497
|
+
return all.filter((rec) => String(rec[field]) === key);
|
|
6498
|
+
}
|
|
6499
|
+
/**
|
|
6500
|
+
* Resolve the VALUE a referencing field actually stores for the row being deleted (`rawKey`,
|
|
6501
|
+
* the backing row's PUT-id) under a given edge's `keyField` — a referencing field always stores
|
|
6502
|
+
* `row[keyField]`, never the PUT-id when the two differ (matrix tier's `lookup(dim, {key})`;
|
|
6503
|
+
* mirrors Task 3's `checkLookupMembership` review fix, Important 1). `keyField === 'id'`
|
|
6504
|
+
* (reserved/static tiers, and the matrix default) needs no row read: `rawKey` IS the value.
|
|
6505
|
+
* Reads the backing row AT MOST ONCE per distinct non-`'id'` `keyField`, even across multiple
|
|
6506
|
+
* edges — `backingCollection` is only `.get()`-able for the matrix tier, which is exactly the
|
|
6507
|
+
* only tier that can ever have `keyField !== 'id'`.
|
|
6508
|
+
*/
|
|
6509
|
+
async resolveLookupCompareKey(backingCollection, rawKey, keyField, cache) {
|
|
6510
|
+
if (keyField === "id") return rawKey;
|
|
6511
|
+
if (cache.has(keyField)) return cache.get(keyField);
|
|
6512
|
+
const row = await this.deps.collection(backingCollection).get(rawKey);
|
|
6513
|
+
const raw = row?.[keyField];
|
|
6514
|
+
const resolved = typeof raw === "string" || typeof raw === "number" ? String(raw) : void 0;
|
|
6515
|
+
cache.set(keyField, resolved);
|
|
6516
|
+
return resolved;
|
|
6517
|
+
}
|
|
6518
|
+
/**
|
|
6519
|
+
* Phase 1 (#650 Task 5, #648) — restrict check, never mutates. Throws
|
|
6520
|
+
* `DictKeyInUseError(dimension, key, usedBy, count)` naming the FIRST referencing collection
|
|
6521
|
+
* still holding a matching record, for every `onDelete:'restrict'` edge the graph's O(1)
|
|
6522
|
+
* `referencingEdgesOf(backingCollection)` reverse lookup finds pointing at this dimension. A
|
|
6523
|
+
* dimension with no declared lookup-referencing edges is a no-op — undeclared refs keep
|
|
6524
|
+
* dangling (today's behavior, unaffected). Called by `Vault.forget()` BEFORE any shred (the
|
|
6525
|
+
* spec §4 "restrict refuses before any shred" design decision) — cascade/nullify propagation
|
|
6526
|
+
* for the SAME edges happens separately, after the shred, via `forgetDerivedFanout`.
|
|
6527
|
+
*
|
|
6528
|
+
* Also resolves (and RETURNS) the live compare-key for EVERY referencing edge, restrict AND
|
|
6529
|
+
* cascade/nullify alike (#650 Task 5 review, Important fix): `forgetDerivedFanout`'s cascade/
|
|
6530
|
+
* nullify propagation runs AFTER `_writeTombstone`, when a non-`'id'` `keyField`'s value can no
|
|
6531
|
+
* longer be read off the (now-shredded) row — resolving it HERE, at the one point every
|
|
6532
|
+
* referencing edge's backing row is still guaranteed live, eliminates that post-shred
|
|
6533
|
+
* dependency. A `keyField` this can't resolve (row unreadable) maps to `undefined` in the
|
|
6534
|
+
* returned map so the caller can report it as residue instead of silently skipping the edge.
|
|
6535
|
+
*/
|
|
6536
|
+
async checkLookupRefsRestrict(graph, dimension, backingCollection, key) {
|
|
6537
|
+
const keyCache = /* @__PURE__ */ new Map();
|
|
6538
|
+
for (const { referencing, onDelete, keyField } of graph.referencingEdgesOf(backingCollection)) {
|
|
6539
|
+
const compareKey = await this.resolveLookupCompareKey(backingCollection, key, keyField, keyCache);
|
|
6540
|
+
if (onDelete !== "restrict" || compareKey === void 0) continue;
|
|
6541
|
+
const coll = this.deps.collection(referencing.collection);
|
|
6542
|
+
const matches = await this.findLookupReferencingRecords(coll, referencing.field, compareKey);
|
|
6543
|
+
if (matches.length > 0) {
|
|
6544
|
+
throw new DictKeyInUseError(dimension, key, referencing.collection, matches.length);
|
|
6545
|
+
}
|
|
6546
|
+
}
|
|
6547
|
+
return keyCache;
|
|
6548
|
+
}
|
|
6549
|
+
/**
|
|
6550
|
+
* Phase 2 (#650 Task 5, #648) — apply `cascade`/`nullify` propagation for this dimension's
|
|
6551
|
+
* non-restrict referencing edges (restrict edges are skipped — phase 1 above already refused
|
|
6552
|
+
* or the caller already checked). `cascade` deletes each matching record through its ordinary
|
|
6553
|
+
* `collection.delete()` (an origin-tagged, fanout-visible delete — participates in sync/history/
|
|
6554
|
+
* the graph dispatch wave exactly like any other delete); `nullify` clears the referencing field
|
|
6555
|
+
* via an ordinary `collection.put()`. Returns the counts so callers (forget's `ForgetFanoutStats`)
|
|
6556
|
+
* can report the propagation additively.
|
|
6557
|
+
*/
|
|
6558
|
+
async applyLookupRefsPropagation(graph, backingCollection, key) {
|
|
6559
|
+
let cascaded = 0;
|
|
6560
|
+
let nullified = 0;
|
|
6561
|
+
const keyCache = /* @__PURE__ */ new Map();
|
|
6562
|
+
for (const { referencing, onDelete, keyField } of graph.referencingEdgesOf(backingCollection)) {
|
|
6563
|
+
if (onDelete === "restrict") continue;
|
|
6564
|
+
const compareKey = await this.resolveLookupCompareKey(backingCollection, key, keyField, keyCache);
|
|
6565
|
+
if (compareKey === void 0) continue;
|
|
6566
|
+
const coll = this.deps.collection(referencing.collection);
|
|
6567
|
+
const matches = await this.findLookupReferencingRecords(coll, referencing.field, compareKey);
|
|
6568
|
+
for (const rec of matches) {
|
|
6569
|
+
const id = rec["id"];
|
|
6570
|
+
if (id === void 0) continue;
|
|
6571
|
+
if (onDelete === "cascade") {
|
|
6572
|
+
await coll.delete(id);
|
|
6573
|
+
cascaded++;
|
|
6574
|
+
} else if (onDelete === "nullify") {
|
|
6575
|
+
await coll.put(id, { ...rec, [referencing.field]: null });
|
|
6576
|
+
nullified++;
|
|
6577
|
+
}
|
|
6578
|
+
}
|
|
6579
|
+
}
|
|
6580
|
+
return { cascaded, nullified };
|
|
6581
|
+
}
|
|
6582
|
+
/**
|
|
6583
|
+
* Convenience: the full restrict-then-propagate sequence for an ORDINARY (non-forget) delete of
|
|
6584
|
+
* a lookup-referenced row — `checkLookupRefsRestrict` then `applyLookupRefsPropagation`. Used by
|
|
6585
|
+
* `enforceRefsOnDelete` above (matrix-tier: the backing row IS an ordinary `Collection` record)
|
|
6586
|
+
* and by `LookupHandle.delete()`'s injected `checkReferencesOnDelete` callback (reserved tier).
|
|
6587
|
+
*/
|
|
6588
|
+
async enforceLookupRefsOnDelete(graph, dimension, backingCollection, key) {
|
|
6589
|
+
await this.checkLookupRefsRestrict(graph, dimension, backingCollection, key);
|
|
6590
|
+
return this.applyLookupRefsPropagation(graph, backingCollection, key);
|
|
6591
|
+
}
|
|
5839
6592
|
/**
|
|
5840
6593
|
* @internal — apply link `onDelete` policy when an endpoint record is
|
|
5841
6594
|
* deleted. `'strict'` throws (blocks the delete), `'cascade'`
|
|
@@ -5992,7 +6745,7 @@ var VaultLinks = class {
|
|
|
5992
6745
|
// src/with-shape/links/lazy-handle.ts
|
|
5993
6746
|
function makeLazyLinkSetHandle(d) {
|
|
5994
6747
|
let engine = null;
|
|
5995
|
-
const load = () => engine ??= import("./link-set-
|
|
6748
|
+
const load = () => engine ??= import("./link-set-YBAC2DQT.js").then(
|
|
5996
6749
|
({ LinkSet }) => new LinkSet(d.adapter, d.vault, d.name, d.spec, d.encrypted, d.getDEK, d.actor, d.emitter, d.endpointExists)
|
|
5997
6750
|
);
|
|
5998
6751
|
return {
|
|
@@ -6006,6 +6759,166 @@ function makeLazyLinkSetHandle(d) {
|
|
|
6006
6759
|
};
|
|
6007
6760
|
}
|
|
6008
6761
|
|
|
6762
|
+
// src/kernel/via-taint-binding.ts
|
|
6763
|
+
var EMPTY_STRING_SET = /* @__PURE__ */ new Set();
|
|
6764
|
+
function isDefaultPosture(p) {
|
|
6765
|
+
return p.encryptedAtRest === DEFAULT_POSTURE.encryptedAtRest && p.queryable === DEFAULT_POSTURE.queryable && p.exportable === DEFAULT_POSTURE.exportable && p.forgettable === DEFAULT_POSTURE.forgettable;
|
|
6766
|
+
}
|
|
6767
|
+
function buildTaintOverlay(rawPostures, sealFields) {
|
|
6768
|
+
let out;
|
|
6769
|
+
for (const [field, posture] of rawPostures) {
|
|
6770
|
+
if (isDefaultPosture(posture)) continue;
|
|
6771
|
+
out ??= /* @__PURE__ */ new Map();
|
|
6772
|
+
out.set(field, sealFields.has(field) ? { ...posture, queryable: "none" } : posture);
|
|
6773
|
+
}
|
|
6774
|
+
return out;
|
|
6775
|
+
}
|
|
6776
|
+
async function encodeTaintAtRest(record, crypto, fields) {
|
|
6777
|
+
let open = record;
|
|
6778
|
+
let sealed;
|
|
6779
|
+
for (const field of fields) {
|
|
6780
|
+
if (!(field in record)) continue;
|
|
6781
|
+
const value = record[field];
|
|
6782
|
+
if (value === void 0) continue;
|
|
6783
|
+
const ref2 = await crypto.sealedSlots.seal(field, value);
|
|
6784
|
+
if (open === record) open = { ...record };
|
|
6785
|
+
delete open[field];
|
|
6786
|
+
sealed ??= {};
|
|
6787
|
+
sealed[field] = ref2;
|
|
6788
|
+
}
|
|
6789
|
+
return sealed ? { record: open, sealed } : { record };
|
|
6790
|
+
}
|
|
6791
|
+
async function decodeTaintAtRest(record, sealed, crypto, opts, fields) {
|
|
6792
|
+
let out = record;
|
|
6793
|
+
for (const field of fields) {
|
|
6794
|
+
const ref2 = sealed[field];
|
|
6795
|
+
if (ref2 === void 0) continue;
|
|
6796
|
+
if (out === record) out = { ...record };
|
|
6797
|
+
out[field] = opts.asHandles ? new SealedHandle(() => crypto.sealedSlots.unseal(field, ref2)) : await crypto.sealedSlots.unseal(field, ref2);
|
|
6798
|
+
}
|
|
6799
|
+
return out;
|
|
6800
|
+
}
|
|
6801
|
+
function taintBinding(sealFields, presentRedactFields = EMPTY_STRING_SET) {
|
|
6802
|
+
const fields = [...sealFields];
|
|
6803
|
+
const redactFields = [...presentRedactFields];
|
|
6804
|
+
return {
|
|
6805
|
+
brand: "taint",
|
|
6806
|
+
posture: { encryptedAtRest: "sealed", queryable: "none", exportable: false, forgettable: true },
|
|
6807
|
+
covers: (field) => sealFields.has(field) || presentRedactFields.has(field),
|
|
6808
|
+
...fields.length > 0 ? {
|
|
6809
|
+
encodeAtRest: (record, crypto) => encodeTaintAtRest(record, crypto, fields),
|
|
6810
|
+
decodeAtRest: (record, sealed, crypto, opts) => decodeTaintAtRest(record, sealed, crypto, opts, fields)
|
|
6811
|
+
} : {},
|
|
6812
|
+
...redactFields.length > 0 ? {
|
|
6813
|
+
present: (record) => {
|
|
6814
|
+
let out = record;
|
|
6815
|
+
for (const field of redactFields) {
|
|
6816
|
+
if (field in out) {
|
|
6817
|
+
if (out === record) out = { ...record };
|
|
6818
|
+
out[field] = EXPORT_REDACTION_MARKER;
|
|
6819
|
+
}
|
|
6820
|
+
}
|
|
6821
|
+
return out;
|
|
6822
|
+
}
|
|
6823
|
+
} : {}
|
|
6824
|
+
};
|
|
6825
|
+
}
|
|
6826
|
+
|
|
6827
|
+
// src/kernel/via-graph-wiring.ts
|
|
6828
|
+
var CLASSIFIED_POSTURE = { encryptedAtRest: "sealed", queryable: "det-exact", exportable: false, forgettable: true };
|
|
6829
|
+
function registerCollectionGraphSources(graph, name, opts) {
|
|
6830
|
+
const cfg = resolveCollectionConfig(opts);
|
|
6831
|
+
const bindings = cfg.via?.bindings ?? [];
|
|
6832
|
+
const knownFields = /* @__PURE__ */ new Set([
|
|
6833
|
+
...Object.keys(cfg.moneyFields ?? {}),
|
|
6834
|
+
...Object.keys(cfg.i18nFields ?? {}),
|
|
6835
|
+
...Object.keys(cfg.dictKeyFields ?? {}),
|
|
6836
|
+
// #650 Task 2 — native lookup()/enumOf()/dict() fields register their
|
|
6837
|
+
// posture (`lookupBinding().posture`) the same generic way every other
|
|
6838
|
+
// binding does; no `'ref'` EdgeKind / graph edge yet (Task 5).
|
|
6839
|
+
...Object.keys(cfg.lookupFields ?? {}),
|
|
6840
|
+
...cfg.classified !== void 0 ? Object.keys(cfg.classified.byField) : []
|
|
6841
|
+
]);
|
|
6842
|
+
for (const field of knownFields) {
|
|
6843
|
+
for (const binding of bindings) {
|
|
6844
|
+
if (binding.covers?.(field)) {
|
|
6845
|
+
graph.registerField(name, field, binding.posture);
|
|
6846
|
+
break;
|
|
6847
|
+
}
|
|
6848
|
+
}
|
|
6849
|
+
}
|
|
6850
|
+
for (const edge of cfg.computedEdges) graph.registerDerived(edge.target, edge.sources, "computed", edge.grain ?? "record");
|
|
6851
|
+
for (const edge of cfg.viaDepsEdges) graph.registerDerived(edge.target, edge.sources, "computed", edge.grain ?? "record");
|
|
6852
|
+
if (cfg.classified !== void 0) graph.markClassified(name);
|
|
6853
|
+
const depFields = new Set(cfg.computedEdges.map((edge) => edge.target.field));
|
|
6854
|
+
for (const field of cfg.computedFieldNames) {
|
|
6855
|
+
if (!depFields.has(field)) graph.markDepslessComputed(name, field);
|
|
6856
|
+
}
|
|
6857
|
+
}
|
|
6858
|
+
function validateReconcileGraphEdges(graph, name, options) {
|
|
6859
|
+
const resolvedClassified = options.classifiedFields !== void 0 ? resolveClassifiedFields(name, options.classifiedFields) : void 0;
|
|
6860
|
+
if (resolvedClassified !== void 0 && Object.values(resolvedClassified.byField).some((spec) => spec.storage !== "never")) {
|
|
6861
|
+
const riderNames = new Set(Object.keys(resolvedClassified.riderComputed));
|
|
6862
|
+
const leaking = [...graph.depslessComputedFields(name)].filter((field) => !riderNames.has(field));
|
|
6863
|
+
if (leaking.length > 0) {
|
|
6864
|
+
const field = leaking[0];
|
|
6865
|
+
throw new ValidationError(
|
|
6866
|
+
`Collection "${name}": computed field "${field}" has no declared \`deps\` and the collection declares classified fields \u2014 an opaque computed function could silently copy a classified field's plaintext into an ordinary, unredacted field. Declare \`deps\` naming the source fields it reads, e.g. computed: { ${field}: { fn, deps: [...] } }.`
|
|
6867
|
+
);
|
|
6868
|
+
}
|
|
6869
|
+
}
|
|
6870
|
+
const combinedHasClassified = graph.isClassified(name) || resolvedClassified !== void 0;
|
|
6871
|
+
let edges = [];
|
|
6872
|
+
let depslessComputedFields = [];
|
|
6873
|
+
if (options.computed !== void 0) {
|
|
6874
|
+
for (const [field, entry] of Object.entries(options.computed)) {
|
|
6875
|
+
if (computedEntryParts(entry).mode === "virtual") {
|
|
6876
|
+
throw new ValidationError(
|
|
6877
|
+
`Collection "${name}": computed field "${field}" declares mode: 'virtual' on a reconcile call \u2014 virtual computed fields are construction-only (no late-attach reconcile door); declare them in the collection's first vault.collection("${name}", { ... }) call.`
|
|
6878
|
+
);
|
|
6879
|
+
}
|
|
6880
|
+
}
|
|
6881
|
+
const knownFields = collectKnownFieldNames({
|
|
6882
|
+
moneyFields: options.moneyFields,
|
|
6883
|
+
classifiedFields: resolvedClassified?.byField,
|
|
6884
|
+
computed: options.computed
|
|
6885
|
+
});
|
|
6886
|
+
edges = resolveComputedEdges(name, options.computed, combinedHasClassified, knownFields);
|
|
6887
|
+
const depFields = new Set(edges.map((edge) => edge.target.field));
|
|
6888
|
+
depslessComputedFields = Object.keys(options.computed).filter((field) => !depFields.has(field));
|
|
6889
|
+
}
|
|
6890
|
+
return {
|
|
6891
|
+
edges,
|
|
6892
|
+
depslessComputedFields,
|
|
6893
|
+
classifiedFieldNames: resolvedClassified !== void 0 ? Object.keys(resolvedClassified.byField) : []
|
|
6894
|
+
};
|
|
6895
|
+
}
|
|
6896
|
+
function commitReconcileGraphEdges(graph, name, plan) {
|
|
6897
|
+
for (const edge of plan.edges) {
|
|
6898
|
+
if (!graph.hasDerived(edge.target)) graph.registerDerived(edge.target, edge.sources, "computed", edge.grain ?? "record");
|
|
6899
|
+
}
|
|
6900
|
+
for (const field of plan.depslessComputedFields) graph.markDepslessComputed(name, field);
|
|
6901
|
+
for (const field of plan.classifiedFieldNames) graph.registerField(name, field, CLASSIFIED_POSTURE);
|
|
6902
|
+
if (plan.classifiedFieldNames.length > 0) graph.markClassified(name);
|
|
6903
|
+
}
|
|
6904
|
+
function applyTaintOverlay(coll, graph, name) {
|
|
6905
|
+
const postures = buildTaintOverlay(graph.taintedPostures(name), graph.taintSealedFields(name));
|
|
6906
|
+
if (!postures) return;
|
|
6907
|
+
const sealFields = graph.taintSealedFields(name);
|
|
6908
|
+
const provenance = graph.taintProvenance(name);
|
|
6909
|
+
const virtualFields = graph.virtualFields(name);
|
|
6910
|
+
const virtualExportRedact = /* @__PURE__ */ new Set();
|
|
6911
|
+
for (const [field, posture] of postures) {
|
|
6912
|
+
if (posture.exportable === false && virtualFields.has(field)) virtualExportRedact.add(field);
|
|
6913
|
+
}
|
|
6914
|
+
const c = coll;
|
|
6915
|
+
const needsTaintBinding = sealFields.size > 0 || virtualExportRedact.size > 0;
|
|
6916
|
+
const bindings = needsTaintBinding ? [...c.via?.bindings ?? [], taintBinding(sealFields, virtualExportRedact)] : c.via?.bindings ?? [];
|
|
6917
|
+
const taint = { postures, sealFields, provenance };
|
|
6918
|
+
c.via = ViaPipeline.build(bindings, taint);
|
|
6919
|
+
c.codec.setVia(c.via);
|
|
6920
|
+
}
|
|
6921
|
+
|
|
6009
6922
|
// src/with-shape/satellites/registry.ts
|
|
6010
6923
|
var SatelliteRegistry = class {
|
|
6011
6924
|
byBase = /* @__PURE__ */ new Map();
|
|
@@ -6116,7 +7029,7 @@ function declareSatellite(ctx, collectionName, options, existingRegistry) {
|
|
|
6116
7029
|
ctx.forEachSyncEngine((e) => e.remirrorPairResolvers([spec.base, spec.satellite]));
|
|
6117
7030
|
const registry = reg;
|
|
6118
7031
|
const baseSchema = ctx.getBaseSchema(spec.base);
|
|
6119
|
-
void import("./post-register-
|
|
7032
|
+
void import("./post-register-MROIUA5O.js").then((m) => m.postRegister(ctx.adapter, ctx.vaultName, spec, ctx.getDEK, baseSchema, registry)).catch(() => {
|
|
6120
7033
|
});
|
|
6121
7034
|
return reg;
|
|
6122
7035
|
}
|
|
@@ -6609,38 +7522,15 @@ var VaultAttestation = class {
|
|
|
6609
7522
|
};
|
|
6610
7523
|
|
|
6611
7524
|
// src/kernel/vault.ts
|
|
6612
|
-
function resolveLabelFromMap(labels, locale, fallback) {
|
|
6613
|
-
if (labels[locale] !== void 0) return labels[locale];
|
|
6614
|
-
const chain = Array.isArray(fallback) ? fallback : fallback ? [fallback] : [];
|
|
6615
|
-
for (const fb of chain) {
|
|
6616
|
-
if (fb === "any") {
|
|
6617
|
-
const any = Object.values(labels)[0];
|
|
6618
|
-
if (any !== void 0) return any;
|
|
6619
|
-
} else if (labels[fb] !== void 0) {
|
|
6620
|
-
return labels[fb];
|
|
6621
|
-
}
|
|
6622
|
-
}
|
|
6623
|
-
return void 0;
|
|
6624
|
-
}
|
|
6625
7525
|
var Vault = class {
|
|
6626
7526
|
adapter;
|
|
6627
7527
|
/** The vault's name as passed to `openVault()`. Stable for the instance lifetime. */
|
|
6628
7528
|
name;
|
|
6629
|
-
/**
|
|
6630
|
-
*
|
|
6631
|
-
* (e.g. `as-*` reader `apply()` paths gating on `withTransactions()`)
|
|
6632
|
-
* reach the strategy seam without threading `db` through every API.
|
|
6633
|
-
*
|
|
6634
|
-
* Type-only Noydb import keeps the module graph acyclic at runtime.
|
|
6635
|
-
*/
|
|
7529
|
+
/** Backreference to the parent `Noydb` — lets vault-scoped services reach the strategy seam
|
|
7530
|
+
* without threading `db` through every API (type-only import keeps the module graph acyclic). */
|
|
6636
7531
|
noydb;
|
|
6637
|
-
/**
|
|
6638
|
-
*
|
|
6639
|
-
* needs to refresh it after restoring a different keyring file —
|
|
6640
|
-
* otherwise the in-memory DEKs (from the pre-load session) and
|
|
6641
|
-
* the on-disk wrapped DEKs (from the loaded backup) drift apart
|
|
6642
|
-
* and every subsequent decrypt fails with TamperedError.
|
|
6643
|
-
*/
|
|
7532
|
+
/** The active in-memory keyring. NOT readonly — `load()` refreshes it after restoring a
|
|
7533
|
+
* different keyring file, or in-memory/on-disk DEKs drift apart and decrypt fails with TamperedError. */
|
|
6644
7534
|
keyring;
|
|
6645
7535
|
encrypted;
|
|
6646
7536
|
emitter;
|
|
@@ -6649,11 +7539,8 @@ var Vault = class {
|
|
|
6649
7539
|
syncAdapter;
|
|
6650
7540
|
getPurgeableTargets;
|
|
6651
7541
|
historyConfig;
|
|
6652
|
-
/**
|
|
6653
|
-
*
|
|
6654
|
-
* means "blobs are off for this vault"; every `collection.blob(id)`
|
|
6655
|
-
* call throws with a pointer at `@noy-db/hub/blobs`.
|
|
6656
|
-
*/
|
|
7542
|
+
/** tree-shake seam for the optional blob service; `undefined` means "blobs are off for this
|
|
7543
|
+
* vault" — every `collection.blob(id)` call throws with a pointer at `@noy-db/hub/blobs`. */
|
|
6657
7544
|
blobStrategy;
|
|
6658
7545
|
objectStore;
|
|
6659
7546
|
/** Cold-storage archival strategy (the archive target store). */
|
|
@@ -6666,11 +7553,8 @@ var Vault = class {
|
|
|
6666
7553
|
crdtStrategy;
|
|
6667
7554
|
tiersStrategy;
|
|
6668
7555
|
searchStrategy;
|
|
6669
|
-
/**
|
|
6670
|
-
*
|
|
6671
|
-
* `withCargo()` was passed. Public so the `extractPartition` free function
|
|
6672
|
-
* (which takes a `Vault`) routes through it.
|
|
6673
|
-
*/
|
|
7556
|
+
/** Cargo (partition extraction) strategy — `NO_CARGO` (throwing) unless `withCargo()` was passed.
|
|
7557
|
+
* Public so the `extractPartition` free function (which takes a `Vault`) routes through it. */
|
|
6674
7558
|
cargoStrategy;
|
|
6675
7559
|
sealedRecordStrategy;
|
|
6676
7560
|
portabilityStrategy;
|
|
@@ -6684,42 +7568,26 @@ var Vault = class {
|
|
|
6684
7568
|
i18nStrategy;
|
|
6685
7569
|
syncStrategy;
|
|
6686
7570
|
classifiedStrategy;
|
|
6687
|
-
/**
|
|
6688
|
-
*
|
|
6689
|
-
* `null` for vaults that never register any guard strategy. The
|
|
6690
|
-
* runtime class is dynamic-imported on demand so consumers that
|
|
6691
|
-
* never use guards don't pull `GuardRegistry`/`GuardExecutor` into
|
|
6692
|
-
* their bundle.
|
|
6693
|
-
*/
|
|
7571
|
+
/** Per-vault guard registry; `null` until `_initGuards()` runs (or for vaults that never register
|
|
7572
|
+
* one) — the runtime class is dynamic-imported on demand to keep it out of the floor bundle. */
|
|
6694
7573
|
guardRegistry = null;
|
|
6695
|
-
/**
|
|
6696
|
-
*
|
|
6697
|
-
*
|
|
6698
|
-
* least one strategy handle.
|
|
6699
|
-
*/
|
|
7574
|
+
/** Per-vault derivation/MV/overlay registries — same lazy-load contract as
|
|
7575
|
+
* `guardRegistry`: each stays `null` until its `_init*()` runs with at
|
|
7576
|
+
* least one strategy/MV/overlay handle. */
|
|
6700
7577
|
derivationRegistry = null;
|
|
6701
|
-
/**
|
|
6702
|
-
* Per-vault materialized-view registry. Same lazy-load
|
|
6703
|
-
* contract as `derivationRegistry` — `null` until
|
|
6704
|
-
* `_initMaterializedViews()` runs with at least one MV handle.
|
|
6705
|
-
*/
|
|
6706
7578
|
materializedViewRegistry = null;
|
|
6707
|
-
/**
|
|
6708
|
-
* Per-vault overlay registry. Same lazy-load contract as
|
|
6709
|
-
* `materializedViewRegistry` — `null` until `_initOverlayedViews()`
|
|
6710
|
-
* runs with at least one handle.
|
|
6711
|
-
*/
|
|
6712
7579
|
overlayedViewRegistry = null;
|
|
6713
|
-
/**
|
|
6714
|
-
*
|
|
6715
|
-
*
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
*
|
|
6721
|
-
*
|
|
6722
|
-
|
|
7580
|
+
/** Per-vault dependency graph (#638) — field postures + derivation/MV/overlay/
|
|
7581
|
+
* computed/via-deps edges. Metadata-only (field names, postures, grains —
|
|
7582
|
+
* never values or key material); always present, unlike the registries above. */
|
|
7583
|
+
graph;
|
|
7584
|
+
/** #638 Task 4 — open sync/cutover/restore touch collector, or `undefined` between batches. */
|
|
7585
|
+
_graphBatch;
|
|
7586
|
+
/** Cached read-only facades for guard/derivation callbacks — split by
|
|
7587
|
+
* resolution layer (`layer:'guard'` vs `'derivation'`, each field's
|
|
7588
|
+
* `onMissing` policy differs). Allocated eagerly inside `_initGuards()`/
|
|
7589
|
+
* `_initDerivations()` so read accessors stay synchronous; `null` for
|
|
7590
|
+
* vaults without that service. */
|
|
6723
7591
|
guardFacade = null;
|
|
6724
7592
|
derivationFacade = null;
|
|
6725
7593
|
getDEK;
|
|
@@ -6748,18 +7616,9 @@ var Vault = class {
|
|
|
6748
7616
|
* @see docs/superpowers/specs/2026-06-17-fr6-deed-custodian-liberate-design.md
|
|
6749
7617
|
*/
|
|
6750
7618
|
custody;
|
|
6751
|
-
/**
|
|
6752
|
-
*
|
|
6753
|
-
*
|
|
6754
|
-
* after the on-disk keyring file has been replaced — refreshes
|
|
6755
|
-
* `this.keyring` so the next DEK access uses the loaded wrapped
|
|
6756
|
-
* DEKs instead of the stale pre-load ones.
|
|
6757
|
-
*
|
|
6758
|
-
* Provided by Noydb at openVault() time. Tests that
|
|
6759
|
-
* construct Vault directly can pass `undefined`; load()
|
|
6760
|
-
* skips the refresh in that case (which is fine for plaintext
|
|
6761
|
-
* compartments — there's nothing to re-unwrap).
|
|
6762
|
-
*/
|
|
7619
|
+
/** Re-derives an UnlockedKeyring from the adapter using the active user's passphrase; called by
|
|
7620
|
+
* `load()` after the on-disk keyring file has been replaced. Provided by Noydb at openVault()
|
|
7621
|
+
* time; `undefined` in tests that construct Vault directly (load() then skips the refresh). */
|
|
6763
7622
|
reloadKeyring;
|
|
6764
7623
|
collectionCache = /* @__PURE__ */ new Map();
|
|
6765
7624
|
satelliteRegistry = null;
|
|
@@ -6841,46 +7700,22 @@ var Vault = class {
|
|
|
6841
7700
|
* should use separate Vault instances.
|
|
6842
7701
|
*/
|
|
6843
7702
|
consentContext = null;
|
|
6844
|
-
/**
|
|
6845
|
-
* Registry of dictKey fields declared across all collections in this
|
|
6846
|
-
* vault. Keyed by collection name → field name → dictionary name.
|
|
6847
|
-
* Used by `DictionaryHandle.rename()` to find and update all records
|
|
6848
|
-
* referencing a renamed key.
|
|
6849
|
-
*
|
|
6850
|
-
* Populated by `collection()` when the `dictKeyFields` option is passed.
|
|
6851
|
-
*/
|
|
7703
|
+
/** dictKeyField registry: collection name → field name → dictionary name — `DictionaryHandle.rename()`'s reference-update source; populated by `collection()` from `dictKeyFields`. */
|
|
6852
7704
|
dictKeyFieldRegistry = /* @__PURE__ */ new Map();
|
|
6853
|
-
/**
|
|
6854
|
-
* Names of dictionaries backed by a `staticDict()` descriptor.
|
|
6855
|
-
* A static dict skips the `dictKeyFieldRegistry` rename machinery, but the
|
|
6856
|
-
* vault must still *know* a name is static so `vault.dictionary(name)` can
|
|
6857
|
-
* refuse mutation (`StaticDictReadonlyError`). Populated at `collection()`
|
|
6858
|
-
* config time whenever a `StaticDictDescriptor` is seen.
|
|
6859
|
-
*/
|
|
7705
|
+
/** Names of `staticDict()`-backed dictionaries (skip rename tracking; `vault.dictionary(name)` refuses mutation on these via `StaticDictReadonlyError`). Populated at `collection()` config time. */
|
|
6860
7706
|
staticDictNames = /* @__PURE__ */ new Set();
|
|
6861
|
-
/**
|
|
6862
|
-
* Static-dict descriptors keyed by dictionary name. Backs the
|
|
6863
|
-
* read-path label resolver (resolve from the in-memory table) and the
|
|
6864
|
-
* query-seam `resolveDictSource` snapshot. Last writer wins when the same
|
|
6865
|
-
* name is registered by multiple collections (identical-across-vaults by
|
|
6866
|
-
* construction, so the tables match).
|
|
6867
|
-
*/
|
|
7707
|
+
/** Static-dict descriptors by dictionary name — backs the read-path label resolver and the `resolveDictSource` snapshot. Last writer wins across collections (tables match by construction). */
|
|
6868
7708
|
staticByName = /* @__PURE__ */ new Map();
|
|
6869
|
-
/**
|
|
6870
|
-
* Per-collection map of field name → StaticDictDescriptor. Used by
|
|
6871
|
-
* `enforceStaticDictOnPut` to validate stored codes against `desc.keys`.
|
|
6872
|
-
*/
|
|
7709
|
+
/** Per-collection field name → StaticDictDescriptor, validated by `enforceStaticDictOnPut`. */
|
|
6873
7710
|
staticDescriptorByField = /* @__PURE__ */ new Map();
|
|
6874
|
-
/**
|
|
6875
|
-
* Registry of i18nText fields declared across all collections. Keyed
|
|
6876
|
-
* by collection name → field name → I18nTextDescriptor. Used by
|
|
6877
|
-
* `applyI18nLocale` on reads and by `validateI18nTextValue` on puts.
|
|
6878
|
-
*
|
|
6879
|
-
* Populated by `collection()` when the `i18nFields` option is passed.
|
|
6880
|
-
*/
|
|
7711
|
+
/** i18nText fields: collection name → field name → descriptor. Used by `applyI18nLocale`/`validateI18nTextValue`; populated by `collection()` from `i18nFields`. */
|
|
6881
7712
|
i18nFieldRegistry = /* @__PURE__ */ new Map();
|
|
6882
7713
|
/** Cache of DictionaryHandle instances, one per dictionary name. */
|
|
6883
7714
|
dictionaryCache = /* @__PURE__ */ new Map();
|
|
7715
|
+
/** #650 Task 4 (#647) — declared reserved-lookup collection name → dimension name, for dimensions
|
|
7716
|
+
* with `backing:'reserved'` (dict()/dictKey()). The explicit sync-pull registry: NOT a blanket
|
|
7717
|
+
* underscore-glob — populated at `collection()`-declare time and at `dictionary()`-call time. */
|
|
7718
|
+
reservedLookupCollections = /* @__PURE__ */ new Map();
|
|
6884
7719
|
/** Registered link specs, keyed by link name; set by `vault.link()`. */
|
|
6885
7720
|
linkRegistry = /* @__PURE__ */ new Map();
|
|
6886
7721
|
/** Cache of link-set handles, one per link name (lazy -- see links()). */
|
|
@@ -6957,6 +7792,7 @@ var Vault = class {
|
|
|
6957
7792
|
this.i18nStrategy = opts.i18nStrategy ?? NO_I18N;
|
|
6958
7793
|
this.syncStrategy = opts.syncStrategy ?? NO_SYNC;
|
|
6959
7794
|
this.classifiedStrategy = opts.classifiedStrategy ?? NO_CLASSIFIED;
|
|
7795
|
+
this.graph = new ViaGraph();
|
|
6960
7796
|
void opts.guardStrategies;
|
|
6961
7797
|
this.historyConfig = opts.historyConfig ?? { enabled: true };
|
|
6962
7798
|
this.reloadKeyring = opts.reloadKeyring;
|
|
@@ -7083,6 +7919,7 @@ var Vault = class {
|
|
|
7083
7919
|
}, collectionName, { ...options, satelliteOf: options.satelliteOf }, this.satelliteRegistry);
|
|
7084
7920
|
}
|
|
7085
7921
|
let coll = this.collectionCache.get(collectionName);
|
|
7922
|
+
const reconcilePlan = coll && (options?.computed || options?.classifiedFields) ? validateReconcileGraphEdges(this.graph, collectionName, options) : void 0;
|
|
7086
7923
|
if (coll && options?.moneyFields) {
|
|
7087
7924
|
coll._applyMoneyFields(options.moneyFields);
|
|
7088
7925
|
}
|
|
@@ -7098,12 +7935,17 @@ var Vault = class {
|
|
|
7098
7935
|
if (coll && options?.classifiedFields) {
|
|
7099
7936
|
coll._applyClassifiedFields(options.classifiedFields);
|
|
7100
7937
|
}
|
|
7938
|
+
if (reconcilePlan) {
|
|
7939
|
+
commitReconcileGraphEdges(this.graph, collectionName, reconcilePlan);
|
|
7940
|
+
applyTaintOverlay(coll, this.graph, collectionName);
|
|
7941
|
+
}
|
|
7101
7942
|
if (!coll) {
|
|
7943
|
+
const effectiveViaFields = mergeViaFields({ moneyFields: options?.moneyFields, i18nFields: options?.i18nFields, dictKeyFields: options?.dictKeyFields, lookupFields: options?.lookupFields, viaFields: options?.viaFields });
|
|
7102
7944
|
if (options?.refs) {
|
|
7103
7945
|
this.refRegistry.register(collectionName, options.refs);
|
|
7104
7946
|
}
|
|
7105
|
-
if (
|
|
7106
|
-
this.i18nFieldRegistry.set(collectionName,
|
|
7947
|
+
if (effectiveViaFields.i18nFields) {
|
|
7948
|
+
this.i18nFieldRegistry.set(collectionName, effectiveViaFields.i18nFields);
|
|
7107
7949
|
}
|
|
7108
7950
|
if (options?.blobFields) {
|
|
7109
7951
|
this.blobFieldsRegistry.set(collectionName, options.blobFields);
|
|
@@ -7114,10 +7956,10 @@ var Vault = class {
|
|
|
7114
7956
|
if (options?.attestation !== void 0) {
|
|
7115
7957
|
this.attestation.register(collectionName, options.attestation);
|
|
7116
7958
|
}
|
|
7117
|
-
if (
|
|
7959
|
+
if (effectiveViaFields.dictKeyFields || effectiveViaFields.lookupFields) {
|
|
7118
7960
|
const dictFieldMap = {};
|
|
7119
7961
|
const staticFieldMap = {};
|
|
7120
|
-
for (const [field, desc] of Object.entries(
|
|
7962
|
+
for (const [field, desc] of Object.entries(effectiveViaFields.dictKeyFields ?? {})) {
|
|
7121
7963
|
if (isStaticDictDescriptor(desc)) {
|
|
7122
7964
|
staticFieldMap[field] = desc;
|
|
7123
7965
|
this.staticDictNames.add(desc.name);
|
|
@@ -7126,8 +7968,18 @@ var Vault = class {
|
|
|
7126
7968
|
dictFieldMap[field] = desc.name;
|
|
7127
7969
|
}
|
|
7128
7970
|
}
|
|
7971
|
+
const lookupCompat = collectLookupDictCompat(effectiveViaFields.lookupFields);
|
|
7972
|
+
Object.assign(dictFieldMap, lookupCompat.dictFieldMap);
|
|
7973
|
+
for (const [field, desc] of lookupCompat.staticEntries) {
|
|
7974
|
+
staticFieldMap[field] = desc;
|
|
7975
|
+
this.staticDictNames.add(desc.name);
|
|
7976
|
+
this.staticByName.set(desc.name, desc);
|
|
7977
|
+
}
|
|
7129
7978
|
if (Object.keys(dictFieldMap).length > 0) {
|
|
7130
7979
|
this.dictKeyFieldRegistry.set(collectionName, dictFieldMap);
|
|
7980
|
+
for (const dictName of new Set(Object.values(dictFieldMap))) {
|
|
7981
|
+
this.reservedLookupCollections.set(dictCollectionName(dictName), dictName);
|
|
7982
|
+
}
|
|
7131
7983
|
}
|
|
7132
7984
|
if (Object.keys(staticFieldMap).length > 0) {
|
|
7133
7985
|
this.staticDescriptorByField.set(collectionName, staticFieldMap);
|
|
@@ -7142,7 +7994,7 @@ var Vault = class {
|
|
|
7142
7994
|
const strategies = options.schemaUpdate ?? [];
|
|
7143
7995
|
const work = (async () => {
|
|
7144
7996
|
const dek = await this.getDEK(collectionName);
|
|
7145
|
-
const { persistSchemaIfNeeded } = await import("./register-
|
|
7997
|
+
const { persistSchemaIfNeeded } = await import("./register-OVJKW7BS.js");
|
|
7146
7998
|
const result = await persistSchemaIfNeeded({
|
|
7147
7999
|
store: this.adapter,
|
|
7148
8000
|
vault: this.name,
|
|
@@ -7207,6 +8059,7 @@ var Vault = class {
|
|
|
7207
8059
|
defaultLocale: this.locale,
|
|
7208
8060
|
onRegisterConflictResolver: this.onRegisterConflictResolver,
|
|
7209
8061
|
onAccess: (op, id) => this._logConsent(op, collectionName, id),
|
|
8062
|
+
graphDispatch: { collect: (collection, id) => this._collectGraphTouch(collection, id) },
|
|
7210
8063
|
// Derivation source is only wired when the corresponding registry
|
|
7211
8064
|
// has been initialised. Guard source was removed in Track A slice 3b
|
|
7212
8065
|
// — guards now run via the gate bus in Noydb.#registerGuardGate.
|
|
@@ -7277,9 +8130,10 @@ var Vault = class {
|
|
|
7277
8130
|
if (options?.warmIndexOnOpen !== void 0) collOpts.warmIndexOnOpen = options.warmIndexOnOpen;
|
|
7278
8131
|
if (options?.textIndexPersist !== void 0) collOpts.textIndexPersist = options.textIndexPersist;
|
|
7279
8132
|
if (options?.moneyFields !== void 0) collOpts.moneyFields = options.moneyFields;
|
|
8133
|
+
if (options?.viaFields !== void 0) collOpts.viaFields = options.viaFields;
|
|
7280
8134
|
if (options?.computed !== void 0) collOpts.computed = options.computed;
|
|
7281
8135
|
if (options?.classifiedFields !== void 0) collOpts.classifiedFields = options.classifiedFields;
|
|
7282
|
-
if (
|
|
8136
|
+
if (effectiveViaFields.dictKeyFields !== void 0 || effectiveViaFields.lookupFields !== void 0) {
|
|
7283
8137
|
collOpts.dictLabelResolver = async (dictName, key, locale, fallback) => {
|
|
7284
8138
|
const stat = this.staticByName.get(dictName);
|
|
7285
8139
|
if (stat) {
|
|
@@ -7290,15 +8144,23 @@ var Vault = class {
|
|
|
7290
8144
|
return handle.resolveLabel(key, locale, fallback);
|
|
7291
8145
|
};
|
|
7292
8146
|
collOpts.getDictionary = async (name) => this.dictionary(name);
|
|
7293
|
-
collOpts.dictKeyFields = options
|
|
8147
|
+
collOpts.dictKeyFields = options?.dictKeyFields;
|
|
7294
8148
|
}
|
|
7295
|
-
if (
|
|
8149
|
+
if (effectiveViaFields.lookupFields !== void 0) {
|
|
8150
|
+
collOpts.lookupLabelResolver = collOpts.dictLabelResolver;
|
|
8151
|
+
collOpts.getLookupBacking = (dimension) => async (key) => await this.collection(dimension).get(key) ?? void 0;
|
|
8152
|
+
collOpts.lookupFields = options?.lookupFields;
|
|
8153
|
+
collOpts.membership = (field, key) => checkLookupMembership(effectiveViaFields.lookupFields[field], key, (n) => this.dictionary(n), (n) => this.collection(n));
|
|
8154
|
+
collOpts.getAltIndex = (d) => buildLookupAltIndex(d, (n) => this.dictionary(n), (n) => this.collection(n));
|
|
8155
|
+
collOpts.snapshotFor = (d) => buildLookupSnapshotRows(d, (n) => this.reservedLookupCollections.has(dictCollectionName(n)), (n) => this.dictionary(n), (n) => this.collection(n));
|
|
8156
|
+
}
|
|
8157
|
+
if (effectiveViaFields.i18nFields !== void 0 || effectiveViaFields.dictKeyFields !== void 0) {
|
|
7296
8158
|
collOpts.i18nPutValidator = (record) => {
|
|
7297
8159
|
this.enforceI18nOnPut(collectionName, record);
|
|
7298
8160
|
this.enforceStaticDictOnPut(collectionName, record);
|
|
7299
8161
|
};
|
|
7300
8162
|
}
|
|
7301
|
-
if (
|
|
8163
|
+
if (effectiveViaFields.i18nFields !== void 0 && this.translateText) {
|
|
7302
8164
|
collOpts.autoTranslateHook = this.translateText;
|
|
7303
8165
|
}
|
|
7304
8166
|
if (options?.fieldMeta !== void 0) {
|
|
@@ -7312,6 +8174,9 @@ var Vault = class {
|
|
|
7312
8174
|
}
|
|
7313
8175
|
coll = new Collection(collOpts);
|
|
7314
8176
|
this.collectionCache.set(collectionName, coll);
|
|
8177
|
+
registerCollectionGraphSources(this.graph, collectionName, collOpts);
|
|
8178
|
+
registerLookupRefEdges(this.graph, collectionName, effectiveViaFields.lookupFields);
|
|
8179
|
+
applyTaintOverlay(coll, this.graph, collectionName);
|
|
7315
8180
|
if (options?.warmIndexOnOpen === true && options.prefetch !== false) {
|
|
7316
8181
|
void coll.warmIndex().catch(() => {
|
|
7317
8182
|
});
|
|
@@ -7321,7 +8186,7 @@ var Vault = class {
|
|
|
7321
8186
|
const work = (async () => {
|
|
7322
8187
|
try {
|
|
7323
8188
|
const dek = await this.getDEK(collectionName);
|
|
7324
|
-
await (await import("./register-
|
|
8189
|
+
await (await import("./register-OVJKW7BS.js")).persistSchemaIfNeeded({
|
|
7325
8190
|
store: this.adapter,
|
|
7326
8191
|
vault: this.name,
|
|
7327
8192
|
collectionName,
|
|
@@ -7384,7 +8249,9 @@ var Vault = class {
|
|
|
7384
8249
|
async #runCutoverTransform(collectionName, transform) {
|
|
7385
8250
|
const coll = this.collectionCache.get(collectionName);
|
|
7386
8251
|
if (!coll) return;
|
|
8252
|
+
this._beginGraphBatch();
|
|
7387
8253
|
await coll._applyCutoverTransform(transform);
|
|
8254
|
+
await this._flushGraphBatch();
|
|
7388
8255
|
}
|
|
7389
8256
|
/**
|
|
7390
8257
|
* Refresh a loaded collection's view of one document from a peer
|
|
@@ -7396,12 +8263,45 @@ var Vault = class {
|
|
|
7396
8263
|
if (!coll) return;
|
|
7397
8264
|
await coll._applyRemoteChange(docId, action);
|
|
7398
8265
|
}
|
|
7399
|
-
/** @internal #598: refresh cache entries a sync-applied write rewrote underneath us. No-op if not loaded this session.
|
|
8266
|
+
/** @internal #598: refresh cache entries a sync-applied write rewrote underneath us. No-op if not loaded this session.
|
|
8267
|
+
* #650 Task 4 (#647): a reserved-lookup collection has no `Collection` instance — instead, refresh
|
|
8268
|
+
* its already-warmed `LookupHandle._syncCache` (membership/altIndex/snapshot reads never see a
|
|
8269
|
+
* stale verdict for a pulled vocabulary edit) and collect the touch into any open graph batch
|
|
8270
|
+
* (the sync-apply wave-reachability seam Task 5's ref edges will use). No-op if never warmed. */
|
|
7400
8271
|
async _invalidateSyncApplied(collection, id) {
|
|
7401
8272
|
const coll = this.collectionCache.get(collection);
|
|
7402
|
-
if (
|
|
7403
|
-
|
|
7404
|
-
|
|
8273
|
+
if (coll) {
|
|
8274
|
+
await coll._onRecordMutated(id, "put", "sync-apply");
|
|
8275
|
+
return;
|
|
8276
|
+
}
|
|
8277
|
+
const dimName = this.reservedLookupCollections.get(collection);
|
|
8278
|
+
if (dimName === void 0) return;
|
|
8279
|
+
await this.dictionaryCache.get(dimName)?._refreshSyncCache(id);
|
|
8280
|
+
this._collectGraphTouch(collection, id);
|
|
8281
|
+
}
|
|
8282
|
+
/** @internal #650 Task 4 (#647) — declared reserved-lookup collection names, for `SyncEngine.pull()`'s explicit prefix registry. */
|
|
8283
|
+
_reservedLookupCollectionNames() {
|
|
8284
|
+
return [...this.reservedLookupCollections.keys()];
|
|
8285
|
+
}
|
|
8286
|
+
/** @internal #638 Task 4 — open a graph-dispatch touch batch (sync pull()/push()/cutover). */
|
|
8287
|
+
_beginGraphBatch() {
|
|
8288
|
+
this._graphBatch = /* @__PURE__ */ new Map();
|
|
8289
|
+
}
|
|
8290
|
+
/** @internal #638 Task 4 — `Collection._onRecordMutated`'s sync-apply/cutover/restore hook; a
|
|
8291
|
+
* no-op when no batch is open (`_beginGraphBatch()` was never called for this call chain). */
|
|
8292
|
+
_collectGraphTouch(collection, id) {
|
|
8293
|
+
if (!this._graphBatch) return;
|
|
8294
|
+
this._graphBatch.set(collection, (this._graphBatch.get(collection) ?? /* @__PURE__ */ new Set()).add(id));
|
|
8295
|
+
}
|
|
8296
|
+
/** @internal #638 Task 4 — close the open batch and run ONE dispatch wave over it. */
|
|
8297
|
+
async _flushGraphBatch() {
|
|
8298
|
+
const batch = this._graphBatch;
|
|
8299
|
+
this._graphBatch = void 0;
|
|
8300
|
+
if (batch && batch.size > 0) await runGraphDispatchWave(this, batch);
|
|
8301
|
+
}
|
|
8302
|
+
/** @internal #638 Task 4 — cached-collection lookup for `runGraphDispatchWave` (`VaultLike`); never constructs. */
|
|
8303
|
+
_getCollection(name) {
|
|
8304
|
+
return this.collectionCache.get(name);
|
|
7405
8305
|
}
|
|
7406
8306
|
/**
|
|
7407
8307
|
* @internal #589 → #604. Physically remove delete markers with `_ts` strictly
|
|
@@ -7543,8 +8443,12 @@ var Vault = class {
|
|
|
7543
8443
|
* Validate i18nText fields on a `put()`. Called by Collection just
|
|
7544
8444
|
* before the adapter write, after schema validation. Throws
|
|
7545
8445
|
* `MissingTranslationError` when a required translation is absent.
|
|
8446
|
+
*
|
|
8447
|
+
* Delegates through the Via registry (#623) — a no-op only when i18n was
|
|
8448
|
+
* never declared (mirrors the registry check below).
|
|
7546
8449
|
*/
|
|
7547
8450
|
enforceI18nOnPut(collectionName, record) {
|
|
8451
|
+
if (!isViaInstalled("i18n")) return;
|
|
7548
8452
|
const i18nFields = this.i18nFieldRegistry.get(collectionName);
|
|
7549
8453
|
if (!i18nFields || Object.keys(i18nFields).length === 0) return;
|
|
7550
8454
|
if (!record || typeof record !== "object") return;
|
|
@@ -7561,79 +8465,15 @@ var Vault = class {
|
|
|
7561
8465
|
* Validate staticDict codes on a `put()`. For each `staticDict()`
|
|
7562
8466
|
* field, every stored code must be a declared key of the descriptor's
|
|
7563
8467
|
* table, else `UnknownDictCodeError`. Opt out per descriptor with
|
|
7564
|
-
* `{ validateCodes: false }`.
|
|
7565
|
-
* field paths via `getAtPath` (same path support as i18n validation).
|
|
7566
|
-
*/
|
|
7567
|
-
enforceStaticDictOnPut(collectionName, record) {
|
|
7568
|
-
const staticFields = this.staticDescriptorByField.get(collectionName);
|
|
7569
|
-
if (!staticFields || Object.keys(staticFields).length === 0) return;
|
|
7570
|
-
if (!record || typeof record !== "object") return;
|
|
7571
|
-
const obj = record;
|
|
7572
|
-
for (const [field, desc] of Object.entries(staticFields)) {
|
|
7573
|
-
if (desc.validateCodes === false) continue;
|
|
7574
|
-
const known = new Set(desc.keys);
|
|
7575
|
-
const values = getAtPath(obj, field);
|
|
7576
|
-
for (const value of values) {
|
|
7577
|
-
if (value === void 0 || value === null) continue;
|
|
7578
|
-
const codes = Array.isArray(value) ? value : [value];
|
|
7579
|
-
for (const code of codes) {
|
|
7580
|
-
if (typeof code !== "string") continue;
|
|
7581
|
-
if (!known.has(code)) {
|
|
7582
|
-
throw new UnknownDictCodeError(desc.name, field, code);
|
|
7583
|
-
}
|
|
7584
|
-
}
|
|
7585
|
-
}
|
|
7586
|
-
}
|
|
7587
|
-
}
|
|
7588
|
-
/**
|
|
7589
|
-
* Apply locale resolution to a record for the given collection.
|
|
8468
|
+
* `{ validateCodes: false }`.
|
|
7590
8469
|
*
|
|
7591
|
-
*
|
|
7592
|
-
*
|
|
8470
|
+
* Delegates through the Via registry — see {@link enforceI18nOnPut}. The
|
|
8471
|
+
* per-field validation itself lives in `shape/via-lookup/registry.ts`
|
|
8472
|
+
* (#650 Task 1 extraction), reached via `port/with/lookup-strategy.ts`.
|
|
7593
8473
|
*/
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
const hasStaticDisplay = staticFields !== void 0 && Object.values(staticFields).some((d) => d.displayLocale !== void 0);
|
|
7598
|
-
if (!locale && !hasStaticDisplay) return record;
|
|
7599
|
-
let result = record;
|
|
7600
|
-
if (locale) {
|
|
7601
|
-
const i18nFields = this.i18nFieldRegistry.get(collectionName);
|
|
7602
|
-
if (i18nFields && Object.keys(i18nFields).length > 0) {
|
|
7603
|
-
result = this.i18nStrategy.applyI18nLocale(result, i18nFields, locale, localeOpts.fallback);
|
|
7604
|
-
}
|
|
7605
|
-
}
|
|
7606
|
-
const dictFields = this.dictKeyFieldRegistry.get(collectionName);
|
|
7607
|
-
if (locale && dictFields && Object.keys(dictFields).length > 0 && locale !== "raw") {
|
|
7608
|
-
const withLabels = { ...result };
|
|
7609
|
-
for (const [field, dictName] of Object.entries(dictFields)) {
|
|
7610
|
-
const key = result[field];
|
|
7611
|
-
if (typeof key !== "string") continue;
|
|
7612
|
-
const handle = this.dictionary(dictName);
|
|
7613
|
-
const label = await handle.resolveLabel(key, locale, localeOpts.fallback);
|
|
7614
|
-
if (label !== void 0) {
|
|
7615
|
-
withLabels[`${field}Label`] = label;
|
|
7616
|
-
}
|
|
7617
|
-
}
|
|
7618
|
-
result = withLabels;
|
|
7619
|
-
}
|
|
7620
|
-
if (staticFields && Object.keys(staticFields).length > 0 && locale !== "raw") {
|
|
7621
|
-
const withLabels = { ...result };
|
|
7622
|
-
for (const [field, desc] of Object.entries(staticFields)) {
|
|
7623
|
-
const effLocale = locale ?? desc.displayLocale;
|
|
7624
|
-
if (!effLocale) continue;
|
|
7625
|
-
const key = result[field];
|
|
7626
|
-
if (typeof key !== "string") continue;
|
|
7627
|
-
const labels = desc.table[key];
|
|
7628
|
-
if (!labels) continue;
|
|
7629
|
-
const label = resolveLabelFromMap(labels, effLocale, localeOpts.fallback ?? desc.substitute);
|
|
7630
|
-
if (label !== void 0) {
|
|
7631
|
-
withLabels[`${field}Label`] = label;
|
|
7632
|
-
}
|
|
7633
|
-
}
|
|
7634
|
-
result = withLabels;
|
|
7635
|
-
}
|
|
7636
|
-
return result;
|
|
8474
|
+
enforceStaticDictOnPut(collectionName, record) {
|
|
8475
|
+
if (!isViaInstalled("i18n")) return;
|
|
8476
|
+
enforceStaticDictOnPut(this.staticDescriptorByField.get(collectionName), record);
|
|
7637
8477
|
}
|
|
7638
8478
|
/**
|
|
7639
8479
|
* Open a dictionary by name. Returns a `DictionaryHandle` for CRUD
|
|
@@ -7657,6 +8497,7 @@ var Vault = class {
|
|
|
7657
8497
|
if (this.staticDictNames.has(name)) {
|
|
7658
8498
|
throw new StaticDictReadonlyError(name);
|
|
7659
8499
|
}
|
|
8500
|
+
this.reservedLookupCollections.set(dictCollectionName(name), name);
|
|
7660
8501
|
let handle = this.dictionaryCache.get(name);
|
|
7661
8502
|
if (!handle) {
|
|
7662
8503
|
handle = this.i18nStrategy.buildDictionaryHandle({
|
|
@@ -7664,37 +8505,36 @@ var Vault = class {
|
|
|
7664
8505
|
compartmentName: this.name,
|
|
7665
8506
|
dictionaryName: name,
|
|
7666
8507
|
keyring: this.keyring,
|
|
7667
|
-
|
|
8508
|
+
reservedEnvelopes: makeReservedEnvelopes((collection) => this.getDEK(collection), ["_dict_"])("_dict_"),
|
|
7668
8509
|
encrypted: this.encrypted,
|
|
7669
8510
|
ledger: this.getLedgerOrNull() ?? void 0,
|
|
7670
8511
|
options,
|
|
7671
8512
|
// findAndUpdateReferences: rewrite dictKey fields in all
|
|
7672
|
-
// registered collections when rename() is called
|
|
8513
|
+
// registered collections when rename() is called. Body extracted to
|
|
8514
|
+
// shape/via-lookup/registry.ts (#650 Task 1) — reached via the
|
|
8515
|
+
// port/with/lookup-strategy.ts seam.
|
|
7673
8516
|
findAndUpdateReferences: async (dictionaryName, oldKey, newKey) => {
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
}
|
|
7693
|
-
}
|
|
7694
|
-
}
|
|
7695
|
-
}
|
|
8517
|
+
await updateReferencingRecords(
|
|
8518
|
+
this.dictKeyFieldRegistry,
|
|
8519
|
+
(collectionName) => this.collection(collectionName),
|
|
8520
|
+
dictionaryName,
|
|
8521
|
+
oldKey,
|
|
8522
|
+
newKey
|
|
8523
|
+
);
|
|
8524
|
+
},
|
|
8525
|
+
emitter: this.emitter,
|
|
8526
|
+
buildDeleteMarker,
|
|
8527
|
+
// #647 fix wave 1 — real kernel/enclave builder; LookupHandle can't import it directly
|
|
8528
|
+
// #650 Task 4 (#647) — choke-point participation for LOCAL writes: dirty-log tracking + a
|
|
8529
|
+
// one-shot graph-dispatch wave (local-write's `graphDispatch.collect`-equivalent; Task 5's ref edges give it dependents).
|
|
8530
|
+
onDirty: this.onDirty,
|
|
8531
|
+
onRecordMutated: async (collection, id) => {
|
|
8532
|
+
this._beginGraphBatch();
|
|
8533
|
+
this._collectGraphTouch(collection, id);
|
|
8534
|
+
await this._flushGraphBatch();
|
|
7696
8535
|
},
|
|
7697
|
-
|
|
8536
|
+
checkReferencesOnDelete: (key) => this.linksEnforcer.enforceLookupRefsOnDelete(this.graph, name, dictCollectionName(name), key)
|
|
8537
|
+
// #650 Task 5
|
|
7698
8538
|
});
|
|
7699
8539
|
this.dictionaryCache.set(name, handle);
|
|
7700
8540
|
}
|
|
@@ -7763,65 +8603,24 @@ var Vault = class {
|
|
|
7763
8603
|
return handle;
|
|
7764
8604
|
}
|
|
7765
8605
|
/**
|
|
7766
|
-
* Build a `JoinableSource` for a dictKey field, for use in dict joins
|
|
7767
|
-
|
|
7768
|
-
* records — one per dictionary entry — keyed by the stable key.
|
|
7769
|
-
*
|
|
7770
|
-
* Returns `null` when `field` is not a dictKey in `leftCollection`.
|
|
7771
|
-
*
|
|
7772
|
-
* The snapshot is built synchronously from whatever the dictionary
|
|
7773
|
-
* handle has in its cached state. For empty dictionaries this returns
|
|
7774
|
-
* an empty snapshot rather than `null`.
|
|
7775
|
-
*/
|
|
7776
|
-
/**
|
|
7777
|
-
* Build a `JoinableSource` for a dictKey field, for use in dict joins
|
|
7778
|
-
*. Returns a source whose snapshot contains
|
|
7779
|
-
* `{ key, labels, ...labels }` records — one per dictionary entry —
|
|
7780
|
-
* keyed by the stable key.
|
|
8606
|
+
* Build a `JoinableSource` for a dictKey field, for use in dict joins.
|
|
8607
|
+
* Returns a source whose snapshot contains `{ key, labels, ...labels }`
|
|
8608
|
+
* records — one per dictionary entry — keyed by the stable key. Returns
|
|
8609
|
+
* `null` when `field` is not a dictKey in `leftCollection`.
|
|
7781
8610
|
*
|
|
7782
|
-
*
|
|
7783
|
-
*
|
|
7784
|
-
*
|
|
7785
|
-
*
|
|
7786
|
-
* to warm the cache.
|
|
7787
|
-
*
|
|
7788
|
-
* Returns `null` when `field` is not a dictKey in `leftCollection`.
|
|
8611
|
+
* Body extracted to `shape/via-lookup/registry.ts` (#650 Task 1) —
|
|
8612
|
+
* reached via the `port/with/lookup-strategy.ts` seam. See that
|
|
8613
|
+
* function's doc comment for the static-vs-dynamic-dict split and the
|
|
8614
|
+
* cache-warming caveat.
|
|
7789
8615
|
*/
|
|
7790
8616
|
resolveDictSource(leftCollection, field) {
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
)
|
|
7797
|
-
|
|
7798
|
-
snapshot() {
|
|
7799
|
-
return rows;
|
|
7800
|
-
},
|
|
7801
|
-
lookupById(id) {
|
|
7802
|
-
return rows.find((e) => e["key"] === id);
|
|
7803
|
-
}
|
|
7804
|
-
};
|
|
7805
|
-
if (desc.displayLocale !== void 0) {
|
|
7806
|
-
;
|
|
7807
|
-
source.displayLocale = desc.displayLocale;
|
|
7808
|
-
}
|
|
7809
|
-
return source;
|
|
7810
|
-
}
|
|
7811
|
-
const dictFields = this.dictKeyFieldRegistry.get(leftCollection);
|
|
7812
|
-
if (!dictFields || !(field in dictFields)) return null;
|
|
7813
|
-
const dictName = dictFields[field];
|
|
7814
|
-
if (!dictName) return null;
|
|
7815
|
-
const handle = this.dictionary(dictName);
|
|
7816
|
-
return {
|
|
7817
|
-
snapshot() {
|
|
7818
|
-
return handle.snapshotEntries();
|
|
7819
|
-
},
|
|
7820
|
-
lookupById(id) {
|
|
7821
|
-
const entries = handle.snapshotEntries();
|
|
7822
|
-
return entries.find((e) => e["key"] === id);
|
|
7823
|
-
}
|
|
7824
|
-
};
|
|
8617
|
+
return resolveDictSource(
|
|
8618
|
+
leftCollection,
|
|
8619
|
+
field,
|
|
8620
|
+
this.staticDescriptorByField,
|
|
8621
|
+
this.dictKeyFieldRegistry,
|
|
8622
|
+
(name) => this.dictionary(name)
|
|
8623
|
+
);
|
|
7825
8624
|
}
|
|
7826
8625
|
/**
|
|
7827
8626
|
* Set or update the vault-default locale at runtime.
|
|
@@ -8025,7 +8824,9 @@ var Vault = class {
|
|
|
8025
8824
|
listRecordIds: (c) => this.adapter.list(this.name, c),
|
|
8026
8825
|
getRecord: async (c, id) => await this.collection(c).get(id, { locale: "raw" }),
|
|
8027
8826
|
getEnvelope: (c, id) => this.adapter.get(this.name, c, id),
|
|
8028
|
-
removeFromPrimary: (c, id) =>
|
|
8827
|
+
removeFromPrimary: async (c, id) => {
|
|
8828
|
+
await this.collection(c)._internalDelete(id);
|
|
8829
|
+
},
|
|
8029
8830
|
restoreToPrimary: async (c, id, env) => {
|
|
8030
8831
|
await this.adapter.put(this.name, c, id, env);
|
|
8031
8832
|
await this.collection(c)._invalidateCacheEntry(id);
|
|
@@ -8112,7 +8913,7 @@ var Vault = class {
|
|
|
8112
8913
|
* cascade guard. See `with-shape/links/vault-facade.ts`.
|
|
8113
8914
|
*/
|
|
8114
8915
|
async enforceRefsOnDelete(collectionName, id) {
|
|
8115
|
-
return this.linksEnforcer.enforceRefsOnDelete(collectionName, id);
|
|
8916
|
+
return this.linksEnforcer.enforceRefsOnDelete(this.graph, collectionName, id);
|
|
8116
8917
|
}
|
|
8117
8918
|
// ─── Join resolver) ────────────────────
|
|
8118
8919
|
/**
|
|
@@ -8216,23 +9017,19 @@ var Vault = class {
|
|
|
8216
9017
|
);
|
|
8217
9018
|
}
|
|
8218
9019
|
/**
|
|
8219
|
-
* GDPR crypto-shred of a data subject. Consults the encrypted subject
|
|
8220
|
-
*
|
|
8221
|
-
*
|
|
8222
|
-
*
|
|
8223
|
-
*
|
|
8224
|
-
* the collection DEK and every OTHER record stay intact. Then appends ONE
|
|
8225
|
-
* `op:'forget'` ledger entry whose `payloadHash` is `sha256Hex(subjectId)` —
|
|
8226
|
-
*
|
|
8227
|
-
*
|
|
8228
|
-
*
|
|
8229
|
-
*
|
|
8230
|
-
*
|
|
8231
|
-
*
|
|
8232
|
-
* is still tombstoned, but its pre-shred ciphertext (if leaked to a
|
|
8233
|
-
* backup before migration) stays decryptable. Migrate, then re-forget.
|
|
8234
|
-
* - `blobResidueCollections`: a shredded record still has blob attachments,
|
|
8235
|
-
* which are keyed off a separate `_blob` DEK and are out of scope here.
|
|
9020
|
+
* GDPR crypto-shred of a data subject. Consults the encrypted subject index and, per matching
|
|
9021
|
+
* record: rewrites the LIVE envelope to a tombstone (drops `_iv`/`_data`/`_cek`/`_det`), tombstones
|
|
9022
|
+
* every `_history` version, and fans out to derived residue via the graph (#622) — record-grain
|
|
9023
|
+
* artifacts (MV rows, derivation copies) ERASED, aggregate-grain rollups RECOMPUTED without the
|
|
9024
|
+
* forgotten contribution (skip+audit in a frozen period). The body/history become permanently
|
|
9025
|
+
* undecryptable while the collection DEK and every OTHER record stay intact. Then appends ONE
|
|
9026
|
+
* `op:'forget'` ledger entry whose `payloadHash` is `sha256Hex(subjectId)` — the chain still
|
|
9027
|
+
* `verify()`s, PROVING the subject existed and was erased without retaining any plaintext.
|
|
9028
|
+
*
|
|
9029
|
+
* Reports — but does not silently swallow — two completeness gaps: `unmigratedRecords` (a record
|
|
9030
|
+
* whose body was NOT yet migrated to a per-record CEK — still tombstoned, but pre-shred backups
|
|
9031
|
+
* stay decryptable; migrate, then re-forget) and `blobResidueCollections` (blob attachments, keyed
|
|
9032
|
+
* off a separate `_blob` DEK, out of scope here).
|
|
8236
9033
|
*
|
|
8237
9034
|
* @throws ForgetStrategyNotConfiguredError when no `withForgetCascade` was set.
|
|
8238
9035
|
*/
|
|
@@ -8257,23 +9054,29 @@ var Vault = class {
|
|
|
8257
9054
|
const indexResidue = [];
|
|
8258
9055
|
const blobsEnabled = this.blobStrategy !== void 0;
|
|
8259
9056
|
const actor = this.keyring.userId;
|
|
9057
|
+
const fanoutStats = { recordsErased: 0, aggregatesRecomputed: 0, residueFrozen: [], lookupReferencesCascaded: 0, lookupReferencesNullified: 0, lookupReferencesResidue: [] };
|
|
8260
9058
|
for (const ref2 of allRefs) {
|
|
8261
9059
|
const coll = this.collection(ref2.collection);
|
|
8262
9060
|
const satelliteOf = ref2.satelliteOf;
|
|
8263
9061
|
const perRecordKeys = this.forgetStrategy.subjects[satelliteOf ?? ref2.collection] !== void 0;
|
|
9062
|
+
const lookupCompareKeys = await this.linksEnforcer.checkLookupRefsRestrict(this.graph, ref2.collection, ref2.collection, ref2.id);
|
|
8264
9063
|
const live = await this.adapter.get(this.name, ref2.collection, ref2.id);
|
|
8265
9064
|
if (perRecordKeys && live && live._data && !hasPerRecordKey(live)) {
|
|
8266
9065
|
unmigratedRecords.push(`${ref2.collection}:${ref2.id}`);
|
|
8267
9066
|
}
|
|
8268
9067
|
if (live?._sealed !== void 0) {
|
|
8269
9068
|
try {
|
|
8270
|
-
const
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
9069
|
+
const viaReport = await coll._onViaErase(ref2.id, live);
|
|
9070
|
+
if (viaReport) {
|
|
9071
|
+
sealedFieldsShredded += viaReport.shredded;
|
|
9072
|
+
for (const entry of viaReport.residue) {
|
|
9073
|
+
if (entry.kind === "classified-sealed-dek-residue" && entry.field !== void 0) sealedResidue.push(`${ref2.collection}:${ref2.id}:${entry.field}`);
|
|
8274
9074
|
}
|
|
8275
|
-
|
|
8276
|
-
|
|
9075
|
+
} else {
|
|
9076
|
+
const cls = await coll._classifySealedShred(live);
|
|
9077
|
+
for (const slot of cls.slots) {
|
|
9078
|
+
if (slot.class === "shreddable" || slot.class === "live-shreddable+dekResidue-in-backups") sealedFieldsShredded += 1;
|
|
9079
|
+
if (slot.class === "dekResidue" || slot.class === "live-shreddable+dekResidue-in-backups") sealedResidue.push(`${ref2.collection}:${ref2.id}:${slot.field}`);
|
|
8277
9080
|
}
|
|
8278
9081
|
}
|
|
8279
9082
|
} catch {
|
|
@@ -8306,6 +9109,7 @@ var Vault = class {
|
|
|
8306
9109
|
if (shred !== null) {
|
|
8307
9110
|
recordsShredded++;
|
|
8308
9111
|
collections.add(ref2.collection);
|
|
9112
|
+
await forgetDerivedFanout(this, { collection: ref2.collection, id: ref2.id }, live, fanoutStats, lookupCompareKeys);
|
|
8309
9113
|
}
|
|
8310
9114
|
historyVersionsShredded += await this.historyStrategy.tombstoneHistory(
|
|
8311
9115
|
this.adapter,
|
|
@@ -8389,7 +9193,14 @@ var Vault = class {
|
|
|
8389
9193
|
sealedCekEnvelopesPurged,
|
|
8390
9194
|
sealedCekResidue,
|
|
8391
9195
|
sealedResidue,
|
|
8392
|
-
ledgerEntry
|
|
9196
|
+
ledgerEntry,
|
|
9197
|
+
derivedRecordsErased: fanoutStats.recordsErased,
|
|
9198
|
+
derivedAggregatesRecomputed: fanoutStats.aggregatesRecomputed,
|
|
9199
|
+
derivedResidueFrozen: fanoutStats.residueFrozen,
|
|
9200
|
+
lookupReferencesCascaded: fanoutStats.lookupReferencesCascaded,
|
|
9201
|
+
lookupReferencesNullified: fanoutStats.lookupReferencesNullified,
|
|
9202
|
+
lookupReferencesResidue: fanoutStats.lookupReferencesResidue
|
|
9203
|
+
// #650 Task 5 (+ review Important fix: residue)
|
|
8393
9204
|
};
|
|
8394
9205
|
}
|
|
8395
9206
|
// ─── Record-scoped CEK sealing ─────────────────────────────────────
|
|
@@ -8485,7 +9296,7 @@ var Vault = class {
|
|
|
8485
9296
|
if (handles.length === 0) return;
|
|
8486
9297
|
const [{ GuardRegistry }, { ReadOnlyVaultFacade }] = await Promise.all([
|
|
8487
9298
|
import("./registry-VW6P6A3J.js"),
|
|
8488
|
-
import("./read-only-facade-
|
|
9299
|
+
import("./read-only-facade-UCYNRE56.js")
|
|
8489
9300
|
]);
|
|
8490
9301
|
const registry = new GuardRegistry();
|
|
8491
9302
|
for (const h of handles) registry.register(h.spec);
|
|
@@ -8512,14 +9323,15 @@ var Vault = class {
|
|
|
8512
9323
|
async _initDerivations(handles) {
|
|
8513
9324
|
if (handles.length === 0) return;
|
|
8514
9325
|
const [{ DerivationRegistry }, { ReadOnlyVaultFacade }] = await Promise.all([
|
|
8515
|
-
import("./registry-
|
|
8516
|
-
import("./read-only-facade-
|
|
9326
|
+
import("./registry-DQRNTWRL.js"),
|
|
9327
|
+
import("./read-only-facade-UCYNRE56.js")
|
|
8517
9328
|
]);
|
|
8518
9329
|
const registry = new DerivationRegistry();
|
|
8519
9330
|
for (const h of handles) {
|
|
8520
9331
|
await registry.register(h.spec);
|
|
8521
9332
|
}
|
|
8522
|
-
registry.
|
|
9333
|
+
for (const edge of registry.edges()) this.graph.registerDerived(edge.target, edge.sources, edge.kind, edge.grain);
|
|
9334
|
+
registry.validate(this.graph);
|
|
8523
9335
|
this.derivationRegistry = registry;
|
|
8524
9336
|
if (this.derivationFacade === null) {
|
|
8525
9337
|
this.derivationFacade = new ReadOnlyVaultFacade(this, "derivation");
|
|
@@ -8543,14 +9355,15 @@ var Vault = class {
|
|
|
8543
9355
|
*/
|
|
8544
9356
|
async _initMaterializedViews(handles) {
|
|
8545
9357
|
if (handles.length === 0) return;
|
|
8546
|
-
const { MaterializedViewRegistry } = await import("./registry-
|
|
9358
|
+
const { MaterializedViewRegistry } = await import("./registry-LTGLERS7.js");
|
|
8547
9359
|
const registry = new MaterializedViewRegistry();
|
|
8548
9360
|
this.materializedViewRegistry = registry;
|
|
8549
9361
|
const db = this;
|
|
8550
9362
|
for (const h of handles) {
|
|
8551
9363
|
await registry.register(h.spec, db);
|
|
8552
9364
|
}
|
|
8553
|
-
registry.
|
|
9365
|
+
for (const edge of registry.edges()) this.graph.registerDerived(edge.target, edge.sources, edge.kind, edge.grain);
|
|
9366
|
+
registry.validate(this.graph);
|
|
8554
9367
|
}
|
|
8555
9368
|
/**
|
|
8556
9369
|
* @internal — consumed by `Collection.put` at write-time. Returns
|
|
@@ -8567,7 +9380,7 @@ var Vault = class {
|
|
|
8567
9380
|
*/
|
|
8568
9381
|
async _initOverlayedViews(handles) {
|
|
8569
9382
|
if (handles.length === 0) return;
|
|
8570
|
-
const { OverlayedViewRegistry } = await import("./registry-
|
|
9383
|
+
const { OverlayedViewRegistry } = await import("./registry-5RJSK5AJ.js");
|
|
8571
9384
|
const registry = new OverlayedViewRegistry();
|
|
8572
9385
|
const mvRegistry = this.materializedViewRegistry;
|
|
8573
9386
|
const overlayNames = /* @__PURE__ */ new Set();
|
|
@@ -8584,6 +9397,7 @@ var Vault = class {
|
|
|
8584
9397
|
isOverlayName: (n) => overlayNames.has(n) && n !== h.spec.name,
|
|
8585
9398
|
isMVOutput
|
|
8586
9399
|
});
|
|
9400
|
+
this.graph.registerDerived({ collection: h.spec.name, field: "*" }, [{ collection: h.spec.base, field: "*" }], "overlay", "record");
|
|
8587
9401
|
}
|
|
8588
9402
|
this.overlayedViewRegistry = registry;
|
|
8589
9403
|
}
|
|
@@ -8594,16 +9408,15 @@ var Vault = class {
|
|
|
8594
9408
|
_getOverlayedViewRegistry() {
|
|
8595
9409
|
return this.overlayedViewRegistry;
|
|
8596
9410
|
}
|
|
9411
|
+
/** @internal — ctx for `putDerivedOutput`'s frozen-period skip+audit (#638 Task 5). */
|
|
9412
|
+
_dispatchCtx(source) {
|
|
9413
|
+
return { emit: (e, p) => this.emitter.emit(e, p), source, audit: ledgerAuditHook(this.getLedgerOrNull() ?? void 0, this.keyring.userId) };
|
|
9414
|
+
}
|
|
8597
9415
|
/**
|
|
8598
|
-
* Manual re-materialize for a single registered MV
|
|
8599
|
-
*
|
|
8600
|
-
*
|
|
8601
|
-
*
|
|
8602
|
-
*
|
|
8603
|
-
* Returns `{ written, deleted, failed }`. `deleted` is always 0
|
|
8604
|
-
* when tombstoning is not enabled.
|
|
8605
|
-
*
|
|
8606
|
-
* Throws if `name` is not a registered MV.
|
|
9416
|
+
* Manual re-materialize for a single registered MV (`refresh: 'manual'` consumers,
|
|
9417
|
+
* stale-bit recovery on vault reopen, bulk-recompute escape hatch after a strategy
|
|
9418
|
+
* change). Returns `{ written, deleted, failed }` (`deleted` always 0 without
|
|
9419
|
+
* tombstoning). Throws if `name` is not a registered MV.
|
|
8607
9420
|
*/
|
|
8608
9421
|
async refreshView(name) {
|
|
8609
9422
|
const registry = this.materializedViewRegistry;
|
|
@@ -8614,42 +9427,42 @@ var Vault = class {
|
|
|
8614
9427
|
if (!reg) {
|
|
8615
9428
|
throw new Error(`refreshView: no MV registered with name "${name}"`);
|
|
8616
9429
|
}
|
|
8617
|
-
const { MaterializedViewExecutor } = await import("./executor-
|
|
9430
|
+
const { MaterializedViewExecutor } = await import("./executor-IL7SRNUZ.js");
|
|
8618
9431
|
const result = await MaterializedViewExecutor.refresh(reg, {
|
|
8619
9432
|
getCollection: (n) => this.collection(n),
|
|
8620
9433
|
getActiveTxContext: () => this.noydb._activeTxContextOrNull,
|
|
8621
|
-
getQueryContext: () => this
|
|
9434
|
+
getQueryContext: () => this,
|
|
9435
|
+
dispatchCtx: this._dispatchCtx({ collection: name, id: "refreshView" })
|
|
8622
9436
|
});
|
|
8623
|
-
const { clearMVStale } = await import("./stale-
|
|
9437
|
+
const { clearMVStale } = await import("./stale-IYYXEH3U.js");
|
|
8624
9438
|
clearMVStale(registry, name);
|
|
8625
9439
|
return result;
|
|
8626
9440
|
}
|
|
8627
9441
|
/**
|
|
8628
|
-
* Re-derive every record in the named source collection
|
|
8629
|
-
*
|
|
8630
|
-
* up-to-date.
|
|
8631
|
-
*
|
|
8632
|
-
* Sequential in v1; parallelisation deferred to v2.
|
|
9442
|
+
* Re-derive every record in the named source collection (useful after a strategy
|
|
9443
|
+
* change to bring previously-derived records up-to-date). Sequential in v1.
|
|
8633
9444
|
*/
|
|
8634
9445
|
async deriveAll(sourceCollection) {
|
|
8635
9446
|
const registry = this._getDerivationRegistry();
|
|
8636
|
-
if (registry === null) return { derived: 0, failed: 0 };
|
|
9447
|
+
if (registry === null) return { derived: 0, failed: 0, skippedFrozen: 0 };
|
|
8637
9448
|
const strategies = registry.strategiesForSource(sourceCollection);
|
|
8638
|
-
if (strategies.length === 0) return { derived: 0, failed: 0 };
|
|
8639
|
-
const { DerivationExecutor } = await import("./executor-
|
|
9449
|
+
if (strategies.length === 0) return { derived: 0, failed: 0, skippedFrozen: 0 };
|
|
9450
|
+
const { DerivationExecutor } = await import("./executor-WAEI6PDV.js");
|
|
8640
9451
|
const sourceColl = this.collection(sourceCollection);
|
|
8641
9452
|
const records = await sourceColl.list();
|
|
8642
|
-
const ctx = { vault: this.derivationFacade ?? new (await import("./read-only-facade-
|
|
9453
|
+
const ctx = { vault: this.derivationFacade ?? new (await import("./read-only-facade-UCYNRE56.js")).ReadOnlyVaultFacade(this, "derivation") };
|
|
8643
9454
|
let derived = 0;
|
|
8644
9455
|
let failed = 0;
|
|
9456
|
+
let skippedFrozen = 0;
|
|
8645
9457
|
for (const record of records) {
|
|
8646
9458
|
if (typeof record !== "object" || record === null) continue;
|
|
8647
9459
|
const id = record.id;
|
|
8648
9460
|
if (typeof id !== "string") continue;
|
|
9461
|
+
const dispatchCtx = this._dispatchCtx({ collection: sourceCollection, id });
|
|
8649
9462
|
for (const { spec, strategyHash } of strategies) {
|
|
8650
9463
|
const sourceWithId = { ...record, id };
|
|
8651
9464
|
const result = await DerivationExecutor.run(spec, sourceWithId, 0, strategyHash, ctx);
|
|
8652
|
-
let anyFailed = false;
|
|
9465
|
+
let anyFailed = false, anyFrozenSkip = false;
|
|
8653
9466
|
for (const key of Object.keys(spec.outputs)) {
|
|
8654
9467
|
const out = result.outputs[key];
|
|
8655
9468
|
if (!out) continue;
|
|
@@ -8661,7 +9474,7 @@ var Vault = class {
|
|
|
8661
9474
|
if (!outSpec) continue;
|
|
8662
9475
|
const outputColl = this.collection(outSpec.collection);
|
|
8663
9476
|
if (out.kind === "array") {
|
|
8664
|
-
const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-
|
|
9477
|
+
const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-CI5BCMQM.js");
|
|
8665
9478
|
const prior = await loadFanoutSidecar(this.adapter, this.name, spec.source, id, key, this.getDEK, this.encrypted);
|
|
8666
9479
|
const prevKeys = new Set(prior?.keys ?? []);
|
|
8667
9480
|
const newKeysList = out.entries.map((e) => e.key);
|
|
@@ -8671,7 +9484,7 @@ var Vault = class {
|
|
|
8671
9484
|
await outputColl._internalDelete(k);
|
|
8672
9485
|
}
|
|
8673
9486
|
for (const entry of out.entries) {
|
|
8674
|
-
await outputColl
|
|
9487
|
+
if (await putDerivedOutput(outputColl, entry.key, entry.value, dispatchCtx) === "skipped-frozen") anyFrozenSkip = true;
|
|
8675
9488
|
}
|
|
8676
9489
|
await saveFanoutSidecar(this.adapter, this.name, {
|
|
8677
9490
|
source: spec.source,
|
|
@@ -8686,13 +9499,14 @@ var Vault = class {
|
|
|
8686
9499
|
await outputColl._internalDelete(id);
|
|
8687
9500
|
continue;
|
|
8688
9501
|
}
|
|
8689
|
-
await outputColl
|
|
9502
|
+
if (await putDerivedOutput(outputColl, id, out.value, dispatchCtx) === "skipped-frozen") anyFrozenSkip = true;
|
|
8690
9503
|
}
|
|
8691
9504
|
if (anyFailed) failed++;
|
|
9505
|
+
else if (anyFrozenSkip) skippedFrozen++;
|
|
8692
9506
|
else derived++;
|
|
8693
9507
|
}
|
|
8694
9508
|
}
|
|
8695
|
-
return { derived, failed };
|
|
9509
|
+
return { derived, failed, skippedFrozen };
|
|
8696
9510
|
}
|
|
8697
9511
|
/**
|
|
8698
9512
|
* @internal — exposed for `runTransaction({ amendment: true })` so
|
|
@@ -8883,7 +9697,7 @@ var Vault = class {
|
|
|
8883
9697
|
* collection.
|
|
8884
9698
|
*/
|
|
8885
9699
|
async delegate(opts) {
|
|
8886
|
-
const { issueDelegation, DELEGATIONS_COLLECTION } = await import("./delegation-
|
|
9700
|
+
const { issueDelegation, DELEGATIONS_COLLECTION } = await import("./delegation-ZKII5UBI.js");
|
|
8887
9701
|
if (!this.keyring.kek) {
|
|
8888
9702
|
throw new ValidationError(
|
|
8889
9703
|
"issueDelegation: keyring.kek is null \u2014 issuing a delegation requires a tier-1 unlock. Re-authenticate at tier 1 (passphrase) first."
|
|
@@ -8905,7 +9719,7 @@ var Vault = class {
|
|
|
8905
9719
|
* if the id does not exist.
|
|
8906
9720
|
*/
|
|
8907
9721
|
async revokeDelegation(id) {
|
|
8908
|
-
const { revokeDelegation, DELEGATIONS_COLLECTION } = await import("./delegation-
|
|
9722
|
+
const { revokeDelegation, DELEGATIONS_COLLECTION } = await import("./delegation-ZKII5UBI.js");
|
|
8909
9723
|
await revokeDelegation(this.adapter, this.name, id);
|
|
8910
9724
|
void DELEGATIONS_COLLECTION;
|
|
8911
9725
|
}
|
|
@@ -9153,7 +9967,7 @@ var Vault = class {
|
|
|
9153
9967
|
* @see docs/superpowers/specs/2026-05-22-schema-dump-design.md
|
|
9154
9968
|
*/
|
|
9155
9969
|
async dumpSchema(opts = {}) {
|
|
9156
|
-
const { dumpVaultSchema } = await import("./walk-
|
|
9970
|
+
const { dumpVaultSchema } = await import("./walk-76UCFXYV.js");
|
|
9157
9971
|
return dumpVaultSchema(this, opts);
|
|
9158
9972
|
}
|
|
9159
9973
|
/**
|
|
@@ -9288,7 +10102,7 @@ var Vault = class {
|
|
|
9288
10102
|
* @see https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/public-envelope.md
|
|
9289
10103
|
*/
|
|
9290
10104
|
async getPublicEnvelope(opts = {}) {
|
|
9291
|
-
const { readPublicEnvelope } = await import("./public-envelope-
|
|
10105
|
+
const { readPublicEnvelope } = await import("./public-envelope-J5PLQOW2.js");
|
|
9292
10106
|
return readPublicEnvelope(this.adapter, this.name, opts);
|
|
9293
10107
|
}
|
|
9294
10108
|
/**
|
|
@@ -9303,7 +10117,7 @@ var Vault = class {
|
|
|
9303
10117
|
* both modes round-trip cleanly.
|
|
9304
10118
|
*/
|
|
9305
10119
|
async dump() {
|
|
9306
|
-
const { dumpVault } = await import("./backup-
|
|
10120
|
+
const { dumpVault } = await import("./backup-ODXO7OGA.js");
|
|
9307
10121
|
return dumpVault(this.backupContext());
|
|
9308
10122
|
}
|
|
9309
10123
|
/**
|
|
@@ -9355,7 +10169,7 @@ var Vault = class {
|
|
|
9355
10169
|
* — there's no chain to verify against.
|
|
9356
10170
|
*/
|
|
9357
10171
|
async load(backupJson) {
|
|
9358
|
-
const { loadVault } = await import("./backup-
|
|
10172
|
+
const { loadVault } = await import("./backup-ODXO7OGA.js");
|
|
9359
10173
|
return loadVault(this.backupContext(), backupJson);
|
|
9360
10174
|
}
|
|
9361
10175
|
/**
|
|
@@ -9387,7 +10201,7 @@ var Vault = class {
|
|
|
9387
10201
|
* way to detect tampering of an in-place vault.
|
|
9388
10202
|
*/
|
|
9389
10203
|
async verifyBackupIntegrity() {
|
|
9390
|
-
const { verifyBackupIntegrity } = await import("./backup-
|
|
10204
|
+
const { verifyBackupIntegrity } = await import("./backup-ODXO7OGA.js");
|
|
9391
10205
|
return verifyBackupIntegrity(this.backupContext());
|
|
9392
10206
|
}
|
|
9393
10207
|
/**
|
|
@@ -9493,7 +10307,7 @@ var Vault = class {
|
|
|
9493
10307
|
const records = [];
|
|
9494
10308
|
for (const id of ids) {
|
|
9495
10309
|
const record = await coll.get(id, localeOpts);
|
|
9496
|
-
if (record !== null) records.push(record);
|
|
10310
|
+
if (record !== null) records.push(exportRedact(coll, record));
|
|
9497
10311
|
}
|
|
9498
10312
|
const chunk = {
|
|
9499
10313
|
collection: collectionName,
|
|
@@ -9512,7 +10326,7 @@ var Vault = class {
|
|
|
9512
10326
|
collection: collectionName,
|
|
9513
10327
|
schema,
|
|
9514
10328
|
refs,
|
|
9515
|
-
records: [record],
|
|
10329
|
+
records: [exportRedact(coll, record)],
|
|
9516
10330
|
...dictionaries !== void 0 ? { dictionaries } : {},
|
|
9517
10331
|
...ledgerHead ? { ledgerHead } : {}
|
|
9518
10332
|
};
|
|
@@ -9605,7 +10419,7 @@ var Vault = class {
|
|
|
9605
10419
|
* is the live validator object, not a serialization of it).
|
|
9606
10420
|
*/
|
|
9607
10421
|
async exportJSON(opts = {}) {
|
|
9608
|
-
const { exportVaultJSON } = await import("./backup-
|
|
10422
|
+
const { exportVaultJSON } = await import("./backup-ODXO7OGA.js");
|
|
9609
10423
|
return exportVaultJSON(this.backupContext(), opts);
|
|
9610
10424
|
}
|
|
9611
10425
|
};
|
|
@@ -10319,17 +11133,17 @@ function memoryStore() {
|
|
|
10319
11133
|
}
|
|
10320
11134
|
|
|
10321
11135
|
// src/with-party/session/strategy.ts
|
|
10322
|
-
function
|
|
11136
|
+
function notEnabled3(op) {
|
|
10323
11137
|
return new Error(
|
|
10324
11138
|
`${op} requires the session strategy. Import \`{ withSession }\` from "@noy-db/hub/session" and pass it to \`createNoydb({ sessionStrategy: withSession() })\`.`
|
|
10325
11139
|
);
|
|
10326
11140
|
}
|
|
10327
11141
|
var NO_SESSION = {
|
|
10328
11142
|
validateSessionPolicy() {
|
|
10329
|
-
throw
|
|
11143
|
+
throw notEnabled3("sessionPolicy");
|
|
10330
11144
|
},
|
|
10331
11145
|
createEnforcer() {
|
|
10332
|
-
throw
|
|
11146
|
+
throw notEnabled3("session policy enforcement");
|
|
10333
11147
|
},
|
|
10334
11148
|
revokeAllSessions() {
|
|
10335
11149
|
}
|
|
@@ -11478,7 +12292,7 @@ var Noydb = class {
|
|
|
11478
12292
|
if (!facade) return;
|
|
11479
12293
|
const ctx = { existing, vault: facade, userId: e.userId, role: e.role };
|
|
11480
12294
|
await registry.runChecks(e.collection, incoming, ctx);
|
|
11481
|
-
const { GuardExecutor } = await import("./executor-
|
|
12295
|
+
const { GuardExecutor } = await import("./executor-OUA2Y6YC.js");
|
|
11482
12296
|
for (const g of guards) {
|
|
11483
12297
|
await GuardExecutor.checkFrozenFields(g, e.docId, existing, incoming, e.computedFieldNames);
|
|
11484
12298
|
}
|
|
@@ -11699,6 +12513,8 @@ var Noydb = class {
|
|
|
11699
12513
|
});
|
|
11700
12514
|
this._forEachSyncEngine(name, (engine) => {
|
|
11701
12515
|
engine.setCacheInvalidator((collection, id) => comp._invalidateSyncApplied(collection, id));
|
|
12516
|
+
engine.setGraphBatchController({ begin: () => comp._beginGraphBatch(), flush: () => comp._flushGraphBatch() });
|
|
12517
|
+
engine.setReservedLookupSource({ collections: () => comp._reservedLookupCollectionNames() });
|
|
11702
12518
|
});
|
|
11703
12519
|
await comp._initGuards(this.options.guardStrategies ?? []);
|
|
11704
12520
|
await comp._initDerivations(this.options.derivationStrategies ?? []);
|
|
@@ -12743,7 +13559,7 @@ var Noydb = class {
|
|
|
12743
13559
|
"setPublicEnvelope: the public-envelope feature is not enabled. Pass `publicEnvelope: true` (or a schema object) to `createNoydb`."
|
|
12744
13560
|
);
|
|
12745
13561
|
}
|
|
12746
|
-
const { loadPublicEnvelope, savePublicEnvelope, resolveSchema, validatePublicEnvelopeInput } = await import("./public-envelope-
|
|
13562
|
+
const { loadPublicEnvelope, savePublicEnvelope, resolveSchema, validatePublicEnvelopeInput } = await import("./public-envelope-J5PLQOW2.js");
|
|
12747
13563
|
const schema = resolveSchema(this.options.publicEnvelope);
|
|
12748
13564
|
validatePublicEnvelopeInput(input, schema);
|
|
12749
13565
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -12772,7 +13588,7 @@ var Noydb = class {
|
|
|
12772
13588
|
* regardless).
|
|
12773
13589
|
*/
|
|
12774
13590
|
async getPublicEnvelope(vault, opts = {}) {
|
|
12775
|
-
const { readPublicEnvelope } = await import("./public-envelope-
|
|
13591
|
+
const { readPublicEnvelope } = await import("./public-envelope-J5PLQOW2.js");
|
|
12776
13592
|
return readPublicEnvelope(this.options.store, vault, opts);
|
|
12777
13593
|
}
|
|
12778
13594
|
// ─── Auth introspection ─────────────────────────────────────────
|
|
@@ -12963,9 +13779,9 @@ async function createNoydb(options) {
|
|
|
12963
13779
|
throw new ValidationError("A secret (passphrase) or getKeyring callback is required when encryption is enabled");
|
|
12964
13780
|
}
|
|
12965
13781
|
if (!options.coordinationStrategy) options = { ...options, coordinationStrategy: await createDefaultCoordinationProvider(options.store) };
|
|
12966
|
-
if (!options.userApiFactory) options = { ...options, userApiFactory: (await import("./api-
|
|
13782
|
+
if (!options.userApiFactory) options = { ...options, userApiFactory: (await import("./api-HV4PDUWF.js")).createUserApi };
|
|
12967
13783
|
if (!options.policyFactory) {
|
|
12968
|
-
const policyModule = await import("./policy-
|
|
13784
|
+
const policyModule = await import("./policy-SDGRWVIL.js");
|
|
12969
13785
|
options = { ...options, policyFactory: policyModule.createNoydbPolicy, policyCheckGateFn: policyModule.checkGate };
|
|
12970
13786
|
}
|
|
12971
13787
|
return new Noydb(options);
|
|
@@ -12982,6 +13798,9 @@ function normalizeSyncTargets(sync) {
|
|
|
12982
13798
|
export {
|
|
12983
13799
|
memoryStore,
|
|
12984
13800
|
withArchive,
|
|
13801
|
+
buildLookupSnapshot,
|
|
13802
|
+
via,
|
|
13803
|
+
isViaFieldSpec,
|
|
12985
13804
|
Collection,
|
|
12986
13805
|
ELEVATION_AUDIT_COLLECTION,
|
|
12987
13806
|
ElevatedHandle,
|
|
@@ -13000,4 +13819,4 @@ export {
|
|
|
13000
13819
|
Noydb,
|
|
13001
13820
|
createNoydb
|
|
13002
13821
|
};
|
|
13003
|
-
//# sourceMappingURL=chunk-
|
|
13822
|
+
//# sourceMappingURL=chunk-4C4WBS4V.js.map
|