@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 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/blobs/mime-magic.ts","../src/blobs/blob-set.ts"],"sourcesContent":["/**\n * Lightweight MIME type detection from magic bytes (file signatures).\n *\n * Designed for the blob store's auto-detection feature. Operates on the first 16 bytes of\n * plaintext — no filesystem access, no filename guessing.\n *\n * ## Detection strategies\n *\n * 1. **Prefix match** — magic bytes at offset 0 (most formats).\n * 2. **Offset match** — magic bytes at a fixed offset > 0 (ISOBMFF: offset 4).\n * 3. **Compound match** — two separate byte sequences at different offsets\n * (RIFF-based: bytes 0-3 + bytes 8-11).\n *\n * ## Formats excluded (require offset > 16 bytes)\n *\n * - TAR (`ustar` at offset 257)\n * - ISO 9660 (`CD001` at offset 32769)\n *\n * @module\n */\n\n// ─── Types ───────────────────────────────────────────────────────────────\n\ninterface MagicRule {\n /** IANA MIME type (or widely-used x- type). */\n readonly mime: string\n /** Human-readable format name for diagnostics. */\n readonly format: string\n /** Magic bytes to match, as a Uint8Array. */\n readonly bytes: Uint8Array\n /** Byte offset where the magic starts. Default 0. */\n readonly offset?: number\n /**\n * For compound checks (RIFF, FORM): a second byte sequence that must\n * also match at `secondaryOffset`.\n */\n readonly secondaryBytes?: Uint8Array\n /** Offset of the secondary match. */\n readonly secondaryOffset?: number\n /** If true, the format is already compressed — skip gzip in blob.put(). */\n readonly preCompressed?: true\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────\n\n/** Convert a hex string like `'FF D8 FF'` to Uint8Array. */\nfunction hex(s: string): Uint8Array {\n return new Uint8Array(s.split(' ').map((b) => parseInt(b, 16)))\n}\n\n// ─── Magic rules ─────────────────────────────────────────────────────────\n//\n// Ordered by detection priority: more specific (longer) signatures first\n// within the same offset group, so that e.g. RAR v5 (8 bytes) is tested\n// before RAR v4 (7 bytes).\n//\n// Sources verified against:\n// - Gary Kessler's File Signatures Table\n// - Wikipedia \"List of file signatures\"\n// - IANA MIME type registry\n// - Individual format specifications (PNG RFC 2083, PDF ISO 32000, etc.)\n//\n// Each entry includes the original CSV row number for traceability.\n\nconst MAGIC_RULES: readonly MagicRule[] = [\n // ── Images ───────────────────────────────────────────────────────────\n\n // #2 PNG — full 8-byte signature (RFC 2083)\n { mime: 'image/png', format: 'PNG', bytes: hex('89 50 4E 47 0D 0A 1A 0A'), preCompressed: true },\n\n // #1 JPEG — FF D8 FF (third byte is start of APP marker, always FF)\n { mime: 'image/jpeg', format: 'JPEG', bytes: hex('FF D8 FF'), preCompressed: true },\n\n // #7 WebP — RIFF compound: bytes 0-3 = RIFF, bytes 8-11 = WEBP\n {\n mime: 'image/webp',\n format: 'WebP',\n bytes: hex('52 49 46 46'),\n secondaryBytes: hex('57 45 42 50'),\n secondaryOffset: 8,\n preCompressed: true,\n },\n\n // #5 TIFF (little-endian) — II + version 42\n { mime: 'image/tiff', format: 'TIFF', bytes: hex('49 49 2A 00') },\n\n // #6 TIFF (big-endian) — MM + version 42\n { mime: 'image/tiff', format: 'TIFF', bytes: hex('4D 4D 00 2A') },\n\n // #3 GIF — GIF8 (covers GIF87a and GIF89a)\n { mime: 'image/gif', format: 'GIF', bytes: hex('47 49 46 38'), preCompressed: true },\n\n // #4 BMP — BM\n { mime: 'image/bmp', format: 'BMP', bytes: hex('42 4D') },\n\n // PSD — 8BPS\n { mime: 'image/vnd.adobe.photoshop', format: 'PSD', bytes: hex('38 42 50 53') },\n\n // #8 ICO — 00 00 01 00 (note: 00 00 02 00 is CUR cursor format)\n { mime: 'image/x-icon', format: 'ICO', bytes: hex('00 00 01 00') },\n\n // #9 HEIC — ISOBMFF: ftyp at offset 4, brand \"heic\" at offset 8\n {\n mime: 'image/heic',\n format: 'HEIC',\n bytes: hex('66 74 79 70'),\n offset: 4,\n secondaryBytes: hex('68 65 69 63'),\n secondaryOffset: 8,\n preCompressed: true,\n },\n\n // ── Documents ────────────────────────────────────────────────────────\n\n // PDF — %PDF\n { mime: 'application/pdf', format: 'PDF', bytes: hex('25 50 44 46') },\n\n // RTF — {\\rtf\n { mime: 'application/rtf', format: 'RTF', bytes: hex('7B 5C 72 74 66') },\n\n // ── Archives & compression ───────────────────────────────────────────\n\n // RAR v5 — 8-byte signature (test before RAR v4)\n { mime: 'application/vnd.rar', format: 'RAR v5', bytes: hex('52 61 72 21 1A 07 01 00'), preCompressed: true },\n\n // RAR v4 — 7-byte signature\n { mime: 'application/vnd.rar', format: 'RAR v4', bytes: hex('52 61 72 21 1A 07 00'), preCompressed: true },\n\n // 7-Zip — 6-byte signature\n { mime: 'application/x-7z-compressed', format: '7Z', bytes: hex('37 7A BC AF 27 1C'), preCompressed: true },\n\n // XZ — 6-byte stream header\n { mime: 'application/x-xz', format: 'XZ', bytes: hex('FD 37 7A 58 5A 00'), preCompressed: true },\n\n // ZIP — PK\\x03\\x04 (local file header)\n { mime: 'application/zip', format: 'ZIP', bytes: hex('50 4B 03 04'), preCompressed: true },\n\n // GZIP — 1F 8B\n { mime: 'application/gzip', format: 'GZIP', bytes: hex('1F 8B'), preCompressed: true },\n\n // BZIP2 — BZh\n { mime: 'application/x-bzip2', format: 'BZIP2', bytes: hex('42 5A 68'), preCompressed: true },\n\n // LZIP — LZIP\n { mime: 'application/x-lzip', format: 'LZIP', bytes: hex('4C 5A 49 50'), preCompressed: true },\n\n // ── Audio ────────────────────────────────────────────────────────────\n\n // WAV — RIFF compound: bytes 0-3 = RIFF, bytes 8-11 = WAVE\n {\n mime: 'audio/wav',\n format: 'WAV',\n bytes: hex('52 49 46 46'),\n secondaryBytes: hex('57 41 56 45'),\n secondaryOffset: 8,\n },\n\n // AIFF — FORM compound: bytes 0-3 = FORM, bytes 8-11 = AIFF\n {\n mime: 'audio/aiff',\n format: 'AIFF',\n bytes: hex('46 4F 52 4D'),\n secondaryBytes: hex('41 49 46 46'),\n secondaryOffset: 8,\n },\n\n // FLAC — fLaC\n { mime: 'audio/flac', format: 'FLAC', bytes: hex('66 4C 61 43') },\n\n // OGG — OggS (container — may hold Vorbis, Opus, Theora, etc.)\n { mime: 'application/ogg', format: 'OGG', bytes: hex('4F 67 67 53') },\n\n // MIDI — MThd\n { mime: 'audio/midi', format: 'MIDI', bytes: hex('4D 54 68 64') },\n\n // MP3 (ID3-tagged) — ID3\n { mime: 'audio/mpeg', format: 'MP3', bytes: hex('49 44 33'), preCompressed: true },\n\n // ── Video ────────────────────────────────────────────────────────────\n\n // AVI — RIFF compound: bytes 0-3 = RIFF, bytes 8-11 = AVI\\x20\n {\n mime: 'video/x-msvideo',\n format: 'AVI',\n bytes: hex('52 49 46 46'),\n secondaryBytes: hex('41 56 49 20'),\n secondaryOffset: 8,\n preCompressed: true,\n },\n\n // WMV/ASF — 8-byte ASF header GUID prefix\n { mime: 'video/x-ms-wmv', format: 'WMV', bytes: hex('30 26 B2 75 8E 66 CF 11'), preCompressed: true },\n\n // MKV/WebM — EBML header (Matroska container)\n { mime: 'video/x-matroska', format: 'MKV', bytes: hex('1A 45 DF A3'), preCompressed: true },\n\n // FLV — FLV\n { mime: 'video/x-flv', format: 'FLV', bytes: hex('46 4C 56'), preCompressed: true },\n\n // MOV — ISOBMFF: ftyp at offset 4, brand \"qt \" at offset 8\n {\n mime: 'video/quicktime',\n format: 'MOV',\n bytes: hex('66 74 79 70'),\n offset: 4,\n secondaryBytes: hex('71 74 20 20'),\n secondaryOffset: 8,\n preCompressed: true,\n },\n\n // MP4 — ISOBMFF: ftyp at offset 4 (brands vary: isom, mp41, mp42, etc.)\n // Tested AFTER MOV and HEIC so their specific brands match first.\n { mime: 'video/mp4', format: 'MP4', bytes: hex('66 74 79 70'), offset: 4, preCompressed: true },\n\n // ── Executables & binaries ───────────────────────────────────────────\n\n // SQLite — \"SQLite 3\" (first 8 bytes of the 16-byte header)\n { mime: 'application/vnd.sqlite3', format: 'SQLite', bytes: hex('53 51 4C 69 74 65 20 33') },\n\n // WASM — \\0asm\n { mime: 'application/wasm', format: 'WASM', bytes: hex('00 61 73 6D') },\n\n // ELF — \\x7FELF\n { mime: 'application/x-elf', format: 'ELF', bytes: hex('7F 45 4C 46') },\n\n // PE (EXE/DLL) — MZ\n { mime: 'application/vnd.microsoft.portable-executable', format: 'PE', bytes: hex('4D 5A') },\n\n // Mach-O — all four single-arch variants\n { mime: 'application/x-mach-binary', format: 'Mach-O 64 LE', bytes: hex('CF FA ED FE') },\n { mime: 'application/x-mach-binary', format: 'Mach-O 64 BE', bytes: hex('FE ED FA CF') },\n { mime: 'application/x-mach-binary', format: 'Mach-O 32 LE', bytes: hex('CE FA ED FE') },\n { mime: 'application/x-mach-binary', format: 'Mach-O 32 BE', bytes: hex('FE ED FA CE') },\n\n // Java Class — CA FE BA BE\n // Note: collides with Mach-O Universal Binary. Disambiguated by checking\n // bytes 4-7: Java class version is >= 0x002D (45), while fat binary\n // arch count is a small number (typically 0x00000002).\n // We place Java after Mach-O single-arch entries so the more common\n // Mach-O variants match first. The CA FE BA BE collision between Java\n // and Mach-O fat binary is resolved by the caller if needed.\n { mime: 'application/java-vm', format: 'Java Class', bytes: hex('CA FE BA BE') },\n\n // DEX — dex\\n (Android Dalvik Executable)\n { mime: 'application/vnd.android.dex', format: 'DEX', bytes: hex('64 65 78 0A') },\n\n // ── Package formats ──────────────────────────────────────────────────\n\n // DEB — !<arch> (ar archive; DEB-specific member follows)\n { mime: 'application/vnd.debian.binary-package', format: 'DEB', bytes: hex('21 3C 61 72 63 68 3E') },\n\n // RPM — ED AB EE DB\n { mime: 'application/x-rpm', format: 'RPM', bytes: hex('ED AB EE DB') },\n\n // CAB — MSCF\n { mime: 'application/vnd.ms-cab-compressed', format: 'CAB', bytes: hex('4D 53 43 46'), preCompressed: true },\n\n // ── Capture & Flash ──────────────────────────────────────────────────\n\n // PCAP (little-endian) — D4 C3 B2 A1\n { mime: 'application/vnd.tcpdump.pcap', format: 'PCAP', bytes: hex('D4 C3 B2 A1') },\n\n // PCAP (big-endian) — A1 B2 C3 D4\n { mime: 'application/vnd.tcpdump.pcap', format: 'PCAP BE', bytes: hex('A1 B2 C3 D4') },\n\n // PCAPNG — Section Header Block\n { mime: 'application/x-pcapng', format: 'PCAPNG', bytes: hex('0A 0D 0D 0A') },\n\n // SWF — all three variants (uncompressed, zlib, LZMA)\n { mime: 'application/x-shockwave-flash', format: 'SWF', bytes: hex('46 57 53') },\n { mime: 'application/x-shockwave-flash', format: 'SWF zlib', bytes: hex('43 57 53'), preCompressed: true },\n { mime: 'application/x-shockwave-flash', format: 'SWF LZMA', bytes: hex('5A 57 53'), preCompressed: true },\n\n // ── Data formats ─────────────────────────────────────────────────────\n\n // Parquet — PAR1 (no registered IANA MIME; using Apache's informal type)\n { mime: 'application/vnd.apache.parquet', format: 'Parquet', bytes: hex('50 41 52 31') },\n\n // Avro Object Container — Obj\\x01\n { mime: 'application/avro', format: 'Avro', bytes: hex('4F 62 6A 01') },\n\n // NES ROM — NES\\x1A (iNES header)\n { mime: 'application/x-nintendo-nes-rom', format: 'NES ROM', bytes: hex('4E 45 53 1A') },\n] as const\n\n// ─── MP3 sync word ───────────────────────────────────────────────────────\n//\n// MP3 files without an ID3 tag start with a frame sync word where the top\n// 11 bits are set: 0xFFE0 mask. The ID3 signature (49 44 33) is handled\n// as a normal rule above. The sync-word check is a fallback tested in\n// `detectMimeType` after all rules.\n\nfunction isMp3SyncWord(byte0: number, byte1: number): boolean {\n return byte0 === 0xff && (byte1 & 0xe0) === 0xe0\n}\n\n// ─── Detection ───────────────────────────────────────────────────────────\n\n/**\n * Detect MIME type from the first bytes of a file.\n *\n * @param header - The first 16 bytes (or more) of the plaintext. Passing\n * fewer than 16 bytes may miss compound and offset-based matches.\n * @returns Detected MIME type, or `'application/octet-stream'` if unknown.\n */\nexport function detectMimeType(header: Uint8Array): string {\n const result = detectMagic(header)\n return result?.mime ?? 'application/octet-stream'\n}\n\n/**\n * Detect MIME type and whether the format is already compressed.\n *\n * Used by `BlobSet.put()` to decide whether to skip gzip compression.\n *\n * @param header - The first 16 bytes (or more) of the plaintext.\n * @returns `{ mime, preCompressed }` or `null` if no match.\n */\nexport function detectMagic(\n header: Uint8Array,\n): { mime: string; format: string; preCompressed: boolean } | null {\n for (const rule of MAGIC_RULES) {\n if (matchRule(header, rule)) {\n return {\n mime: rule.mime,\n format: rule.format,\n preCompressed: rule.preCompressed ?? false,\n }\n }\n }\n\n // Fallback: MP3 sync word (no ID3 tag)\n if (header.length >= 2 && isMp3SyncWord(header[0]!, header[1]!)) {\n return { mime: 'audio/mpeg', format: 'MP3', preCompressed: true }\n }\n\n return null\n}\n\n/**\n * Check whether a format is already compressed (should skip gzip).\n *\n * @param mimeType - A MIME type string.\n * @returns `true` if the format is known to be pre-compressed.\n */\nexport function isPreCompressed(mimeType: string): boolean {\n return PRE_COMPRESSED_MIMES.has(mimeType)\n}\n\n// ─── Internal matching ───────────────────────────────────────────────────\n\nfunction matchRule(header: Uint8Array, rule: MagicRule): boolean {\n const offset = rule.offset ?? 0\n const end = offset + rule.bytes.length\n\n // Not enough data for the primary match\n if (header.length < end) return false\n\n // Primary byte sequence\n for (let i = 0; i < rule.bytes.length; i++) {\n if (header[offset + i] !== rule.bytes[i]) return false\n }\n\n // Secondary byte sequence (compound check)\n if (rule.secondaryBytes && rule.secondaryOffset !== undefined) {\n const sEnd = rule.secondaryOffset + rule.secondaryBytes.length\n if (header.length < sEnd) return false\n for (let i = 0; i < rule.secondaryBytes.length; i++) {\n if (header[rule.secondaryOffset + i] !== rule.secondaryBytes[i]) return false\n }\n }\n\n return true\n}\n\n// ─── Pre-compressed MIME set ─────────────────────────────────────────────\n//\n// Built from the rules above. Used by `isPreCompressed()` for callers who\n// already know the MIME type (e.g. from a Content-Type header) and want to\n// skip the magic-byte detection step.\n\nconst PRE_COMPRESSED_MIMES = new Set<string>(\n MAGIC_RULES.filter((r) => r.preCompressed).map((r) => r.mime),\n)\n","import type {\n NoydbStore,\n EncryptedEnvelope,\n BlobObject,\n SlotRecord,\n SlotInfo,\n VersionRecord,\n BlobPutOptions,\n BlobResponseOptions,\n} from '../types.js'\nimport { NOYDB_FORMAT_VERSION } from '../types.js'\nimport {\n encrypt,\n decrypt,\n hmacSha256Hex,\n encryptBytesWithAAD,\n decryptBytesWithAAD,\n bufferToBase64,\n base64ToBuffer,\n generateDEK,\n} from '../crypto.js'\nimport { wrapCek, unwrapCek } from '../record-keys/index.js'\nimport { ConflictError, NotFoundError } from '../errors.js'\nimport { detectMagic, isPreCompressed } from './mime-magic.js'\n\n// ─── Internal collection names ─────────────────────────────────────────\n\n/**\n * DEK slot name for vault-shared blob data. Calling `getDEK('_blob')`\n * auto-creates a blob DEK the first time — same lazy-creation mechanism\n * used for any user-defined collection.\n */\nexport const BLOB_COLLECTION = '_blob'\n\n/** Stores `BlobObject` metadata envelopes, keyed by eTag. */\nexport const BLOB_INDEX_COLLECTION = '_blob_index'\n\n/**\n * Stores encrypted chunk envelopes, keyed by `{eTag}/{chunkIndex}`.\n * NOT loaded into the in-memory query layer. Fetched on demand by\n * `BlobSet.get()` / `BlobSet.response()`.\n */\nexport const BLOB_CHUNKS_COLLECTION = '_blob_chunks'\n\n/** Prefix for per-collection slot metadata collections. */\nexport const BLOB_SLOTS_PREFIX = '_blob_slots_'\n\n/** Prefix for per-collection version records. */\nexport const BLOB_VERSIONS_PREFIX = '_blob_versions_'\n\n/**\n * Default chunk size: 256 KB raw bytes.\n * After AES-GCM (same size) + base64 (~33% inflation) → ~342 KB per\n * envelope, safely within DynamoDB's 400 KB item limit.\n */\nexport const DEFAULT_CHUNK_SIZE = 256 * 1024\n\n/** Maximum CAS retry attempts for refCount and slot metadata updates. */\nconst MAX_CAS_RETRIES = 5\n\n// ─── Compression helpers ───────────────────────────────────────────────\n\nasync function compressBytes(\n data: Uint8Array,\n): Promise<{ bytes: Uint8Array; algorithm: 'gzip' | 'none' }> {\n if (typeof CompressionStream === 'undefined') {\n return { bytes: data, algorithm: 'none' }\n }\n const cs = new CompressionStream('gzip')\n const writer = cs.writable.getWriter()\n await writer.write(data as Uint8Array<ArrayBuffer>)\n await writer.close()\n const buf = await new Response(cs.readable).arrayBuffer()\n return { bytes: new Uint8Array(buf), algorithm: 'gzip' }\n}\n\nasync function decompressBytes(data: Uint8Array): Promise<Uint8Array> {\n if (typeof DecompressionStream === 'undefined') {\n throw new Error(\n '[noy-db] DecompressionStream not available — cannot decompress blob chunk',\n )\n }\n const ds = new DecompressionStream('gzip')\n const writer = ds.writable.getWriter()\n await writer.write(data as Uint8Array<ArrayBuffer>)\n await writer.close()\n const buf = await new Response(ds.readable).arrayBuffer()\n return new Uint8Array(buf)\n}\n\nfunction concatChunks(chunks: Uint8Array[]): Uint8Array {\n const total = chunks.reduce((s, c) => s + c.byteLength, 0)\n const out = new Uint8Array(total)\n let offset = 0\n for (const c of chunks) {\n out.set(c, offset)\n offset += c.byteLength\n }\n return out\n}\n\n/** Build the AAD binding for chunk integrity: \"{eTag}:{chunkIndex}:{chunkCount}\" */\nfunction chunkAAD(eTag: string, chunkIndex: number, chunkCount: number): Uint8Array {\n return new TextEncoder().encode(`${eTag}:${chunkIndex}:${chunkCount}`)\n}\n\n// ─── BlobSet ──────────────────────────────────────────────────────────\n\n/**\n * Handle for reading, writing, versioning, and deleting binary blobs\n * on a specific record.\n *\n * Obtained via `collection.blob(id)`. No I/O is performed until you\n * call a method.\n *\n * ## Storage layout\n *\n * ```\n * _blob_index/{eTag} BlobObject metadata (vault-shared DEK)\n * _blob_chunks/{eTag}/{chunkIndex} Encrypted chunk data (vault-shared DEK + AAD)\n * _blob_slots_{collection}/{recordId} Slot map (parent collection DEK)\n * _blob_versions_{collection}/{recordId}/{slot}/{label} Published versions (parent collection DEK)\n * ```\n *\n * ## Deduplication\n *\n * `put()` computes `eTag = HMAC-SHA-256(blobDEK, plaintext)` — keyed so the\n * store cannot predict eTags for known content. If another record already\n * uploaded the same bytes, the chunks are reused and `refCount` is incremented.\n *\n * ## Chunk integrity\n *\n * Each chunk is encrypted with AES-256-GCM using AAD = `{eTag}:{index}:{count}`,\n * preventing chunk reorder, substitution, and truncation attacks.\n */\nexport class BlobSet {\n private readonly store: NoydbStore\n private readonly vault: string\n private readonly collection: string\n private readonly recordId: string\n private readonly getDEK: (name: string) => Promise<CryptoKey>\n private readonly encrypted: boolean\n private readonly userId: string | undefined\n private readonly maxBlobBytes: number | undefined\n private readonly erasableBlobs: boolean\n\n constructor(opts: {\n store: NoydbStore\n vault: string\n collection: string\n recordId: string\n getDEK: (name: string) => Promise<CryptoKey>\n encrypted: boolean\n userId?: string\n maxBlobBytes?: number\n erasableBlobs?: boolean\n }) {\n this.store = opts.store\n this.vault = opts.vault\n this.collection = opts.collection\n this.recordId = opts.recordId\n this.getDEK = opts.getDEK\n this.encrypted = opts.encrypted\n this.userId = opts.userId\n this.maxBlobBytes = opts.maxBlobBytes\n this.erasableBlobs = opts.erasableBlobs === true\n }\n\n /**\n * Resolve the key the blob's CHUNKS are encrypted under.\n *\n * - `_cek` present (erasable blob) → unwrap the per-blob content CEK under\n * the `_blob` DEK. Deleting the BlobObject (at `refCount → 0`) makes this\n * key unrecoverable → the chunks are crypto-shredded.\n * - `_cek` absent (legacy) → the `_blob` DEK encrypts chunks directly.\n * - unencrypted vault → `null` (chunks stored as plaintext base64).\n */\n private async resolveChunkKey(blob: Pick<BlobObject, '_cek'>): Promise<CryptoKey | null> {\n if (!this.encrypted) return null\n const blobDEK = await this.getDEK(BLOB_COLLECTION)\n return blob._cek !== undefined ? await unwrapCek(blob._cek, blobDEK) : blobDEK\n }\n\n /** The internal collection that holds slot metadata for this collection's blobs. */\n private get slotsCollection(): string {\n return `${BLOB_SLOTS_PREFIX}${this.collection}`\n }\n\n /** The internal collection that holds published versions for this collection's blobs. */\n private get versionsCollection(): string {\n return `${BLOB_VERSIONS_PREFIX}${this.collection}`\n }\n\n // ─── Slot Metadata I/O (CAS-protected) ─────────────────────────────\n\n private async loadSlots(): Promise<{\n slots: Record<string, SlotRecord>\n version: number\n }> {\n const envelope = await this.store.get(this.vault, this.slotsCollection, this.recordId)\n if (!envelope) return { slots: {}, version: 0 }\n\n if (!this.encrypted) {\n return {\n slots: JSON.parse(envelope._data) as Record<string, SlotRecord>,\n version: envelope._v,\n }\n }\n\n const dek = await this.getDEK(this.collection)\n const json = await decrypt(envelope._iv, envelope._data, dek)\n return {\n slots: JSON.parse(json) as Record<string, SlotRecord>,\n version: envelope._v,\n }\n }\n\n private async saveSlots(\n slots: Record<string, SlotRecord>,\n currentVersion: number,\n ): Promise<void> {\n const json = JSON.stringify(slots)\n const now = new Date().toISOString()\n let envelope: EncryptedEnvelope\n\n if (this.encrypted) {\n const dek = await this.getDEK(this.collection)\n const { iv, data } = await encrypt(json, dek)\n envelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: currentVersion + 1,\n _ts: now,\n _iv: iv,\n _data: data,\n }\n } else {\n envelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: currentVersion + 1,\n _ts: now,\n _iv: '',\n _data: json,\n }\n }\n\n await this.store.put(\n this.vault,\n this.slotsCollection,\n this.recordId,\n envelope,\n currentVersion > 0 ? currentVersion : undefined,\n )\n }\n\n /**\n * CAS retry loop for slot metadata updates. Re-reads slots on conflict\n * and re-applies the mutation function.\n */\n private async casUpdateSlots(\n mutate: (slots: Record<string, SlotRecord>) => Record<string, SlotRecord> | null,\n ): Promise<void> {\n for (let attempt = 0; attempt < MAX_CAS_RETRIES; attempt++) {\n const { slots, version } = await this.loadSlots()\n const updated = mutate(slots)\n if (updated === null) return // no-op\n try {\n await this.saveSlots(updated, version)\n return\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_CAS_RETRIES - 1) continue\n throw err\n }\n }\n }\n\n // ─── Blob Index I/O (versioned for CAS refCount) ──────────────────\n\n private async loadBlobObject(eTag: string): Promise<{ blob: BlobObject; version: number } | null> {\n const envelope = await this.store.get(this.vault, BLOB_INDEX_COLLECTION, eTag)\n if (!envelope) return null\n\n if (!this.encrypted) {\n return { blob: JSON.parse(envelope._data) as BlobObject, version: envelope._v }\n }\n\n const dek = await this.getDEK(BLOB_COLLECTION)\n const json = await decrypt(envelope._iv, envelope._data, dek)\n return { blob: JSON.parse(json) as BlobObject, version: envelope._v }\n }\n\n private async writeBlobObject(blob: BlobObject, expectedVersion?: number): Promise<void> {\n const json = JSON.stringify(blob)\n const now = new Date().toISOString()\n const newVersion = (expectedVersion ?? 0) + 1\n let envelope: EncryptedEnvelope\n\n if (this.encrypted) {\n const dek = await this.getDEK(BLOB_COLLECTION)\n const { iv, data } = await encrypt(json, dek)\n envelope = { _noydb: NOYDB_FORMAT_VERSION, _v: newVersion, _ts: now, _iv: iv, _data: data }\n } else {\n envelope = { _noydb: NOYDB_FORMAT_VERSION, _v: newVersion, _ts: now, _iv: '', _data: json }\n }\n\n await this.store.put(\n this.vault,\n BLOB_INDEX_COLLECTION,\n blob.eTag,\n envelope,\n expectedVersion,\n )\n }\n\n /**\n * CAS retry loop for refCount changes on a BlobObject.\n */\n private async casUpdateRefCount(eTag: string, delta: number): Promise<number> {\n for (let attempt = 0; attempt < MAX_CAS_RETRIES; attempt++) {\n const result = await this.loadBlobObject(eTag)\n if (!result) throw new NotFoundError(`BlobObject ${eTag} not found`)\n const { blob, version } = result\n const updated: BlobObject = { ...blob, refCount: blob.refCount + delta }\n try {\n await this.writeBlobObject(updated, version)\n return updated.refCount\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_CAS_RETRIES - 1) continue\n throw err\n }\n }\n throw new ConflictError(-1) // exhausted retries\n }\n\n /**\n * Release `n` references to a blob and reclaim it at refCount 0 (#365 slice 4).\n *\n * The single reclaim choke point for every reference-drop path — slot\n * delete/overwrite, published-version delete, and `forget()` shred — so the\n * refCount-0 policy is uniform:\n * - **erasable blob** (`_cek` present) → delete the `BlobObject` (the SOLE\n * copy of the wrapped content CEK → chunks permanently undecryptable) and\n * reclaim the chunks. The crypto-shred is EAGER on every path: GDPR erasure\n * must not wait on orphan retention.\n * - **legacy blob** (no `_cek`) → reclaimed only when `reclaimLegacy` (the\n * `forget()` erasure path); otherwise left for deferred GC so the existing\n * `BlobLifecyclePolicy.orphanRetentionDays` semantics are preserved.\n *\n * @returns `'shredded'` (erasable, refCount 0, chunks dead) · `'retainedShared'`\n * (erasable, still referenced) · `'residue'` (legacy — not a crypto-shred).\n */\n private async releaseRef(\n eTag: string,\n n: number,\n reclaimLegacy: boolean,\n ): Promise<'shredded' | 'retainedShared' | 'residue'> {\n const loaded = await this.loadBlobObject(eTag)\n if (!loaded) return 'shredded' // already gone\n const erasable = loaded.blob._cek !== undefined\n const remaining = await this.casUpdateRefCount(eTag, -n)\n if (remaining > 0) return erasable ? 'retainedShared' : 'residue'\n\n if (erasable || reclaimLegacy) {\n await this.store.delete(this.vault, BLOB_INDEX_COLLECTION, eTag)\n for (let i = 0; i < loaded.blob.chunkCount; i++) {\n await this.store.delete(this.vault, BLOB_CHUNKS_COLLECTION, `${eTag}_${i}`)\n }\n }\n return erasable ? 'shredded' : 'residue'\n }\n\n /**\n * Crypto-shred this record's blob attachments (#365 slice 2) — called by\n * `vault.forget()`.\n *\n * For each distinct eTag the record references (a record may attach the same\n * content under several slot names → several refCount holds): decrement the\n * blob's refCount by that many. When it reaches 0:\n * - **erasable blob** (`_cek` present) → delete the `BlobObject` (the SOLE\n * recoverable copy of the wrapped content CEK → chunks permanently\n * undecryptable) and reclaim the chunk bytes. This is the crypto-shred.\n * - **legacy blob** (no `_cek`) → chunks are under the shared `_blob` DEK and\n * stay decryptable until byte-deleted; we delete the orphaned chunks +\n * index but report it as residue, not a cryptographic erasure.\n * When refCount stays > 0 the content legitimately persists for its other\n * owner — reported as `retainedShared` (or `residue` if legacy).\n *\n * Finally drops the record's slot map, severing the subject's link.\n */\n async shredAllForRecord(): Promise<{\n shredded: string[]\n retainedShared: string[]\n residue: string[]\n }> {\n const { slots } = await this.loadSlots()\n const slotNames = Object.keys(slots)\n const shredded: string[] = []\n const retainedShared: string[] = []\n const residue: string[] = []\n if (slotNames.length === 0) return { shredded, retainedShared, residue }\n\n // Reference count from THIS record per eTag.\n const holds = new Map<string, number>()\n for (const name of slotNames) {\n const eTag = slots[name]!.eTag\n holds.set(eTag, (holds.get(eTag) ?? 0) + 1)\n }\n\n for (const [eTag, n] of holds) {\n // Forget erasure reclaims legacy orphans too (the record is being erased),\n // so reclaimLegacy = true — but only erasable blobs count as a crypto-shred.\n const outcome = await this.releaseRef(eTag, n, true)\n if (outcome === 'shredded') shredded.push(eTag)\n else if (outcome === 'retainedShared') retainedShared.push(eTag)\n else residue.push(eTag)\n }\n\n // Sever the subject's link: drop the record's slot map.\n await this.store.delete(this.vault, this.slotsCollection, this.recordId)\n return { shredded, retainedShared, residue }\n }\n\n /** CAS retry loop for an arbitrary BlobObject mutation. */\n private async casUpdateBlobObject(\n eTag: string,\n mutate: (blob: BlobObject) => BlobObject,\n ): Promise<void> {\n for (let attempt = 0; attempt < MAX_CAS_RETRIES; attempt++) {\n const result = await this.loadBlobObject(eTag)\n if (!result) throw new NotFoundError(`BlobObject ${eTag} not found`)\n try {\n await this.writeBlobObject(mutate(result.blob), result.version)\n return\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_CAS_RETRIES - 1) continue\n throw err\n }\n }\n throw new ConflictError(-1) // exhausted retries\n }\n\n /**\n * Migrate this record's LEGACY blobs (no `_cek`, chunks under the shared\n * `_blob` DEK) to per-blob content CEKs so they become crypto-shreddable\n * (#365 slice 3). Returns the eTags migrated vs. already-erasable.\n *\n * **Explicit maintenance pass** (mirrors the record-CEK migration posture):\n * re-encrypts the existing compressed chunks IN PLACE under a fresh content\n * CEK — preserving the eTag, chunkCount, chunkSize, and compression — then\n * flips the `_cek` discriminant. Crash-safe + idempotent via `_cekPending`:\n * 1. persist the wrapped content CEK in `_cekPending` (readers ignore it →\n * the blob stays readable under the `_blob` DEK; the key survives a crash);\n * 2. re-encrypt each chunk under the content CEK (a resume reads an\n * already-migrated chunk under the content CEK, else under the `_blob` DEK);\n * 3. promote `_cekPending` → `_cek` (atomic flip). Reads now use the CEK.\n * A re-run after a crash resumes from whichever phase was reached.\n *\n * Dedup-safe: migrating a shared blob (refCount > 1) re-keys it for every\n * referencer at once; a non-erasable collection still reads it (it unwraps\n * `_cek` under the `_blob` DEK it holds).\n */\n async migrate(): Promise<{ migrated: string[]; alreadyErasable: string[] }> {\n const migrated: string[] = []\n const alreadyErasable: string[] = []\n if (!this.encrypted) return { migrated, alreadyErasable }\n\n const blobDEK = await this.getDEK(BLOB_COLLECTION)\n const { slots } = await this.loadSlots()\n const eTags = new Set(Object.values(slots).map((s) => s.eTag))\n\n for (const eTag of eTags) {\n const loaded = await this.loadBlobObject(eTag)\n if (!loaded) continue\n const blob = loaded.blob\n if (blob._cek !== undefined) { alreadyErasable.push(eTag); continue }\n\n // Phase 1 — persist the content CEK (resume reuses an existing pending one).\n let contentCek: CryptoKey\n if (blob._cekPending !== undefined) {\n contentCek = await unwrapCek(blob._cekPending, blobDEK)\n } else {\n contentCek = await generateDEK()\n const wrapped = await wrapCek(contentCek, blobDEK)\n await this.casUpdateBlobObject(eTag, (b) => ({ ...b, _cekPending: wrapped }))\n }\n\n // Phase 2 — re-encrypt each chunk under the content CEK, in place.\n for (let i = 0; i < blob.chunkCount; i++) {\n let raw: Uint8Array | null\n try {\n raw = await this.readChunk(eTag, i, blob.chunkCount, blobDEK)\n } catch {\n // Already re-encrypted under the content CEK (crash resume).\n raw = await this.readChunk(eTag, i, blob.chunkCount, contentCek)\n }\n if (!raw) {\n throw new NotFoundError(\n `Blob chunk ${i}/${blob.chunkCount} missing for eTag \"${eTag}\" during migration`,\n )\n }\n await this.writeChunk(eTag, i, blob.chunkCount, raw, contentCek)\n }\n\n // Phase 3 — promote _cekPending → _cek (atomic flip).\n await this.casUpdateBlobObject(eTag, (b) => {\n const { _cekPending, ...rest } = b\n return _cekPending === undefined ? b : { ...rest, _cek: _cekPending }\n })\n migrated.push(eTag)\n }\n return { migrated, alreadyErasable }\n }\n\n // ─── Chunk I/O (with AAD binding) ─────────────────────────────────\n\n private async writeChunk(\n eTag: string,\n index: number,\n chunkCount: number,\n chunk: Uint8Array,\n dek: CryptoKey | null,\n ): Promise<void> {\n const id = `${eTag}_${index}`\n const now = new Date().toISOString()\n let envelope: EncryptedEnvelope\n\n if (dek) {\n const aad = chunkAAD(eTag, index, chunkCount)\n const { iv, data } = await encryptBytesWithAAD(chunk, dek, aad)\n envelope = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: now, _iv: iv, _data: data }\n } else {\n envelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: 1,\n _ts: now,\n _iv: '',\n _data: bufferToBase64(chunk),\n }\n }\n\n await this.store.put(this.vault, BLOB_CHUNKS_COLLECTION, id, envelope)\n }\n\n private async readChunk(\n eTag: string,\n index: number,\n chunkCount: number,\n dek: CryptoKey | null,\n ): Promise<Uint8Array | null> {\n const envelope = await this.store.get(this.vault, BLOB_CHUNKS_COLLECTION, `${eTag}_${index}`)\n if (!envelope) return null\n\n if (dek) {\n const aad = chunkAAD(eTag, index, chunkCount)\n return await decryptBytesWithAAD(envelope._iv, envelope._data, dek, aad)\n }\n\n return base64ToBuffer(envelope._data)\n }\n\n // ─── Version record I/O ───────────────────────────────────────────\n\n private versionKey(slotName: string, label: string): string {\n return `${this.recordId}::${slotName}::${label}`\n }\n\n private async loadVersionRecord(slotName: string, label: string): Promise<VersionRecord | null> {\n const key = this.versionKey(slotName, label)\n const envelope = await this.store.get(this.vault, this.versionsCollection, key)\n if (!envelope) return null\n\n if (!this.encrypted) {\n return JSON.parse(envelope._data) as VersionRecord\n }\n\n const dek = await this.getDEK(this.collection)\n const json = await decrypt(envelope._iv, envelope._data, dek)\n return JSON.parse(json) as VersionRecord\n }\n\n private async writeVersionRecord(slotName: string, record: VersionRecord): Promise<void> {\n const key = this.versionKey(slotName, record.label)\n const json = JSON.stringify(record)\n const now = new Date().toISOString()\n let envelope: EncryptedEnvelope\n\n if (this.encrypted) {\n const dek = await this.getDEK(this.collection)\n const { iv, data } = await encrypt(json, dek)\n envelope = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: now, _iv: iv, _data: data }\n } else {\n envelope = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: now, _iv: '', _data: json }\n }\n\n await this.store.put(this.vault, this.versionsCollection, key, envelope)\n }\n\n private async deleteVersionRecord(slotName: string, label: string): Promise<void> {\n const key = this.versionKey(slotName, label)\n await this.store.delete(this.vault, this.versionsCollection, key)\n }\n\n // ─── Effective chunk size ─────────────────────────────────────────\n\n private effectiveChunkSize(opts?: BlobPutOptions): number {\n if (opts?.chunkSize) return opts.chunkSize\n if (this.maxBlobBytes) return this.maxBlobBytes\n return DEFAULT_CHUNK_SIZE\n }\n\n // ─── Fetch all chunks for a blob ──────────────────────────────────\n\n private async fetchAllChunks(blob: BlobObject): Promise<Uint8Array> {\n // Chunks are keyed under the per-blob content CEK (erasable) or directly\n // under the `_blob` DEK (legacy) — resolveChunkKey discriminates on `_cek`.\n const chunkKey = await this.resolveChunkKey(blob)\n const chunks: Uint8Array[] = []\n\n for (let i = 0; i < blob.chunkCount; i++) {\n const chunk = await this.readChunk(blob.eTag, i, blob.chunkCount, chunkKey)\n if (!chunk) {\n throw new NotFoundError(\n `Blob chunk ${i}/${blob.chunkCount} missing for eTag \"${blob.eTag}\" on record \"${this.recordId}\"`,\n )\n }\n chunks.push(chunk)\n }\n\n const assembled = concatChunks(chunks)\n return blob.compression === 'gzip' ? await decompressBytes(assembled) : assembled\n }\n\n // ─── Public API: Slot management ──────────────────────────────────\n\n /**\n * Upload bytes and attach them to this record under `slotName`.\n *\n * 1. Computes `eTag = HMAC-SHA-256(blobDEK, plaintext)` for keyed content-addressing.\n * 2. Auto-detects MIME type from magic bytes if not provided.\n * 3. If a blob with this eTag already exists, skips chunk upload (deduplication)\n * and CAS-increments refCount.\n * 4. Otherwise: compresses → splits into chunks → encrypts each chunk with\n * AAD binding → writes `_blob_chunks` → writes `BlobObject` to `_blob_index`.\n * 5. CAS-updates the slot metadata in `_blob_slots_{collection}`.\n * If overwriting an existing slot, decrements the old eTag's refCount.\n */\n async put(slotName: string, data: Uint8Array, opts?: BlobPutOptions): Promise<void> {\n // Step 1 — keyed content-hash (plaintext, before compression)\n const blobDEK = this.encrypted ? await this.getDEK(BLOB_COLLECTION) : null\n const eTag = blobDEK\n ? await hmacSha256Hex(blobDEK, data)\n : await plainSha256Hex(data)\n\n // Step 2 — MIME detection\n let mimeType = opts?.mimeType\n if (!mimeType) {\n const detected = detectMagic(data.subarray(0, 16))\n if (detected) mimeType = detected.mime\n }\n\n // Determine compression: explicit opt > auto-detect > default true\n let shouldCompress: boolean\n if (opts?.compress !== undefined) {\n shouldCompress = opts.compress\n } else if (mimeType && isPreCompressed(mimeType)) {\n shouldCompress = false\n } else {\n shouldCompress = true\n }\n\n // Step 3 — deduplication check\n const existingBlob = await this.loadBlobObject(eTag)\n\n if (existingBlob) {\n // eTag already exists — just increment refCount (CAS retry). Dedup is\n // preserved across the content-CEK split: the chunks (and the BlobObject's\n // `_cek`, if any) are reused as-is; a new referencer never re-encrypts.\n await this.casUpdateRefCount(eTag, +1)\n } else {\n // Step 4 — compress\n const { bytes: compressed, algorithm } = shouldCompress\n ? await compressBytes(data)\n : { bytes: data, algorithm: 'none' as const }\n\n const chunkSize = this.effectiveChunkSize(opts)\n const chunkCount = Math.max(1, Math.ceil(compressed.byteLength / chunkSize))\n\n // Erasable collection (`perRecordKeys`): mint a fresh per-blob content\n // CEK and encrypt the chunks under it instead of the shared `_blob` DEK,\n // so the BlobObject's wrapped `_cek` is the sole recoverable copy → a\n // refCount-0 delete crypto-shreds the chunks. `eTag` (the dedup address)\n // is still keyed off the `_blob` DEK, unchanged.\n let chunkKey = blobDEK\n let wrappedCek: string | undefined\n if (blobDEK && this.erasableBlobs) {\n const contentCek = await generateDEK()\n wrappedCek = await wrapCek(contentCek, blobDEK)\n chunkKey = contentCek\n }\n\n // Step 5 — write chunks FIRST with AAD binding (safe failure order)\n for (let i = 0; i < chunkCount; i++) {\n const start = i * chunkSize\n await this.writeChunk(\n eTag, i, chunkCount,\n compressed.subarray(start, start + chunkSize),\n chunkKey,\n )\n }\n\n // Step 6 — write blob index entry after all chunks succeed\n await this.writeBlobObject({\n eTag,\n size: data.byteLength,\n compressedSize: compressed.byteLength,\n compression: algorithm,\n chunkSize,\n chunkCount,\n ...(mimeType !== undefined ? { mimeType } : {}),\n createdAt: new Date().toISOString(),\n refCount: 1,\n ...(wrappedCek !== undefined ? { _cek: wrappedCek } : {}),\n })\n }\n\n // Step 7 — CAS-update slot metadata\n const uploaderUserId = opts?.uploadedBy ?? this.userId\n await this.casUpdateSlots((slots) => {\n const oldETag = slots[slotName]?.eTag\n slots[slotName] = {\n eTag,\n filename: slotName,\n size: data.byteLength,\n ...(mimeType !== undefined ? { mimeType } : {}),\n uploadedAt: new Date().toISOString(),\n ...(uploaderUserId !== undefined ? { uploadedBy: uploaderUserId } : {}),\n }\n // Schedule old eTag refCount decrement (non-blocking best-effort)\n if (oldETag && oldETag !== eTag) {\n this._deferredRefDecrement = oldETag\n }\n return slots\n })\n\n // Release the old eTag outside the CAS loop. An erasable blob dropping to\n // refCount 0 here is crypto-shredded eagerly; a legacy one defers to GC.\n if (this._deferredRefDecrement) {\n const oldETag = this._deferredRefDecrement\n this._deferredRefDecrement = undefined\n await this.releaseRef(oldETag, 1, false).catch(() => {\n // Best-effort — a missed decrement is reconciled by a later pass.\n })\n }\n }\n\n private _deferredRefDecrement: string | undefined\n\n /**\n * Fetch all bytes for the named slot.\n * Returns `null` if the slot does not exist.\n * Throws `NotFoundError` if the index entry exists but a chunk is missing.\n */\n async get(slotName: string): Promise<Uint8Array | null> {\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) return null\n\n const result = await this.loadBlobObject(slot.eTag)\n if (!result) return null\n\n return this.fetchAllChunks(result.blob)\n }\n\n /**\n * List all slot entries for this record.\n * Returns metadata only — no chunk data is loaded.\n */\n async list(): Promise<SlotInfo[]> {\n const { slots } = await this.loadSlots()\n return Object.entries(slots).map(([name, slot]) => ({ name, ...slot }))\n }\n\n /**\n * Delete the named slot from this record.\n * Decrements refCount on the blob. Chunks are GC'd by `vault.blobGC()`.\n */\n async delete(slotName: string): Promise<void> {\n let eTagToRelease: string | undefined\n\n await this.casUpdateSlots((slots) => {\n if (!(slotName in slots)) return null\n eTagToRelease = slots[slotName]!.eTag\n delete slots[slotName]\n return slots\n })\n\n if (eTagToRelease) {\n // Erasable blobs are crypto-shredded at refCount 0 (this also covers\n // compaction eviction, which routes through delete()); legacy blobs keep\n // deferred-GC / orphan-retention semantics.\n await this.releaseRef(eTagToRelease, 1, false).catch(() => {\n // Best-effort — a missed decrement is reconciled by a later pass.\n })\n }\n }\n\n /**\n * Return a native `Response` whose body streams the decrypted,\n * decompressed blob bytes with full HTTP metadata headers.\n *\n * Note: implementation is buffered — all chunks are loaded into\n * memory before being enqueued. True streaming deferred to.\n *\n * Returns `null` if the slot does not exist.\n */\n async response(slotName: string, opts?: BlobResponseOptions): Promise<Response | null> {\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) return null\n\n const result = await this.loadBlobObject(slot.eTag)\n if (!result) return null\n\n return this.buildResponse(slot, result.blob, opts)\n }\n\n /**\n * Decrypt the slot and wrap the bytes in a browser ObjectURL ready\n * to feed into `<img src>`, `<a href>`, etc. The caller MUST call\n * `revoke()` when the URL is no longer needed — otherwise the URL\n * (and the underlying decrypted Blob) are pinned for the lifetime\n * of the document, which leaks memory in long-lived pages.\n *\n * Returns `null` when the slot does not exist.\n *\n * Throws when `URL.createObjectURL` is unavailable in the host\n * environment (Node without DOM, restricted workers). Framework\n * adapters — `useBlobURL` in `@noy-db/in-vue`, etc. — guard against\n * this for SSR contexts and stay at `null` instead of propagating.\n */\n async objectURL(\n slotName: string,\n opts?: { mimeType?: string },\n ): Promise<{ url: string; revoke: () => void } | null> {\n if (typeof URL === 'undefined' || typeof URL.createObjectURL !== 'function') {\n throw new Error(\n 'BlobSet.objectURL: URL.createObjectURL is unavailable in this environment. ' +\n 'Call this from the browser, or use BlobSet.get() and create the URL yourself.',\n )\n }\n const bytes = await this.get(slotName)\n if (!bytes) return null\n\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n const type = opts?.mimeType ?? slot?.mimeType ?? 'application/octet-stream'\n\n // Pinning the underlying ArrayBuffer in a Blob is what backs the\n // ObjectURL — once we createObjectURL the URL holds a strong ref\n // to the Blob, so the local `blob` variable can fall out of scope.\n // Copy through a fresh ArrayBuffer so TS narrows away the\n // SharedArrayBuffer branch of `ArrayBufferLike` (Uint8Array is\n // generic over the backing buffer type since TS 5.7).\n const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer\n const blob = new Blob([buffer], { type })\n const url = URL.createObjectURL(blob)\n let revoked = false\n const revoke = (): void => {\n if (revoked) return\n revoked = true\n URL.revokeObjectURL(url)\n }\n return { url, revoke }\n }\n\n // ─── Public API: Published versions (UC-3 amendment versioning) ───\n\n /**\n * Publish the current slot content as a named version snapshot.\n *\n * The published version holds an independent refCount reference to\n * the blob. Even if the slot is later overwritten or deleted, the\n * published version keeps the blob data alive.\n *\n * Publishing with an existing label overwrites it — if the eTags differ,\n * refCounts are adjusted accordingly.\n */\n async publish(slotName: string, label: string): Promise<void> {\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) throw new NotFoundError(`Slot \"${slotName}\" not found on record \"${this.recordId}\"`)\n\n // Check for existing version with this label\n const existing = await this.loadVersionRecord(slotName, label)\n if (existing && existing.eTag === slot.eTag) return // no-op: same blob\n\n // Write the version record\n const record: VersionRecord = {\n label,\n eTag: slot.eTag,\n publishedAt: new Date().toISOString(),\n ...(this.userId !== undefined ? { publishedBy: this.userId } : {}),\n }\n await this.writeVersionRecord(slotName, record)\n\n // Increment refCount for the new version's eTag\n await this.casUpdateRefCount(slot.eTag, +1)\n\n // If overwriting an existing version with a different eTag, release the old\n // one (crypto-shred at refCount 0 when erasable).\n if (existing && existing.eTag !== slot.eTag) {\n await this.releaseRef(existing.eTag, 1, false).catch(() => {})\n }\n }\n\n /**\n * Fetch bytes for a published version.\n * Returns `null` if the version does not exist.\n */\n async getVersion(slotName: string, label: string): Promise<Uint8Array | null> {\n const record = await this.loadVersionRecord(slotName, label)\n if (!record) return null\n\n const result = await this.loadBlobObject(record.eTag)\n if (!result) return null\n\n return this.fetchAllChunks(result.blob)\n }\n\n /**\n * List all published versions for a slot.\n */\n async listVersions(slotName: string): Promise<VersionRecord[]> {\n const prefix = `${this.recordId}::${slotName}::`\n const allKeys = await this.store.list(this.vault, this.versionsCollection)\n const matchingKeys = allKeys.filter((k) => k.startsWith(prefix))\n\n const versions: VersionRecord[] = []\n for (const key of matchingKeys) {\n const envelope = await this.store.get(this.vault, this.versionsCollection, key)\n if (!envelope) continue\n\n if (!this.encrypted) {\n versions.push(JSON.parse(envelope._data) as VersionRecord)\n } else {\n const dek = await this.getDEK(this.collection)\n const json = await decrypt(envelope._iv, envelope._data, dek)\n versions.push(JSON.parse(json) as VersionRecord)\n }\n }\n\n return versions\n }\n\n /**\n * Delete a published version. Decrements refCount on its blob.\n */\n async deleteVersion(slotName: string, label: string): Promise<void> {\n const record = await this.loadVersionRecord(slotName, label)\n if (!record) return\n\n await this.deleteVersionRecord(slotName, label)\n await this.releaseRef(record.eTag, 1, false).catch(() => {})\n }\n\n /**\n * Return a `Response` for a published version — same as `response()`\n * but reads from the version record's eTag instead of the current slot.\n */\n async responseVersion(\n slotName: string,\n label: string,\n opts?: BlobResponseOptions,\n ): Promise<Response | null> {\n const record = await this.loadVersionRecord(slotName, label)\n if (!record) return null\n\n const result = await this.loadBlobObject(record.eTag)\n if (!result) return null\n\n // Build a synthetic SlotRecord from the version + blob data\n const slotLike: SlotRecord = {\n eTag: record.eTag,\n filename: opts?.filename ?? `${slotName}-${label}`,\n size: result.blob.size,\n ...(result.blob.mimeType !== undefined ? { mimeType: result.blob.mimeType } : {}),\n uploadedAt: record.publishedAt,\n ...(record.publishedBy !== undefined ? { uploadedBy: record.publishedBy } : {}),\n }\n\n return this.buildResponse(slotLike, result.blob, opts)\n }\n\n // ─── Diagnostics ──────────────────────────────────────────────────\n\n /**\n * Return the `BlobObject` metadata for the named slot.\n * Returns `null` if the slot or blob does not exist.\n */\n async blobInfo(slotName: string): Promise<BlobObject | null> {\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) return null\n const result = await this.loadBlobObject(slot.eTag)\n return result?.blob ?? null\n }\n\n // ─── Presigned URL (E5) ────────────────────────────────────────────\n\n /**\n * Generate a presigned URL for direct client download of the blob's\n * ciphertext. Only works when the blob store supports `presignUrl`.\n *\n * **Important:** The URL returns encrypted data. The caller must\n * decrypt client-side using `decryptResponse()` or a service worker.\n *\n * Returns `null` if the slot doesn't exist or the store doesn't support presigning.\n */\n async presignedUrl(slotName: string, expiresInSeconds = 3600): Promise<string | null> {\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) return null\n\n const result = await this.loadBlobObject(slot.eTag)\n if (!result) return null\n\n // Only works for single-chunk blobs where the store supports presigning\n if (result.blob.chunkCount !== 1) return null\n if (!this.store.presignUrl) return null\n\n const chunkId = `${slot.eTag}_0`\n return this.store.presignUrl(this.vault, '_blob_chunks', chunkId, expiresInSeconds)\n }\n\n /**\n * Decrypt a ciphertext Response (e.g. from a presigned URL fetch)\n * back into a plaintext Response with correct headers.\n *\n * Usage with service worker or client-side fetch:\n * ```ts\n * const url = await blobs.presignedUrl('invoice.pdf')\n * const cipherResponse = await fetch(url)\n * const plainResponse = await blobs.decryptResponse('invoice.pdf', cipherResponse)\n * ```\n */\n async decryptResponse(slotName: string, cipherResponse: Response): Promise<Response | null> {\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) return null\n\n const result = await this.loadBlobObject(slot.eTag)\n if (!result) return null\n\n // Parse the envelope from the ciphertext response\n const text = await cipherResponse.text()\n const envelope = JSON.parse(text) as { _iv: string; _data: string }\n\n const blobDEK = this.encrypted ? await this.getDEK('_blob') : null\n if (!blobDEK) {\n return this.buildResponse(slot, result.blob, { inline: true })\n }\n\n // Decrypt the single chunk\n const aad = chunkAAD(slot.eTag, 0, result.blob.chunkCount)\n const { decryptBytesWithAAD: decryptAAD } = await import('../crypto.js')\n const decrypted = await decryptAAD(envelope._iv, envelope._data, blobDEK, aad)\n const plaintext = result.blob.compression === 'gzip'\n ? await decompressBytes(decrypted)\n : decrypted\n\n const body = new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(plaintext)\n controller.close()\n },\n })\n\n const filename = slot.filename\n return new Response(body, {\n headers: {\n 'Content-Type': slot.mimeType ?? 'application/octet-stream',\n 'Content-Length': String(slot.size),\n 'ETag': `\"${slot.eTag}\"`,\n 'Content-Disposition': `inline; filename=\"${filename}\"`,\n 'Last-Modified': new Date(slot.uploadedAt).toUTCString(),\n },\n })\n }\n\n // ─── Internal: build Response from slot + blob ────────────────────\n\n private async buildResponse(\n slot: SlotRecord,\n blob: BlobObject,\n opts?: BlobResponseOptions,\n ): Promise<Response> {\n const fetchAllChunks = this.fetchAllChunks.bind(this)\n\n // buffered — all chunks loaded into memory then enqueued.\n const body = new ReadableStream<Uint8Array>({\n async start(controller) {\n try {\n const output = await fetchAllChunks(blob)\n controller.enqueue(output)\n controller.close()\n } catch (err) {\n controller.error(err)\n }\n },\n })\n\n const filename = opts?.filename ?? slot.filename\n const disposition = opts?.inline\n ? `inline; filename=\"${filename}\"`\n : `attachment; filename=\"${filename}\"`\n\n return new Response(body, {\n headers: {\n 'Content-Type': slot.mimeType ?? 'application/octet-stream',\n 'Content-Length': String(slot.size),\n 'ETag': `\"${slot.eTag}\"`,\n 'Content-Disposition': disposition,\n 'Last-Modified': new Date(slot.uploadedAt).toUTCString(),\n },\n })\n }\n}\n\n// ─── Fallback for unencrypted mode ──────────────────────────────────────\n\nimport { sha256Hex } from '../crypto.js'\n\nasync function plainSha256Hex(data: Uint8Array): Promise<string> {\n return sha256Hex(data)\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts"],"sourcesContent":["/**\n * All NOYDB error classes — a single import surface for `catch` blocks and\n * `instanceof` checks.\n *\n * ## Class hierarchy\n *\n * ```\n * Error\n * └─ NoydbError (code: string)\n * ├─ Crypto errors\n * │ ├─ DecryptionError — AES-GCM tag failure\n * │ ├─ TamperedError — ciphertext modified after write\n * │ └─ InvalidKeyError — wrong passphrase / corrupt keyring\n * ├─ Access errors\n * │ ├─ NoAccessError — no DEK for this collection\n * │ ├─ ReadOnlyError — ro permission, write attempted\n * │ ├─ PermissionDeniedError — role too low for operation\n * │ ├─ PrivilegeEscalationError — grant wider than grantor holds\n * │ └─ StoreCapabilityError — optional store method missing\n * ├─ Sync errors\n * │ ├─ ConflictError — optimistic-lock version mismatch\n * │ ├─ BundleVersionConflictError — bundle push rejected by remote\n * │ └─ NetworkError — push/pull network failure\n * ├─ Data errors\n * │ ├─ NotFoundError — get(id) on missing record\n * │ ├─ ValidationError — application-level guard failed\n * │ └─ SchemaValidationError — Standard Schema v1 rejection\n * ├─ Query errors\n * │ ├─ JoinTooLargeError — join row ceiling exceeded\n * │ ├─ CrossJoinTooLargeError — cross-join row ceiling exceeded\n * │ ├─ CrossJoinSourceUnknownError — target collection not in vault\n * │ ├─ DanglingReferenceError — strict ref() points at nothing\n * │ ├─ GroupCardinalityError — groupBy bucket cap exceeded\n * │ ├─ IndexRequiredError — lazy-mode query touches unindexed field\n * │ ├─ IndexWriteFailureError — index side-car put/delete failed post-main\n * │ ├─ UniqueConstraintError — duplicate value on unique index\n * │ └─ UnsupportedIndexOptionError — unique+lazy or unique+crdt at registration\n * ├─ i18n / Dictionary errors\n * │ ├─ ReservedCollectionNameError\n * │ ├─ DictKeyMissingError\n * │ ├─ DictKeyInUseError\n * │ ├─ MissingTranslationError\n * │ ├─ LocaleNotSpecifiedError\n * │ ├─ ScriptViolationError\n * │ ├─ StaticDictReadonlyError\n * │ ├─ UnknownDictCodeError\n * │ └─ TranslatorNotConfiguredError\n * ├─ Backup errors\n * │ ├─ BackupLedgerError — hash-chain verification failed\n * │ └─ BackupCorruptedError — envelope hash mismatch in dump\n * ├─ Bundle errors\n * │ └─ BundleIntegrityError — .noydb body sha256 mismatch\n * ├─ Session errors\n * │ ├─ SessionExpiredError\n * │ ├─ SessionNotFoundError\n * │ └─ SessionPolicyError\n * ├─ Snapshot errors\n * │ └─ SnapshotNotFoundError — snapshot key absent from snapshot store\n * └─ Computed field errors\n * └─ ComputedFieldError — computed function threw during a write\n * └─ Erasure errors\n * └─ ForgetStrategyNotConfiguredError — vault.forget() with no withForgetCascade\n * └─ Sealed-record errors (record-scoped CEK sealing, #306)\n * ├─ SealedRecordExpiredError — sealed CEK binding past expiresAt\n * ├─ SealedRecordMismatchError — CEK sealed for record A used on record B\n * └─ RecordCekNotFoundError — record missing or no per-record `_cek`\n * ```\n *\n * ## Catching all NOYDB errors\n *\n * ```ts\n * import { NoydbError, InvalidKeyError, ConflictError } from '@noy-db/hub'\n *\n * try {\n * await vault.unlock(passphrase)\n * } catch (e) {\n * if (e instanceof InvalidKeyError) { showBadPassphraseUI(); return }\n * if (e instanceof NoydbError) { logToSentry(e.code, e); return }\n * throw e // unexpected — re-throw\n * }\n * ```\n *\n * @module\n */\n\n/**\n * Base class for all NOYDB errors.\n *\n * Every error thrown by `@noy-db/hub` extends this class, so consumers can\n * catch all NOYDB errors in a single `catch (e) { if (e instanceof NoydbError) ... }`\n * block. The `code` field is a machine-readable string (e.g. `'DECRYPTION_FAILED'`)\n * suitable for `switch` statements and logging pipelines.\n */\nexport class NoydbError extends Error {\n /** Machine-readable error code. Stable across library versions. */\n readonly code: string\n\n constructor(code: string, message: string) {\n super(message)\n this.name = 'NoydbError'\n this.code = code\n }\n}\n\n// ─── Crypto Errors ─────────────────────────────────────────────────────\n\n/**\n * Thrown when AES-GCM decryption fails.\n *\n * The most common cause is a wrong passphrase or a corrupted ciphertext.\n * A `DecryptionError` at the wrong passphrase level is caught internally\n * and re-thrown as `InvalidKeyError` — so in practice this surfaces for\n * per-record corruption rather than authentication failures.\n */\nexport class DecryptionError extends NoydbError {\n constructor(message = 'Decryption failed') {\n super('DECRYPTION_FAILED', message)\n this.name = 'DecryptionError'\n }\n}\n\n/**\n * Thrown when GCM tag verification fails, indicating the ciphertext was\n * modified after encryption.\n *\n * AES-256-GCM is authenticated encryption — the tag over the ciphertext\n * is checked on every decrypt. If any byte was flipped (accidental\n * corruption or deliberate tampering), decryption throws this error.\n * Treat it as a security alert: the stored bytes are not what NOYDB wrote.\n */\nexport class TamperedError extends NoydbError {\n constructor(message = 'Data integrity check failed — record may have been tampered with') {\n super('TAMPERED', message)\n this.name = 'TamperedError'\n }\n}\n\n/**\n * Thrown when key unwrapping fails, typically because the passphrase is wrong\n * or the keyring file is corrupted.\n *\n * NOYDB uses AES-KW (RFC 3394) to wrap DEKs with the KEK. If AES-KW\n * unwrapping fails, it means either the KEK was derived from the wrong\n * passphrase (PBKDF2 with 600K iterations) or the keyring bytes are\n * corrupted. This is the error shown to the user on a failed unlock attempt.\n */\nexport class InvalidKeyError extends NoydbError {\n constructor(message = 'Invalid key — wrong passphrase or corrupted keyring') {\n super('INVALID_KEY', message)\n this.name = 'InvalidKeyError'\n }\n}\n\n/**\n * Thrown when a keyring's wrapped-DEK set unwraps partially — at least\n * one DEK succeeds (proving the KEK is correct) but at least one fails.\n * The passphrase is right; the failed entries are corrupted.\n *\n * This is distinct from {@link InvalidKeyError} so that\n * `NoydbOptions.onInvalidKey: 'reset'` does NOT fire — resetting on\n * partial corruption would destroy the still-valid DEKs and the data\n * they protect, which is silent data loss in response to a feature\n * designed for stale-credential recovery.\n */\nexport class KeyringCorruptError extends NoydbError {\n readonly failedCollections: readonly string[]\n readonly intactCount: number\n constructor(opts: { failedCollections: readonly string[]; intactCount: number; message?: string }) {\n super(\n 'KEYRING_CORRUPT',\n opts.message ??\n `Keyring has ${opts.failedCollections.length} corrupted wrapped DEK(s) ` +\n `(${opts.failedCollections.join(', ')}); ${opts.intactCount} other DEK(s) ` +\n `unwrapped successfully — the passphrase is correct, the entries are damaged. ` +\n `Do NOT use onInvalidKey: 'reset' here — that would destroy the intact DEKs.`,\n )\n this.name = 'KeyringCorruptError'\n this.failedCollections = opts.failedCollections\n this.intactCount = opts.intactCount\n }\n}\n\n// ─── Access Errors ─────────────────────────────────────────────────────\n\n/**\n * Thrown when the authenticated user does not have a DEK for the requested\n * collection — i.e. the collection is not in their keyring at all.\n *\n * This is the \"no key for this door\" error. It is different from\n * `ReadOnlyError` (user has a key but it only grants ro) and from\n * `PermissionDeniedError` (user's role doesn't allow the operation).\n */\nexport class NoAccessError extends NoydbError {\n constructor(message = 'No access — user does not have a key for this collection') {\n super('NO_ACCESS', message)\n this.name = 'NoAccessError'\n }\n}\n\n/**\n * Thrown when a user with read-only (`ro`) permission attempts a write\n * operation (`put` or `delete`) on a collection.\n *\n * The user has a DEK for the collection (they can decrypt and read), but\n * their keyring grants only `ro`. To fix: re-grant the user with `rw`\n * permission, or do not attempt writes as a viewer/client role.\n */\nexport class ReadOnlyError extends NoydbError {\n constructor(message = 'Read-only — user has ro permission on this collection') {\n super('READ_ONLY', message)\n this.name = 'ReadOnlyError'\n }\n}\n\n/**\n * Thrown when a write is attempted against a historical view produced\n * by `vault.at(timestamp)`. Time-machine views are read-only by\n * contract — mutating the past would require either the shadow-vault\n * mechanism or a ledger-history rewrite (which breaks\n * the tamper-evidence guarantee).\n *\n * Distinct from {@link ReadOnlyError} (keyring-level) and\n * {@link PermissionDeniedError} (role-level): this error is about the\n * *view* being historical, independent of the caller's permissions.\n */\nexport class ReadOnlyAtInstantError extends NoydbError {\n constructor(operation: string, timestamp: string) {\n super(\n 'READ_ONLY_AT_INSTANT',\n `Cannot ${operation}() on a vault view anchored at ${timestamp} — time-machine views are read-only`,\n )\n this.name = 'ReadOnlyAtInstantError'\n }\n}\n\n/**\n * Thrown when a write is attempted against a shadow-vault frame\n * produced by `vault.frame()`. Frames are read-only by contract —\n * the use case is screen-sharing / demos / compliance review where\n * the operator wants to prevent accidental edits.\n *\n * Behavioural enforcement only — the underlying keyring still holds\n * write-capable DEKs. See {@link VaultFrame} for the full caveat.\n */\nexport class ReadOnlyFrameError extends NoydbError {\n constructor(operation: string) {\n super(\n 'READ_ONLY_FRAME',\n `Cannot ${operation}() on a vault frame — frames are read-only presentations of the current vault`,\n )\n this.name = 'ReadOnlyFrameError'\n }\n}\n\n/**\n * Thrown when the authenticated user's role does not permit the requested\n * operation — e.g. a `viewer` calling `grantAccess()`, or an `operator`\n * calling `rotateKeys()`.\n *\n * This is a role-level check (what the user's role allows), distinct from\n * `NoAccessError` (collection not in keyring) and `ReadOnlyError` (in\n * keyring, but write not allowed).\n */\nexport class PermissionDeniedError extends NoydbError {\n constructor(message = 'Permission denied — insufficient role for this operation') {\n super('PERMISSION_DENIED', message)\n this.name = 'PermissionDeniedError'\n }\n}\n\n/**\n * Thrown when an `@noy-db/as-*` export is attempted without the\n * required capability bit on the invoking keyring.\n *\n * Two sub-cases discriminated by the `tier` field:\n *\n * - `tier: 'plaintext'` — a plaintext-tier export (`as-xlsx`,\n * `as-csv`, `as-blob`, `as-zip`, …) was attempted but the\n * keyring's `exportCapability.plaintext` does not include the\n * requested `format` (nor the `'*'` wildcard). Default for every\n * role is `plaintext: []` — the owner must positively grant.\n * - `tier: 'bundle'` — an encrypted `as-noydb` bundle export was\n * attempted but the keyring's `exportCapability.bundle` is\n * `false`. Default for `owner`/`admin` is `true`; for\n * `operator`/`viewer`/`client` it is `false`.\n *\n * Distinct from `PermissionDeniedError` (role-level check) and\n * `NoAccessError` (collection not readable). Surfaces separately so\n * UI layers can show a \"request the export capability from your\n * admin\" flow rather than a generic permission error.\n */\nexport class ExportCapabilityError extends NoydbError {\n readonly tier: 'plaintext' | 'bundle'\n readonly format?: string\n readonly userId: string\n\n constructor(opts: {\n tier: 'plaintext' | 'bundle'\n userId: string\n format?: string\n message?: string\n }) {\n const msg =\n opts.message ??\n (opts.tier === 'plaintext'\n ? `Export capability denied — keyring \"${opts.userId}\" is not granted plaintext-export capability for format \"${opts.format ?? '<unknown>'}\". Ask a vault owner or admin to grant it via vault.grant({ exportCapability: { plaintext: ['${opts.format ?? '<format>'}'] } }).`\n : `Export capability denied — keyring \"${opts.userId}\" is not granted encrypted-bundle export capability. Ask a vault owner or admin to grant it via vault.grant({ exportCapability: { bundle: true } }).`)\n super('EXPORT_CAPABILITY', msg)\n this.name = 'ExportCapabilityError'\n this.tier = opts.tier\n this.userId = opts.userId\n if (opts.format !== undefined) this.format = opts.format\n }\n}\n\n/**\n * Thrown when a keyring file's `expires_at` cutoff has passed.\n * Surfaced by `loadKeyring` before any DEK unwrap is attempted —\n * past the cutoff the slot refuses to open even with the right\n * passphrase. Distinct from PBKDF2 / unwrap errors so consumer code\n * can show a precise \"this bundle slot has expired\" message instead\n * of the generic decryption-failure UX.\n *\n * Used predominantly on `BundleRecipient` slots produced by\n * `writeNoydbBundle({ recipients: [...] })` to time-box audit access.\n */\nexport class KeyringExpiredError extends NoydbError {\n readonly userId: string\n readonly expiresAt: string\n constructor(opts: { userId: string; expiresAt: string }) {\n super(\n 'KEYRING_EXPIRED',\n `Keyring \"${opts.userId}\" expired at ${opts.expiresAt}. ` +\n 'The slot refuses to unlock past its expiry timestamp.',\n )\n this.name = 'KeyringExpiredError'\n this.userId = opts.userId\n this.expiresAt = opts.expiresAt\n }\n}\n\n/**\n * Thrown when an `@noy-db/as-*` import is attempted but the invoking\n * keyring lacks the required import-capability bit.\n *\n * - `tier: 'plaintext'` — a plaintext-tier import (`as-csv`, `as-json`,\n * `as-ndjson`, `as-zip`, …) was attempted but the keyring's\n * `importCapability.plaintext` does not include the requested\n * `format` (nor the `'*'` wildcard).\n * - `tier: 'bundle'` — a `.noydb` bundle import was attempted but the\n * keyring's `importCapability.bundle` is not `true`.\n *\n * Default for every role on every dimension is closed — owners and\n * admins must positively grant the capability. Distinct from\n * `PermissionDeniedError` and `NoAccessError` so UI layers can show a\n * specific \"request the import capability\" flow.\n */\nexport class ImportCapabilityError extends NoydbError {\n readonly tier: 'plaintext' | 'bundle'\n readonly format?: string\n readonly userId: string\n\n constructor(opts: {\n tier: 'plaintext' | 'bundle'\n userId: string\n format?: string\n message?: string\n }) {\n const msg =\n opts.message ??\n (opts.tier === 'plaintext'\n ? `Import capability denied — keyring \"${opts.userId}\" is not granted plaintext-import capability for format \"${opts.format ?? '<unknown>'}\". Ask a vault owner or admin to grant it via vault.grant({ importCapability: { plaintext: ['${opts.format ?? '<format>'}'] } }).`\n : `Import capability denied — keyring \"${opts.userId}\" is not granted encrypted-bundle import capability. Ask a vault owner or admin to grant it via vault.grant({ importCapability: { bundle: true } }).`)\n super('IMPORT_CAPABILITY', msg)\n this.name = 'ImportCapabilityError'\n this.tier = opts.tier\n this.userId = opts.userId\n if (opts.format !== undefined) this.format = opts.format\n }\n}\n\n/**\n * Thrown when a grant would give the grantee a permission the grantor\n * does not themselves hold — the \"admin cannot grant what admin cannot\n * do\" rule from the admin-delegation work.\n *\n * Distinct from `PermissionDeniedError` so callers can tell the two\n * cases apart in logs and tests:\n *\n * - `PermissionDeniedError` — \"you are not allowed to perform this\n * operation at all\" (wrong role).\n * - `PrivilegeEscalationError` — \"you are allowed to grant, but not\n * with these specific permissions\" (widening attempt).\n *\n * Under the admin model the grantee of an admin-grants-admin call\n * inherits the caller's entire DEK set by construction, so this error\n * is structurally unreachable in typical flows. The check and error\n * class exist so that future per-collection admin scoping cannot\n * accidentally bypass the subset rule — the guard is already wired in.\n *\n * `offendingCollection` carries the first collection name that failed\n * the subset check, to make the violation actionable in error output.\n */\n/**\n * Thrown when a caller invokes an API that requires an optional\n * store capability the active store does not implement.\n *\n * Today the only call site is `Noydb.listAccessibleVaults()`,\n * which depends on the optional `NoydbStore.listVaults()`\n * method. The error message names the missing method and the calling\n * API so consumers know exactly which combination is unsupported,\n * and the `capability` field is machine-readable so library code can\n * pattern-match in catch blocks (e.g. fall back to a candidate-list\n * shape).\n *\n * The class lives in `errors.ts` rather than as a generic\n * `ValidationError` because the diagnostic shape is different: a\n * `ValidationError` says \"the inputs you passed are wrong\"; this\n * error says \"the inputs are fine, but the store you wired up\n * doesn't support what you're asking for.\" Different fix, different\n * documentation.\n */\nexport class StoreCapabilityError extends NoydbError {\n /** The store method/capability that was missing. */\n readonly capability: string\n\n constructor(capability: string, callerApi: string, storeName?: string) {\n super(\n 'STORE_CAPABILITY',\n `${callerApi} requires the optional store capability \"${capability}\" ` +\n `but the active store${storeName ? ` (${storeName})` : ''} does not implement it. ` +\n `Use a store that supports \"${capability}\" (store-memory, store-file) or pass an explicit ` +\n `vault list to bypass enumeration.`,\n )\n this.name = 'StoreCapabilityError'\n this.capability = capability\n }\n}\n\nexport class PrivilegeEscalationError extends NoydbError {\n readonly offendingCollection: string\n\n constructor(offendingCollection: string, message?: string) {\n super(\n 'PRIVILEGE_ESCALATION',\n message ??\n `Privilege escalation: grantor has no DEK for collection \"${offendingCollection}\" and cannot grant access to it.`,\n )\n this.name = 'PrivilegeEscalationError'\n this.offendingCollection = offendingCollection\n }\n}\n\n/**\n * Thrown when a reserved internal vault name (e.g. `__noydb_state__`) is used\n * as a group name or partition key.\n *\n * Internal vault names are prefixed or surrounded with double-underscores to\n * avoid collisions with user-defined vault names. Attempting to use one as a\n * group name or partition key bypasses the naming policy and is rejected\n * eagerly so the mis-configuration is surfaced immediately.\n */\nexport class ReservedVaultNameError extends NoydbError {\n /** The rejected vault name. */\n readonly vaultName: string\n\n constructor(vaultName: string) {\n super(\n 'RESERVED_VAULT_NAME',\n `\"${vaultName}\" is a reserved internal vault name and cannot be used as a group name or partition key`,\n )\n this.name = 'ReservedVaultNameError'\n this.vaultName = vaultName\n }\n}\n\n/**\n * Thrown by `Collection.put` / `.delete` when the target record's\n * envelope `_ts` falls within a closed accounting period.\n *\n * Distinct from `ReadOnlyError` (keyring-level), `ReadOnlyAtInstantError`\n * (historical view), and `ReadOnlyFrameError` (shadow vault): this\n * error is about the STORED RECORD being sealed by an operator call\n * to `vault.closePeriod()`, independent of caller permissions or\n * view type. The `periodName` and `endDate` fields name the sealing\n * period so audit UIs can surface a \"this record is locked in\n * FY2026-Q1 (closed 2026-03-31)\" message without parsing the error\n * string.\n *\n * To apply a correction after close, book a compensating entry in a\n * new period rather than unlocking the old one. Re-opening a closed\n * period is deliberately unsupported.\n */\nexport class PeriodClosedError extends NoydbError {\n readonly periodName: string\n readonly endDate: string\n readonly recordTs: string\n\n constructor(periodName: string, endDate: string, recordTs: string) {\n super(\n 'PERIOD_CLOSED',\n `Cannot modify record (last written ${recordTs}) — sealed by closed period ` +\n `\"${periodName}\" (endDate: ${endDate}). Post a compensating entry in a ` +\n `new period instead.`,\n )\n this.name = 'PeriodClosedError'\n this.periodName = periodName\n this.endDate = endDate\n this.recordTs = recordTs\n }\n}\n\n/**\n * Thrown when a `put()` or `delete()` is rejected by a guard's `check`\n * function. The `reason` is the message the guard supplied — typically a\n * short business description (e.g. \"invoice is issued\"). The full\n * collection + id are surfaced so audit UIs can link back to the record.\n */\nexport class RecordLockedError extends NoydbError {\n readonly collection: string\n readonly id: string\n readonly reason: string\n\n constructor(collection: string, id: string, reason: string) {\n super(\n 'RECORD_LOCKED',\n `Cannot modify ${collection}/${id} — locked by guard: ${reason}. ` +\n `Use withTransactions({ amendment: true, reason }) with admin/owner role to override.`,\n )\n this.name = 'RecordLockedError'\n this.collection = collection\n this.id = id\n this.reason = reason\n }\n}\n\n/**\n * Thrown when a `put()` changes one or more fields that are frozen by a\n * `frozenFields` guard. The `fields` list contains the specific paths\n * that were detected as changed.\n */\nexport class FieldFrozenError extends NoydbError {\n readonly collection: string\n readonly id: string\n readonly fields: readonly string[]\n\n constructor(collection: string, id: string, fields: readonly string[]) {\n super(\n 'FIELD_FROZEN',\n `Cannot change frozen field(s) on ${collection}/${id}: ${fields.join(', ')}. ` +\n `Use withTransactions({ amendment: true, reason }) with admin/owner role to override.`,\n )\n this.name = 'FieldFrozenError'\n this.collection = collection\n this.id = id\n this.fields = fields\n }\n}\n\n/**\n * Thrown by an amendment invariant when the proposed change-set violates\n * the declared business rule (e.g. disbursement total not preserved).\n * Triggers a full transaction rollback via the existing revert pass.\n */\nexport class InvariantError extends NoydbError {\n constructor(message: string) {\n super('INVARIANT_VIOLATED', message)\n this.name = 'InvariantError'\n }\n}\n\n/**\n * Thrown at `withTransactions({ amendment: true })` open if the caller's\n * role is not in the guard's allowed amendment roles. Fail-fast: thrown\n * before any writes are attempted.\n */\nexport class AmendmentForbiddenError extends NoydbError {\n readonly userId: string\n readonly role: string\n\n constructor(userId: string, role: string) {\n super(\n 'AMENDMENT_FORBIDDEN',\n `User \"${userId}\" with role \"${role}\" cannot open an amendment transaction. ` +\n `Amendments require admin or owner role.`,\n )\n this.name = 'AmendmentForbiddenError'\n this.userId = userId\n this.role = role\n }\n}\n\n/**\n * Thrown by `listUsersWithEnvelopes` when the vault's user directory\n * has been disabled (via `db.setDirectoryEnabled(vault, false)`) and\n * the caller's role is neither `owner` nor `admin`. Owner/admin can\n * still enumerate users — the toggle is a UX privacy switch, not a\n * security boundary.\n *\n * Honest caveat: this is a UX flag, not a privacy guarantee. The\n * envelope ciphertext is still in the store, the keyring file is\n * still listed at `_keyring/*`, and anyone with direct store read\n * access can count keyrings without going through the hub. See\n * `docs/subsystems/user-envelope.md` → \"Directory visibility\".\n */\nexport class DirectoryDisabledError extends NoydbError {\n readonly vault: string\n\n constructor(vault: string) {\n super(\n 'DIRECTORY_DISABLED',\n `Vault \"${vault}\" has its user directory disabled. ` +\n `Only owners and admins can call listUsersWithEnvelopes() here. ` +\n `Use db.setDirectoryEnabled(vault, true) to re-enable.`,\n )\n this.name = 'DirectoryDisabledError'\n this.vault = vault\n }\n}\n\n// ─── Hierarchical Access Errors ─────────────────────\n\n/**\n * Thrown when a user tries to act at a tier they are not cleared for.\n *\n * This is the umbrella error for tier write refusals:\n * - `put({ tier: N })` when the user's keyring lacks tier-N DEK.\n * - `elevate(id, N)` when the caller cannot reach tier N.\n *\n * Distinct from `TierAccessDeniedError` which covers *read* refusals on\n * the invisibility/ghost path.\n */\nexport class TierNotGrantedError extends NoydbError {\n readonly tier: number\n readonly collection: string\n\n constructor(collection: string, tier: number) {\n super(\n 'TIER_NOT_GRANTED',\n `User has no DEK for tier ${tier} in collection \"${collection}\"`,\n )\n this.name = 'TierNotGrantedError'\n this.collection = collection\n this.tier = tier\n }\n}\n\n/**\n * Thrown when an elevated-handle operation runs after the elevation's\n * TTL expired. Reads continue at the original tier; only writes\n * through the scoped handle flip to throwing once expired.\n */\nexport class ElevationExpiredError extends NoydbError {\n readonly tier: number\n readonly expiresAt: number\n\n constructor(opts: { tier: number; expiresAt: number }) {\n super(\n 'ELEVATION_EXPIRED',\n `Elevation to tier ${opts.tier} expired at ${new Date(opts.expiresAt).toISOString()}`,\n )\n this.name = 'ElevationExpiredError'\n this.tier = opts.tier\n this.expiresAt = opts.expiresAt\n }\n}\n\n/**\n * Thrown by `vault.elevate(...)` when an elevation is already active\n * on the vault. Adopters must `release()` the existing handle before\n * starting a new elevation.\n */\nexport class AlreadyElevatedError extends NoydbError {\n readonly activeTier: number\n\n constructor(activeTier: number) {\n super(\n 'ALREADY_ELEVATED',\n `Vault is already elevated to tier ${activeTier}; release the existing handle first`,\n )\n this.name = 'AlreadyElevatedError'\n this.activeTier = activeTier\n }\n}\n\n/**\n * Thrown when `demote()` is called by someone who is not the original\n * elevator and not an owner.\n */\nexport class TierDemoteDeniedError extends NoydbError {\n constructor(id: string, tier: number) {\n super(\n 'TIER_DEMOTE_DENIED',\n `Only the original elevator or an owner can demote record \"${id}\" from tier ${tier}`,\n )\n this.name = 'TierDemoteDeniedError'\n }\n}\n\n/**\n * Thrown when `db.delegate()` is called against a user that has no\n * keyring in the target vault — the delegation token cannot be\n * constructed without the target user's KEK wrap.\n */\nexport class DelegationTargetMissingError extends NoydbError {\n readonly toUser: string\n\n constructor(toUser: string) {\n super(\n 'DELEGATION_TARGET_MISSING',\n `Delegation target user \"${toUser}\" has no keyring in this vault`,\n )\n this.name = 'DelegationTargetMissingError'\n this.toUser = toUser\n }\n}\n\n// ─── Sync Errors ───────────────────────────────────────────────────────\n\n/**\n * Thrown when a `put()` detects an optimistic concurrency conflict.\n *\n * NOYDB uses version numbers (`_v`) for optimistic locking. If a `put()`\n * is called with `expectedVersion: N` but the stored record is at version\n * `M ≠ N`, the write is rejected and the caller must re-read, re-apply their\n * change, and retry. The `version` field carries the actual stored version\n * so callers can decide whether to retry or surface the conflict to the user.\n */\nexport class ConflictError extends NoydbError {\n /** The actual stored version at the time of conflict. */\n readonly version: number\n\n constructor(version: number, message = 'Version conflict') {\n super('CONFLICT', message)\n this.name = 'ConflictError'\n this.version = version\n }\n}\n\n/**\n * Thrown by `LedgerStore.append()` after exhausting its CAS retry\n * budget under multi-writer contention. Two browser tabs, a\n * web app + an offline mobile peer, or a server worker pool all\n * producing ledger entries against the same vault can race on the\n * \"read head, write head+1\" cycle; the optimistic-CAS retry loop\n * resolves the race for `casAtomic: true` stores, but pathological\n * contention (or a buggy peer) can still exhaust the budget. When\n * that happens, the chain is intact — the failed writer simply\n * couldn't claim a slot. Caller's choice whether to retry, queue,\n * or surface the failure to the user.\n */\nexport class LedgerContentionError extends NoydbError {\n readonly attempts: number\n\n constructor(attempts: number) {\n super(\n 'LEDGER_CONTENTION',\n `LedgerStore.append: failed to claim a chain slot after ${attempts} optimistic-CAS retries`,\n )\n this.name = 'LedgerContentionError'\n this.attempts = attempts\n }\n}\n\n/**\n * Thrown by `vault.sequence(name).next()` after exhausting its CAS retry\n * budget under contention. The counter is intact; the caller may retry.\n */\nexport class SequenceContentionError extends NoydbError {\n readonly sequence: string\n readonly attempts: number\n\n constructor(sequence: string, attempts: number) {\n super(\n 'SEQUENCE_CONTENTION',\n `vault.sequence(\"${sequence}\").next(): failed to allocate after ${attempts} optimistic-CAS retries`,\n )\n this.name = 'SequenceContentionError'\n this.sequence = sequence\n this.attempts = attempts\n }\n}\n\n/**\n * Thrown by `vault.sequence(name).next()` when the backing store is not\n * CAS-capable (`capabilities.casAtomic !== true`). Gap-free numbering\n * requires single-authority serialization, which an offline / non-CAS\n * store cannot provide — this is a deliberate online-only wall.\n */\nexport class SequenceOfflineError extends NoydbError {\n constructor() {\n super(\n 'SEQUENCE_OFFLINE',\n 'vault.sequence().next() requires an online CAS-capable store ' +\n '(capabilities.casAtomic). Gap-free numbering cannot be serialized offline.',\n )\n this.name = 'SequenceOfflineError'\n }\n}\n\n/** Thrown by a deferred-numbering pass when the store clock is unavailable or its uncertainty cannot be resolved. */\nexport class NumberingUncertaintyError extends NoydbError {\n readonly series: string\n constructor(series: string) {\n super(\n 'NUMBERING_UNCERTAINTY',\n `Deferred numbering for series \"${series}\" cannot run: the store does not expose getStoreTime() ` +\n `(capabilities.serverWriteTime). Use a CAS sequence or a store with serverWriteTime.`,\n )\n this.name = 'NumberingUncertaintyError'\n this.series = series\n }\n}\n\n/**\n * Thrown when a bundle push is rejected because the remote has been updated\n * since the local bundle was last pulled.\n *\n * Unlike `ConflictError` (per-record), this is a whole-bundle conflict —\n * the remote's bundle handle has changed. The caller must pull the new\n * bundle, merge, and re-push. `remoteVersion` is the handle of the newer\n * remote bundle for use in diagnostics.\n */\nexport class BundleVersionConflictError extends NoydbError {\n /** The bundle handle of the newer remote version that rejected the push. */\n readonly remoteVersion: string\n\n constructor(remoteVersion: string, message = 'Bundle version conflict — remote has been updated') {\n super('BUNDLE_VERSION_CONFLICT', message)\n this.name = 'BundleVersionConflictError'\n this.remoteVersion = remoteVersion\n }\n}\n\n/**\n * Thrown when a sync operation (push or pull) fails due to a network error.\n *\n * NOYDB's offline-first design means network errors are expected during sync.\n * Callers should catch `NetworkError`, surface connectivity status in the UI,\n * and rely on the `SyncScheduler` to retry when connectivity is restored.\n */\nexport class NetworkError extends NoydbError {\n constructor(message = 'Network error') {\n super('NETWORK_ERROR', message)\n this.name = 'NetworkError'\n }\n}\n\n// ─── Data Errors ───────────────────────────────────────────────────────\n\n/**\n * Thrown when `collection.get(id)` is called with an ID that does not exist.\n *\n * NOYDB collections are memory-first, so this error is synchronous and cheap —\n * it does not make a network round-trip. Callers that expect the record to be\n * absent should use `collection.getOrNull(id)` instead.\n */\nexport class NotFoundError extends NoydbError {\n constructor(message = 'Record not found') {\n super('NOT_FOUND', message)\n this.name = 'NotFoundError'\n }\n}\n\n/**\n * Thrown when application-level validation fails before encryption.\n *\n * Distinct from `SchemaValidationError` (Standard Schema v1 validator)\n * and `MissingTranslationError` (i18nText). `ValidationError` is the\n * general-purpose validation base — use it for custom guards in `put()`\n * hooks or store middleware.\n */\nexport class ValidationError extends NoydbError {\n constructor(message = 'Validation error') {\n super('VALIDATION_ERROR', message)\n this.name = 'ValidationError'\n }\n}\n\n/**\n * Thrown when a Standard Schema v1 validator rejects a record on\n * `put()` (input validation) or on read (output validation). Carries\n * the raw issue list so callers can render field-level errors.\n *\n * `direction` distinguishes the two cases:\n * - `'input'`: the user passed bad data into `put()`. This is a\n * normal error case that application code should handle — typically\n * by showing validation messages in the UI.\n * - `'output'`: stored data does not match the current schema. This\n * indicates a schema drift (the schema was changed without\n * migrating the existing records) and should be treated as a bug\n * — the application should not swallow it silently.\n *\n * The `issues` type is deliberately `readonly unknown[]` on this class\n * so that `errors.ts` doesn't need to import from `schema.ts` (and\n * create a dependency cycle). Callers who know they're holding a\n * `SchemaValidationError` can cast to the more precise\n * `readonly StandardSchemaV1Issue[]` from `schema.ts`.\n */\nexport class SchemaValidationError extends NoydbError {\n readonly issues: readonly unknown[]\n readonly direction: 'input' | 'output'\n\n constructor(\n message: string,\n issues: readonly unknown[],\n direction: 'input' | 'output',\n ) {\n super('SCHEMA_VALIDATION_FAILED', message)\n this.name = 'SchemaValidationError'\n this.issues = issues\n this.direction = direction\n }\n}\n\n/** Base for schema-evolution strategy rejections. */\nexport class SchemaUpdateError extends NoydbError {\n constructor(code: string, message: string) {\n super(code, message)\n this.name = 'SchemaUpdateError'\n }\n}\n\n/** A non-additive schema change was rejected by the `additiveOnly()` strategy. */\nexport class NonAdditiveSchemaChangeError extends SchemaUpdateError {\n constructor(message: string) {\n super('NON_ADDITIVE_SCHEMA_CHANGE', message)\n this.name = 'NonAdditiveSchemaChangeError'\n }\n}\n\n/** A schema change was rejected by the `lockSchema()` strategy. */\nexport class SchemaLockedError extends SchemaUpdateError {\n constructor(message: string) {\n super('SCHEMA_LOCKED', message)\n this.name = 'SchemaLockedError'\n }\n}\n\n/** Write attempted while a schema cutover fence is up (draining/migrating, or this collection has a pending cutover). */\nexport class SchemaFenceError extends SchemaUpdateError {\n constructor(message: string) {\n super('SCHEMA_FENCE', message)\n this.name = 'SchemaFenceError'\n }\n}\n\n/** Write attempted by a client whose generation snapshot is behind the live fence — reload required. */\nexport class MigrationRequiredError extends SchemaUpdateError {\n constructor(message: string) {\n super('MIGRATION_REQUIRED', message)\n this.name = 'MigrationRequiredError'\n }\n}\n\n/** A coordinated cutover timed out waiting for active clients to quiesce. */\nexport class QuiesceTimeoutError extends SchemaUpdateError {\n constructor(message: string) {\n super('QUIESCE_TIMEOUT', message)\n this.name = 'QuiesceTimeoutError'\n }\n}\n\n// ─── Query DSL Errors ─────────────────────────────────────────────────\n\n/**\n * Thrown when `.groupBy().aggregate()` produces more than the hard\n * cardinality cap (default 100_000 groups)..\n *\n * The cap exists because `.groupBy()` materializes one bucket per\n * distinct key value in memory, and runaway cardinality — a groupBy\n * on a high-uniqueness field like `id` or `createdAt` — is almost\n * always a query mistake rather than legitimate use. A hard error is\n * better than silent OOM: the consumer sees an actionable message\n * naming the field and the observed cardinality, with guidance to\n * either narrow the query with `.where()` or accept the ceiling\n * override.\n *\n * A separate one-shot warning fires at 10% of the cap (10_000\n * groups) so consumers get a heads-up before the hard error — same\n * pattern as `JoinTooLargeError` and the `.join()` row ceiling.\n *\n * **Not overridable in.** The 100k cap is a fixed constant so\n * the failure mode is consistent across the codebase; a\n * `{ maxGroups }` override can be added later without a break if a\n * real consumer asks.\n */\nexport class GroupCardinalityError extends NoydbError {\n /** The field being grouped on. */\n readonly field: string\n /** Observed number of distinct groups at the moment the cap tripped. */\n readonly cardinality: number\n /** The cap that was exceeded. */\n readonly maxGroups: number\n\n constructor(field: string, cardinality: number, maxGroups: number) {\n super(\n 'GROUP_CARDINALITY',\n `.groupBy(\"${field}\") produced ${cardinality} distinct groups, ` +\n `exceeding the ${maxGroups}-group ceiling. This is almost always a ` +\n `query mistake — grouping on a high-uniqueness field like \"id\" or ` +\n `\"createdAt\" produces one bucket per record. Narrow the query with ` +\n `.where() before grouping, or group on a lower-cardinality field ` +\n `(status, category, clientId). If you genuinely need high-cardinality ` +\n `grouping, file an issue with your use case.`,\n )\n this.name = 'GroupCardinalityError'\n this.field = field\n this.cardinality = cardinality\n this.maxGroups = maxGroups\n }\n}\n\n/**\n * Thrown in lazy mode when a `.query()` / `.where()` / `.orderBy()` clause\n * references a field that does not have a declared index.\n *\n * Lazy-mode queries only work when every touched field is indexed.\n * This is deliberate — silent scan-fallback would hide the performance\n * cliff that lazy-mode indexes exist to prevent.\n *\n * Payload:\n * - `collection` — name of the collection queried\n * - `touchedFields` — every field referenced by the query (filter + order)\n * - `missingFields` — subset of `touchedFields` that have no declared index\n */\nexport class IndexRequiredError extends NoydbError {\n readonly collection: string\n readonly touchedFields: readonly string[]\n readonly missingFields: readonly string[]\n\n constructor(args: { collection: string; touchedFields: readonly string[]; missingFields: readonly string[] }) {\n super(\n 'INDEX_REQUIRED',\n `Collection \"${args.collection}\": query references unindexed fields in lazy mode ` +\n `(missing: ${args.missingFields.join(', ')}). ` +\n `Declare an index on each field, or use collection.scan() for non-indexed iteration.`,\n )\n this.name = 'IndexRequiredError'\n this.collection = args.collection\n this.touchedFields = [...args.touchedFields]\n this.missingFields = [...args.missingFields]\n }\n}\n\n/**\n * Thrown by `Collection.put()` when writing a record would violate a\n * unique-index constraint — the same field value (or composite field\n * tuple) is already held by a *different* record id in the collection.\n *\n * Properties:\n * - `collection` — name of the collection the write was targeting\n * - `recordId` — the id of the record being written (the would-be violator)\n * - `fields` — the constrained field(s), e.g. `['taxId']` or `['workerId','employerEntityId']`\n * - `conflictingId` — the id of the record already holding the value\n *\n * Null-distinct semantics: if any constrained field is `null`/`undefined`,\n * the row is exempt (the constraint does not fire). This matches standard\n * SQL NULL-distinct behavior.\n */\nexport class UniqueConstraintError extends NoydbError {\n readonly collection: string\n readonly recordId: string\n readonly fields: readonly string[]\n readonly conflictingId: string\n\n constructor(collection: string, recordId: string, fields: readonly string[], conflictingId: string) {\n super(\n 'UNIQUE_CONSTRAINT',\n `Unique constraint on ${collection}.[${fields.join(', ')}] violated: ` +\n `record \"${recordId}\" duplicates a value already held by \"${conflictingId}\".`,\n )\n this.name = 'UniqueConstraintError'\n this.collection = collection\n this.recordId = recordId\n this.fields = fields\n this.conflictingId = conflictingId\n }\n}\n\n/**\n * Thrown at collection registration when an index option is declared that\n * is incompatible with the collection's operating mode.\n *\n * Currently covers two cases:\n * - `unique: true` on a lazy-mode (`prefetch: false`) collection — lazy mode\n * does not pre-load all records, so an in-memory uniqueness map cannot be\n * maintained reliably.\n * - `unique: true` on a CRDT collection (`crdt: 'lww-map' | 'rga' | 'yjs'`) —\n * CRDT put() short-circuits the unique-constraint check, so enforcement would\n * silently not fire.\n *\n * Both cases are caught eagerly at `vault.collection()` time so the developer\n * sees the incompatibility immediately rather than shipping silently-ignored\n * constraints.\n *\n * The `option` field names the incompatible option (`'unique'`) so catch blocks\n * can pattern-match without inspecting the error message.\n */\nexport class UnsupportedIndexOptionError extends NoydbError {\n readonly option: string\n constructor(option: string, message: string) {\n super('UNSUPPORTED_INDEX_OPTION', message)\n this.name = 'UnsupportedIndexOptionError'\n this.option = option\n }\n}\n\n/**\n * Thrown (or surfaced via the `index:write-partial` event) when one or more\n * per-indexed-field side-car writes fail after the main record write has\n * already succeeded.\n *\n * Not thrown out of `.put()` / `.delete()` directly — those succeed when the\n * main record succeeds. Instead, `IndexWriteFailureError` instances are collected\n * into the session-scoped reconcile queue and emitted on the Collection\n * emitter as `index:write-partial`.\n *\n * Payload:\n * - `recordId` — the id of the main record whose side-car writes failed\n * - `field` — the indexed field whose side-car write failed\n * - `op` — `'put'` or `'delete'`, indicating which mutation was in flight\n * - `cause` — the underlying error from the store\n */\nexport class IndexWriteFailureError extends NoydbError {\n readonly recordId: string\n readonly field: string\n readonly op: 'put' | 'delete'\n override readonly cause: unknown\n\n constructor(args: { recordId: string; field: string; op: 'put' | 'delete'; cause: unknown }) {\n super(\n 'INDEX_WRITE_FAILURE',\n `Index side-car ${args.op} failed for field \"${args.field}\" on record \"${args.recordId}\"`,\n )\n this.name = 'IndexWriteFailureError'\n this.recordId = args.recordId\n this.field = args.field\n this.op = args.op\n this.cause = args.cause\n }\n}\n\n// ─── Bundle Format Errors ─────────────────────────────────\n\n/**\n * Thrown by `readNoydbBundle()` when the body bytes don't match\n * the integrity hash declared in the bundle header — i.e. someone\n * modified the bytes between write and read.\n *\n * Distinct from a generic `Error` (which would be thrown for\n * format violations like a missing magic prefix or malformed\n * header JSON) so consumers can pattern-match the corruption case\n * and handle it differently from a producer bug. A\n * `BundleIntegrityError` indicates \"the bytes you got are not\n * what was written\"; a plain `Error` from `parsePrefixAndHeader`\n * indicates \"what was written wasn't a valid bundle in the first\n * place.\"\n *\n * Also thrown when decompression fails after the integrity hash\n * passed — that's a producer bug (the wrong algorithm byte was\n * written) but it surfaces with the same error class because the\n * end result is \"the body cannot be turned back into a dump.\"\n */\nexport class BundleIntegrityError extends NoydbError {\n constructor(message: string) {\n super('BUNDLE_INTEGRITY', `.noydb bundle integrity check failed: ${message}`)\n this.name = 'BundleIntegrityError'\n }\n}\n\n/**\n * Thrown by `readNoydbBundle` when the bundle carries\n * sealed per-user passphrases but no supplied `SealingKeyProvider`\n * has a `.id` (= `pid`) matching the sealed entry's `pid`.\n *\n * Carries the failing pid + the user id so the recipient can\n * surface an actionable prompt:\n *\n * ```\n * BundleSealMismatchError: bundle carries sealed passphrase for user \"alice\"\n * under provider \"macos-keychain:com.acme.app/alice@acme.example\",\n * but no registered provider matches that pid.\n * ```\n *\n * Three resolution paths the message names (per foundation §11.9.4):\n *\n * 1. Configure a provider matching the pid and retry import.\n * 2. Pass `attemptUnsealAcrossProviders: true` to try each\n * registered provider regardless of pid.\n * 3. Inspect without unsealing — pass no `sealingProviders` to\n * receive the sealed entries unmodified for offline analysis.\n */\nexport class BundleSealMismatchError extends NoydbError {\n readonly userId: string\n readonly pid: string\n constructor(userId: string, pid: string) {\n super(\n 'BUNDLE_SEAL_MISMATCH',\n `bundle carries sealed passphrase for user \"${userId}\" under provider `\n + `\"${pid}\", but no registered provider matches that pid.\\n\\n`\n + 'Resolutions:\\n'\n + ' 1. Configure a provider matching the pid and retry import.\\n'\n + ' 2. Pass `attemptUnsealAcrossProviders: true` to try each registered\\n'\n + ' provider regardless of pid (extra credential prompts may surface).\\n'\n + ' 3. Inspect the bundle without unsealing — pass no `sealingProviders`\\n'\n + ' to receive the sealed entries unmodified for offline analysis.',\n )\n this.name = 'BundleSealMismatchError'\n this.userId = userId\n this.pid = pid\n }\n}\n\n// ─── i18n / Dictionary Errors ──────────────────────────\n\n/**\n * Thrown when `vault.collection()` is called with a name that is\n * reserved for NOYDB internal use (any name starting with `_dict_`).\n *\n * Dictionary collections are accessed exclusively via\n * `vault.dictionary(name)` — attempting to open one as a regular\n * collection would bypass the dictionary invariants (ACL, rename\n * tracking, reserved-name policy).\n */\nexport class ReservedCollectionNameError extends NoydbError {\n /** The rejected collection name. */\n readonly collectionName: string\n\n constructor(collectionName: string) {\n super(\n 'RESERVED_COLLECTION_NAME',\n `\"${collectionName}\" is a reserved collection name. ` +\n `Use vault.dictionary(\"${collectionName.replace(/^_dict_/, '')}\") ` +\n `to access dictionary collections.`,\n )\n this.name = 'ReservedCollectionNameError'\n this.collectionName = collectionName\n }\n}\n\n/**\n * Thrown by `DictionaryHandle.get()` and `DictionaryHandle.delete()` when\n * the requested key does not exist in the dictionary.\n *\n * Distinct from `NotFoundError` (which is for data records) so callers\n * can distinguish \"data record missing\" from \"dictionary key missing\"\n * without inspecting error messages.\n */\nexport class DictKeyMissingError extends NoydbError {\n /** The dictionary name. */\n readonly dictionaryName: string\n /** The key that was not found. */\n readonly key: string\n\n constructor(dictionaryName: string, key: string) {\n super(\n 'DICT_KEY_MISSING',\n `Dictionary \"${dictionaryName}\" has no entry for key \"${key}\".`,\n )\n this.name = 'DictKeyMissingError'\n this.dictionaryName = dictionaryName\n this.key = key\n }\n}\n\n/**\n * Thrown by `DictionaryHandle.delete()` in strict mode when the key to\n * be deleted is still referenced by one or more records.\n *\n * The caller must either rename the key first (the only sanctioned\n * mass-mutation path) or pass `{ mode: 'warn' }` to skip the check\n * (development only).\n */\nexport class DictKeyInUseError extends NoydbError {\n /** The dictionary name. */\n readonly dictionaryName: string\n /** The key that is still referenced. */\n readonly key: string\n /** Name of the first collection found to reference this key. */\n readonly usedBy: string\n /** Number of records in `usedBy` that reference this key. */\n readonly count: number\n\n constructor(\n dictionaryName: string,\n key: string,\n usedBy: string,\n count: number,\n ) {\n super(\n 'DICT_KEY_IN_USE',\n `Cannot delete key \"${key}\" from dictionary \"${dictionaryName}\": ` +\n `${count} record(s) in \"${usedBy}\" still reference it. ` +\n `Use dictionary.rename(\"${key}\", newKey) to rewrite references first.`,\n )\n this.name = 'DictKeyInUseError'\n this.dictionaryName = dictionaryName\n this.key = key\n this.usedBy = usedBy\n this.count = count\n }\n}\n\n/**\n * Thrown by `Collection.put()` when an `i18nText` field is missing one\n * or more required translations.\n *\n * The `missing` array names each locale code that was absent from the\n * field value. The `field` property names the field so callers can\n * render a field-level error message without parsing the string.\n */\nexport class MissingTranslationError extends NoydbError {\n /** The field name whose translation(s) are missing. */\n readonly field: string\n /** Locale codes that were required but absent. */\n readonly missing: readonly string[]\n\n constructor(field: string, missing: readonly string[], message?: string) {\n super(\n 'MISSING_TRANSLATION',\n message ??\n `Field \"${field}\": missing required translation(s): ${missing.join(', ')}.`,\n )\n this.name = 'MissingTranslationError'\n this.field = field\n this.missing = missing\n }\n}\n\n/**\n * Thrown when reading an `i18nText` field without specifying a locale —\n * either at the call site (`get(id, { locale })`) or on the vault\n * (`openVault(name, { locale })`).\n *\n * Also thrown when `resolveI18nText()` exhausts the fallback chain and\n * no translation is available for the requested locale.\n *\n * The `field` property names the field that triggered the error so the\n * caller can surface it in the UI.\n */\nexport class LocaleNotSpecifiedError extends NoydbError {\n /** The field name that required a locale. */\n readonly field: string\n\n constructor(field: string, message?: string) {\n super(\n 'LOCALE_NOT_SPECIFIED',\n message ??\n `Cannot read i18nText field \"${field}\" without a locale. ` +\n `Pass { locale } to get()/list()/query() or set a default via ` +\n `openVault(name, { locale }).`,\n )\n this.name = 'LocaleNotSpecifiedError'\n this.field = field\n }\n}\n\n/**\n * Thrown at write time when an `i18nText` slot's value contains\n * characters outside the script set allowed for that locale, and the\n * field's `onScriptViolation` policy is `'reject'` (the default).\n *\n * Distinct from {@link MissingTranslationError} (write-shape) and\n * {@link LocaleNotSpecifiedError} (read-hole) so callers can tell a\n * wrong-script value from a missing one.\n */\nexport class ScriptViolationError extends NoydbError {\n /** The field whose value violated its script constraint. */\n readonly field: string\n /** The locale slot (e.g. `'en'`) that was checked. */\n readonly locale: string\n /** The Unicode scripts allowed for this slot. */\n readonly expected: readonly string[]\n /** A short sample of the offending characters, for diagnostics. */\n readonly sample: string\n\n constructor(\n field: string,\n locale: string,\n expected: readonly string[],\n sample: string,\n message?: string,\n ) {\n super(\n 'SCRIPT_VIOLATION',\n message ??\n `Field \"${field}\" slot \"${locale}\" expects script(s) [${expected.join(', ')}] ` +\n `but contains disallowed character(s): \"${sample}\".`,\n )\n this.name = 'ScriptViolationError'\n this.field = field\n this.locale = locale\n this.expected = expected\n this.sample = sample\n }\n}\n\n/**\n * Thrown when a mutation (`put`/`putAll`/`rename`/`delete`) is attempted\n * against a dictionary name that is backed by a `staticDict()` descriptor.\n *\n * A static dict's labels are code constants with no per-vault storage and no\n * mutation surface — a label change is a code deploy, not a runtime write.\n * Distinct from the other dictionary errors so callers can tell a\n * \"this dict is read-only by construction\" refusal from a missing-key or\n * key-in-use failure. (#291)\n */\nexport class StaticDictReadonlyError extends NoydbError {\n /** The static dictionary name that was the target of the mutation. */\n readonly dictionaryName: string\n\n constructor(dictionaryName: string) {\n super(\n 'STATIC_DICT_READONLY',\n `Dictionary \"${dictionaryName}\" is a staticDict — its labels are code ` +\n `constants with no mutation surface. put/putAll/rename/delete are not ` +\n `supported; change the label in the staticDict() table and redeploy.`,\n )\n this.name = 'StaticDictReadonlyError'\n this.dictionaryName = dictionaryName\n }\n}\n\n/**\n * Thrown at put-time when a record stores a code for a `staticDict()` field\n * that is not in the descriptor's declared `keys` (a typo or a stale code).\n *\n * Codes are closed by construction, so an unknown code is treated as a bug by\n * default. Opt out per descriptor with `{ validateCodes: false }`.\n *\n * Distinct from {@link LocaleNotSpecifiedError} (a read-hole) — this is a\n * write-shape error. (#291)\n */\nexport class UnknownDictCodeError extends NoydbError {\n /** The static dictionary name. */\n readonly dictionaryName: string\n /** The field that carried the unknown code. */\n readonly field: string\n /** The offending code value. */\n readonly code: string\n\n constructor(dictionaryName: string, field: string, code: string) {\n super(\n 'UNKNOWN_DICT_CODE',\n `Field \"${field}\": code \"${code}\" is not a known key of staticDict ` +\n `\"${dictionaryName}\". Use a declared code, or pass ` +\n `{ validateCodes: false } on the descriptor to allow open codes.`,\n )\n this.name = 'UnknownDictCodeError'\n this.dictionaryName = dictionaryName\n this.field = field\n this.code = code\n }\n}\n\n// ─── Translator Errors ─────────────────────────────────────\n\n/**\n * Thrown when a collection has an `i18nText` field with\n * `autoTranslate: true` but no `plaintextTranslator` was configured\n * on `createNoydb()`.\n *\n * The error is raised at `put()` time (not at schema construction) so\n * the mis-configuration is surfaced by the first write rather than\n * silently at startup.\n */\nexport class TranslatorNotConfiguredError extends NoydbError {\n /** The field that requested auto-translation. */\n readonly field: string\n /** The collection the put was targeting. */\n readonly collection: string\n\n constructor(field: string, collection: string) {\n super(\n 'TRANSLATOR_NOT_CONFIGURED',\n `Field \"${field}\" in collection \"${collection}\" has autoTranslate: true, ` +\n `but no plaintextTranslator was configured on createNoydb(). ` +\n `Either configure a plaintextTranslator or remove autoTranslate from the schema.`,\n )\n this.name = 'TranslatorNotConfiguredError'\n this.field = field\n this.collection = collection\n }\n}\n\n// ─── Backup Errors ─────────────────────────────────────────\n\n/**\n * Thrown when `Vault.load()` finds that a backup's hash chain\n * doesn't verify, or that its embedded `ledgerHead.hash` doesn't\n * match the chain head reconstructed from the loaded entries.\n *\n * Distinct from `BackupCorruptedError` so callers can choose to\n * recover from one but not the other (e.g., a corrupted JSON file is\n * unrecoverable; a chain mismatch might mean the backup is from an\n * incompatible noy-db version).\n */\nexport class BackupLedgerError extends NoydbError {\n /** First-broken-entry index, if known. */\n readonly divergedAt?: number\n\n constructor(message: string, divergedAt?: number) {\n super('BACKUP_LEDGER', message)\n this.name = 'BackupLedgerError'\n if (divergedAt !== undefined) this.divergedAt = divergedAt\n }\n}\n\n/**\n * Thrown when `Vault.load()` finds that the backup's data\n * collection content doesn't match the ledger's recorded\n * `payloadHash`es. This is the \"envelope was tampered with after\n * dump\" detection — the chain itself can be intact, but if any\n * encrypted record bytes were swapped, this check catches it.\n */\nexport class BackupCorruptedError extends NoydbError {\n /** The (collection, id) pair whose envelope failed the hash check. */\n readonly collection: string\n readonly id: string\n\n constructor(collection: string, id: string, message: string) {\n super('BACKUP_CORRUPTED', message)\n this.name = 'BackupCorruptedError'\n this.collection = collection\n this.id = id\n }\n}\n\n/**\n * Thrown by partition-extraction primitives when the\n * transitive-closure walk fails — e.g. the FK graph is deeper than\n * `maxDepth`, signalling a runaway or unexpectedly cyclic graph.\n */\nexport class PartitionExtractionError extends NoydbError {\n constructor(message: string) {\n super('PARTITION_EXTRACTION', message)\n this.name = 'PartitionExtractionError'\n }\n}\n\n/**\n * Thrown by `adoptPartition` when the transfer seal can't be\n * opened — a wrong/short transfer key (AES-GCM auth-tag failure) or a\n * malformed sealed payload.\n */\nexport class TransferSealError extends NoydbError {\n constructor(message: string) {\n super('TRANSFER_SEAL', message)\n this.name = 'TransferSealError'\n }\n}\n\n/**\n * Thrown when an adoption-lifecycle precondition fails — re-adopting a\n * partition already consumed in this store, or owner-creation on a\n * vault that isn't in the adopted-unowned state.\n */\nexport class AdoptionStateError extends NoydbError {\n constructor(message: string) {\n super('ADOPTION_STATE', message)\n this.name = 'AdoptionStateError'\n }\n}\n\n// ─── Attestation Errors ────────────────────────────────────\n\n/** Document-attestation failures: undeclared field-schema, non-owner issue, missing field, signer failure. */\nexport class AttestationError extends NoydbError {\n constructor(message: string) {\n super('ATTESTATION', message)\n this.name = 'AttestationError'\n }\n}\n\n// ─── Session Errors ───────────────────────────────────────\n\n/**\n * Thrown by `resolveSession()` when the session token's `expiresAt`\n * timestamp is in the past. The session key is also removed from the\n * in-memory store when this is thrown, so retrying with the same sessionId\n * will produce `SessionNotFoundError`.\n *\n * Separate from `SessionNotFoundError` so callers can distinguish between\n * \"session is gone\" (key store cleared, tab reloaded) and \"session is\n * still in the store but has exceeded its lifetime\" (idle timeout, absolute\n * timeout, policy-driven expiry). The remediation differs: expired sessions\n * should prompt a fresh unlock; not-found sessions may indicate a bug or a\n * cross-tab scenario where the session was never established.\n */\nexport class SessionExpiredError extends NoydbError {\n readonly sessionId: string\n\n constructor(sessionId: string) {\n super('SESSION_EXPIRED', `Session \"${sessionId}\" has expired. Re-unlock to continue.`)\n this.name = 'SessionExpiredError'\n this.sessionId = sessionId\n }\n}\n\n/**\n * Thrown by `resolveSession()` when the session key cannot be found in\n * the module-level store. This happens when:\n * - The session was explicitly revoked via `revokeSession()`.\n * - The JS context was reloaded (tab navigation, page refresh, worker restart).\n * - `Noydb.close()` was called (which calls `revokeAllSessions()`).\n * - The sessionId is wrong or was generated by a different JS context.\n *\n * The session token (if the caller holds it) is permanently useless after\n * this error — the key is gone and cannot be recovered.\n */\nexport class SessionNotFoundError extends NoydbError {\n readonly sessionId: string\n\n constructor(sessionId: string) {\n super('SESSION_NOT_FOUND', `Session key for \"${sessionId}\" not found. The session may have been revoked or the page reloaded.`)\n this.name = 'SessionNotFoundError'\n this.sessionId = sessionId\n }\n}\n\n/**\n * Thrown when a session policy blocks an operation — for example,\n * `requireReAuthFor: ['export']` is set and the caller attempts to\n * call `exportStream()` without re-authenticating for this session.\n *\n * The `operation` field names the specific operation that was blocked\n * (e.g. `'export'`, `'grant'`, `'rotate'`) so the caller can surface\n * a targeted prompt (\"Please re-enter your passphrase to export data\").\n */\nexport class SessionPolicyError extends NoydbError {\n readonly operation: string\n\n constructor(operation: string, message?: string) {\n super(\n 'SESSION_POLICY',\n message ?? `Operation \"${operation}\" requires re-authentication per the active session policy.`,\n )\n this.name = 'SessionPolicyError'\n this.operation = operation\n }\n}\n\n// ─── Query / Join Errors ────────────────────────────────────\n\n/**\n * Thrown when a `.join()` would exceed its configured row ceiling on\n * either side. The ceiling defaults to 50,000 per side and can be\n * overridden via the `{ maxRows }` option on `.join()`.\n *\n * Carries both row counts so the error message can show which side\n * tripped the limit (e.g. \"left had 60,000 rows, right had 1,200,\n * max was 50,000\"). The `side` field is machine-readable so test\n * code and devtools can match on it without regex-parsing the\n * message.\n *\n * The row ceiling exists because joins are bounded in-memory\n * operations over materialized record sets. Consumers whose\n * collections genuinely exceed the ceiling should track \n * (streaming joins over `scan()`) or filter the left side further\n * with `where()` / `limit()` before joining.\n */\nexport class JoinTooLargeError extends NoydbError {\n readonly leftRows: number\n readonly rightRows: number\n readonly maxRows: number\n readonly side: 'left' | 'right'\n\n constructor(opts: {\n leftRows: number\n rightRows: number\n maxRows: number\n side: 'left' | 'right'\n message: string\n }) {\n super('JOIN_TOO_LARGE', opts.message)\n this.name = 'JoinTooLargeError'\n this.leftRows = opts.leftRows\n this.rightRows = opts.rightRows\n this.maxRows = opts.maxRows\n this.side = opts.side\n }\n}\n\n/**\n * Thrown by `.crossJoin()` when the cumulative cartesian product (or lateral\n * filtered count) exceeds the configured ceiling. Check before allocating.\n * Mirrors the pattern of `JoinTooLargeError` and the `.join()` row ceiling.\n *\n * @see CrossJoinClause.maxRows — per-clause override\n * @see DEFAULT_CROSS_JOIN_MAX_ROWS — package default (50_000)\n */\nexport class CrossJoinTooLargeError extends NoydbError {\n readonly target: string\n readonly expected: number\n readonly limit: number\n\n constructor(opts: { target: string; expected: number; limit: number }) {\n super(\n 'CROSS_JOIN_TOO_LARGE',\n `crossJoin(\"${opts.target}\"): would produce ${opts.expected} rows, ` +\n `exceeding the limit of ${opts.limit}. ` +\n `Narrow the left side with .where() first, or raise the ceiling ` +\n `with crossJoin(\"${opts.target}\", { ..., maxRows: ${opts.expected} }).`,\n )\n this.name = 'CrossJoinTooLargeError'\n this.target = opts.target\n this.expected = opts.expected\n this.limit = opts.limit\n }\n}\n\n/**\n * Thrown at cross-join execution time when the target collection is not\n * reachable from the current vault. The left collection is included in the\n * message for context.\n */\nexport class CrossJoinSourceUnknownError extends NoydbError {\n readonly target: string\n readonly leftCollection: string\n\n constructor(target: string, leftCollection: string) {\n super(\n 'CROSS_JOIN_SOURCE_UNKNOWN',\n `crossJoin(\"${target}\"): collection \"${target}\" is not known in the vault ` +\n `(cross-joining from \"${leftCollection}\"). ` +\n `Make sure \"${target}\" is open in the same vault before executing this query.`,\n )\n this.name = 'CrossJoinSourceUnknownError'\n this.target = target\n this.leftCollection = leftCollection\n }\n}\n\n/**\n * Thrown by `.join()` in strict `ref()` mode when a left-side record\n * points at a right-side id that does not exist in the target\n * collection.\n *\n * Distinct from `RefIntegrityError` so test code can pattern-match\n * on the *read-time* dangling case without catching *write-time*\n * integrity violations. Both indicate \"ref points at nothing\" but\n * happen at different lifecycle phases and deserve different\n * remediation in documentation: a RefIntegrityError on `put()`\n * means the input is invalid; a DanglingReferenceError on `.join()`\n * means stored data has drifted and `vault.checkIntegrity()`\n * is the right tool to find the full set of orphans.\n */\nexport class DanglingReferenceError extends NoydbError {\n readonly field: string\n readonly target: string\n readonly refId: string\n\n constructor(opts: {\n field: string\n target: string\n refId: string\n message: string\n }) {\n super('DANGLING_REFERENCE', opts.message)\n this.name = 'DanglingReferenceError'\n this.field = opts.field\n this.target = opts.target\n this.refId = opts.refId\n }\n}\n\n/**\n * Thrown by {@link sanitizeFilename} when an input filename cannot be\n * made safe — NUL byte, empty after normalization, missing\n * `opaqueId` for the opaque profile, `..` segment, or a `maxBytes`\n * cap too small to hold a single code point.\n */\nexport class FilenameSanitizationError extends NoydbError {\n constructor(message: string) {\n super('FILENAME_SANITIZATION', message)\n this.name = 'FilenameSanitizationError'\n }\n}\n\n/**\n * Thrown when a write target resolves OUTSIDE the requested\n * directory after sanitization — the canonical Zip-Slip class. The\n * sanitizer's job is to strip path-traversal segments; this error\n * is the defense-in-depth fallback at the FS write site.\n */\nexport class PathEscapeError extends NoydbError {\n readonly attempted: string\n readonly targetDir: string\n\n constructor(opts: { attempted: string; targetDir: string }) {\n super(\n 'PATH_ESCAPE',\n `Sanitized filename \"${opts.attempted}\" resolves outside target dir \"${opts.targetDir}\"`,\n )\n this.name = 'PathEscapeError'\n this.attempted = opts.attempted\n this.targetDir = opts.targetDir\n }\n}\n\n// ─── Derivation Errors ──────────────────────────────\n\n/**\n * Thrown at vault open if the derivation graph contains a cycle.\n * `path` is the offending chain (e.g. `['a', 'b', 'c', 'a']`).\n */\nexport class DerivationCycleError extends NoydbError {\n readonly path: readonly string[]\n\n constructor(path: readonly string[]) {\n super(\n 'DERIVATION_CYCLE',\n `Derivation graph contains a cycle: ${path.join(' → ')}. ` +\n `Refusing to open vault — break the cycle before retrying.`,\n )\n this.name = 'DerivationCycleError'\n this.path = path\n }\n}\n\n/**\n * Thrown when a cascade of source → output → source → … exceeds the\n * configured `maxDepth` (default 5).\n */\nexport class DerivationDepthError extends NoydbError {\n readonly limit: number\n readonly attempted: number\n\n constructor(limit: number, attempted: number) {\n super(\n 'DERIVATION_DEPTH',\n `Derivation cascade exceeded max depth ${limit} (attempted ${attempted}). ` +\n `Pass lifecycle: { maxDepth: N } to raise the limit if intentional.`,\n )\n this.name = 'DerivationDepthError'\n this.limit = limit\n this.attempted = attempted\n }\n}\n\n/**\n * Thrown at registration if a `withDerivation` strategy references an\n * output `collection` that isn't otherwise declared (no schema, no use\n * elsewhere). Surfacing this early catches typos in collection names.\n */\nexport class DerivationOutputUnknownError extends NoydbError {\n readonly collection: string\n\n constructor(collection: string) {\n super(\n 'DERIVATION_OUTPUT_UNKNOWN',\n `Derivation output collection \"${collection}\" is not declared on the vault. ` +\n `Register the collection (e.g. via schema) before registering a derivation that writes to it.`,\n )\n this.name = 'DerivationOutputUnknownError'\n this.collection = collection\n }\n}\n\n/**\n * Thrown when the user's `derive` function returns a value that doesn't\n * match the declared output spec (e.g. wrong shape, wrong key set).\n */\nexport class DerivationOutputShapeError extends NoydbError {\n readonly outputKey: string\n\n constructor(outputKey: string, detail: string) {\n super(\n 'DERIVATION_OUTPUT_SHAPE',\n `Derivation output \"${outputKey}\" has invalid shape: ${detail}.`,\n )\n this.name = 'DerivationOutputShapeError'\n this.outputKey = outputKey\n }\n}\n\n/**\n * Thrown by array-shape derivations when the `derive` function\n * returns more rows than the output's `maxFanout` cap. The cap exists\n * to keep dispatch cost bounded — without it a single source-row\n * update could fan out to thousands of derived rows, dominating the\n * write path.\n *\n * Defaults to `maxFanout: 64`. Raise on the output spec for\n * carry-forward expansion cases (e.g. monthly rows across multi-year\n * contracts).\n */\nexport class DerivationCapExceededError extends NoydbError {\n readonly outputKey: string\n readonly returned: number\n readonly maxFanout: number\n\n constructor(outputKey: string, returned: number, maxFanout: number) {\n super(\n 'DERIVATION_CAP_EXCEEDED',\n `Derivation array output \"${outputKey}\" returned ${returned} rows, exceeding `\n + `maxFanout=${maxFanout}. Raise \\`maxFanout\\` on the OutputSpec if this fanout `\n + 'is intended (the cap exists to keep dispatch cost bounded).',\n )\n this.name = 'DerivationCapExceededError'\n this.outputKey = outputKey\n this.returned = returned\n this.maxFanout = maxFanout\n }\n}\n\n/**\n * Thrown at vault open if the materialized-view graph contains a\n * cycle. `path` is the offending chain (e.g. `['a-mv', 'b-mv', 'a-mv']`).\n * Detected by the same shared DFS that catches `DerivationCycleError`;\n * surfaces with a distinct error type so consumers can disambiguate.\n */\nexport class MaterializedViewCycleError extends NoydbError {\n readonly path: readonly string[]\n\n constructor(path: readonly string[]) {\n super(\n 'MATERIALIZED_VIEW_CYCLE',\n `Materialized-view graph contains a cycle: ${path.join(' → ')}. ` +\n `Refusing to open vault — break the cycle before retrying.`,\n )\n this.name = 'MaterializedViewCycleError'\n this.path = path\n }\n}\n\n/**\n * Thrown at MV registration if the query references a source\n * collection that isn't declared on the vault. Surfacing this early\n * catches typos in collection names.\n */\nexport class MaterializedViewSourceUnknownError extends NoydbError {\n readonly mvName: string\n readonly collection: string\n\n constructor(mvName: string, collection: string) {\n super(\n 'MATERIALIZED_VIEW_SOURCE_UNKNOWN',\n `Materialized view \"${mvName}\" references unknown source collection \"${collection}\". ` +\n `Declare the collection (e.g. via schema or by writing to it once) before registering the MV.`,\n )\n this.name = 'MaterializedViewSourceUnknownError'\n this.mvName = mvName\n this.collection = collection\n }\n}\n\n/**\n * Thrown by the MV executor when a refresh produces more rows than\n * the configured ceiling. Default ceiling is 100k rows; override\n * per-MV via `maxRows`. Mirrors `JoinTooLargeError` /\n * `GroupCardinalityError` from the query DSL — the explosion is\n * detected BEFORE writes hit the store, so the source-write\n * transaction can roll back cleanly via strict-mode.\n */\nexport class MaterializedViewTooLargeError extends NoydbError {\n readonly mvName: string\n readonly expected: number\n readonly limit: number\n\n constructor(mvName: string, expected: number, limit: number) {\n super(\n 'MATERIALIZED_VIEW_TOO_LARGE',\n `Materialized view \"${mvName}\" would emit ${expected} rows, exceeding the configured limit of ${limit}. ` +\n `Override via { maxRows: N } on the MV strategy if intentional, or tighten the query's filter/groupBy.`,\n )\n this.name = 'MaterializedViewTooLargeError'\n this.mvName = mvName\n this.expected = expected\n this.limit = limit\n }\n}\n\n/**\n * Thrown by `withMaterializedView()` at registration time when the\n * strategy is structurally malformed. Distinct from\n * `MaterializedViewSourceUnknownError` (the source list is well-formed\n * but names a collection the vault doesn't know) and\n * `MaterializedViewCycleError` (the source graph has a cycle): this\n * error fires before either check, at the moment the spec is being\n * normalized.\n *\n * Today the trigger cases are all about the `query` / `unionSources`\n * dichotomy:\n * - both `query` and `unionSources` were set (mutually exclusive),\n * - neither `query` nor `unionSources` was set,\n * - `unionSources` has fewer than 2 arms,\n * - two arms in `unionSources` reference the same `collection`.\n *\n * The error message is prefixed with `[noy-db] withMaterializedView:`\n * so it's grep-friendly in logs and looks consistent with the existing\n * `ValidationError` messages from the same factory.\n */\nexport class MaterializedViewConfigError extends NoydbError {\n constructor(message: string) {\n super(\n 'MATERIALIZED_VIEW_CONFIG',\n `[noy-db] withMaterializedView: ${message}`,\n )\n this.name = 'MaterializedViewConfigError'\n }\n}\n\n/**\n * Thrown at vault open when a `withOverlayedView` declaration uses\n * another virtual-overlay name as its `base`. Multi-overlay stacking\n * is a v2 non-goal — the shallow expansion in\n * `QueryDependencyAnalyzer` would truncate at the inner overlay\n * name, leaving downstream MVs silently stale.\n */\nexport class OverlayBaseIsVirtualError extends NoydbError {\n readonly overlayName: string\n readonly base: string\n\n constructor(overlayName: string, base: string) {\n super(\n 'OVERLAY_BASE_IS_VIRTUAL',\n `withOverlayedView \"${overlayName}\": base \"${base}\" is another overlay's virtual name. ` +\n `Multi-overlay stacking is a v3 feature; base must reference a concrete collection (a real source or an MV output).`,\n )\n this.name = 'OverlayBaseIsVirtualError'\n this.overlayName = overlayName\n this.base = base\n }\n}\n\n/**\n * Thrown at vault open when a `withOverlayedView`'s `overlay`\n * references an unknown collection or an MV-owned collection. The\n * overlay collection is user-writable; MV-owned collections aren't.\n */\nexport class OverlayCollectionUnavailableError extends NoydbError {\n readonly overlayName: string\n readonly overlay: string\n\n constructor(overlayName: string, overlay: string) {\n super(\n 'OVERLAY_COLLECTION_UNAVAILABLE',\n `withOverlayedView \"${overlayName}\": overlay collection \"${overlay}\" is unavailable. ` +\n `It must be a real vault-known collection that is NOT itself an MV output collection.`,\n )\n this.name = 'OverlayCollectionUnavailableError'\n this.overlayName = overlayName\n this.overlay = overlay\n }\n}\n\n/**\n * Thrown at vault open when a `withOverlayedView`'s virtual `name`\n * collides with an MV output or a concrete source collection.\n */\nexport class OverlayNameCollisionError extends NoydbError {\n readonly overlayName: string\n\n constructor(overlayName: string) {\n super(\n 'OVERLAY_NAME_COLLISION',\n `withOverlayedView \"${overlayName}\": virtual name collides with an MV output or a concrete source collection. ` +\n `Pick a unique name for the virtual collection.`,\n )\n this.name = 'OverlayNameCollisionError'\n this.overlayName = overlayName\n }\n}\n\n/**\n * Thrown by the virtual overlay's `put(id, record)` when the\n * consumer-supplied `id` doesn't match `rowKey(record)`. Catches\n * fat-finger separator typos that would otherwise silently produce\n * orphaned overlay rows. Direct writes to the underlying overlay\n * collection (bypass the virtual layer) skip this validation.\n */\nexport class OverlayIdMismatchError extends NoydbError {\n readonly actual: string\n readonly expected: string\n\n constructor(actual: string, expected: string) {\n super(\n 'OVERLAY_ID_MISMATCH',\n `Overlay put(id, record): id \"${actual}\" does not match the base MV's rowKey(record) → \"${expected}\". ` +\n `Pass the row directly via .put(record) to derive the id, or fix the id to match the base MV's rowKey output.`,\n )\n this.name = 'OverlayIdMismatchError'\n this.actual = actual\n this.expected = expected\n }\n}\n\n// ─── Snapshot Errors ──────────────────────────────────────\n\n/**\n * Thrown when a requested snapshot version does not exist in the\n * snapshot store — either it was never created, was pruned by the\n * retention policy, or was deleted manually.\n *\n * The `version` field carries the key that was looked up so callers\n * can surface an actionable \"snapshot X not found\" message without\n * parsing the error string.\n */\nexport class SnapshotNotFoundError extends NoydbError {\n readonly version: string\n\n constructor(version: string) {\n super(\n 'SNAPSHOT_NOT_FOUND',\n `Snapshot not found: \"${version}\" does not exist in the snapshot store. ` +\n `It may have been pruned by the retention policy or deleted manually.`,\n )\n this.name = 'SnapshotNotFoundError'\n this.version = version\n }\n}\n\n// ─── Federation (multi-vault partition) Errors ──────────────────────────\n\n/**\n * Thrown when a write targets a partition key that has no shard and\n * `sharding.autoCreate` is disabled.\n */\nexport class UnknownShardError extends NoydbError {\n readonly partitionKey: string\n\n constructor(partitionKey: string, groupName: string) {\n super(\n 'SHARD_UNKNOWN',\n `No shard for partition key \"${partitionKey}\" in vault group \"${groupName}\" ` +\n `and autoCreate is disabled. Call group.createShard(${JSON.stringify(partitionKey)}) ` +\n `first, or enable sharding.autoCreate.`,\n )\n this.name = 'UnknownShardError'\n this.partitionKey = partitionKey\n }\n}\n\n/**\n * Thrown by `createShard` when the registry has a row for a partition\n * but the corresponding vault is not provisioned in the store —\n * a registry/store divergence. Refusing to recreate avoids masking\n * data loss.\n */\nexport class ShardProvisioningError extends NoydbError {\n readonly vaultId: string\n\n constructor(vaultId: string, partitionKey: string) {\n super(\n 'SHARD_PROVISIONING',\n `Registry has a row for partition \"${partitionKey}\" (vault \"${vaultId}\") but that ` +\n `vault is not provisioned in the store. Refusing to recreate it — the registry and ` +\n `store have diverged. Investigate before retrying.`,\n )\n this.name = 'ShardProvisioningError'\n this.vaultId = vaultId\n }\n}\n\n/**\n * Thrown by `ShardedQuery.crossShardJoin` / `broadcastJoin` for\n * deterministic, query-shaping errors: an undeclared join ref (which\n * would fail identically on every shard), or calling a deferred\n * reactive/aggregate surface on a query that already carries join legs.\n */\nexport class CrossShardJoinError extends NoydbError {\n constructor(message: string) {\n super('CROSS_SHARD_JOIN', message)\n this.name = 'CrossShardJoinError'\n }\n}\n\n/** Thrown when a VaultGroup references a template name that was never registered. */\nexport class VaultTemplateNotFoundError extends NoydbError {\n readonly templateName: string\n\n constructor(templateName: string) {\n super(\n 'VAULT_TEMPLATE_NOT_FOUND',\n `No vault template registered under \"${templateName}\". Register it with ` +\n `db.withVaultTemplate(${JSON.stringify(templateName)}, { version, configure }) ` +\n `before opening the vault group.`,\n )\n this.name = 'VaultTemplateNotFoundError'\n this.templateName = templateName\n }\n}\n\n// ─── Erasure Errors ────────────────────────────────────────────────────\n\n/**\n * Thrown when `vault.forget(subjectId)` is called on a vault whose\n * `createNoydb({ forgetStrategy })` declared no subject fields (the\n * default `NO_FORGET`). GDPR crypto-shred needs a declared subject →\n * record index to know which records belong to a data subject; without\n * one there is nothing to erase and a silent no-op would be a dangerous\n * false \"erased\" signal. Configure with\n * `forgetStrategy: withForgetCascade({ subjects: { invoices: 'buyerId' } })`.\n */\nexport class ForgetStrategyNotConfiguredError extends NoydbError {\n constructor(\n message = 'vault.forget() requires a forget strategy. Pass ' +\n '`forgetStrategy: withForgetCascade({ subjects: { <collection>: <subjectField> } })` ' +\n 'from \"@noy-db/hub/forget\" to createNoydb().',\n ) {\n super('FORGET_NOT_CONFIGURED', message)\n this.name = 'ForgetStrategyNotConfiguredError'\n }\n}\n\n// ─── Sealed-record (record-scoped CEK sealing, #306) Errors ─────────────\n\n/**\n * Thrown by `openSealedRecord()` when the sealed CEK's binding has passed its\n * `expiresAt`. Surfaced on two checks: a cheap fast-path check on the delivery\n * envelope's clear-text `expiresAt`, and the AUTHORITATIVE check on the\n * `expiresAt` inside the sealed binding (the latter cannot be forged by editing\n * the delivery envelope). Distinct from {@link KeyringExpiredError} (bundle-slot\n * expiry) so a host can tell \"this single-record grant lapsed\" from a keyring-\n * level expiry.\n */\nexport class SealedRecordExpiredError extends NoydbError {\n readonly expiresAt: string\n constructor(expiresAt: string) {\n super(\n 'SEALED_RECORD_EXPIRED',\n `Sealed record CEK expired at ${expiresAt}. The grantor must re-seal the ` +\n `record with a later expiresAt.`,\n )\n this.name = 'SealedRecordExpiredError'\n this.expiresAt = expiresAt\n }\n}\n\n/**\n * Thrown by `openSealedRecord()` when the sealed binding's\n * `{collection, id}` does not match the record envelope the host is trying to\n * decrypt. This is the host-denial boundary: a CEK sealed for record A cannot\n * be replayed against record B's envelope. (A CEK sealed for a PRE-rotation\n * version of a record, applied to the POST-rotation live envelope, is a\n * different failure — the binding still matches `{collection, id}` so it gets\n * past this check, and the AES-GCM auth-tag failure surfaces as\n * {@link TamperedError} instead.)\n */\nexport class SealedRecordMismatchError extends NoydbError {\n readonly expected: { collection: string; id: string }\n readonly actual: { collection: string; id: string }\n constructor(\n expected: { collection: string; id: string },\n actual: { collection: string; id: string },\n ) {\n super(\n 'SEALED_RECORD_MISMATCH',\n `Sealed CEK binding is for ${actual.collection}/${actual.id} but was ` +\n `presented against ${expected.collection}/${expected.id}. A CEK sealed ` +\n `for one record cannot decrypt another.`,\n )\n this.name = 'SealedRecordMismatchError'\n this.expected = expected\n this.actual = actual\n }\n}\n\n/**\n * Thrown by `vault.sealRecordToHost()` / `vault.rotateRecordCek()` when the\n * target record has no live envelope, or its live envelope carries no `_cek`\n * (a legacy / non-`perRecordKeys` collection has nothing record-scoped to\n * seal — its body is keyed off the shared collection DEK, which sealing\n * deliberately never exposes).\n */\nexport class RecordCekNotFoundError extends NoydbError {\n readonly collection: string\n readonly id: string\n constructor(collection: string, id: string) {\n super(\n 'RECORD_CEK_NOT_FOUND',\n `No per-record CEK for ${collection}/${id}. The record is missing, or its ` +\n `collection was not opened with { perRecordKeys: true } — only per-record-key ` +\n `records carry a sealable CEK.`,\n )\n this.name = 'RecordCekNotFoundError'\n this.collection = collection\n this.id = id\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MaterializedViewExecutor
|
|
3
|
-
} from "./chunk-QO6RGLLD.js";
|
|
4
|
-
import "./chunk-2FU2FTXD.js";
|
|
5
|
-
import "./chunk-7H2GEJ3O.js";
|
|
6
|
-
import "./chunk-J7RWBXFY.js";
|
|
7
|
-
import "./chunk-ZEGSDPB7.js";
|
|
8
|
-
export {
|
|
9
|
-
MaterializedViewExecutor
|
|
10
|
-
};
|
|
11
|
-
//# sourceMappingURL=executor-3W63Y44O.js.map
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { av as GuardStrategy, az as GuardStrategyHandle, aw as GuardChange, ax as GuardContext } from './types-CljIHm_J.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Register a guard for a collection. Guards run on every `put()` /
|
|
5
|
-
* `delete()` for the named collection (after permissions, before
|
|
6
|
-
* encryption) and may:
|
|
7
|
-
*
|
|
8
|
-
* - `check` — block writes by throwing (typically `RecordLockedError`)
|
|
9
|
-
* - `frozenFields` — freeze specific fields once a condition is true
|
|
10
|
-
* - `amendment` — declare an authorized-override path with invariant
|
|
11
|
-
*
|
|
12
|
-
* Pass the returned handle to `createNoydb({ strategies: [...] })`.
|
|
13
|
-
*
|
|
14
|
-
* @see docs/superpowers/specs/2026-05-18-guards-design.md
|
|
15
|
-
*/
|
|
16
|
-
declare function withGuard<T extends Record<string, unknown>>(strategy: GuardStrategy<T>): GuardStrategyHandle<T>;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* `immutableGuard` — declarative WORM / append-only sugar over the guard
|
|
20
|
-
* subsystem.
|
|
21
|
-
*
|
|
22
|
-
* Issued fiscal documents (invoices, DDTs) must be immutable after issue.
|
|
23
|
-
* That is expressible today with a hand-rolled `withGuard` (block on
|
|
24
|
-
* `check`/`onDelete`, allow an admin `amendment`), but the boilerplate is
|
|
25
|
-
* repetitive and easy to get subtly wrong. `immutableGuard` generates
|
|
26
|
-
* exactly that guard from a declarative config, reusing the guard
|
|
27
|
-
* machinery wholesale — `check`/`onDelete` rejection, the ledgered
|
|
28
|
-
* `amendment` override, and composition with `periods`/`history`.
|
|
29
|
-
*
|
|
30
|
-
* ```ts
|
|
31
|
-
* createNoydb({ guardStrategies: [
|
|
32
|
-
* immutableGuard({
|
|
33
|
-
* collection: 'invoices',
|
|
34
|
-
* after: (r) => r.status === 'issued', // immutable once issued
|
|
35
|
-
* }),
|
|
36
|
-
* ] })
|
|
37
|
-
* ```
|
|
38
|
-
*
|
|
39
|
-
* A record is mutable until `after(record)` holds; from then on, updates
|
|
40
|
-
* and deletes throw `RecordLockedError` unless performed inside an
|
|
41
|
-
* `amendment` transaction by an authorized role (the override is
|
|
42
|
-
* ledgered by the guard amendment mechanism). `appendOnly: true` is
|
|
43
|
-
* shorthand for `after: () => true` — immutable from creation.
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
interface ImmutableGuardConfig<T extends Record<string, unknown>> {
|
|
47
|
-
/** The collection to make WORM. */
|
|
48
|
-
collection: string;
|
|
49
|
-
/**
|
|
50
|
-
* A record becomes immutable once this predicate holds. Evaluated on
|
|
51
|
-
* the *existing* (already-persisted) record, so the write that first
|
|
52
|
-
* makes it true is still allowed; subsequent writes are blocked.
|
|
53
|
-
* Mutually exclusive with `appendOnly`.
|
|
54
|
-
*/
|
|
55
|
-
after?: (record: T) => boolean;
|
|
56
|
-
/** Shorthand for `after: () => true` — immutable from creation. */
|
|
57
|
-
appendOnly?: boolean;
|
|
58
|
-
/** Roles permitted to override via an amendment transaction. Default `['admin', 'owner']`. */
|
|
59
|
-
amendmentRoles?: ReadonlyArray<'admin' | 'owner'>;
|
|
60
|
-
/**
|
|
61
|
-
* Optional set-level invariant run over the amendment change-set after
|
|
62
|
-
* the writes execute. Signature matches `GuardStrategy.amendment.invariant`
|
|
63
|
-
* exactly: it receives every {before, after} pair touching this
|
|
64
|
-
* collection in the amendment plus the guard context; throwing reverts
|
|
65
|
-
* the whole amendment and surfaces as `InvariantError`.
|
|
66
|
-
*
|
|
67
|
-
* Use this to keep a constraint inviolable EVEN under amendment — e.g.
|
|
68
|
-
* forbid deleting an issued document by re-throwing on any
|
|
69
|
-
* `before !== null && after === null` change, or assert a cross-record
|
|
70
|
-
* sum is preserved. When omitted the amendment is unconditionally
|
|
71
|
-
* allowed (the amendment itself is the sanctioned, ledgered override) —
|
|
72
|
-
* this is the backward-compatible default.
|
|
73
|
-
*/
|
|
74
|
-
amendmentInvariant?: (changes: ReadonlyArray<GuardChange<T>>, ctx: GuardContext<T>) => Promise<void> | void;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Build an immutability guard. Pass the returned handle to
|
|
78
|
-
* `createNoydb({ guardStrategies: [...] })`.
|
|
79
|
-
*/
|
|
80
|
-
declare function immutableGuard<T extends Record<string, unknown>>(config: ImmutableGuardConfig<T>): GuardStrategyHandle<T>;
|
|
81
|
-
|
|
82
|
-
export { type ImmutableGuardConfig as I, immutableGuard as i, withGuard as w };
|