@noy-db/hub 0.2.0-pre.1 → 0.2.0-pre.10
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 +9 -0
- package/dist/aggregate/index.cjs.map +1 -1
- package/dist/aggregate/index.d.cts +2 -2
- package/dist/aggregate/index.d.ts +2 -2
- package/dist/aggregate/index.js +4 -4
- package/dist/attestation/index.cjs +305 -0
- package/dist/attestation/index.cjs.map +1 -0
- package/dist/attestation/index.d.cts +52 -0
- package/dist/attestation/index.d.ts +52 -0
- package/dist/attestation/index.js +36 -0
- package/dist/attestation/index.js.map +1 -0
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +7 -6
- package/dist/blobs/index.d.ts +7 -6
- package/dist/blobs/index.js +10 -8
- package/dist/blobs/index.js.map +1 -1
- package/dist/bundle/index.cjs +18899 -129
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +175 -6
- package/dist/bundle/index.d.ts +175 -6
- package/dist/bundle/index.js +533 -5
- package/dist/bundle/index.js.map +1 -1
- package/dist/{chunk-6HPZY4ON.js → chunk-26NK23DZ.js} +9 -4
- package/dist/chunk-26NK23DZ.js.map +1 -0
- package/dist/{chunk-XGSOTWYX.js → chunk-2LPPNWF6.js} +3 -3
- package/dist/{chunk-5SCJ5UEF.js → chunk-2N62W5YP.js} +3 -3
- package/dist/{chunk-537VFZTR.js → chunk-3LPV6BXR.js} +4 -4
- package/dist/{chunk-UA4RI7OT.js → chunk-4CLICFEY.js} +5 -5
- package/dist/chunk-4CLICFEY.js.map +1 -0
- package/dist/chunk-4USCAEDT.js +10529 -0
- package/dist/chunk-4USCAEDT.js.map +1 -0
- package/dist/chunk-5IXJGFF2.js +83 -0
- package/dist/chunk-5IXJGFF2.js.map +1 -0
- package/dist/{chunk-HB3Z2GCR.js → chunk-5OEJ6GOT.js} +2 -2
- package/dist/chunk-5OEJ6GOT.js.map +1 -0
- package/dist/chunk-5OX6XVNS.js +79 -0
- package/dist/chunk-5OX6XVNS.js.map +1 -0
- package/dist/{chunk-VMIO4IXG.js → chunk-6EOXTJS2.js} +6 -229
- package/dist/chunk-6EOXTJS2.js.map +1 -0
- package/dist/{chunk-UZXLQCHP.js → chunk-6T2UDBKG.js} +2 -2
- package/dist/chunk-6T2UDBKG.js.map +1 -0
- package/dist/{chunk-23TTQXVO.js → chunk-6YLPHBKR.js} +214 -9
- package/dist/chunk-6YLPHBKR.js.map +1 -0
- package/dist/{chunk-Z72JH4KG.js → chunk-7CEGU63S.js} +5 -35
- package/dist/chunk-7CEGU63S.js.map +1 -0
- package/dist/{chunk-PEULZC6M.js → chunk-A3JMGXPG.js} +8 -1
- package/dist/chunk-A3JMGXPG.js.map +1 -0
- package/dist/{chunk-7H6DOO3E.js → chunk-BB27JMWB.js} +211 -36
- package/dist/chunk-BB27JMWB.js.map +1 -0
- package/dist/{chunk-I6MX32UC.js → chunk-BDV7INMP.js} +4 -4
- package/dist/{chunk-MKSA2V7A.js → chunk-C3WE6UJY.js} +2 -2
- package/dist/{chunk-FCXOFQAJ.js → chunk-CH22FZHT.js} +19 -2
- package/dist/chunk-CH22FZHT.js.map +1 -0
- package/dist/{chunk-DYBQG5PQ.js → chunk-CXFOITNS.js} +2 -2
- package/dist/{chunk-5ZGZ6HIZ.js → chunk-CXJG63MA.js} +11 -2
- package/dist/chunk-CXJG63MA.js.map +1 -0
- package/dist/{chunk-EGQYGYIU.js → chunk-DAP2XL7Q.js} +3 -3
- package/dist/chunk-DAP2XL7Q.js.map +1 -0
- package/dist/{chunk-4TFSM22V.js → chunk-DJRWA3Q5.js} +4 -4
- package/dist/chunk-DRXIZOFV.js +233 -0
- package/dist/chunk-DRXIZOFV.js.map +1 -0
- package/dist/{chunk-DPMFBCV6.js → chunk-FO3UEG4S.js} +20 -3
- package/dist/chunk-FO3UEG4S.js.map +1 -0
- package/dist/{chunk-SIZWEV2Y.js → chunk-GAUEWM7D.js} +7 -5
- package/dist/chunk-GAUEWM7D.js.map +1 -0
- package/dist/{chunk-NIOHFJPJ.js → chunk-GNHAC43Q.js} +218 -119
- package/dist/chunk-GNHAC43Q.js.map +1 -0
- package/dist/chunk-HHOO7HGH.js +57 -0
- package/dist/chunk-HHOO7HGH.js.map +1 -0
- package/dist/{chunk-PA6R5ZCI.js → chunk-HQSQC2XL.js} +5 -5
- package/dist/chunk-HQSQC2XL.js.map +1 -0
- package/dist/chunk-IMYKDWB4.js +139 -0
- package/dist/chunk-IMYKDWB4.js.map +1 -0
- package/dist/{chunk-5DWL3JBF.js → chunk-LSTBFLL2.js} +2 -2
- package/dist/{chunk-ADQ5MQ54.js → chunk-O6EJ6WTI.js} +163 -2
- package/dist/chunk-O6EJ6WTI.js.map +1 -0
- package/dist/{chunk-OMLIZL2P.js → chunk-PC6ZEDRL.js} +12 -2
- package/dist/{chunk-OMLIZL2P.js.map → chunk-PC6ZEDRL.js.map} +1 -1
- package/dist/chunk-PM3QYWUU.js +251 -0
- package/dist/chunk-PM3QYWUU.js.map +1 -0
- package/dist/{chunk-34YSDCDP.js → chunk-PVUUIWHY.js} +2 -2
- package/dist/{chunk-CBAHB2BF.js → chunk-PXTQPZO4.js} +7 -70
- package/dist/chunk-PXTQPZO4.js.map +1 -0
- package/dist/{chunk-DYECX3IX.js → chunk-QSOYKKMD.js} +4 -4
- package/dist/chunk-QSOYKKMD.js.map +1 -0
- package/dist/{chunk-WCA2NROQ.js → chunk-R233SLY3.js} +2 -2
- package/dist/chunk-RC6SU5NO.js +36 -0
- package/dist/chunk-RC6SU5NO.js.map +1 -0
- package/dist/{chunk-P7EQ2S5O.js → chunk-RRNA5GKT.js} +2 -2
- package/dist/{chunk-ZNOEIM6Y.js → chunk-RYIL3PI2.js} +2 -2
- package/dist/chunk-STNPB3UM.js +9 -0
- package/dist/chunk-STNPB3UM.js.map +1 -0
- package/dist/{chunk-MRIBLZL3.js → chunk-TV3YZ35S.js} +5 -1
- package/dist/chunk-TV3YZ35S.js.map +1 -0
- package/dist/chunk-TY32C732.js +59 -0
- package/dist/chunk-TY32C732.js.map +1 -0
- package/dist/{chunk-YMYK7US4.js → chunk-WIBHRONM.js} +2 -2
- package/dist/chunk-WIBHRONM.js.map +1 -0
- package/dist/{chunk-YS3POABP.js → chunk-WIRRPTFH.js} +1 -1
- package/dist/chunk-WIRRPTFH.js.map +1 -0
- package/dist/{chunk-KESP7GOK.js → chunk-Y26YV5R3.js} +3 -3
- package/dist/{chunk-MIQHZESA.js → chunk-YM7LFCG7.js} +5 -5
- package/dist/{chunk-MIQHZESA.js.map → chunk-YM7LFCG7.js.map} +1 -1
- package/dist/{chunk-2AXFIYHT.js → chunk-Z6FNBOTC.js} +1 -1
- package/dist/chunk-Z6FNBOTC.js.map +1 -0
- package/dist/{chunk-RD5LYKD6.js → chunk-ZROPXHJY.js} +2 -2
- package/dist/chunk-ZROPXHJY.js.map +1 -0
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +7 -6
- package/dist/consent/index.d.ts +7 -6
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-A7FRXYHC.js → crypto-2CRLG4F4.js} +3 -3
- package/dist/{delegation-YBA4X4JN.js → delegation-ZTRT2PRV.js} +5 -5
- package/dist/derivations/index.cjs +18 -1
- package/dist/derivations/index.cjs.map +1 -1
- package/dist/derivations/index.d.cts +10 -9
- package/dist/derivations/index.d.ts +10 -9
- package/dist/derivations/index.js +4 -4
- package/dist/{dev-unlock-DRwVSy2S.d.cts → dev-unlock-AglVnkPY.d.cts} +1 -1
- package/dist/{dev-unlock-D9s-loPr.d.ts → dev-unlock-BOEYl1xl.d.ts} +1 -1
- package/dist/discriminant-BN9REW3o.d.cts +60 -0
- package/dist/discriminant-BN9REW3o.d.ts +60 -0
- package/dist/executor-S76VN45G.js +8 -0
- package/dist/executor-UCXLIGLW.js +11 -0
- package/dist/executor-ZCNZJMGR.js +8 -0
- package/dist/{fanout-sidecar-VJ52RIEY.js → fanout-sidecar-OKPMMPLG.js} +2 -2
- package/dist/fanout-sidecar-OKPMMPLG.js.map +1 -0
- package/dist/guards/index.cjs +7 -0
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +8 -7
- package/dist/guards/index.d.ts +8 -7
- package/dist/guards/index.js +4 -4
- package/dist/{hash-DXXXusyk.d.ts → hash-B9m3_fhj.d.ts} +1 -1
- package/dist/{hash-DtRih9MQ.d.cts → hash-RVqz2zi8.d.cts} +1 -1
- package/dist/history/index.cjs +2 -2
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +8 -7
- package/dist/history/index.d.ts +8 -7
- package/dist/history/index.js +6 -6
- package/dist/i18n/index.cjs +287 -27
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +7 -6
- package/dist/i18n/index.d.ts +7 -6
- package/dist/i18n/index.js +21 -6
- package/dist/i18n/index.js.map +1 -1
- package/dist/{index-CmVgTkqk.d.cts → index-B8bjExET.d.cts} +214 -18
- package/dist/{index-CNwA-B6-.d.ts → index-DfUbNad8.d.ts} +214 -18
- package/dist/index.cjs +3329 -446
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -22
- package/dist/index.d.ts +43 -22
- package/dist/index.js +179 -8799
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs +5 -1
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.d.cts +3 -3
- package/dist/indexing/index.d.ts +3 -3
- package/dist/indexing/index.js +4 -4
- package/dist/issue-3W6IVLKH.js +12 -0
- package/dist/{lazy-builder-Rpd-V3jP.d.ts → lazy-builder-Ci5_YG73.d.cts} +2 -2
- package/dist/{lazy-builder-C-rPfWG0.d.cts → lazy-builder-D5GU14TS.d.ts} +2 -2
- package/dist/{ledger-3TXNP47J.js → ledger-O7FXOG3D.js} +6 -6
- package/dist/materialized-views/index.cjs +21 -2
- package/dist/materialized-views/index.cjs.map +1 -1
- package/dist/materialized-views/index.d.cts +23 -20
- package/dist/materialized-views/index.d.ts +23 -20
- package/dist/materialized-views/index.js +12 -12
- package/dist/noydb-YAZNH5TI.js +34 -0
- package/dist/overlay-views/index.cjs +11 -1
- package/dist/overlay-views/index.cjs.map +1 -1
- package/dist/overlay-views/index.d.cts +10 -9
- package/dist/overlay-views/index.d.ts +10 -9
- package/dist/overlay-views/index.js +6 -4
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +7 -6
- package/dist/periods/index.d.ts +7 -6
- package/dist/periods/index.js +6 -6
- package/dist/{predicate-Dnu81tsS.d.cts → predicate-Bt5ft-9c.d.cts} +28 -3
- package/dist/{predicate-Dnu81tsS.d.ts → predicate-Bt5ft-9c.d.ts} +28 -3
- package/dist/{public-envelope-PY6NKFLI.js → public-envelope-HMYHZIRH.js} +4 -4
- package/dist/query/index.cjs +255 -6
- 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 +12 -6
- package/dist/registry-DKEXOJVO.js +7 -0
- package/dist/{registry-3L3N3PTG.js → registry-ST2VNFZC.js} +3 -3
- package/dist/registry-UFIK7CSR.js +8 -0
- package/dist/registry-ZGYYSM5I.js +8 -0
- package/dist/registry-ZGYYSM5I.js.map +1 -0
- package/dist/revoke-S6JMSLUN.js +17 -0
- package/dist/revoke-S6JMSLUN.js.map +1 -0
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +8 -7
- package/dist/session/index.d.ts +8 -7
- package/dist/session/index.js +3 -3
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +7 -6
- package/dist/shadow/index.d.ts +7 -6
- package/dist/shadow/index.js +2 -2
- package/dist/signer-7NPTB3SQ.js +18 -0
- package/dist/signer-7NPTB3SQ.js.map +1 -0
- package/dist/snapshots/index.cjs +937 -0
- package/dist/snapshots/index.cjs.map +1 -0
- package/dist/snapshots/index.d.cts +28 -0
- package/dist/snapshots/index.d.ts +28 -0
- package/dist/snapshots/index.js +152 -0
- package/dist/snapshots/index.js.map +1 -0
- package/dist/{stale-HSC5YO2O.js → stale-VKXSXJF4.js} +2 -2
- package/dist/stale-VKXSXJF4.js.map +1 -0
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +7 -6
- package/dist/store/index.d.ts +7 -6
- package/dist/store/index.js +2 -2
- package/dist/{strategy-DSTrsZ8t.d.cts → strategy-CT2LCKAX.d.cts} +12 -0
- package/dist/{strategy-DSTrsZ8t.d.ts → strategy-CT2LCKAX.d.ts} +12 -0
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +6 -5
- package/dist/sync/index.d.ts +6 -5
- package/dist/sync/index.js +4 -4
- package/dist/team/index.cjs +1 -1
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +7 -6
- package/dist/team/index.d.ts +7 -6
- package/dist/team/index.js +13 -11
- package/dist/tx/index.cjs +82 -2
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +8 -7
- package/dist/tx/index.d.ts +8 -7
- package/dist/tx/index.js +56 -3
- package/dist/tx/index.js.map +1 -1
- package/dist/{types-DW9RGSSs.d.ts → types-CaNQm4i8.d.ts} +1270 -259
- package/dist/{types-C4lwMKKF.d.cts → types-n2_IfwlQ.d.cts} +1270 -259
- package/dist/{index-4agOpzqd.d.ts → ulid-B9SMWj5i.d.ts} +64 -46
- package/dist/{index-hdFvZkBP.d.cts → ulid-CLMjmyhG.d.cts} +64 -46
- package/dist/util/index.cjs +7 -0
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.d.cts +2 -0
- package/dist/util/index.d.ts +2 -0
- package/dist/util/index.js +5 -1
- package/dist/util/index.js.map +1 -1
- package/dist/{with-derivation-g-pGoMzL.d.ts → with-derivation-CVIOPTUf.d.ts} +1 -1
- package/dist/{with-derivation-C8LDlV7t.d.cts → with-derivation-aKrtS7Jj.d.cts} +1 -1
- package/dist/{with-guard-jI1x9Z3k.d.cts → with-guard-DZQbPzoP.d.cts} +1 -1
- package/dist/{with-guard-DWOCK4Ca.d.ts → with-guard-DseETUrF.d.ts} +1 -1
- package/dist/{with-materialized-view-DaKR-N6J.d.ts → with-materialized-view-C1eA1_T_.d.cts} +2 -2
- package/dist/{with-materialized-view-DcTx4H3j.d.cts → with-materialized-view-DaYaE8-Q.d.ts} +2 -2
- package/dist/{with-overlayed-view-N7jYuNOS.d.ts → with-overlayed-view-DQsh2p8H.d.ts} +2 -2
- package/dist/{with-overlayed-view-D-6oWAgM.d.cts → with-overlayed-view-DleJfKcV.d.cts} +2 -2
- package/package.json +27 -4
- package/dist/chunk-23TTQXVO.js.map +0 -1
- package/dist/chunk-2AXFIYHT.js.map +0 -1
- package/dist/chunk-5ZGZ6HIZ.js.map +0 -1
- package/dist/chunk-6HPZY4ON.js.map +0 -1
- package/dist/chunk-7H6DOO3E.js.map +0 -1
- package/dist/chunk-ADQ5MQ54.js.map +0 -1
- package/dist/chunk-CBAHB2BF.js.map +0 -1
- package/dist/chunk-DPMFBCV6.js.map +0 -1
- package/dist/chunk-DYECX3IX.js.map +0 -1
- package/dist/chunk-EGQYGYIU.js.map +0 -1
- package/dist/chunk-FCXOFQAJ.js.map +0 -1
- package/dist/chunk-HB3Z2GCR.js.map +0 -1
- package/dist/chunk-MRIBLZL3.js.map +0 -1
- package/dist/chunk-NIOHFJPJ.js.map +0 -1
- package/dist/chunk-PA6R5ZCI.js.map +0 -1
- package/dist/chunk-PEULZC6M.js.map +0 -1
- package/dist/chunk-RD5LYKD6.js.map +0 -1
- package/dist/chunk-SIZWEV2Y.js.map +0 -1
- package/dist/chunk-UA4RI7OT.js.map +0 -1
- package/dist/chunk-UZXLQCHP.js.map +0 -1
- package/dist/chunk-VMIO4IXG.js.map +0 -1
- package/dist/chunk-YMYK7US4.js.map +0 -1
- package/dist/chunk-YS3POABP.js.map +0 -1
- package/dist/chunk-Z72JH4KG.js.map +0 -1
- package/dist/executor-7E3VFGW7.js +0 -11
- package/dist/executor-CEWX2FQI.js +0 -8
- package/dist/executor-X4SQ3ZLC.js +0 -8
- package/dist/fanout-sidecar-VJ52RIEY.js.map +0 -1
- package/dist/registry-O47PUPSY.js +0 -8
- package/dist/registry-RFGGMVNJ.js +0 -7
- package/dist/registry-WLLMODKN.js +0 -8
- /package/dist/{chunk-XGSOTWYX.js.map → chunk-2LPPNWF6.js.map} +0 -0
- /package/dist/{chunk-5SCJ5UEF.js.map → chunk-2N62W5YP.js.map} +0 -0
- /package/dist/{chunk-537VFZTR.js.map → chunk-3LPV6BXR.js.map} +0 -0
- /package/dist/{chunk-I6MX32UC.js.map → chunk-BDV7INMP.js.map} +0 -0
- /package/dist/{chunk-MKSA2V7A.js.map → chunk-C3WE6UJY.js.map} +0 -0
- /package/dist/{chunk-DYBQG5PQ.js.map → chunk-CXFOITNS.js.map} +0 -0
- /package/dist/{chunk-4TFSM22V.js.map → chunk-DJRWA3Q5.js.map} +0 -0
- /package/dist/{chunk-5DWL3JBF.js.map → chunk-LSTBFLL2.js.map} +0 -0
- /package/dist/{chunk-34YSDCDP.js.map → chunk-PVUUIWHY.js.map} +0 -0
- /package/dist/{chunk-WCA2NROQ.js.map → chunk-R233SLY3.js.map} +0 -0
- /package/dist/{chunk-P7EQ2S5O.js.map → chunk-RRNA5GKT.js.map} +0 -0
- /package/dist/{chunk-ZNOEIM6Y.js.map → chunk-RYIL3PI2.js.map} +0 -0
- /package/dist/{chunk-KESP7GOK.js.map → chunk-Y26YV5R3.js.map} +0 -0
- /package/dist/{crypto-A7FRXYHC.js.map → crypto-2CRLG4F4.js.map} +0 -0
- /package/dist/{delegation-YBA4X4JN.js.map → delegation-ZTRT2PRV.js.map} +0 -0
- /package/dist/{executor-7E3VFGW7.js.map → executor-S76VN45G.js.map} +0 -0
- /package/dist/{executor-CEWX2FQI.js.map → executor-UCXLIGLW.js.map} +0 -0
- /package/dist/{executor-X4SQ3ZLC.js.map → executor-ZCNZJMGR.js.map} +0 -0
- /package/dist/{ledger-3TXNP47J.js.map → issue-3W6IVLKH.js.map} +0 -0
- /package/dist/{public-envelope-PY6NKFLI.js.map → ledger-O7FXOG3D.js.map} +0 -0
- /package/dist/{registry-3L3N3PTG.js.map → noydb-YAZNH5TI.js.map} +0 -0
- /package/dist/{registry-O47PUPSY.js.map → public-envelope-HMYHZIRH.js.map} +0 -0
- /package/dist/{registry-RFGGMVNJ.js.map → registry-DKEXOJVO.js.map} +0 -0
- /package/dist/{registry-WLLMODKN.js.map → registry-ST2VNFZC.js.map} +0 -0
- /package/dist/{stale-HSC5YO2O.js.map → registry-UFIK7CSR.js.map} +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ATTESTATIONS_COLLECTION,
|
|
3
|
+
REVOKED_RECORD_ID,
|
|
4
|
+
loadOrCreateSigner
|
|
5
|
+
} from "./chunk-HHOO7HGH.js";
|
|
6
|
+
import {
|
|
7
|
+
NOYDB_FORMAT_VERSION
|
|
8
|
+
} from "./chunk-WIRRPTFH.js";
|
|
9
|
+
import {
|
|
10
|
+
decrypt,
|
|
11
|
+
encrypt
|
|
12
|
+
} from "./chunk-R233SLY3.js";
|
|
13
|
+
import {
|
|
14
|
+
AttestationError,
|
|
15
|
+
ConflictError
|
|
16
|
+
} from "./chunk-O6EJ6WTI.js";
|
|
17
|
+
|
|
18
|
+
// src/attestation/revoke.ts
|
|
19
|
+
import { signRevocationList } from "@noy-db/attestation";
|
|
20
|
+
function requireOwner(ctx, op) {
|
|
21
|
+
if (ctx.role !== "owner") {
|
|
22
|
+
throw new AttestationError(`${op} requires the 'owner' role; caller is '${ctx.role}'. Revocation is the firm's identity operation.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function readSet(store, vault, dek) {
|
|
26
|
+
const env = await store.get(vault, ATTESTATIONS_COLLECTION, REVOKED_RECORD_ID);
|
|
27
|
+
if (!env) return { docIds: /* @__PURE__ */ new Set(), version: void 0 };
|
|
28
|
+
const set = JSON.parse(await decrypt(env._iv, env._data, dek));
|
|
29
|
+
return { docIds: new Set(set.docIds), version: env._v };
|
|
30
|
+
}
|
|
31
|
+
async function mutateSet(ctx, mutate) {
|
|
32
|
+
const dek = await ctx.getDEK();
|
|
33
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
34
|
+
const { docIds, version } = await readSet(ctx.store, ctx.vault, dek);
|
|
35
|
+
mutate(docIds);
|
|
36
|
+
const payload = { docIds: [...docIds].sort(), updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
37
|
+
const { iv, data } = await encrypt(JSON.stringify(payload), dek);
|
|
38
|
+
const expectedVersion = version ?? 0;
|
|
39
|
+
const env = {
|
|
40
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
41
|
+
_v: expectedVersion + 1,
|
|
42
|
+
_ts: payload.updatedAt,
|
|
43
|
+
_iv: iv,
|
|
44
|
+
_data: data
|
|
45
|
+
};
|
|
46
|
+
try {
|
|
47
|
+
await ctx.store.put(ctx.vault, ATTESTATIONS_COLLECTION, REVOKED_RECORD_ID, env, expectedVersion);
|
|
48
|
+
return;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
if (e instanceof ConflictError && attempt === 0) continue;
|
|
51
|
+
throw e;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function revokeDocCore(ctx, docId) {
|
|
56
|
+
requireOwner(ctx, "revokeAttestation");
|
|
57
|
+
const issued = await ctx.store.get(ctx.vault, ATTESTATIONS_COLLECTION, docId);
|
|
58
|
+
if (!issued) throw new AttestationError(`revokeAttestation: attestation '${docId}' not found (was it issued by this vault?).`);
|
|
59
|
+
await mutateSet(ctx, (ids) => ids.add(docId));
|
|
60
|
+
}
|
|
61
|
+
async function unrevokeDocCore(ctx, docId) {
|
|
62
|
+
requireOwner(ctx, "unrevokeAttestation");
|
|
63
|
+
await mutateSet(ctx, (ids) => ids.delete(docId));
|
|
64
|
+
}
|
|
65
|
+
async function getRevokedDocIdsCore(ctx) {
|
|
66
|
+
const dek = await ctx.getDEK();
|
|
67
|
+
const { docIds } = await readSet(ctx.store, ctx.vault, dek);
|
|
68
|
+
return [...docIds].sort();
|
|
69
|
+
}
|
|
70
|
+
async function publishRevocationListCore(ctx) {
|
|
71
|
+
requireOwner(ctx, "publishRevocationList");
|
|
72
|
+
const docIds = await getRevokedDocIdsCore(ctx);
|
|
73
|
+
const signer = await loadOrCreateSigner(ctx.store, ctx.vault, () => ctx.getDEK());
|
|
74
|
+
return signRevocationList(docIds, (/* @__PURE__ */ new Date()).toISOString(), signer.keyId, signer.privateKeyPkcs8B64);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
revokeDocCore,
|
|
79
|
+
unrevokeDocCore,
|
|
80
|
+
getRevokedDocIdsCore,
|
|
81
|
+
publishRevocationListCore
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=chunk-5IXJGFF2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/attestation/revoke.ts"],"sourcesContent":["import type { NoydbStore, EncryptedEnvelope } from '../types.js'\nimport { NOYDB_FORMAT_VERSION } from '../types.js'\nimport { encrypt, decrypt } from '../crypto.js'\nimport { AttestationError, ConflictError } from '../errors.js'\nimport { loadOrCreateSigner, ATTESTATIONS_COLLECTION, REVOKED_RECORD_ID } from './signer.js'\nimport { signRevocationList, type RevocationList } from '@noy-db/attestation'\n\n/** Everything the revoke core needs from the Vault, injected for testability. */\nexport interface RevokeContext {\n readonly store: NoydbStore\n readonly vault: string\n readonly role: string\n /** The _attestations collection DEK. */\n getDEK(): Promise<CryptoKey>\n}\n\ninterface RevokedSet {\n docIds: string[]\n updatedAt: string\n}\n\nfunction requireOwner(ctx: RevokeContext, op: string): void {\n if (ctx.role !== 'owner') {\n throw new AttestationError(`${op} requires the 'owner' role; caller is '${ctx.role}'. Revocation is the firm's identity operation.`)\n }\n}\n\nasync function readSet(store: NoydbStore, vault: string, dek: CryptoKey): Promise<{ docIds: Set<string>; version: number | undefined }> {\n const env = await store.get(vault, ATTESTATIONS_COLLECTION, REVOKED_RECORD_ID)\n if (!env) return { docIds: new Set<string>(), version: undefined }\n const set = JSON.parse(await decrypt(env._iv, env._data, dek)) as RevokedSet\n return { docIds: new Set(set.docIds), version: env._v }\n}\n\n/** Read-modify-write the _revoked set with optimistic concurrency + one retry. */\nasync function mutateSet(ctx: RevokeContext, mutate: (ids: Set<string>) => void): Promise<void> {\n const dek = await ctx.getDEK()\n for (let attempt = 0; attempt < 2; attempt++) {\n const { docIds, version } = await readSet(ctx.store, ctx.vault, dek)\n mutate(docIds)\n const payload: RevokedSet = { docIds: [...docIds].sort(), updatedAt: new Date().toISOString() }\n const { iv, data } = await encrypt(JSON.stringify(payload), dek)\n const expectedVersion = version ?? 0\n const env: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION, _v: expectedVersion + 1, _ts: payload.updatedAt, _iv: iv, _data: data,\n }\n try {\n await ctx.store.put(ctx.vault, ATTESTATIONS_COLLECTION, REVOKED_RECORD_ID, env, expectedVersion)\n return\n } catch (e) {\n if (e instanceof ConflictError && attempt === 0) continue\n throw e\n }\n }\n}\n\nexport async function revokeDocCore(ctx: RevokeContext, docId: string): Promise<void> {\n requireOwner(ctx, 'revokeAttestation')\n const issued = await ctx.store.get(ctx.vault, ATTESTATIONS_COLLECTION, docId)\n if (!issued) throw new AttestationError(`revokeAttestation: attestation '${docId}' not found (was it issued by this vault?).`)\n await mutateSet(ctx, (ids) => ids.add(docId))\n}\n\nexport async function unrevokeDocCore(ctx: RevokeContext, docId: string): Promise<void> {\n requireOwner(ctx, 'unrevokeAttestation')\n await mutateSet(ctx, (ids) => ids.delete(docId))\n}\n\nexport async function getRevokedDocIdsCore(ctx: RevokeContext): Promise<string[]> {\n const dek = await ctx.getDEK()\n const { docIds } = await readSet(ctx.store, ctx.vault, dek)\n return [...docIds].sort()\n}\n\nexport async function publishRevocationListCore(ctx: RevokeContext): Promise<RevocationList> {\n requireOwner(ctx, 'publishRevocationList')\n const docIds = await getRevokedDocIdsCore(ctx)\n const signer = await loadOrCreateSigner(ctx.store, ctx.vault, () => ctx.getDEK())\n return signRevocationList(docIds, new Date().toISOString(), signer.keyId, signer.privateKeyPkcs8B64)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAKA,SAAS,0BAA+C;AAgBxD,SAAS,aAAa,KAAoB,IAAkB;AAC1D,MAAI,IAAI,SAAS,SAAS;AACxB,UAAM,IAAI,iBAAiB,GAAG,EAAE,0CAA0C,IAAI,IAAI,iDAAiD;AAAA,EACrI;AACF;AAEA,eAAe,QAAQ,OAAmB,OAAe,KAA+E;AACtI,QAAM,MAAM,MAAM,MAAM,IAAI,OAAO,yBAAyB,iBAAiB;AAC7E,MAAI,CAAC,IAAK,QAAO,EAAE,QAAQ,oBAAI,IAAY,GAAG,SAAS,OAAU;AACjE,QAAM,MAAM,KAAK,MAAM,MAAM,QAAQ,IAAI,KAAK,IAAI,OAAO,GAAG,CAAC;AAC7D,SAAO,EAAE,QAAQ,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,IAAI,GAAG;AACxD;AAGA,eAAe,UAAU,KAAoB,QAAmD;AAC9F,QAAM,MAAM,MAAM,IAAI,OAAO;AAC7B,WAAS,UAAU,GAAG,UAAU,GAAG,WAAW;AAC5C,UAAM,EAAE,QAAQ,QAAQ,IAAI,MAAM,QAAQ,IAAI,OAAO,IAAI,OAAO,GAAG;AACnE,WAAO,MAAM;AACb,UAAM,UAAsB,EAAE,QAAQ,CAAC,GAAG,MAAM,EAAE,KAAK,GAAG,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE;AAC9F,UAAM,EAAE,IAAI,KAAK,IAAI,MAAM,QAAQ,KAAK,UAAU,OAAO,GAAG,GAAG;AAC/D,UAAM,kBAAkB,WAAW;AACnC,UAAM,MAAyB;AAAA,MAC7B,QAAQ;AAAA,MAAsB,IAAI,kBAAkB;AAAA,MAAG,KAAK,QAAQ;AAAA,MAAW,KAAK;AAAA,MAAI,OAAO;AAAA,IACjG;AACA,QAAI;AACF,YAAM,IAAI,MAAM,IAAI,IAAI,OAAO,yBAAyB,mBAAmB,KAAK,eAAe;AAC/F;AAAA,IACF,SAAS,GAAG;AACV,UAAI,aAAa,iBAAiB,YAAY,EAAG;AACjD,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,cAAc,KAAoB,OAA8B;AACpF,eAAa,KAAK,mBAAmB;AACrC,QAAM,SAAS,MAAM,IAAI,MAAM,IAAI,IAAI,OAAO,yBAAyB,KAAK;AAC5E,MAAI,CAAC,OAAQ,OAAM,IAAI,iBAAiB,mCAAmC,KAAK,6CAA6C;AAC7H,QAAM,UAAU,KAAK,CAAC,QAAQ,IAAI,IAAI,KAAK,CAAC;AAC9C;AAEA,eAAsB,gBAAgB,KAAoB,OAA8B;AACtF,eAAa,KAAK,qBAAqB;AACvC,QAAM,UAAU,KAAK,CAAC,QAAQ,IAAI,OAAO,KAAK,CAAC;AACjD;AAEA,eAAsB,qBAAqB,KAAuC;AAChF,QAAM,MAAM,MAAM,IAAI,OAAO;AAC7B,QAAM,EAAE,OAAO,IAAI,MAAM,QAAQ,IAAI,OAAO,IAAI,OAAO,GAAG;AAC1D,SAAO,CAAC,GAAG,MAAM,EAAE,KAAK;AAC1B;AAEA,eAAsB,0BAA0B,KAA6C;AAC3F,eAAa,KAAK,uBAAuB;AACzC,QAAM,SAAS,MAAM,qBAAqB,GAAG;AAC7C,QAAM,SAAS,MAAM,mBAAmB,IAAI,OAAO,IAAI,OAAO,MAAM,IAAI,OAAO,CAAC;AAChF,SAAO,mBAAmB,SAAQ,oBAAI,KAAK,GAAE,YAAY,GAAG,OAAO,OAAO,OAAO,kBAAkB;AACrG;","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DerivationCapExceededError,
|
|
3
3
|
DerivationOutputShapeError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-O6EJ6WTI.js";
|
|
5
5
|
|
|
6
6
|
// src/derivations/executor.ts
|
|
7
7
|
var DerivationExecutor = {
|
|
@@ -121,4 +121,4 @@ var DerivationExecutor = {
|
|
|
121
121
|
export {
|
|
122
122
|
DerivationExecutor
|
|
123
123
|
};
|
|
124
|
-
//# sourceMappingURL=chunk-
|
|
124
|
+
//# sourceMappingURL=chunk-5OEJ6GOT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/derivations/executor.ts"],"sourcesContent":["import { DerivationCapExceededError, DerivationOutputShapeError } from '../errors.js'\nimport type { DerivationContext, DerivationStrategy, DerivedFromMeta } from './types.js'\n\nexport interface RunResult {\n outputs: Record<string, OutputResult>\n failed: boolean\n}\n\n/**\n * Per-output result of a strategy invocation. Discriminated by\n * `kind`:\n *\n * - `record` — the existing v1 shape: one value (or a \"skipped\"\n * marker if the output was optional and `derive` returned null).\n * - `array` — a list of `(key, value)` entries.\n * The caller diffs these against the previously-emitted key set\n * (loaded from the fanout sidecar) to compute deletes + upserts.\n */\nexport type OutputResult =\n | RecordOutputResult\n | ArrayOutputResult\n | FailedOutputResult\n\nexport interface RecordOutputResult {\n kind: 'record'\n value: Record<string, unknown>\n ok: true\n /**\n * `true` when an optional output returned `null` /\n * `undefined`. The caller deletes any previously-emitted output at\n * the same id (mirrors \"tombstone for derived data\"); a never-emitted\n * output is a silent no-op. `ok: true` because skipping is a\n * successful outcome, not a failure.\n */\n skipped?: boolean\n}\n\nexport interface ArrayOutputResult {\n kind: 'array'\n ok: true\n /** One `(key, value)` per derived row. Empty array means \"all prior outputs for this source go.\" */\n entries: ReadonlyArray<{ readonly key: string; readonly value: Record<string, unknown> }>\n}\n\nexport interface FailedOutputResult {\n kind: 'failed'\n ok: false\n error: Error\n /** Always empty on failure; present so consumers don't have to narrow. */\n value: Record<string, unknown>\n}\n\n/**\n * Stateless functions that execute a derivation strategy. Persistence\n * (encrypt + store.put) is the caller's job — typically\n * `DerivationRegistry.onSourceWrite` which iterates run() results and\n * writes each output via `Collection.put`.\n */\nexport const DerivationExecutor = {\n /**\n * Run `derive` once, validate output shape against the spec, stamp\n * `_derivedFrom` onto every output. Returns per-output success or\n * failure; throws only for shape mismatches (a contract violation).\n */\n async run<\n TSource extends Record<string, unknown>,\n TOutputs extends Record<string, Record<string, unknown>>,\n >(\n strategy: DerivationStrategy<TSource, TOutputs>,\n source: TSource & { id: string },\n sourceVersion: number,\n strategyHash: string,\n ctx: DerivationContext,\n ): Promise<RunResult> {\n const outputs: Record<string, OutputResult> = {}\n let derived: Partial<TOutputs>\n\n try {\n derived = await Promise.resolve(strategy.derive(source as TSource, ctx))\n } catch (err) {\n for (const key of Object.keys(strategy.outputs)) {\n outputs[key] = {\n kind: 'failed',\n value: {},\n ok: false,\n error: err instanceof Error ? err : new Error(String(err)),\n }\n }\n return { outputs, failed: true }\n }\n\n const meta: DerivedFromMeta = {\n source: strategy.source,\n sourceId: source.id,\n sourceVersion,\n derivedAt: new Date().toISOString(),\n strategyHash,\n }\n\n for (const key of Object.keys(strategy.outputs)) {\n const outSpec = strategy.outputs[key]\n if (!outSpec) continue\n const value = (derived as Record<string, unknown>)[key]\n\n // ── Array-shape branch ─────────────────────────────────────\n if (outSpec.shape === 'array') {\n if (value === undefined || value === null) {\n // Treat null/undefined as \"empty array\" — clears all prior\n // outputs for this (source, output) pair. The caller's\n // diff turns that into deletes.\n outputs[key] = { kind: 'array', ok: true, entries: [] }\n continue\n }\n if (!Array.isArray(value)) {\n throw new DerivationOutputShapeError(\n key,\n `shape 'array' expects an array, got ${typeof value}`,\n )\n }\n const maxFanout = outSpec.maxFanout ?? 64\n if (value.length > maxFanout) {\n throw new DerivationCapExceededError(key, value.length, maxFanout)\n }\n const entries: Array<{ key: string; value: Record<string, unknown> }> = []\n const seenKeys = new Set<string>()\n for (let i = 0; i < value.length; i++) {\n const row = value[i] as unknown\n if (row === null || typeof row !== 'object') {\n throw new DerivationOutputShapeError(\n key,\n `array member at index ${i} must be a non-null object (got ${row === null ? 'null' : typeof row})`,\n )\n }\n let derivedKey: string\n try {\n derivedKey = outSpec.key(row as Record<string, unknown>)\n } catch (err) {\n throw new DerivationOutputShapeError(\n key,\n `key extractor threw on array member at index ${i}: `\n + (err instanceof Error ? err.message : String(err)),\n )\n }\n if (typeof derivedKey !== 'string' || derivedKey.length === 0) {\n throw new DerivationOutputShapeError(\n key,\n `key extractor returned ${typeof derivedKey === 'string' ? 'empty string' : typeof derivedKey} at index ${i}; expected non-empty string`,\n )\n }\n if (seenKeys.has(derivedKey)) {\n throw new DerivationOutputShapeError(\n key,\n `duplicate key \"${derivedKey}\" in array output (index ${i}); each derived row must have a unique key within a single derive() invocation`,\n )\n }\n seenKeys.add(derivedKey)\n entries.push({\n key: derivedKey,\n value: { ...(row as Record<string, unknown>), _derivedFrom: meta },\n })\n }\n outputs[key] = { kind: 'array', ok: true, entries }\n continue\n }\n\n // ── Record-shape branch (existing v1 behavior) ─────────────\n if (value === undefined || value === null) {\n if (outSpec.optional === true) {\n // Optional output explicitly skipped. Mark for caller\n // so any prior-emitted output at this id can be deleted.\n outputs[key] = { kind: 'record', value: {}, ok: true, skipped: true }\n continue\n }\n throw new DerivationOutputShapeError(\n key,\n `expected object, got ${value === undefined ? 'undefined' : 'null'}`,\n )\n }\n if (typeof value !== 'object') {\n throw new DerivationOutputShapeError(\n key,\n `expected object, got ${typeof value}`,\n )\n }\n outputs[key] = {\n kind: 'record',\n value: { ...(value as Record<string, unknown>), _derivedFrom: meta },\n ok: true,\n }\n }\n return { outputs, failed: false }\n },\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureCollectionDEK
|
|
3
|
+
} from "./chunk-HQSQC2XL.js";
|
|
4
|
+
import {
|
|
5
|
+
NOYDB_FORMAT_VERSION
|
|
6
|
+
} from "./chunk-WIRRPTFH.js";
|
|
7
|
+
import {
|
|
8
|
+
decrypt,
|
|
9
|
+
encrypt
|
|
10
|
+
} from "./chunk-R233SLY3.js";
|
|
11
|
+
import {
|
|
12
|
+
PermissionDeniedError
|
|
13
|
+
} from "./chunk-O6EJ6WTI.js";
|
|
14
|
+
|
|
15
|
+
// src/team/sync-credentials.ts
|
|
16
|
+
var SYNC_CREDENTIALS_COLLECTION = "_sync_credentials";
|
|
17
|
+
function requireAdminAccess(keyring) {
|
|
18
|
+
if (keyring.role !== "owner" && keyring.role !== "admin") {
|
|
19
|
+
throw new PermissionDeniedError(
|
|
20
|
+
`Sync credentials require owner or admin role. Current role: "${keyring.role}"`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function putCredential(adapter, vault, keyring, credential) {
|
|
25
|
+
requireAdminAccess(keyring);
|
|
26
|
+
const getDek = await ensureCollectionDEK(adapter, vault, keyring);
|
|
27
|
+
const dek = await getDek(SYNC_CREDENTIALS_COLLECTION);
|
|
28
|
+
const { iv, data } = await encrypt(JSON.stringify(credential), dek);
|
|
29
|
+
const existing = await adapter.get(vault, SYNC_CREDENTIALS_COLLECTION, credential.adapterId);
|
|
30
|
+
const version = existing ? existing._v + 1 : 1;
|
|
31
|
+
const envelope = {
|
|
32
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
33
|
+
_v: version,
|
|
34
|
+
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35
|
+
_iv: iv,
|
|
36
|
+
_data: data,
|
|
37
|
+
_by: keyring.userId
|
|
38
|
+
};
|
|
39
|
+
await adapter.put(
|
|
40
|
+
vault,
|
|
41
|
+
SYNC_CREDENTIALS_COLLECTION,
|
|
42
|
+
credential.adapterId,
|
|
43
|
+
envelope,
|
|
44
|
+
existing ? existing._v : void 0
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
async function getCredential(adapter, vault, keyring, adapterId) {
|
|
48
|
+
requireAdminAccess(keyring);
|
|
49
|
+
const getDek = await ensureCollectionDEK(adapter, vault, keyring);
|
|
50
|
+
const dek = await getDek(SYNC_CREDENTIALS_COLLECTION);
|
|
51
|
+
const envelope = await adapter.get(vault, SYNC_CREDENTIALS_COLLECTION, adapterId);
|
|
52
|
+
if (!envelope) return null;
|
|
53
|
+
const plaintext = await decrypt(envelope._iv, envelope._data, dek);
|
|
54
|
+
return JSON.parse(plaintext);
|
|
55
|
+
}
|
|
56
|
+
async function deleteCredential(adapter, vault, keyring, adapterId) {
|
|
57
|
+
requireAdminAccess(keyring);
|
|
58
|
+
await adapter.delete(vault, SYNC_CREDENTIALS_COLLECTION, adapterId);
|
|
59
|
+
}
|
|
60
|
+
async function listCredentials(adapter, vault, keyring) {
|
|
61
|
+
requireAdminAccess(keyring);
|
|
62
|
+
return adapter.list(vault, SYNC_CREDENTIALS_COLLECTION);
|
|
63
|
+
}
|
|
64
|
+
async function credentialStatus(adapter, vault, keyring, adapterId) {
|
|
65
|
+
const credential = await getCredential(adapter, vault, keyring, adapterId);
|
|
66
|
+
if (!credential) return { exists: false };
|
|
67
|
+
const expired = credential.expiresAt ? Date.now() > new Date(credential.expiresAt).getTime() : false;
|
|
68
|
+
return { exists: true, expired };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
SYNC_CREDENTIALS_COLLECTION,
|
|
73
|
+
putCredential,
|
|
74
|
+
getCredential,
|
|
75
|
+
deleteCredential,
|
|
76
|
+
listCredentials,
|
|
77
|
+
credentialStatus
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=chunk-5OX6XVNS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/team/sync-credentials.ts"],"sourcesContent":["/**\n * _sync_credentials reserved collection —\n *\n * Stores per-adapter OAuth tokens (and any other long-lived sync secrets) as\n * encrypted records inside the vault itself. Tokens are wrapped with the\n * compartment's own DEK, live on disk as ciphertext like any other record, and\n * are accessed only through the dedicated API in this module — never via\n * `vault.collection('_sync_credentials')`.\n *\n * Design decisions\n * ────────────────\n *\n * **Why a reserved collection, not a separate store?**\n * The compartment's existing encryption stack (AES-256-GCM + collection DEK)\n * is exactly the right primitive for protecting OAuth tokens at rest. Using a\n * separate store would require a new encryption surface, new adapter calls,\n * and a new backup/restore path — all of which already exist for collections.\n *\n * **Why not exposed as a regular collection?**\n * The same reason `_keyring` and `_ledger` aren't: they have invariants that\n * must be enforced (naming scheme, no cross-user leakage, no schema\n * validation, no history/ledger writes for privacy). Routing through a\n * dedicated API enforces those invariants.\n *\n * **Token lifecycle:**\n * - `putCredential(vault, adapterId, token)` — store or overwrite\n * - `getCredential(vault, adapterId)` — load and decrypt\n * - `deleteCredential(vault, adapterId)` — remove\n * - `listCredentials(vault)` — enumerate adapter IDs (not tokens)\n *\n * The `adapterId` is the record ID within the `_sync_credentials` collection.\n * It should be a stable, human-readable identifier for the adapter instance\n * (e.g. `'google-drive'`, `'dropbox'`, `'s3-prod'`).\n *\n * **ACL:** only `owner` and `admin` roles can read/write sync credentials.\n * Operators, viewers, and clients cannot call this API. The check is made\n * against the caller's keyring role at call time.\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../types.js'\nimport { NOYDB_FORMAT_VERSION } from '../types.js'\nimport type { UnlockedKeyring } from './keyring.js'\nimport { encrypt, decrypt } from '../crypto.js'\nimport { ensureCollectionDEK } from './keyring.js'\nimport { PermissionDeniedError } from '../errors.js'\n\n/** The reserved collection name. Never collides with user collections. */\nexport const SYNC_CREDENTIALS_COLLECTION = '_sync_credentials'\n\n// ─── Token types ──────────────────────────────────────────────────────\n\n/**\n * An OAuth/auth token stored in `_sync_credentials`.\n *\n * Fields mirror the OAuth2 token response shape. `customData` is an escape\n * hatch for adapter-specific secrets (API keys, connection strings, etc.)\n * that don't fit the OAuth2 shape.\n */\nexport interface SyncCredential {\n /** Stable identifier for the adapter instance (e.g. 'google-drive'). */\n readonly adapterId: string\n /** OAuth token type, usually 'Bearer'. */\n readonly tokenType: string\n /** The access token. Expires at `expiresAt` if set. */\n readonly accessToken: string\n /** Long-lived refresh token for renewing the access token. */\n readonly refreshToken?: string\n /** ISO timestamp when `accessToken` expires. Absent means \"no expiry\". */\n readonly expiresAt?: string\n /** Space-separated OAuth scopes. */\n readonly scopes?: string\n /** Adapter-specific opaque data (API keys, endpoints, etc.). */\n readonly customData?: Record<string, string>\n}\n\n// ─── Access check ─────────────────────────────────────────────────────\n\nfunction requireAdminAccess(keyring: UnlockedKeyring): void {\n if (keyring.role !== 'owner' && keyring.role !== 'admin') {\n throw new PermissionDeniedError(\n `Sync credentials require owner or admin role. Current role: \"${keyring.role}\"`,\n )\n }\n}\n\n// ─── Public API ────────────────────────────────────────────────────────\n\n/**\n * Store or overwrite a sync credential for the given adapter.\n *\n * The credential is encrypted with the `_sync_credentials` collection DEK\n * (auto-generated on first use). The record ID is the `adapterId`.\n *\n * Requires owner or admin role.\n */\nexport async function putCredential(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n credential: SyncCredential,\n): Promise<void> {\n requireAdminAccess(keyring)\n\n const getDek = await ensureCollectionDEK(adapter, vault, keyring)\n const dek = await getDek(SYNC_CREDENTIALS_COLLECTION)\n\n const { iv, data } = await encrypt(JSON.stringify(credential), dek)\n\n const existing = await adapter.get(vault, SYNC_CREDENTIALS_COLLECTION, credential.adapterId)\n const version = existing ? existing._v + 1 : 1\n\n const envelope: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: version,\n _ts: new Date().toISOString(),\n _iv: iv,\n _data: data,\n _by: keyring.userId,\n }\n\n await adapter.put(\n vault,\n SYNC_CREDENTIALS_COLLECTION,\n credential.adapterId,\n envelope,\n existing ? existing._v : undefined,\n )\n}\n\n/**\n * Load and decrypt a sync credential for the given adapter ID.\n *\n * Returns `null` if no credential exists for this adapter.\n * Requires owner or admin role.\n */\nexport async function getCredential(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n adapterId: string,\n): Promise<SyncCredential | null> {\n requireAdminAccess(keyring)\n\n const getDek = await ensureCollectionDEK(adapter, vault, keyring)\n const dek = await getDek(SYNC_CREDENTIALS_COLLECTION)\n\n const envelope = await adapter.get(vault, SYNC_CREDENTIALS_COLLECTION, adapterId)\n if (!envelope) return null\n\n const plaintext = await decrypt(envelope._iv, envelope._data, dek)\n return JSON.parse(plaintext) as SyncCredential\n}\n\n/**\n * Delete a sync credential by adapter ID.\n *\n * No-op if the credential doesn't exist. Requires owner or admin role.\n */\nexport async function deleteCredential(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n adapterId: string,\n): Promise<void> {\n requireAdminAccess(keyring)\n await adapter.delete(vault, SYNC_CREDENTIALS_COLLECTION, adapterId)\n}\n\n/**\n * List all adapter IDs that have stored credentials.\n *\n * Returns only the IDs, never the credential payloads. Useful for\n * displaying \"connected adapters\" in UI without decrypting tokens.\n * Requires owner or admin role.\n */\nexport async function listCredentials(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n): Promise<string[]> {\n requireAdminAccess(keyring)\n return adapter.list(vault, SYNC_CREDENTIALS_COLLECTION)\n}\n\n/**\n * Check whether a credential exists and whether its access token has expired.\n *\n * Returns `{ exists: false }` if no credential is stored, or\n * `{ exists: true, expired: boolean }` based on the `expiresAt` field.\n * Requires owner or admin role.\n */\nexport async function credentialStatus(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n adapterId: string,\n): Promise<{ exists: false } | { exists: true; expired: boolean }> {\n const credential = await getCredential(adapter, vault, keyring, adapterId)\n if (!credential) return { exists: false }\n\n const expired = credential.expiresAt\n ? Date.now() > new Date(credential.expiresAt).getTime()\n : false\n\n return { exists: true, expired }\n}\n"],"mappings":";;;;;;;;;;;;;;;AA+CO,IAAM,8BAA8B;AA8B3C,SAAS,mBAAmB,SAAgC;AAC1D,MAAI,QAAQ,SAAS,WAAW,QAAQ,SAAS,SAAS;AACxD,UAAM,IAAI;AAAA,MACR,gEAAgE,QAAQ,IAAI;AAAA,IAC9E;AAAA,EACF;AACF;AAYA,eAAsB,cACpB,SACA,OACA,SACA,YACe;AACf,qBAAmB,OAAO;AAE1B,QAAM,SAAS,MAAM,oBAAoB,SAAS,OAAO,OAAO;AAChE,QAAM,MAAM,MAAM,OAAO,2BAA2B;AAEpD,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,QAAQ,KAAK,UAAU,UAAU,GAAG,GAAG;AAElE,QAAM,WAAW,MAAM,QAAQ,IAAI,OAAO,6BAA6B,WAAW,SAAS;AAC3F,QAAM,UAAU,WAAW,SAAS,KAAK,IAAI;AAE7C,QAAM,WAA8B;AAAA,IAClC,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAK,oBAAI,KAAK,GAAE,YAAY;AAAA,IAC5B,KAAK;AAAA,IACL,OAAO;AAAA,IACP,KAAK,QAAQ;AAAA,EACf;AAEA,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,WAAW,SAAS,KAAK;AAAA,EAC3B;AACF;AAQA,eAAsB,cACpB,SACA,OACA,SACA,WACgC;AAChC,qBAAmB,OAAO;AAE1B,QAAM,SAAS,MAAM,oBAAoB,SAAS,OAAO,OAAO;AAChE,QAAM,MAAM,MAAM,OAAO,2BAA2B;AAEpD,QAAM,WAAW,MAAM,QAAQ,IAAI,OAAO,6BAA6B,SAAS;AAChF,MAAI,CAAC,SAAU,QAAO;AAEtB,QAAM,YAAY,MAAM,QAAQ,SAAS,KAAK,SAAS,OAAO,GAAG;AACjE,SAAO,KAAK,MAAM,SAAS;AAC7B;AAOA,eAAsB,iBACpB,SACA,OACA,SACA,WACe;AACf,qBAAmB,OAAO;AAC1B,QAAM,QAAQ,OAAO,OAAO,6BAA6B,SAAS;AACpE;AASA,eAAsB,gBACpB,SACA,OACA,SACmB;AACnB,qBAAmB,OAAO;AAC1B,SAAO,QAAQ,KAAK,OAAO,2BAA2B;AACxD;AASA,eAAsB,iBACpB,SACA,OACA,SACA,WACiE;AACjE,QAAM,aAAa,MAAM,cAAc,SAAS,OAAO,SAAS,SAAS;AACzE,MAAI,CAAC,WAAY,QAAO,EAAE,QAAQ,MAAM;AAExC,QAAM,UAAU,WAAW,YACvB,KAAK,IAAI,IAAI,IAAI,KAAK,WAAW,SAAS,EAAE,QAAQ,IACpD;AAEJ,SAAO,EAAE,QAAQ,MAAM,QAAQ;AACjC;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NOYDB_FORMAT_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-WIRRPTFH.js";
|
|
4
4
|
import {
|
|
5
5
|
base64ToBuffer,
|
|
6
6
|
bufferToBase64,
|
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
encryptBytesWithAAD,
|
|
11
11
|
hmacSha256Hex,
|
|
12
12
|
sha256Hex
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-R233SLY3.js";
|
|
14
14
|
import {
|
|
15
15
|
ConflictError,
|
|
16
16
|
NotFoundError
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-O6EJ6WTI.js";
|
|
18
18
|
|
|
19
19
|
// src/blobs/mime-magic.ts
|
|
20
20
|
function hex(s) {
|
|
@@ -820,7 +820,7 @@ var BlobSet = class {
|
|
|
820
820
|
return this.buildResponse(slot, result.blob, { inline: true });
|
|
821
821
|
}
|
|
822
822
|
const aad = chunkAAD(slot.eTag, 0, result.blob.chunkCount);
|
|
823
|
-
const { decryptBytesWithAAD: decryptAAD } = await import("./crypto-
|
|
823
|
+
const { decryptBytesWithAAD: decryptAAD } = await import("./crypto-2CRLG4F4.js");
|
|
824
824
|
const decrypted = await decryptAAD(envelope._iv, envelope._data, blobDEK, aad);
|
|
825
825
|
const plaintext = result.blob.compression === "gzip" ? await decompressBytes(decrypted) : decrypted;
|
|
826
826
|
const body = new ReadableStream({
|
|
@@ -871,224 +871,6 @@ async function plainSha256Hex(data) {
|
|
|
871
871
|
return sha256Hex(data);
|
|
872
872
|
}
|
|
873
873
|
|
|
874
|
-
// src/blobs/export-blobs.ts
|
|
875
|
-
var ExportBlobsAbortedError = class extends Error {
|
|
876
|
-
constructor(reason) {
|
|
877
|
-
super(`exportBlobs aborted: ${reason}`);
|
|
878
|
-
this.name = "ExportBlobsAbortedError";
|
|
879
|
-
}
|
|
880
|
-
};
|
|
881
|
-
var EXPORT_AUDIT_COLLECTION = "_export_audit";
|
|
882
|
-
function createExportBlobsHandle(actor, listAccessibleCollections, getCollection, writeAudit, options) {
|
|
883
|
-
let aborted = false;
|
|
884
|
-
const abort = () => {
|
|
885
|
-
aborted = true;
|
|
886
|
-
};
|
|
887
|
-
if (options.signal) {
|
|
888
|
-
if (options.signal.aborted) aborted = true;
|
|
889
|
-
options.signal.addEventListener("abort", () => {
|
|
890
|
-
aborted = true;
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
function assertLive() {
|
|
894
|
-
if (aborted) throw new ExportBlobsAbortedError("aborted by caller");
|
|
895
|
-
}
|
|
896
|
-
const allowlist = options.collections ? new Set(options.collections) : null;
|
|
897
|
-
let auditPromise = null;
|
|
898
|
-
function writeAuditOnce() {
|
|
899
|
-
if (!auditPromise) {
|
|
900
|
-
auditPromise = writeAudit({
|
|
901
|
-
id: generateBatchId(),
|
|
902
|
-
mechanism: "exportBlobs",
|
|
903
|
-
actor,
|
|
904
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
905
|
-
collections: options.collections ?? null,
|
|
906
|
-
predicate: Boolean(options.where),
|
|
907
|
-
afterBlobId: options.afterBlobId ?? null
|
|
908
|
-
});
|
|
909
|
-
}
|
|
910
|
-
return auditPromise;
|
|
911
|
-
}
|
|
912
|
-
async function* generate() {
|
|
913
|
-
await writeAuditOnce();
|
|
914
|
-
assertLive();
|
|
915
|
-
const allCollections = await listAccessibleCollections();
|
|
916
|
-
const targets = allCollections.filter((name) => {
|
|
917
|
-
if (name.startsWith("_")) return false;
|
|
918
|
-
if (allowlist && !allowlist.has(name)) return false;
|
|
919
|
-
return true;
|
|
920
|
-
});
|
|
921
|
-
let resumeCursorHit = options.afterBlobId === void 0;
|
|
922
|
-
for (const collectionName of targets) {
|
|
923
|
-
if (aborted) return;
|
|
924
|
-
const coll = getCollection(collectionName);
|
|
925
|
-
const records = await coll.list().catch(() => []);
|
|
926
|
-
for (const record of records) {
|
|
927
|
-
if (aborted) return;
|
|
928
|
-
assertLive();
|
|
929
|
-
const idField = record.id;
|
|
930
|
-
if (typeof idField !== "string") continue;
|
|
931
|
-
if (options.where && !options.where(record, { collection: collectionName, id: idField })) continue;
|
|
932
|
-
const blobSet = coll.blob(idField);
|
|
933
|
-
const slots = await blobSet.list().catch(() => []);
|
|
934
|
-
for (const slot of slots) {
|
|
935
|
-
if (aborted) return;
|
|
936
|
-
if (!resumeCursorHit) {
|
|
937
|
-
if (slot.eTag === options.afterBlobId) {
|
|
938
|
-
resumeCursorHit = true;
|
|
939
|
-
}
|
|
940
|
-
continue;
|
|
941
|
-
}
|
|
942
|
-
const bytes = await blobSet.get(slot.name);
|
|
943
|
-
if (!bytes) continue;
|
|
944
|
-
const item = {
|
|
945
|
-
blobId: slot.eTag,
|
|
946
|
-
recordRef: { collection: collectionName, id: idField, slot: slot.name },
|
|
947
|
-
bytes,
|
|
948
|
-
meta: {
|
|
949
|
-
size: slot.size,
|
|
950
|
-
filename: slot.filename,
|
|
951
|
-
...slot.mimeType !== void 0 && { mimeType: slot.mimeType },
|
|
952
|
-
...slot.uploadedAt !== void 0 && { createdAt: slot.uploadedAt }
|
|
953
|
-
}
|
|
954
|
-
};
|
|
955
|
-
yield item;
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
const handle = {
|
|
961
|
-
abort,
|
|
962
|
-
get aborted() {
|
|
963
|
-
return aborted;
|
|
964
|
-
},
|
|
965
|
-
[Symbol.asyncIterator]: () => generate()
|
|
966
|
-
};
|
|
967
|
-
return handle;
|
|
968
|
-
}
|
|
969
|
-
function generateBatchId() {
|
|
970
|
-
const raw = globalThis.crypto.getRandomValues(new Uint8Array(16));
|
|
971
|
-
let s = "";
|
|
972
|
-
for (const b of raw) s += b.toString(16).padStart(2, "0");
|
|
973
|
-
return `batch-${Date.now().toString(36)}-${s.slice(0, 12)}`;
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
// src/blobs/blob-compaction.ts
|
|
977
|
-
var BLOB_EVICTION_AUDIT_COLLECTION = "_blob_eviction_audit";
|
|
978
|
-
async function runCompaction(ctx, options = {}) {
|
|
979
|
-
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
980
|
-
const maxEvictions = options.maxEvictions ?? Infinity;
|
|
981
|
-
const dryRun = options.dryRun === true;
|
|
982
|
-
const allCollections = await ctx.listCollections();
|
|
983
|
-
const byCollection = {};
|
|
984
|
-
let evicted = 0;
|
|
985
|
-
let records = 0;
|
|
986
|
-
let auditEntries = 0;
|
|
987
|
-
let collectionsWithPolicy = 0;
|
|
988
|
-
outer: for (const collectionName of allCollections) {
|
|
989
|
-
if (collectionName.startsWith("_")) continue;
|
|
990
|
-
const config = ctx.getBlobFields(collectionName);
|
|
991
|
-
if (!config) continue;
|
|
992
|
-
const configuredSlots = Object.keys(config);
|
|
993
|
-
if (configuredSlots.length === 0) continue;
|
|
994
|
-
collectionsWithPolicy += 1;
|
|
995
|
-
byCollection[collectionName] = { records: 0, evicted: 0 };
|
|
996
|
-
const ids = await ctx.listRecords(collectionName);
|
|
997
|
-
for (const recordId of ids) {
|
|
998
|
-
if (evicted >= maxEvictions) break outer;
|
|
999
|
-
const record = await ctx.getRecord(collectionName, recordId).catch(() => null);
|
|
1000
|
-
if (record === null) continue;
|
|
1001
|
-
records += 1;
|
|
1002
|
-
byCollection[collectionName].records += 1;
|
|
1003
|
-
const slots = await ctx.listSlots(collectionName, recordId).catch(() => []);
|
|
1004
|
-
for (const slot of slots) {
|
|
1005
|
-
if (evicted >= maxEvictions) break outer;
|
|
1006
|
-
const policy = config[slot.name];
|
|
1007
|
-
if (!policy) continue;
|
|
1008
|
-
const reason = evaluatePolicy(policy, record, slot, now);
|
|
1009
|
-
if (!reason) continue;
|
|
1010
|
-
if (!dryRun) {
|
|
1011
|
-
await ctx.deleteSlot(collectionName, recordId, slot.name);
|
|
1012
|
-
await writeAuditEntry(ctx, {
|
|
1013
|
-
id: generateEvictionId(collectionName, recordId, slot.name),
|
|
1014
|
-
collection: collectionName,
|
|
1015
|
-
recordId,
|
|
1016
|
-
slotName: slot.name,
|
|
1017
|
-
blobHash: slot.eTag,
|
|
1018
|
-
reason,
|
|
1019
|
-
evictedAt: now.toISOString(),
|
|
1020
|
-
actor: ctx.actor
|
|
1021
|
-
});
|
|
1022
|
-
auditEntries += 1;
|
|
1023
|
-
}
|
|
1024
|
-
evicted += 1;
|
|
1025
|
-
byCollection[collectionName].evicted += 1;
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
return {
|
|
1030
|
-
evicted,
|
|
1031
|
-
records,
|
|
1032
|
-
collections: collectionsWithPolicy,
|
|
1033
|
-
auditEntries,
|
|
1034
|
-
byCollection
|
|
1035
|
-
};
|
|
1036
|
-
}
|
|
1037
|
-
function evaluatePolicy(policy, record, slot, now) {
|
|
1038
|
-
let ttlTriggered = false;
|
|
1039
|
-
let predicateTriggered = false;
|
|
1040
|
-
if (policy.retainDays !== void 0 && policy.retainDays > 0) {
|
|
1041
|
-
const uploadedAt = Date.parse(slot.uploadedAt);
|
|
1042
|
-
if (Number.isFinite(uploadedAt)) {
|
|
1043
|
-
const ageMs = now.getTime() - uploadedAt;
|
|
1044
|
-
const limitMs = policy.retainDays * 864e5;
|
|
1045
|
-
if (ageMs > limitMs) ttlTriggered = true;
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
if (policy.evictWhen) {
|
|
1049
|
-
try {
|
|
1050
|
-
if (policy.evictWhen(record)) predicateTriggered = true;
|
|
1051
|
-
} catch {
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
if (ttlTriggered && predicateTriggered) return "both";
|
|
1055
|
-
if (ttlTriggered) return "ttl";
|
|
1056
|
-
if (predicateTriggered) return "predicate";
|
|
1057
|
-
return null;
|
|
1058
|
-
}
|
|
1059
|
-
function generateEvictionId(collection, recordId, slotName) {
|
|
1060
|
-
const rand = globalThis.crypto.getRandomValues(new Uint8Array(8));
|
|
1061
|
-
let suffix = "";
|
|
1062
|
-
for (const b of rand) suffix += b.toString(16).padStart(2, "0");
|
|
1063
|
-
return `${collection}__${recordId}__${slotName}__${suffix}`;
|
|
1064
|
-
}
|
|
1065
|
-
async function writeAuditEntry(ctx, entry) {
|
|
1066
|
-
const json = JSON.stringify(entry);
|
|
1067
|
-
let envelope;
|
|
1068
|
-
if (ctx.encrypted) {
|
|
1069
|
-
const dek = await ctx.getDEK(BLOB_EVICTION_AUDIT_COLLECTION);
|
|
1070
|
-
const { iv, data } = await encrypt(json, dek);
|
|
1071
|
-
envelope = {
|
|
1072
|
-
_noydb: NOYDB_FORMAT_VERSION,
|
|
1073
|
-
_v: 1,
|
|
1074
|
-
_ts: entry.evictedAt,
|
|
1075
|
-
_iv: iv,
|
|
1076
|
-
_data: data,
|
|
1077
|
-
_by: entry.actor
|
|
1078
|
-
};
|
|
1079
|
-
} else {
|
|
1080
|
-
envelope = {
|
|
1081
|
-
_noydb: NOYDB_FORMAT_VERSION,
|
|
1082
|
-
_v: 1,
|
|
1083
|
-
_ts: entry.evictedAt,
|
|
1084
|
-
_iv: "",
|
|
1085
|
-
_data: json,
|
|
1086
|
-
_by: entry.actor
|
|
1087
|
-
};
|
|
1088
|
-
}
|
|
1089
|
-
await ctx.adapter.put(ctx.vault, BLOB_EVICTION_AUDIT_COLLECTION, entry.id, envelope);
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
874
|
export {
|
|
1093
875
|
detectMimeType,
|
|
1094
876
|
detectMagic,
|
|
@@ -1099,11 +881,6 @@ export {
|
|
|
1099
881
|
BLOB_SLOTS_PREFIX,
|
|
1100
882
|
BLOB_VERSIONS_PREFIX,
|
|
1101
883
|
DEFAULT_CHUNK_SIZE,
|
|
1102
|
-
BlobSet
|
|
1103
|
-
ExportBlobsAbortedError,
|
|
1104
|
-
EXPORT_AUDIT_COLLECTION,
|
|
1105
|
-
createExportBlobsHandle,
|
|
1106
|
-
BLOB_EVICTION_AUDIT_COLLECTION,
|
|
1107
|
-
runCompaction
|
|
884
|
+
BlobSet
|
|
1108
885
|
};
|
|
1109
|
-
//# sourceMappingURL=chunk-
|
|
886
|
+
//# sourceMappingURL=chunk-6EOXTJS2.js.map
|