@noy-db/hub 0.2.0-pre.17 → 0.2.0-pre.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aggregate/index.cjs +227 -3
- package/dist/aggregate/index.cjs.map +1 -1
- package/dist/aggregate/index.d.cts +3 -3
- package/dist/aggregate/index.d.ts +3 -3
- package/dist/aggregate/index.js +5 -4
- package/dist/aggregate/index.js.map +1 -1
- package/dist/attestation/index.cjs.map +1 -1
- package/dist/attestation/index.d.cts +5 -5
- package/dist/attestation/index.d.ts +5 -5
- package/dist/attestation/index.js +6 -6
- package/dist/blobs/index.cjs +4 -10
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +6 -6
- package/dist/blobs/index.d.ts +6 -6
- package/dist/blobs/index.js +6 -6
- package/dist/bundle/index.cjs +1587 -392
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +7 -7
- package/dist/bundle/index.d.ts +7 -7
- package/dist/bundle/index.js +10 -10
- package/dist/{chunk-NBBMMJ2H.js → chunk-3FSMVWBN.js} +4 -4
- package/dist/{chunk-HGVSHKZW.js → chunk-3Q2AOPLT.js} +100 -29
- package/dist/chunk-3Q2AOPLT.js.map +1 -0
- package/dist/{chunk-SHX5QBCI.js → chunk-4ULLGYPA.js} +3 -3
- package/dist/{chunk-CD2AVTEM.js → chunk-5IGWRMEC.js} +5 -5
- package/dist/{chunk-QO6RGLLD.js → chunk-6KESZO5D.js} +35 -7
- package/dist/chunk-6KESZO5D.js.map +1 -0
- package/dist/{chunk-GP3SDSH2.js → chunk-6OSOE6BY.js} +15 -2
- package/dist/chunk-6OSOE6BY.js.map +1 -0
- package/dist/{chunk-F4G63NTZ.js → chunk-7C6VFNIY.js} +2 -2
- package/dist/{chunk-XJV6OB4D.js → chunk-7HD67R6U.js} +2 -2
- package/dist/{chunk-UMLVJTYV.js → chunk-ADB7GPM3.js} +7 -4
- package/dist/chunk-ADB7GPM3.js.map +1 -0
- package/dist/{chunk-NYSYPFXJ.js → chunk-B6E5IRPJ.js} +3 -3
- package/dist/chunk-CYNTFU2D.js +129 -0
- package/dist/chunk-CYNTFU2D.js.map +1 -0
- package/dist/{chunk-ZEGSDPB7.js → chunk-DJF3FXW5.js} +35 -1
- package/dist/chunk-DJF3FXW5.js.map +1 -0
- package/dist/{chunk-3G3W65EQ.js → chunk-DY3EOJEN.js} +2 -2
- package/dist/{chunk-YYVZYTWW.js → chunk-E66DSTJP.js} +3 -3
- package/dist/{chunk-5LIROIDM.js → chunk-FBLAWK6A.js} +2 -2
- package/dist/{chunk-E77UKJYL.js → chunk-FPHRTW2Z.js} +5 -5
- package/dist/{state-vault-W2OEABNO.js → chunk-G4PYA575.js} +24 -7
- package/dist/chunk-G4PYA575.js.map +1 -0
- package/dist/{chunk-U5QCMH3W.js → chunk-GKQAU52M.js} +4 -4
- package/dist/{chunk-2FU2FTXD.js → chunk-GYAWXHFO.js} +2 -2
- package/dist/{chunk-ROPJVUG3.js → chunk-H42KZXNV.js} +5 -210
- package/dist/chunk-H42KZXNV.js.map +1 -0
- package/dist/{chunk-XPIHJ34I.js → chunk-IBVTH4JR.js} +4 -4
- package/dist/{chunk-C3HYQPV4.js → chunk-IVP5IVON.js} +2 -2
- package/dist/{chunk-BL5GYANC.js → chunk-KEDJDWWQ.js} +3 -3
- package/dist/{chunk-I5IUYN7B.js → chunk-KNKNOJFS.js} +3 -3
- package/dist/chunk-KNKNOJFS.js.map +1 -0
- package/dist/{chunk-D77ZQSQQ.js → chunk-KYGGXXT6.js} +829 -170
- package/dist/chunk-KYGGXXT6.js.map +1 -0
- package/dist/{chunk-J7RWBXFY.js → chunk-LSIIPKYT.js} +2 -2
- package/dist/{chunk-BSZOCSDZ.js → chunk-M3FPNTO2.js} +4 -4
- package/dist/{chunk-XMVHEWF6.js → chunk-MI36HL5G.js} +4 -4
- package/dist/{chunk-ROVO6NPJ.js → chunk-NN6IISZO.js} +58 -3
- package/dist/chunk-NN6IISZO.js.map +1 -0
- package/dist/{chunk-7H2GEJ3O.js → chunk-OBMYMKGO.js} +29 -6
- package/dist/{chunk-7H2GEJ3O.js.map → chunk-OBMYMKGO.js.map} +1 -1
- package/dist/{chunk-UNTGHX5A.js → chunk-OKOKPYWH.js} +2 -2
- package/dist/{chunk-WV7WV6JO.js → chunk-OY7RX2VL.js} +9 -15
- package/dist/chunk-OY7RX2VL.js.map +1 -0
- package/dist/{chunk-H2MRGONI.js → chunk-PTGQPWMV.js} +2 -2
- package/dist/{chunk-BJSLBUJ7.js → chunk-PWFTQHYX.js} +2 -2
- package/dist/{chunk-5AXTH4QZ.js → chunk-Q5MCHUXZ.js} +2 -2
- package/dist/{chunk-QHM6XEAH.js → chunk-S22UOMHM.js} +6 -6
- package/dist/{chunk-WIAOUFFB.js → chunk-S3XA7G35.js} +2 -2
- package/dist/{chunk-SISBMAPO.js → chunk-SHIUFIPW.js} +1 -1
- package/dist/chunk-SHIUFIPW.js.map +1 -0
- package/dist/{chunk-KCEHMDZF.js → chunk-U7JNBSS3.js} +3 -3
- package/dist/{chunk-ZNGPEV5J.js → chunk-V3VIRTTE.js} +3 -3
- package/dist/{chunk-TIDXB5DF.js → chunk-V5FZWQNN.js} +4 -4
- package/dist/chunk-VEIVAYJ7.js +361 -0
- package/dist/chunk-VEIVAYJ7.js.map +1 -0
- package/dist/{chunk-AEIKD3PP.js → chunk-VNUE6FHP.js} +3 -3
- package/dist/{chunk-DYYYUW5D.js → chunk-WFK2EVYU.js} +10 -2
- package/dist/chunk-WFK2EVYU.js.map +1 -0
- package/dist/{chunk-XMHUK5PN.js → chunk-X7FJMKT3.js} +2 -2
- package/dist/{chunk-FEJDVE3Z.js → chunk-XPH3FWME.js} +7 -2
- package/dist/{chunk-FEJDVE3Z.js.map → chunk-XPH3FWME.js.map} +1 -1
- package/dist/{chunk-SNMJ7SB3.js → chunk-Y5J63SMF.js} +5 -5
- package/dist/{chunk-M476FOQ7.js → chunk-YLRRU72W.js} +2 -2
- package/dist/{chunk-DWEBTE2W.js → chunk-YX333DPS.js} +4 -4
- package/dist/{chunk-BH3X5L6A.js → chunk-YZE6C3TQ.js} +3 -3
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +6 -6
- package/dist/consent/index.d.ts +6 -6
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-7BN2HDWG.js → crypto-B46VNH6X.js} +3 -3
- package/dist/{delegation-MGH5SODX.js → delegation-5HON72PV.js} +5 -5
- package/dist/derivations/index.cjs +82 -2
- package/dist/derivations/index.cjs.map +1 -1
- package/dist/derivations/index.d.cts +7 -7
- package/dist/derivations/index.d.ts +7 -7
- package/dist/derivations/index.js +8 -6
- package/dist/{dev-unlock-iXbYFAWl.d.cts → dev-unlock-BR1rMOS-.d.cts} +1 -1
- package/dist/{dev-unlock-CI1ijTML.d.ts → dev-unlock-whL49sxV.d.ts} +1 -1
- package/dist/{errors-Dz64FA65.d.cts → errors-DL-zTrrF.d.cts} +29 -1
- package/dist/{errors-Dz64FA65.d.ts → errors-DL-zTrrF.d.ts} +29 -1
- package/dist/executor-44R5CUS2.js +12 -0
- package/dist/executor-AOACUK7Z.js +8 -0
- package/dist/executor-OKFLQCDW.js +8 -0
- package/dist/{fanout-sidecar-FIJJ46YG.js → fanout-sidecar-DCQWJQ6S.js} +2 -2
- package/dist/forget/index.cjs.map +1 -1
- package/dist/forget/index.d.cts +1 -1
- package/dist/forget/index.d.ts +1 -1
- package/dist/forget/index.js +4 -4
- package/dist/guards/index.cjs +80 -3
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +7 -7
- package/dist/guards/index.d.ts +7 -7
- package/dist/guards/index.js +8 -4
- package/dist/{hash-tEcM5fnv.d.cts → hash-BEUBmmI4.d.cts} +1 -1
- package/dist/{hash-blk7Bkes.d.ts → hash-Dtb7FwWd.d.ts} +1 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +7 -7
- package/dist/history/index.d.ts +7 -7
- package/dist/history/index.js +5 -5
- package/dist/i18n/index.cjs +149 -132
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +6 -6
- package/dist/i18n/index.d.ts +6 -6
- package/dist/i18n/index.js +14 -14
- package/dist/{index-u-kWzSrL.d.cts → index-BM7O48Ur.d.cts} +85 -9
- package/dist/{index-C-SSRIxP.d.cts → index-BMmajblo.d.cts} +14 -0
- package/dist/{index-C-SSRIxP.d.ts → index-BMmajblo.d.ts} +14 -0
- package/dist/{index-DpU6KWof.d.ts → index-BelbyUwz.d.ts} +85 -9
- package/dist/index.cjs +2206 -992
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -16
- package/dist/index.d.ts +29 -16
- package/dist/index.js +76 -54
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.js +4 -4
- package/dist/issue-EPA2PSWP.js +12 -0
- package/dist/{ledger-LFVLHE5H.js → ledger-LS6GXCBP.js} +5 -5
- package/dist/materialized-views/index.cjs +257 -4
- package/dist/materialized-views/index.cjs.map +1 -1
- package/dist/materialized-views/index.d.cts +7 -7
- package/dist/materialized-views/index.d.ts +7 -7
- package/dist/materialized-views/index.js +8 -7
- package/dist/noydb-BVKFP74P.js +38 -0
- package/dist/overlay-views/index.cjs.map +1 -1
- package/dist/overlay-views/index.d.cts +7 -7
- package/dist/overlay-views/index.d.ts +7 -7
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +6 -6
- package/dist/periods/index.d.ts +6 -6
- package/dist/periods/index.js +5 -5
- package/dist/{public-envelope-RXZNP3V6.js → public-envelope-AGU6SS4Z.js} +4 -4
- package/dist/query/index.cjs +320 -28
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +3 -3
- package/dist/query/index.d.ts +3 -3
- package/dist/query/index.js +7 -6
- package/dist/read-only-facade-EX6WZZBP.js +7 -0
- package/dist/registry-ERNAMRDE.js +8 -0
- package/dist/registry-EXTHSXQW.js +8 -0
- package/dist/{registry-SECUWSGY.js → registry-RDPTFXQ7.js} +3 -3
- package/dist/{revoke-B54H2S2W.js → revoke-IFLXEZA5.js} +6 -6
- package/dist/sealed-record/index.cjs.map +1 -1
- package/dist/sealed-record/index.d.cts +1 -1
- package/dist/sealed-record/index.d.ts +1 -1
- package/dist/sealed-record/index.js +2 -2
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +7 -7
- package/dist/session/index.d.ts +7 -7
- package/dist/session/index.js +3 -3
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +6 -6
- package/dist/shadow/index.d.ts +6 -6
- package/dist/shadow/index.js +2 -2
- package/dist/{signer-YSXZT574.js → signer-UNWOUJAK.js} +5 -5
- package/dist/snapshots/index.cjs.map +1 -1
- package/dist/snapshots/index.d.cts +6 -6
- package/dist/snapshots/index.d.ts +6 -6
- package/dist/snapshots/index.js +4 -4
- package/dist/{stale-TOA36SRK.js → stale-NTEV5SLX.js} +2 -2
- package/dist/state-vault-TUTFRTOA.js +14 -0
- package/dist/state-vault-TUTFRTOA.js.map +1 -0
- package/dist/store/index.cjs +8 -0
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +13 -6
- package/dist/store/index.d.ts +13 -6
- package/dist/store/index.js +2 -2
- package/dist/{strategy-4M9jo172.d.ts → strategy-BDxQnnTX.d.ts} +315 -4
- package/dist/{strategy-CLC1j79g.d.cts → strategy-C5ol6NdV.d.cts} +315 -4
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +5 -5
- package/dist/sync/index.d.ts +5 -5
- package/dist/sync/index.js +4 -4
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +6 -6
- package/dist/team/index.d.ts +6 -6
- package/dist/team/index.js +8 -8
- package/dist/transition-guard-B1N82hMf.d.cts +165 -0
- package/dist/transition-guard-C__YeF3_.d.ts +165 -0
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +6 -6
- package/dist/tx/index.d.ts +6 -6
- package/dist/tx/index.js +3 -3
- package/dist/{types-CljIHm_J.d.ts → types-CraiZOyO.d.ts} +609 -305
- package/dist/{types-CrSpRDuG.d.cts → types-D-gr5t0G.d.cts} +609 -305
- package/dist/{ulid-CrI7PPbA.d.cts → ulid-DQnSAP5W.d.cts} +1 -1
- package/dist/{ulid-CWfL2Vfv.d.ts → ulid-FFRRHkVf.d.ts} +1 -1
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.js +1 -1
- package/dist/{vault-group-DHAHFX2A.js → vault-group-27EV7KB4.js} +205 -8
- package/dist/vault-group-27EV7KB4.js.map +1 -0
- package/dist/{with-materialized-view-NzF71cG_.d.cts → with-materialized-view-BboqxyV3.d.cts} +1 -1
- package/dist/{with-materialized-view-B892zYZV.d.ts → with-materialized-view-CguCeVcT.d.ts} +1 -1
- package/dist/{with-overlayed-view-CR6m7CHe.d.ts → with-overlayed-view-DO08u_tx.d.ts} +1 -1
- package/dist/{with-overlayed-view-UI8qSGL4.d.cts → with-overlayed-view-mmsg5Of3.d.cts} +1 -1
- package/dist/with-rollup-_TyBzz3T.d.ts +47 -0
- package/dist/with-rollup-aaxOZcIb.d.cts +47 -0
- package/package.json +3 -3
- package/dist/chunk-D77ZQSQQ.js.map +0 -1
- package/dist/chunk-DYYYUW5D.js.map +0 -1
- package/dist/chunk-GP3SDSH2.js.map +0 -1
- package/dist/chunk-HGVSHKZW.js.map +0 -1
- package/dist/chunk-I5IUYN7B.js.map +0 -1
- package/dist/chunk-JDWE6JMX.js +0 -139
- package/dist/chunk-JDWE6JMX.js.map +0 -1
- package/dist/chunk-PDULVIBY.js +0 -63
- package/dist/chunk-PDULVIBY.js.map +0 -1
- package/dist/chunk-QO6RGLLD.js.map +0 -1
- package/dist/chunk-ROPJVUG3.js.map +0 -1
- package/dist/chunk-ROVO6NPJ.js.map +0 -1
- package/dist/chunk-SISBMAPO.js.map +0 -1
- package/dist/chunk-UMLVJTYV.js.map +0 -1
- package/dist/chunk-WV7WV6JO.js.map +0 -1
- package/dist/chunk-ZEGSDPB7.js.map +0 -1
- package/dist/executor-3W63Y44O.js +0 -11
- package/dist/executor-CFFWPWBJ.js +0 -8
- package/dist/executor-VDQQOR4F.js +0 -8
- package/dist/immutable-guard-B5M95nbq.d.ts +0 -82
- package/dist/immutable-guard-qN3zF8o1.d.cts +0 -82
- package/dist/issue-TTMGHQ2J.js +0 -12
- package/dist/noydb-36S6GQNC.js +0 -37
- package/dist/read-only-facade-ITU6L7BL.js +0 -7
- package/dist/registry-3YFLZ7WD.js +0 -8
- package/dist/registry-TGZISEWC.js +0 -8
- package/dist/state-vault-W2OEABNO.js.map +0 -1
- package/dist/vault-group-DHAHFX2A.js.map +0 -1
- package/dist/with-derivation-BZ2y4bzF.d.ts +0 -13
- package/dist/with-derivation-Bozs8DmD.d.cts +0 -13
- /package/dist/{chunk-NBBMMJ2H.js.map → chunk-3FSMVWBN.js.map} +0 -0
- /package/dist/{chunk-SHX5QBCI.js.map → chunk-4ULLGYPA.js.map} +0 -0
- /package/dist/{chunk-CD2AVTEM.js.map → chunk-5IGWRMEC.js.map} +0 -0
- /package/dist/{chunk-F4G63NTZ.js.map → chunk-7C6VFNIY.js.map} +0 -0
- /package/dist/{chunk-XJV6OB4D.js.map → chunk-7HD67R6U.js.map} +0 -0
- /package/dist/{chunk-NYSYPFXJ.js.map → chunk-B6E5IRPJ.js.map} +0 -0
- /package/dist/{chunk-3G3W65EQ.js.map → chunk-DY3EOJEN.js.map} +0 -0
- /package/dist/{chunk-YYVZYTWW.js.map → chunk-E66DSTJP.js.map} +0 -0
- /package/dist/{chunk-5LIROIDM.js.map → chunk-FBLAWK6A.js.map} +0 -0
- /package/dist/{chunk-E77UKJYL.js.map → chunk-FPHRTW2Z.js.map} +0 -0
- /package/dist/{chunk-U5QCMH3W.js.map → chunk-GKQAU52M.js.map} +0 -0
- /package/dist/{chunk-2FU2FTXD.js.map → chunk-GYAWXHFO.js.map} +0 -0
- /package/dist/{chunk-XPIHJ34I.js.map → chunk-IBVTH4JR.js.map} +0 -0
- /package/dist/{chunk-C3HYQPV4.js.map → chunk-IVP5IVON.js.map} +0 -0
- /package/dist/{chunk-BL5GYANC.js.map → chunk-KEDJDWWQ.js.map} +0 -0
- /package/dist/{chunk-J7RWBXFY.js.map → chunk-LSIIPKYT.js.map} +0 -0
- /package/dist/{chunk-BSZOCSDZ.js.map → chunk-M3FPNTO2.js.map} +0 -0
- /package/dist/{chunk-XMVHEWF6.js.map → chunk-MI36HL5G.js.map} +0 -0
- /package/dist/{chunk-UNTGHX5A.js.map → chunk-OKOKPYWH.js.map} +0 -0
- /package/dist/{chunk-H2MRGONI.js.map → chunk-PTGQPWMV.js.map} +0 -0
- /package/dist/{chunk-BJSLBUJ7.js.map → chunk-PWFTQHYX.js.map} +0 -0
- /package/dist/{chunk-5AXTH4QZ.js.map → chunk-Q5MCHUXZ.js.map} +0 -0
- /package/dist/{chunk-QHM6XEAH.js.map → chunk-S22UOMHM.js.map} +0 -0
- /package/dist/{chunk-WIAOUFFB.js.map → chunk-S3XA7G35.js.map} +0 -0
- /package/dist/{chunk-KCEHMDZF.js.map → chunk-U7JNBSS3.js.map} +0 -0
- /package/dist/{chunk-ZNGPEV5J.js.map → chunk-V3VIRTTE.js.map} +0 -0
- /package/dist/{chunk-TIDXB5DF.js.map → chunk-V5FZWQNN.js.map} +0 -0
- /package/dist/{chunk-AEIKD3PP.js.map → chunk-VNUE6FHP.js.map} +0 -0
- /package/dist/{chunk-XMHUK5PN.js.map → chunk-X7FJMKT3.js.map} +0 -0
- /package/dist/{chunk-SNMJ7SB3.js.map → chunk-Y5J63SMF.js.map} +0 -0
- /package/dist/{chunk-M476FOQ7.js.map → chunk-YLRRU72W.js.map} +0 -0
- /package/dist/{chunk-DWEBTE2W.js.map → chunk-YX333DPS.js.map} +0 -0
- /package/dist/{chunk-BH3X5L6A.js.map → chunk-YZE6C3TQ.js.map} +0 -0
- /package/dist/{crypto-7BN2HDWG.js.map → crypto-B46VNH6X.js.map} +0 -0
- /package/dist/{delegation-MGH5SODX.js.map → delegation-5HON72PV.js.map} +0 -0
- /package/dist/{executor-3W63Y44O.js.map → executor-44R5CUS2.js.map} +0 -0
- /package/dist/{executor-CFFWPWBJ.js.map → executor-AOACUK7Z.js.map} +0 -0
- /package/dist/{executor-VDQQOR4F.js.map → executor-OKFLQCDW.js.map} +0 -0
- /package/dist/{fanout-sidecar-FIJJ46YG.js.map → fanout-sidecar-DCQWJQ6S.js.map} +0 -0
- /package/dist/{issue-TTMGHQ2J.js.map → issue-EPA2PSWP.js.map} +0 -0
- /package/dist/{ledger-LFVLHE5H.js.map → ledger-LS6GXCBP.js.map} +0 -0
- /package/dist/{noydb-36S6GQNC.js.map → noydb-BVKFP74P.js.map} +0 -0
- /package/dist/{public-envelope-RXZNP3V6.js.map → public-envelope-AGU6SS4Z.js.map} +0 -0
- /package/dist/{read-only-facade-ITU6L7BL.js.map → read-only-facade-EX6WZZBP.js.map} +0 -0
- /package/dist/{registry-3YFLZ7WD.js.map → registry-ERNAMRDE.js.map} +0 -0
- /package/dist/{registry-SECUWSGY.js.map → registry-EXTHSXQW.js.map} +0 -0
- /package/dist/{registry-TGZISEWC.js.map → registry-RDPTFXQ7.js.map} +0 -0
- /package/dist/{revoke-B54H2S2W.js.map → revoke-IFLXEZA5.js.map} +0 -0
- /package/dist/{signer-YSXZT574.js.map → signer-UNWOUJAK.js.map} +0 -0
- /package/dist/{stale-TOA36SRK.js.map → stale-NTEV5SLX.js.map} +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export { w as withDerivation } from '../with-
|
|
2
|
-
import {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
1
|
+
export { w as withDerivation, a as withRollup } from '../with-rollup-aaxOZcIb.cjs';
|
|
2
|
+
import { av as DerivationStrategy, aw as DerivationContext } from '../types-D-gr5t0G.cjs';
|
|
3
|
+
export { ax as ArrayOutputSpec, ay as DerivationRegistry, az as DerivationStrategyHandle, aA as DerivedFromMeta, aB as OutputSpec, aC as RecordOutputSpec } from '../types-D-gr5t0G.cjs';
|
|
4
|
+
export { i as DerivationCapExceededError, j as DerivationCycleError, k as DerivationDepthError, l as DerivationOutputShapeError, m as DerivationOutputUnknownError } from '../errors-DL-zTrrF.cjs';
|
|
5
5
|
import '../lazy-builder-eYZzLEL1.cjs';
|
|
6
6
|
import '../predicate-BmhBSPCH.cjs';
|
|
7
|
-
import '../strategy-
|
|
7
|
+
import '../strategy-C5ol6NdV.cjs';
|
|
8
8
|
import '../strategy-BSxFXGzb.cjs';
|
|
9
|
-
import '../index-
|
|
10
|
-
import '../index-
|
|
9
|
+
import '../index-BMmajblo.cjs';
|
|
10
|
+
import '../index-BM7O48Ur.cjs';
|
|
11
11
|
import '@noy-db/attestation';
|
|
12
12
|
|
|
13
13
|
interface RunResult {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export { w as withDerivation } from '../with-
|
|
2
|
-
import {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
1
|
+
export { w as withDerivation, a as withRollup } from '../with-rollup-_TyBzz3T.js';
|
|
2
|
+
import { av as DerivationStrategy, aw as DerivationContext } from '../types-CraiZOyO.js';
|
|
3
|
+
export { ax as ArrayOutputSpec, ay as DerivationRegistry, az as DerivationStrategyHandle, aA as DerivedFromMeta, aB as OutputSpec, aC as RecordOutputSpec } from '../types-CraiZOyO.js';
|
|
4
|
+
export { i as DerivationCapExceededError, j as DerivationCycleError, k as DerivationDepthError, l as DerivationOutputShapeError, m as DerivationOutputUnknownError } from '../errors-DL-zTrrF.js';
|
|
5
5
|
import '../lazy-builder-ChSqcF5t.js';
|
|
6
6
|
import '../predicate-BmhBSPCH.js';
|
|
7
|
-
import '../strategy-
|
|
7
|
+
import '../strategy-BDxQnnTX.js';
|
|
8
8
|
import '../strategy-BSxFXGzb.js';
|
|
9
|
-
import '../index-
|
|
10
|
-
import '../index-
|
|
9
|
+
import '../index-BMmajblo.js';
|
|
10
|
+
import '../index-BelbyUwz.js';
|
|
11
11
|
import '@noy-db/attestation';
|
|
12
12
|
|
|
13
13
|
interface RunResult {
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
-
withDerivation
|
|
3
|
-
|
|
2
|
+
withDerivation,
|
|
3
|
+
withRollup
|
|
4
|
+
} from "../chunk-CYNTFU2D.js";
|
|
4
5
|
import {
|
|
5
6
|
DerivationRegistry
|
|
6
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-6OSOE6BY.js";
|
|
7
8
|
import {
|
|
8
9
|
DerivationExecutor
|
|
9
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-7HD67R6U.js";
|
|
10
11
|
import {
|
|
11
12
|
DerivationCapExceededError,
|
|
12
13
|
DerivationCycleError,
|
|
13
14
|
DerivationDepthError,
|
|
14
15
|
DerivationOutputShapeError,
|
|
15
16
|
DerivationOutputUnknownError
|
|
16
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-DJF3FXW5.js";
|
|
17
18
|
export {
|
|
18
19
|
DerivationCapExceededError,
|
|
19
20
|
DerivationCycleError,
|
|
@@ -22,6 +23,7 @@ export {
|
|
|
22
23
|
DerivationOutputShapeError,
|
|
23
24
|
DerivationOutputUnknownError,
|
|
24
25
|
DerivationRegistry,
|
|
25
|
-
withDerivation
|
|
26
|
+
withDerivation,
|
|
27
|
+
withRollup
|
|
26
28
|
};
|
|
27
29
|
//# sourceMappingURL=index.js.map
|
|
@@ -398,6 +398,21 @@ declare class FieldFrozenError extends NoydbError {
|
|
|
398
398
|
readonly fields: readonly string[];
|
|
399
399
|
constructor(collection: string, id: string, fields: readonly string[]);
|
|
400
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* Thrown by a `transitionGuard` when a write moves a state field along an
|
|
403
|
+
* arc that the declared transition graph does not allow — either an
|
|
404
|
+
* update `from → to` that is not a listed edge, or an insert whose
|
|
405
|
+
* initial state is not in the allowed `initial` set (reported with
|
|
406
|
+
* `from: '(none)'`). Override via an amendment transaction by an
|
|
407
|
+
* authorized role, like any guard.
|
|
408
|
+
*/
|
|
409
|
+
declare class IllegalTransitionError extends NoydbError {
|
|
410
|
+
readonly collection: string;
|
|
411
|
+
readonly id: string;
|
|
412
|
+
readonly from: string;
|
|
413
|
+
readonly to: string;
|
|
414
|
+
constructor(collection: string, id: string, from: string, to: string);
|
|
415
|
+
}
|
|
401
416
|
/**
|
|
402
417
|
* Thrown by an amendment invariant when the proposed change-set violates
|
|
403
418
|
* the declared business rule (e.g. disbursement total not preserved).
|
|
@@ -1346,6 +1361,19 @@ declare class ShardProvisioningError extends NoydbError {
|
|
|
1346
1361
|
readonly vaultId: string;
|
|
1347
1362
|
constructor(vaultId: string, partitionKey: string);
|
|
1348
1363
|
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Thrown by `VaultGroup.createShard` when `sharding.regionOf` resolves a
|
|
1366
|
+
* required region that doesn't match the placement backend's
|
|
1367
|
+
* `capabilities.region` — the shard would land on a non-compliant backend
|
|
1368
|
+
* (data-residency violation, #271). Raised BEFORE provisioning, so no
|
|
1369
|
+
* vault is created.
|
|
1370
|
+
*/
|
|
1371
|
+
declare class DataResidencyError extends NoydbError {
|
|
1372
|
+
readonly vaultId: string;
|
|
1373
|
+
readonly requiredRegion: string;
|
|
1374
|
+
readonly backendRegion: string | undefined;
|
|
1375
|
+
constructor(vaultId: string, requiredRegion: string, backendRegion: string | undefined);
|
|
1376
|
+
}
|
|
1349
1377
|
/** Thrown when a VaultGroup references a template name that was never registered. */
|
|
1350
1378
|
declare class VaultTemplateNotFoundError extends NoydbError {
|
|
1351
1379
|
readonly templateName: string;
|
|
@@ -1416,4 +1444,4 @@ declare class RecordCekNotFoundError extends NoydbError {
|
|
|
1416
1444
|
constructor(collection: string, id: string);
|
|
1417
1445
|
}
|
|
1418
1446
|
|
|
1419
|
-
export {
|
|
1447
|
+
export { ExportCapabilityError as $, AmendmentForbiddenError as A, BackupCorruptedError as B, MaterializedViewCycleError as C, DictKeyInUseError as D, MaterializedViewSourceUnknownError as E, FieldFrozenError as F, MaterializedViewTooLargeError as G, AlreadyElevatedError as H, IllegalTransitionError as I, ConflictError as J, CrossJoinSourceUnknownError as K, LocaleNotSpecifiedError as L, MissingTranslationError as M, NoydbError as N, OverlayBaseIsVirtualError as O, PartitionExtractionError as P, CrossJoinTooLargeError as Q, ReservedCollectionNameError as R, ScriptViolationError as S, TranslatorNotConfiguredError as T, UnknownDictCodeError as U, DanglingReferenceError as V, DataResidencyError as W, DecryptionError as X, DelegationTargetMissingError as Y, DirectoryDisabledError as Z, ElevationExpiredError as _, DictKeyMissingError as a, FilenameSanitizationError as a0, ForgetStrategyNotConfiguredError as a1, GroupCardinalityError as a2, ImportCapabilityError as a3, IndexRequiredError as a4, IndexWriteFailureError as a5, InvalidKeyError as a6, JoinTooLargeError as a7, KeyringCorruptError as a8, KeyringExpiredError as a9, TierDemoteDeniedError as aA, TierNotGrantedError as aB, UniqueConstraintError as aC, UnknownShardError as aD, UnsupportedIndexOptionError as aE, ValidationError as aF, VaultTemplateNotFoundError as aG, LedgerContentionError as aa, MigrationRequiredError as ab, NetworkError as ac, NoAccessError as ad, NonAdditiveSchemaChangeError as ae, NotFoundError as af, NumberingUncertaintyError as ag, PathEscapeError as ah, PeriodClosedError as ai, PermissionDeniedError as aj, PrivilegeEscalationError as ak, QuiesceTimeoutError as al, ReadOnlyAtInstantError as am, ReadOnlyError as an, ReadOnlyFrameError as ao, RecordCekNotFoundError as ap, ReservedVaultNameError as aq, SchemaFenceError as ar, SchemaLockedError as as, SchemaUpdateError as at, SchemaValidationError as au, SequenceContentionError as av, SequenceOfflineError as aw, ShardProvisioningError as ax, StoreCapabilityError as ay, TamperedError as az, StaticDictReadonlyError as b, SessionExpiredError as c, SessionNotFoundError as d, SessionPolicyError as e, InvariantError as f, RecordLockedError as g, SnapshotNotFoundError as h, DerivationCapExceededError as i, DerivationCycleError as j, DerivationDepthError as k, DerivationOutputShapeError as l, DerivationOutputUnknownError as m, OverlayCollectionUnavailableError as n, OverlayIdMismatchError as o, OverlayNameCollisionError as p, SealedRecordExpiredError as q, SealedRecordMismatchError as r, AttestationError as s, AdoptionStateError as t, BackupLedgerError as u, BundleIntegrityError as v, BundleSealMismatchError as w, BundleVersionConflictError as x, TransferSealError as y, MaterializedViewConfigError as z };
|
|
@@ -398,6 +398,21 @@ declare class FieldFrozenError extends NoydbError {
|
|
|
398
398
|
readonly fields: readonly string[];
|
|
399
399
|
constructor(collection: string, id: string, fields: readonly string[]);
|
|
400
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* Thrown by a `transitionGuard` when a write moves a state field along an
|
|
403
|
+
* arc that the declared transition graph does not allow — either an
|
|
404
|
+
* update `from → to` that is not a listed edge, or an insert whose
|
|
405
|
+
* initial state is not in the allowed `initial` set (reported with
|
|
406
|
+
* `from: '(none)'`). Override via an amendment transaction by an
|
|
407
|
+
* authorized role, like any guard.
|
|
408
|
+
*/
|
|
409
|
+
declare class IllegalTransitionError extends NoydbError {
|
|
410
|
+
readonly collection: string;
|
|
411
|
+
readonly id: string;
|
|
412
|
+
readonly from: string;
|
|
413
|
+
readonly to: string;
|
|
414
|
+
constructor(collection: string, id: string, from: string, to: string);
|
|
415
|
+
}
|
|
401
416
|
/**
|
|
402
417
|
* Thrown by an amendment invariant when the proposed change-set violates
|
|
403
418
|
* the declared business rule (e.g. disbursement total not preserved).
|
|
@@ -1346,6 +1361,19 @@ declare class ShardProvisioningError extends NoydbError {
|
|
|
1346
1361
|
readonly vaultId: string;
|
|
1347
1362
|
constructor(vaultId: string, partitionKey: string);
|
|
1348
1363
|
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Thrown by `VaultGroup.createShard` when `sharding.regionOf` resolves a
|
|
1366
|
+
* required region that doesn't match the placement backend's
|
|
1367
|
+
* `capabilities.region` — the shard would land on a non-compliant backend
|
|
1368
|
+
* (data-residency violation, #271). Raised BEFORE provisioning, so no
|
|
1369
|
+
* vault is created.
|
|
1370
|
+
*/
|
|
1371
|
+
declare class DataResidencyError extends NoydbError {
|
|
1372
|
+
readonly vaultId: string;
|
|
1373
|
+
readonly requiredRegion: string;
|
|
1374
|
+
readonly backendRegion: string | undefined;
|
|
1375
|
+
constructor(vaultId: string, requiredRegion: string, backendRegion: string | undefined);
|
|
1376
|
+
}
|
|
1349
1377
|
/** Thrown when a VaultGroup references a template name that was never registered. */
|
|
1350
1378
|
declare class VaultTemplateNotFoundError extends NoydbError {
|
|
1351
1379
|
readonly templateName: string;
|
|
@@ -1416,4 +1444,4 @@ declare class RecordCekNotFoundError extends NoydbError {
|
|
|
1416
1444
|
constructor(collection: string, id: string);
|
|
1417
1445
|
}
|
|
1418
1446
|
|
|
1419
|
-
export {
|
|
1447
|
+
export { ExportCapabilityError as $, AmendmentForbiddenError as A, BackupCorruptedError as B, MaterializedViewCycleError as C, DictKeyInUseError as D, MaterializedViewSourceUnknownError as E, FieldFrozenError as F, MaterializedViewTooLargeError as G, AlreadyElevatedError as H, IllegalTransitionError as I, ConflictError as J, CrossJoinSourceUnknownError as K, LocaleNotSpecifiedError as L, MissingTranslationError as M, NoydbError as N, OverlayBaseIsVirtualError as O, PartitionExtractionError as P, CrossJoinTooLargeError as Q, ReservedCollectionNameError as R, ScriptViolationError as S, TranslatorNotConfiguredError as T, UnknownDictCodeError as U, DanglingReferenceError as V, DataResidencyError as W, DecryptionError as X, DelegationTargetMissingError as Y, DirectoryDisabledError as Z, ElevationExpiredError as _, DictKeyMissingError as a, FilenameSanitizationError as a0, ForgetStrategyNotConfiguredError as a1, GroupCardinalityError as a2, ImportCapabilityError as a3, IndexRequiredError as a4, IndexWriteFailureError as a5, InvalidKeyError as a6, JoinTooLargeError as a7, KeyringCorruptError as a8, KeyringExpiredError as a9, TierDemoteDeniedError as aA, TierNotGrantedError as aB, UniqueConstraintError as aC, UnknownShardError as aD, UnsupportedIndexOptionError as aE, ValidationError as aF, VaultTemplateNotFoundError as aG, LedgerContentionError as aa, MigrationRequiredError as ab, NetworkError as ac, NoAccessError as ad, NonAdditiveSchemaChangeError as ae, NotFoundError as af, NumberingUncertaintyError as ag, PathEscapeError as ah, PeriodClosedError as ai, PermissionDeniedError as aj, PrivilegeEscalationError as ak, QuiesceTimeoutError as al, ReadOnlyAtInstantError as am, ReadOnlyError as an, ReadOnlyFrameError as ao, RecordCekNotFoundError as ap, ReservedVaultNameError as aq, SchemaFenceError as ar, SchemaLockedError as as, SchemaUpdateError as at, SchemaValidationError as au, SequenceContentionError as av, SequenceOfflineError as aw, ShardProvisioningError as ax, StoreCapabilityError as ay, TamperedError as az, StaticDictReadonlyError as b, SessionExpiredError as c, SessionNotFoundError as d, SessionPolicyError as e, InvariantError as f, RecordLockedError as g, SnapshotNotFoundError as h, DerivationCapExceededError as i, DerivationCycleError as j, DerivationDepthError as k, DerivationOutputShapeError as l, DerivationOutputUnknownError as m, OverlayCollectionUnavailableError as n, OverlayIdMismatchError as o, OverlayNameCollisionError as p, SealedRecordExpiredError as q, SealedRecordMismatchError as r, AttestationError as s, AdoptionStateError as t, BackupLedgerError as u, BundleIntegrityError as v, BundleSealMismatchError as w, BundleVersionConflictError as x, TransferSealError as y, MaterializedViewConfigError as z };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MaterializedViewExecutor
|
|
3
|
+
} from "./chunk-6KESZO5D.js";
|
|
4
|
+
import "./chunk-GYAWXHFO.js";
|
|
5
|
+
import "./chunk-OBMYMKGO.js";
|
|
6
|
+
import "./chunk-LSIIPKYT.js";
|
|
7
|
+
import "./chunk-VEIVAYJ7.js";
|
|
8
|
+
import "./chunk-DJF3FXW5.js";
|
|
9
|
+
export {
|
|
10
|
+
MaterializedViewExecutor
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=executor-44R5CUS2.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NOYDB_FORMAT_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SHIUFIPW.js";
|
|
4
4
|
|
|
5
5
|
// src/derivations/fanout-sidecar.ts
|
|
6
6
|
function recordId(source, sourceId, outputKey) {
|
|
@@ -48,4 +48,4 @@ export {
|
|
|
48
48
|
loadFanoutSidecar,
|
|
49
49
|
saveFanoutSidecar
|
|
50
50
|
};
|
|
51
|
-
//# sourceMappingURL=fanout-sidecar-
|
|
51
|
+
//# sourceMappingURL=fanout-sidecar-DCQWJQ6S.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/forget/index.ts","../../src/forget/strategy.ts","../../src/forget/subject-index.ts"],"sourcesContent":["/**\n * `@noy-db/hub/forget` — GDPR right-to-erasure via per-record CEK crypto-shred\n * (#304). Import `withForgetCascade` and pass it to `createNoydb`:\n *\n * ```ts\n * import { createNoydb } from '@noy-db/hub'\n * import { withForgetCascade } from '@noy-db/hub/forget'\n *\n * const db = createNoydb({\n * secret, user,\n * historyStrategy: withHistory(), // ledger for the erasure proof\n * forgetStrategy: withForgetCascade({ subjects: { invoices: 'buyerId' } }),\n * })\n * const result = await db.vault('main').forget('buyer-123')\n * ```\n *\n * The erasure flow (`vault.forget`), the subject-index maintenance hooks, and\n * the tombstone rewrite live in core (`vault.ts` / `collection.ts`) because\n * they touch the write path directly; this subpath only exposes the\n * declaration factory + result types.\n *\n * @module\n */\nexport {\n withForgetCascade,\n NO_FORGET,\n} from './strategy.js'\nexport type {\n SubjectDeclaration,\n ForgetStrategy,\n ForgetResult,\n} from './strategy.js'\nexport type { SubjectRef } from './subject-index.js'\nexport { SUBJECT_INDEX_COLLECTION } from './subject-index.js'\n","/**\n * `withForgetCascade` — declaration surface for GDPR right-to-erasure via\n * per-record CEK crypto-shred (#304, step 2 of the CEK security epic).\n *\n * This file holds only the *declaration* shape and the disabled sentinel.\n * The actual erasure machinery lives in:\n * - `subject-index.ts` — the encrypted `_subject_index` reserved collection\n * - `vault.ts` `forget()` — the per-record tombstone + ledger flow\n * - `collection.ts` `_writeTombstone` — the envelope rewrite\n *\n * A `ForgetStrategy` declares which collections carry erasable subject data\n * and the (dotted-path) field on each record that names the data subject.\n * Declaring a collection here ALSO forces `perRecordKeys: true` for it (a\n * shred can only erase a record whose body is keyed off a per-record CEK),\n * so adopters opt into the CEK foundation transitively.\n *\n * @module\n */\nimport type { LedgerEntry } from '../history/ledger/entry.js'\n\n/**\n * User-supplied declaration passed to {@link withForgetCascade}. Maps a\n * collection name to the record field (dotted path supported, e.g.\n * `'billing.buyerId'`) that identifies the data subject for erasure.\n *\n * ```ts\n * withForgetCascade({ subjects: { invoices: 'buyerId', contacts: 'id' } })\n * ```\n */\nexport interface SubjectDeclaration {\n readonly subjects: Record<string, string>\n}\n\n/**\n * Resolved forget strategy threaded through Noydb → every Vault. Carries\n * the same `subjects` map the user declared. `NO_FORGET` (empty map) is the\n * off-by-default sentinel; `vault.forget()` throws\n * `ForgetStrategyNotConfiguredError` when the map is empty.\n */\nexport interface ForgetStrategy {\n /** Collection → subject-field (dotted path). Empty under `NO_FORGET`. */\n readonly subjects: Readonly<Record<string, string>>\n}\n\n/**\n * Disabled sentinel — no collections declare a subject field. `vault.forget()`\n * refuses with `ForgetStrategyNotConfiguredError`; no write hooks register; no\n * collection is forced into `perRecordKeys`. Non-adopters pay nothing.\n */\nexport const NO_FORGET: ForgetStrategy = { subjects: {} }\n\n/**\n * Declare GDPR crypto-shred for one or more collections.\n *\n * Each declared collection is forced to `perRecordKeys: true` (a shred can\n * only guarantee erasure of a record whose body is keyed off a per-record\n * CEK). On write, Noydb extracts `record[subjectField]` and maintains an\n * encrypted `_subject_index` mapping `subject → [{collection, id}]`, so\n * `vault.forget(subjectId)` can find every record for a subject and rewrite\n * each to a tombstone (body + history permanently undecryptable) while the\n * collection DEK and every other record stay intact.\n *\n * @example\n * ```ts\n * createNoydb({\n * secret, user,\n * forgetStrategy: withForgetCascade({ subjects: { invoices: 'buyerId' } }),\n * })\n * const result = await vault.forget('buyer-123')\n * // → { subject, recordsShredded, historyVersionsShredded, collections, … }\n * ```\n */\nexport function withForgetCascade(opts: SubjectDeclaration): ForgetStrategy {\n return { subjects: { ...opts.subjects } }\n}\n\n/**\n * The outcome of a `vault.forget(subjectId)` call.\n *\n * `unmigratedRecords` lists `collection:id` pairs that were tombstoned but\n * whose body had NOT been migrated to a per-record CEK at shred time (legacy\n * body still under the shared collection DEK). Those records are tombstoned\n * (live envelope + history stripped) but their pre-shred ciphertext, if it\n * leaked into a backup before migration, remains decryptable under the\n * collection DEK — so erasure-completeness is NOT guaranteed for them. Run\n * the per-record-CEK migration pass, then re-forget, to close the gap.\n *\n * Blob attachments (#365): a shredded record's **erasable** blobs (on a\n * `perRecordKeys` collection) are crypto-shredded inline — `blobsShredded`\n * counts those taken to refCount 0 (BlobObject deleted → chunks permanently\n * undecryptable), `blobsRetainedShared` counts those still referenced by\n * another record (shared content legitimately persists for its other owner).\n * `blobResidueCollections` now lists only collections with blobs that could\n * NOT be crypto-shredded: **legacy** blobs (no per-blob `_cek`, chunks under\n * the shared `_blob` DEK — migrate them), or a session without the blob\n * subsystem loaded. An all-erasable subject yields an empty residue list.\n */\nexport interface ForgetResult {\n /** The subject id passed to `forget()`. Echoed for caller convenience. */\n readonly subject: string\n /** Count of live records rewritten to a tombstone. */\n readonly recordsShredded: number\n /** Count of `_history` envelopes tombstoned across all shredded records. */\n readonly historyVersionsShredded: number\n /** Distinct collections that had at least one record shredded. */\n readonly collections: readonly string[]\n /** `collection:id` pairs shredded while still un-migrated (see type docs). */\n readonly unmigratedRecords: readonly string[]\n /** Count of erasable blobs crypto-shredded (refCount → 0, BlobObject deleted). */\n readonly blobsShredded: number\n /** Count of erasable blobs retained because still referenced elsewhere (shared). */\n readonly blobsRetainedShared: number\n /** Collections with blobs that could NOT be crypto-shredded — legacy (no `_cek`) or blobs disabled (see type docs). */\n readonly blobResidueCollections: readonly string[]\n /** The single `op:'forget'` ledger entry appended for this erasure. */\n readonly ledgerEntry: LedgerEntry\n}\n","/**\n * The encrypted subject index (#304).\n *\n * GDPR crypto-shred needs to answer \"which records belong to data subject\n * X?\" portably (the index must travel with the vault/bundle) WITHOUT leaking\n * subject-equivalence to the store. The rejected alternative — an unencrypted\n * subject tag in envelope metadata — would let anyone with store access see\n * which records share a subject. Instead we keep a reserved `_subject_index`\n * collection, encrypted under its OWN DEK (`getDEK('_subject_index')`):\n *\n * - record id = `sha256Hex(subjectId)` — the raw subject id never appears\n * as a key, so the store can't correlate index entries to a known subject.\n * - record body = AES-GCM(JSON `[{ collection, id }]`) under the index DEK.\n *\n * ## Concurrency (RISK #3 — known v1 limitation)\n *\n * `addSubjectRef` / `removeSubjectRef` are read-modify-write with no CAS. The\n * design assumes a SINGLE WRITER (the noy-db single-process write model). Two\n * concurrent writers racing on the SAME subject can lose an entry (last-write\n * wins on the ref list). This is documented, not fixed in v1:\n * `rebuildSubjectIndex` performs a full scan to recover a correct index from\n * the canonical records, so a lost ref is recoverable. A CAS-backed index is\n * deferred to a later slice.\n *\n * @module\n */\nimport { encrypt, decrypt } from '../crypto.js'\nimport type { NoydbStore, EncryptedEnvelope } from '../types.js'\nimport { NOYDB_FORMAT_VERSION } from '../types.js'\n\n/** Reserved collection holding the encrypted subject → records index. */\nexport const SUBJECT_INDEX_COLLECTION = '_subject_index'\n\n/** A single record reference held in a subject's index entry. */\nexport interface SubjectRef {\n readonly collection: string\n readonly id: string\n}\n\ntype GetDEK = (collectionName: string) => Promise<CryptoKey>\n\n/** SHA-256 hex of a UTF-8 string. The subject-index record key derivation. */\nasync function sha256HexString(input: string): Promise<string> {\n const bytes = new TextEncoder().encode(input)\n const digest = await globalThis.crypto.subtle.digest('SHA-256', bytes)\n return Array.from(new Uint8Array(digest))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/** Stable subject-index record id for a subject id. */\nexport async function subjectKey(subjectId: string): Promise<string> {\n return sha256HexString(subjectId)\n}\n\n/** Read + decrypt the ref list for a subject. Returns `[]` when absent. */\nasync function readRefs(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n key: string,\n): Promise<SubjectRef[]> {\n const env = await adapter.get(vault, SUBJECT_INDEX_COLLECTION, key)\n if (!env || !env._data) return []\n if (!encrypted) return JSON.parse(env._data) as SubjectRef[]\n const dek = await getDEK(SUBJECT_INDEX_COLLECTION)\n const json = await decrypt(env._iv, env._data, dek)\n return JSON.parse(json) as SubjectRef[]\n}\n\n/** Encrypt + write a ref list for a subject under its derived key. */\nasync function writeRefs(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n key: string,\n refs: SubjectRef[],\n): Promise<void> {\n const json = JSON.stringify(refs)\n let env: EncryptedEnvelope\n if (!encrypted) {\n env = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: new Date().toISOString(), _iv: '', _data: json }\n } else {\n const dek = await getDEK(SUBJECT_INDEX_COLLECTION)\n const { iv, data } = await encrypt(json, dek)\n env = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: new Date().toISOString(), _iv: iv, _data: data }\n }\n await adapter.put(vault, SUBJECT_INDEX_COLLECTION, key, env)\n}\n\n/**\n * Add a `{ collection, id }` ref to a subject's index entry (idempotent —\n * a duplicate ref is not appended). Read-modify-write; see the concurrency\n * note in the module docstring.\n */\nexport async function addSubjectRef(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjectId: string,\n ref: SubjectRef,\n): Promise<void> {\n const key = await subjectKey(subjectId)\n const refs = await readRefs(adapter, vault, getDEK, encrypted, key)\n if (refs.some((r) => r.collection === ref.collection && r.id === ref.id)) return\n refs.push(ref)\n await writeRefs(adapter, vault, getDEK, encrypted, key, refs)\n}\n\n/**\n * Remove a `{ collection, id }` ref from a subject's index entry. When the\n * last ref is removed the (now empty) entry is deleted so the store holds no\n * residual key for an erased subject.\n */\nexport async function removeSubjectRef(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjectId: string,\n ref: SubjectRef,\n): Promise<void> {\n const key = await subjectKey(subjectId)\n const refs = await readRefs(adapter, vault, getDEK, encrypted, key)\n const next = refs.filter((r) => !(r.collection === ref.collection && r.id === ref.id))\n if (next.length === refs.length) return\n if (next.length === 0) {\n await adapter.delete(vault, SUBJECT_INDEX_COLLECTION, key)\n return\n }\n await writeRefs(adapter, vault, getDEK, encrypted, key, next)\n}\n\n/** Look up every record ref for a subject. Returns `[]` when none exist. */\nexport async function lookupSubject(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjectId: string,\n): Promise<SubjectRef[]> {\n const key = await subjectKey(subjectId)\n return readRefs(adapter, vault, getDEK, encrypted, key)\n}\n\n/**\n * Rebuild the entire subject index from the canonical records (the recovery\n * path for the documented read-modify-write race). Scans each declared\n * collection, reads `record[subjectField]` (dotted path), and rewrites the\n * index from scratch. Tombstoned (already-shredded) records contribute no\n * ref — their body is gone, so they cannot be re-indexed.\n *\n * `decodeRecord` decrypts an envelope to a plain object (or returns null for\n * a tombstone / unreadable record); supplied by the caller so this module\n * stays free of Collection internals.\n */\nexport async function rebuildSubjectIndex(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjects: Readonly<Record<string, string>>,\n decodeRecord: (collection: string, id: string, env: EncryptedEnvelope) => Promise<Record<string, unknown> | null>,\n): Promise<number> {\n // Drop every existing index entry first so removed refs don't linger.\n const existing = await adapter.list(vault, SUBJECT_INDEX_COLLECTION)\n for (const k of existing) {\n await adapter.delete(vault, SUBJECT_INDEX_COLLECTION, k)\n }\n\n // subjectId → refs, accumulated across all declared collections.\n const bySubject = new Map<string, SubjectRef[]>()\n for (const [collection, field] of Object.entries(subjects)) {\n const ids = await adapter.list(vault, collection)\n for (const id of ids) {\n if (id.startsWith('_')) continue\n const env = await adapter.get(vault, collection, id)\n if (!env || !env._data) continue // missing or tombstone\n const record = await decodeRecord(collection, id, env)\n if (record === null) continue\n const subjectValue = readDottedPath(record, field)\n if (subjectValue === undefined || subjectValue === null) continue\n const subjectId = coerceSubjectId(subjectValue)\n const list = bySubject.get(subjectId) ?? []\n list.push({ collection, id })\n bySubject.set(subjectId, list)\n }\n }\n\n let entries = 0\n for (const [subjectId, refs] of bySubject) {\n const key = await subjectKey(subjectId)\n await writeRefs(adapter, vault, getDEK, encrypted, key, refs)\n entries++\n }\n return entries\n}\n\n/**\n * Coerce a read subject-field value to a stable string id. Primitives use\n * their natural string form; objects/arrays are JSON-stringified so structural\n * subjects still get a deterministic key (avoids the `[object Object]` trap).\n */\nexport function coerceSubjectId(value: unknown): string {\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value)\n }\n return JSON.stringify(value)\n}\n\n/** Read a (possibly dotted) field path from a plain record. */\nexport function readDottedPath(record: Record<string, unknown>, field: string): unknown {\n if (!field.includes('.')) return record[field]\n let cursor: unknown = record\n for (const segment of field.split('.')) {\n if (cursor === null || cursor === undefined) return undefined\n cursor = (cursor as Record<string, unknown>)[segment]\n }\n return cursor\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACiDO,IAAM,YAA4B,EAAE,UAAU,CAAC,EAAE;AAuBjD,SAAS,kBAAkB,MAA0C;AAC1E,SAAO,EAAE,UAAU,EAAE,GAAG,KAAK,SAAS,EAAE;AAC1C;;;AC3CO,IAAM,2BAA2B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/forget/index.ts","../../src/forget/strategy.ts","../../src/forget/subject-index.ts"],"sourcesContent":["/**\n * `@noy-db/hub/forget` — GDPR right-to-erasure via per-record CEK crypto-shred\n * (#304). Import `withForgetCascade` and pass it to `createNoydb`:\n *\n * ```ts\n * import { createNoydb } from '@noy-db/hub'\n * import { withForgetCascade } from '@noy-db/hub/forget'\n *\n * const db = createNoydb({\n * secret, user,\n * historyStrategy: withHistory(), // ledger for the erasure proof\n * forgetStrategy: withForgetCascade({ subjects: { invoices: 'buyerId' } }),\n * })\n * const result = await db.vault('main').forget('buyer-123')\n * ```\n *\n * The erasure flow (`vault.forget`), the subject-index maintenance hooks, and\n * the tombstone rewrite live in core (`vault.ts` / `collection.ts`) because\n * they touch the write path directly; this subpath only exposes the\n * declaration factory + result types.\n *\n * @module\n */\nexport {\n withForgetCascade,\n NO_FORGET,\n} from './strategy.js'\nexport type {\n SubjectDeclaration,\n ForgetStrategy,\n ForgetResult,\n} from './strategy.js'\nexport type { SubjectRef } from './subject-index.js'\nexport { SUBJECT_INDEX_COLLECTION } from './subject-index.js'\n","/**\n * `withForgetCascade` — declaration surface for GDPR right-to-erasure via\n * per-record CEK crypto-shred (#304, step 2 of the CEK security epic).\n *\n * This file holds only the *declaration* shape and the disabled sentinel.\n * The actual erasure machinery lives in:\n * - `subject-index.ts` — the encrypted `_subject_index` reserved collection\n * - `vault.ts` `forget()` — the per-record tombstone + ledger flow\n * - `collection.ts` `_writeTombstone` — the envelope rewrite\n *\n * A `ForgetStrategy` declares which collections carry erasable subject data\n * and the (dotted-path) field on each record that names the data subject.\n * Declaring a collection here ALSO forces `perRecordKeys: true` for it (a\n * shred can only erase a record whose body is keyed off a per-record CEK),\n * so adopters opt into the CEK foundation transitively.\n *\n * @module\n */\nimport type { LedgerEntry } from '../history/ledger/entry.js'\n\n/**\n * User-supplied declaration passed to {@link withForgetCascade}. Maps a\n * collection name to the record field (dotted path supported, e.g.\n * `'billing.buyerId'`) that identifies the data subject for erasure.\n *\n * ```ts\n * withForgetCascade({ subjects: { invoices: 'buyerId', contacts: 'id' } })\n * ```\n */\nexport interface SubjectDeclaration {\n readonly subjects: Record<string, string>\n}\n\n/**\n * Resolved forget strategy threaded through Noydb → every Vault. Carries\n * the same `subjects` map the user declared. `NO_FORGET` (empty map) is the\n * off-by-default sentinel; `vault.forget()` throws\n * `ForgetStrategyNotConfiguredError` when the map is empty.\n */\nexport interface ForgetStrategy {\n /** Collection → subject-field (dotted path). Empty under `NO_FORGET`. */\n readonly subjects: Readonly<Record<string, string>>\n}\n\n/**\n * Disabled sentinel — no collections declare a subject field. `vault.forget()`\n * refuses with `ForgetStrategyNotConfiguredError`; no write hooks register; no\n * collection is forced into `perRecordKeys`. Non-adopters pay nothing.\n */\nexport const NO_FORGET: ForgetStrategy = { subjects: {} }\n\n/**\n * Declare GDPR crypto-shred for one or more collections.\n *\n * Each declared collection is forced to `perRecordKeys: true` (a shred can\n * only guarantee erasure of a record whose body is keyed off a per-record\n * CEK). On write, Noydb extracts `record[subjectField]` and maintains an\n * encrypted `_subject_index` mapping `subject → [{collection, id}]`, so\n * `vault.forget(subjectId)` can find every record for a subject and rewrite\n * each to a tombstone (body + history permanently undecryptable) while the\n * collection DEK and every other record stay intact.\n *\n * @example\n * ```ts\n * createNoydb({\n * secret, user,\n * forgetStrategy: withForgetCascade({ subjects: { invoices: 'buyerId' } }),\n * })\n * const result = await vault.forget('buyer-123')\n * // → { subject, recordsShredded, historyVersionsShredded, collections, … }\n * ```\n */\nexport function withForgetCascade(opts: SubjectDeclaration): ForgetStrategy {\n return { subjects: { ...opts.subjects } }\n}\n\n/**\n * The outcome of a `vault.forget(subjectId)` call.\n *\n * `unmigratedRecords` lists `collection:id` pairs that were tombstoned but\n * whose body had NOT been migrated to a per-record CEK at shred time (legacy\n * body still under the shared collection DEK). Those records are tombstoned\n * (live envelope + history stripped) but their pre-shred ciphertext, if it\n * leaked into a backup before migration, remains decryptable under the\n * collection DEK — so erasure-completeness is NOT guaranteed for them. Run\n * the per-record-CEK migration pass, then re-forget, to close the gap.\n *\n * Blob attachments (#365): a shredded record's **erasable** blobs (on a\n * `perRecordKeys` collection) are crypto-shredded inline — `blobsShredded`\n * counts those taken to refCount 0 (BlobObject deleted → chunks permanently\n * undecryptable), `blobsRetainedShared` counts those still referenced by\n * another record (shared content legitimately persists for its other owner).\n * `blobResidueCollections` now lists only collections with blobs that could\n * NOT be crypto-shredded: **legacy** blobs (no per-blob `_cek`, chunks under\n * the shared `_blob` DEK — migrate them), or a session without the blob\n * subsystem loaded. An all-erasable subject yields an empty residue list.\n */\nexport interface ForgetResult {\n /** The subject id passed to `forget()`. Echoed for caller convenience. */\n readonly subject: string\n /** Count of live records rewritten to a tombstone. */\n readonly recordsShredded: number\n /** Count of `_history` envelopes tombstoned across all shredded records. */\n readonly historyVersionsShredded: number\n /** Distinct collections that had at least one record shredded. */\n readonly collections: readonly string[]\n /** `collection:id` pairs shredded while still un-migrated (see type docs). */\n readonly unmigratedRecords: readonly string[]\n /** Count of erasable blobs crypto-shredded (refCount → 0, BlobObject deleted). */\n readonly blobsShredded: number\n /** Count of erasable blobs retained because still referenced elsewhere (shared). */\n readonly blobsRetainedShared: number\n /** Collections with blobs that could NOT be crypto-shredded — legacy (no `_cek`) or blobs disabled (see type docs). */\n readonly blobResidueCollections: readonly string[]\n /**\n * Count of persisted `_idx/<field>/<recordId>` index side-cars hard-deleted\n * across the shredded records (#401). These live under the retained\n * collection DEK, so crypto-shred alone would leave the indexed field VALUES\n * readable — `forget()` must delete them.\n */\n readonly indexPostingsPurged: number\n /**\n * `collection:id:field` entries whose persisted `_idx` side-car could NOT be\n * deleted (#401) — index residue that still leaks the indexed value under the\n * retained collection DEK. Non-empty means erasure is INCOMPLETE: retry, or\n * purge the side-car out of band.\n */\n readonly indexResidue: readonly string[]\n /** The single `op:'forget'` ledger entry appended for this erasure. */\n readonly ledgerEntry: LedgerEntry\n}\n","/**\n * The encrypted subject index (#304).\n *\n * GDPR crypto-shred needs to answer \"which records belong to data subject\n * X?\" portably (the index must travel with the vault/bundle) WITHOUT leaking\n * subject-equivalence to the store. The rejected alternative — an unencrypted\n * subject tag in envelope metadata — would let anyone with store access see\n * which records share a subject. Instead we keep a reserved `_subject_index`\n * collection, encrypted under its OWN DEK (`getDEK('_subject_index')`):\n *\n * - record id = `sha256Hex(subjectId)` — the raw subject id never appears\n * as a key, so the store can't correlate index entries to a known subject.\n * - record body = AES-GCM(JSON `[{ collection, id }]`) under the index DEK.\n *\n * ## Concurrency (RISK #3 — known v1 limitation)\n *\n * `addSubjectRef` / `removeSubjectRef` are read-modify-write with no CAS. The\n * design assumes a SINGLE WRITER (the noy-db single-process write model). Two\n * concurrent writers racing on the SAME subject can lose an entry (last-write\n * wins on the ref list). This is documented, not fixed in v1:\n * `rebuildSubjectIndex` performs a full scan to recover a correct index from\n * the canonical records, so a lost ref is recoverable. A CAS-backed index is\n * deferred to a later slice.\n *\n * @module\n */\nimport { encrypt, decrypt } from '../crypto.js'\nimport type { NoydbStore, EncryptedEnvelope } from '../types.js'\nimport { NOYDB_FORMAT_VERSION } from '../types.js'\n\n/** Reserved collection holding the encrypted subject → records index. */\nexport const SUBJECT_INDEX_COLLECTION = '_subject_index'\n\n/** A single record reference held in a subject's index entry. */\nexport interface SubjectRef {\n readonly collection: string\n readonly id: string\n}\n\ntype GetDEK = (collectionName: string) => Promise<CryptoKey>\n\n/** SHA-256 hex of a UTF-8 string. The subject-index record key derivation. */\nasync function sha256HexString(input: string): Promise<string> {\n const bytes = new TextEncoder().encode(input)\n const digest = await globalThis.crypto.subtle.digest('SHA-256', bytes)\n return Array.from(new Uint8Array(digest))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/** Stable subject-index record id for a subject id. */\nexport async function subjectKey(subjectId: string): Promise<string> {\n return sha256HexString(subjectId)\n}\n\n/** Read + decrypt the ref list for a subject. Returns `[]` when absent. */\nasync function readRefs(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n key: string,\n): Promise<SubjectRef[]> {\n const env = await adapter.get(vault, SUBJECT_INDEX_COLLECTION, key)\n if (!env || !env._data) return []\n if (!encrypted) return JSON.parse(env._data) as SubjectRef[]\n const dek = await getDEK(SUBJECT_INDEX_COLLECTION)\n const json = await decrypt(env._iv, env._data, dek)\n return JSON.parse(json) as SubjectRef[]\n}\n\n/** Encrypt + write a ref list for a subject under its derived key. */\nasync function writeRefs(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n key: string,\n refs: SubjectRef[],\n): Promise<void> {\n const json = JSON.stringify(refs)\n let env: EncryptedEnvelope\n if (!encrypted) {\n env = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: new Date().toISOString(), _iv: '', _data: json }\n } else {\n const dek = await getDEK(SUBJECT_INDEX_COLLECTION)\n const { iv, data } = await encrypt(json, dek)\n env = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: new Date().toISOString(), _iv: iv, _data: data }\n }\n await adapter.put(vault, SUBJECT_INDEX_COLLECTION, key, env)\n}\n\n/**\n * Add a `{ collection, id }` ref to a subject's index entry (idempotent —\n * a duplicate ref is not appended). Read-modify-write; see the concurrency\n * note in the module docstring.\n */\nexport async function addSubjectRef(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjectId: string,\n ref: SubjectRef,\n): Promise<void> {\n const key = await subjectKey(subjectId)\n const refs = await readRefs(adapter, vault, getDEK, encrypted, key)\n if (refs.some((r) => r.collection === ref.collection && r.id === ref.id)) return\n refs.push(ref)\n await writeRefs(adapter, vault, getDEK, encrypted, key, refs)\n}\n\n/**\n * Remove a `{ collection, id }` ref from a subject's index entry. When the\n * last ref is removed the (now empty) entry is deleted so the store holds no\n * residual key for an erased subject.\n */\nexport async function removeSubjectRef(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjectId: string,\n ref: SubjectRef,\n): Promise<void> {\n const key = await subjectKey(subjectId)\n const refs = await readRefs(adapter, vault, getDEK, encrypted, key)\n const next = refs.filter((r) => !(r.collection === ref.collection && r.id === ref.id))\n if (next.length === refs.length) return\n if (next.length === 0) {\n await adapter.delete(vault, SUBJECT_INDEX_COLLECTION, key)\n return\n }\n await writeRefs(adapter, vault, getDEK, encrypted, key, next)\n}\n\n/** Look up every record ref for a subject. Returns `[]` when none exist. */\nexport async function lookupSubject(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjectId: string,\n): Promise<SubjectRef[]> {\n const key = await subjectKey(subjectId)\n return readRefs(adapter, vault, getDEK, encrypted, key)\n}\n\n/**\n * Rebuild the entire subject index from the canonical records (the recovery\n * path for the documented read-modify-write race). Scans each declared\n * collection, reads `record[subjectField]` (dotted path), and rewrites the\n * index from scratch. Tombstoned (already-shredded) records contribute no\n * ref — their body is gone, so they cannot be re-indexed.\n *\n * `decodeRecord` decrypts an envelope to a plain object (or returns null for\n * a tombstone / unreadable record); supplied by the caller so this module\n * stays free of Collection internals.\n */\nexport async function rebuildSubjectIndex(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjects: Readonly<Record<string, string>>,\n decodeRecord: (collection: string, id: string, env: EncryptedEnvelope) => Promise<Record<string, unknown> | null>,\n): Promise<number> {\n // Drop every existing index entry first so removed refs don't linger.\n const existing = await adapter.list(vault, SUBJECT_INDEX_COLLECTION)\n for (const k of existing) {\n await adapter.delete(vault, SUBJECT_INDEX_COLLECTION, k)\n }\n\n // subjectId → refs, accumulated across all declared collections.\n const bySubject = new Map<string, SubjectRef[]>()\n for (const [collection, field] of Object.entries(subjects)) {\n const ids = await adapter.list(vault, collection)\n for (const id of ids) {\n if (id.startsWith('_')) continue\n const env = await adapter.get(vault, collection, id)\n if (!env || !env._data) continue // missing or tombstone\n const record = await decodeRecord(collection, id, env)\n if (record === null) continue\n const subjectValue = readDottedPath(record, field)\n if (subjectValue === undefined || subjectValue === null) continue\n const subjectId = coerceSubjectId(subjectValue)\n const list = bySubject.get(subjectId) ?? []\n list.push({ collection, id })\n bySubject.set(subjectId, list)\n }\n }\n\n let entries = 0\n for (const [subjectId, refs] of bySubject) {\n const key = await subjectKey(subjectId)\n await writeRefs(adapter, vault, getDEK, encrypted, key, refs)\n entries++\n }\n return entries\n}\n\n/**\n * Coerce a read subject-field value to a stable string id. Primitives use\n * their natural string form; objects/arrays are JSON-stringified so structural\n * subjects still get a deterministic key (avoids the `[object Object]` trap).\n */\nexport function coerceSubjectId(value: unknown): string {\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value)\n }\n return JSON.stringify(value)\n}\n\n/** Read a (possibly dotted) field path from a plain record. */\nexport function readDottedPath(record: Record<string, unknown>, field: string): unknown {\n if (!field.includes('.')) return record[field]\n let cursor: unknown = record\n for (const segment of field.split('.')) {\n if (cursor === null || cursor === undefined) return undefined\n cursor = (cursor as Record<string, unknown>)[segment]\n }\n return cursor\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACiDO,IAAM,YAA4B,EAAE,UAAU,CAAC,EAAE;AAuBjD,SAAS,kBAAkB,MAA0C;AAC1E,SAAO,EAAE,UAAU,EAAE,GAAG,KAAK,SAAS,EAAE;AAC1C;;;AC3CO,IAAM,2BAA2B;","names":[]}
|
package/dist/forget/index.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { b as ForgetResult, F as ForgetStrategy, N as NO_FORGET, d as SUBJECT_INDEX_COLLECTION, e as SubjectDeclaration, S as SubjectRef, w as withForgetCascade } from '../index-
|
|
1
|
+
export { b as ForgetResult, F as ForgetStrategy, N as NO_FORGET, d as SUBJECT_INDEX_COLLECTION, e as SubjectDeclaration, S as SubjectRef, w as withForgetCascade } from '../index-BMmajblo.cjs';
|
package/dist/forget/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { b as ForgetResult, F as ForgetStrategy, N as NO_FORGET, d as SUBJECT_INDEX_COLLECTION, e as SubjectDeclaration, S as SubjectRef, w as withForgetCascade } from '../index-
|
|
1
|
+
export { b as ForgetResult, F as ForgetStrategy, N as NO_FORGET, d as SUBJECT_INDEX_COLLECTION, e as SubjectDeclaration, S as SubjectRef, w as withForgetCascade } from '../index-BMmajblo.js';
|
package/dist/forget/index.js
CHANGED
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
NO_FORGET,
|
|
3
3
|
SUBJECT_INDEX_COLLECTION,
|
|
4
4
|
withForgetCascade
|
|
5
|
-
} from "../chunk-
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
5
|
+
} from "../chunk-KNKNOJFS.js";
|
|
6
|
+
import "../chunk-SHIUFIPW.js";
|
|
7
|
+
import "../chunk-OKOKPYWH.js";
|
|
8
|
+
import "../chunk-DJF3FXW5.js";
|
|
9
9
|
export {
|
|
10
10
|
NO_FORGET,
|
|
11
11
|
SUBJECT_INDEX_COLLECTION,
|
package/dist/guards/index.cjs
CHANGED
|
@@ -24,10 +24,12 @@ __export(guards_exports, {
|
|
|
24
24
|
FieldFrozenError: () => FieldFrozenError,
|
|
25
25
|
GuardExecutor: () => GuardExecutor,
|
|
26
26
|
GuardRegistry: () => GuardRegistry,
|
|
27
|
+
IllegalTransitionError: () => IllegalTransitionError,
|
|
27
28
|
InvariantError: () => InvariantError,
|
|
28
29
|
ReadOnlyVaultFacade: () => ReadOnlyVaultFacade,
|
|
29
30
|
RecordLockedError: () => RecordLockedError,
|
|
30
31
|
immutableGuard: () => immutableGuard,
|
|
32
|
+
transitionGuard: () => transitionGuard,
|
|
31
33
|
withGuard: () => withGuard
|
|
32
34
|
});
|
|
33
35
|
module.exports = __toCommonJS(guards_exports);
|
|
@@ -72,6 +74,23 @@ var FieldFrozenError = class extends NoydbError {
|
|
|
72
74
|
this.fields = fields;
|
|
73
75
|
}
|
|
74
76
|
};
|
|
77
|
+
var IllegalTransitionError = class extends NoydbError {
|
|
78
|
+
collection;
|
|
79
|
+
id;
|
|
80
|
+
from;
|
|
81
|
+
to;
|
|
82
|
+
constructor(collection, id, from, to) {
|
|
83
|
+
super(
|
|
84
|
+
"ILLEGAL_TRANSITION",
|
|
85
|
+
`Cannot transition ${collection}/${id} from "${from}" to "${to}" \u2014 not a declared arc. Use withTransactions({ amendment: true, reason }) with admin/owner role to override.`
|
|
86
|
+
);
|
|
87
|
+
this.name = "IllegalTransitionError";
|
|
88
|
+
this.collection = collection;
|
|
89
|
+
this.id = id;
|
|
90
|
+
this.from = from;
|
|
91
|
+
this.to = to;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
75
94
|
var InvariantError = class extends NoydbError {
|
|
76
95
|
constructor(message) {
|
|
77
96
|
super("INVARIANT_VIOLATED", message);
|
|
@@ -156,6 +175,59 @@ function immutableGuard(config) {
|
|
|
156
175
|
return withGuard(spec);
|
|
157
176
|
}
|
|
158
177
|
|
|
178
|
+
// src/guards/transition-guard.ts
|
|
179
|
+
function recordId2(record) {
|
|
180
|
+
const id = record?.id;
|
|
181
|
+
return typeof id === "string" ? id : "";
|
|
182
|
+
}
|
|
183
|
+
function stateOf(record, field) {
|
|
184
|
+
const v = record[field];
|
|
185
|
+
return typeof v === "string" ? v : String(v);
|
|
186
|
+
}
|
|
187
|
+
function transitionGuard(config) {
|
|
188
|
+
const { collection, field, transitions, initial, amendmentRoles, amendmentInvariant } = config;
|
|
189
|
+
const allowIdempotent = config.allowIdempotent ?? true;
|
|
190
|
+
if (!field) {
|
|
191
|
+
throw new ValidationError("transitionGuard: `field` is required");
|
|
192
|
+
}
|
|
193
|
+
if (transitions === void 0 || typeof transitions !== "object") {
|
|
194
|
+
throw new ValidationError("transitionGuard: `transitions` must be a state\u2192states map");
|
|
195
|
+
}
|
|
196
|
+
const spec = {
|
|
197
|
+
collection,
|
|
198
|
+
check: (incoming, ctx) => {
|
|
199
|
+
const rec = incoming;
|
|
200
|
+
const to = stateOf(rec, field);
|
|
201
|
+
if (ctx.existing === null) {
|
|
202
|
+
if (initial !== void 0 && !initial.includes(to)) {
|
|
203
|
+
throw new IllegalTransitionError(collection, recordId2(rec), "(none)", to);
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const from = stateOf(ctx.existing, field);
|
|
208
|
+
if (from === to) {
|
|
209
|
+
if (allowIdempotent) return;
|
|
210
|
+
throw new IllegalTransitionError(collection, recordId2(rec), from, to);
|
|
211
|
+
}
|
|
212
|
+
const allowed = transitions[from] ?? [];
|
|
213
|
+
if (!allowed.includes(to)) {
|
|
214
|
+
throw new IllegalTransitionError(collection, recordId2(rec), from, to);
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
// The authorized override: inside an amendment transaction the check
|
|
218
|
+
// is skipped and the change is ledgered. By default no extra invariant
|
|
219
|
+
// — the amendment itself is the sanctioned exception. Callers may
|
|
220
|
+
// supply `amendmentInvariant` to keep a constraint inviolable even
|
|
221
|
+
// under amendment; a throw reverts the amendment as `InvariantError`.
|
|
222
|
+
amendment: {
|
|
223
|
+
roles: amendmentRoles ?? ["admin", "owner"],
|
|
224
|
+
invariant: amendmentInvariant ?? (() => {
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
return withGuard(spec);
|
|
229
|
+
}
|
|
230
|
+
|
|
159
231
|
// src/guards/registry.ts
|
|
160
232
|
var GuardRegistry = class {
|
|
161
233
|
_byCollection = /* @__PURE__ */ new Map();
|
|
@@ -351,14 +423,17 @@ function deepEqual(a, b) {
|
|
|
351
423
|
// src/guards/read-only-facade.ts
|
|
352
424
|
var ReadOnlyVaultFacade = class {
|
|
353
425
|
_vault;
|
|
354
|
-
|
|
426
|
+
_layer;
|
|
427
|
+
constructor(vault, layer = "read") {
|
|
355
428
|
this._vault = vault;
|
|
429
|
+
this._layer = layer;
|
|
356
430
|
}
|
|
357
431
|
collection(name) {
|
|
358
432
|
const c = this._vault.collection(name);
|
|
433
|
+
const layer = this._layer;
|
|
359
434
|
return {
|
|
360
|
-
get: (id) => c.get(id),
|
|
361
|
-
list: () => c.list(),
|
|
435
|
+
get: (id) => c.get(id, { _layer: layer }),
|
|
436
|
+
list: () => c.list({ _layer: layer }),
|
|
362
437
|
query: () => c.query()
|
|
363
438
|
};
|
|
364
439
|
}
|
|
@@ -369,10 +444,12 @@ var ReadOnlyVaultFacade = class {
|
|
|
369
444
|
FieldFrozenError,
|
|
370
445
|
GuardExecutor,
|
|
371
446
|
GuardRegistry,
|
|
447
|
+
IllegalTransitionError,
|
|
372
448
|
InvariantError,
|
|
373
449
|
ReadOnlyVaultFacade,
|
|
374
450
|
RecordLockedError,
|
|
375
451
|
immutableGuard,
|
|
452
|
+
transitionGuard,
|
|
376
453
|
withGuard
|
|
377
454
|
});
|
|
378
455
|
//# sourceMappingURL=index.cjs.map
|