@noy-db/hub 0.2.0-pre.1 → 0.2.0-pre.2
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.map +1 -1
- package/dist/aggregate/index.js +2 -2
- 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 +4 -3
- package/dist/blobs/index.d.ts +4 -3
- package/dist/blobs/index.js +9 -7
- package/dist/blobs/index.js.map +1 -1
- package/dist/bundle/index.cjs +16701 -129
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +172 -3
- package/dist/bundle/index.d.ts +172 -3
- package/dist/bundle/index.js +533 -5
- package/dist/bundle/index.js.map +1 -1
- package/dist/{chunk-5SCJ5UEF.js → chunk-243PNUA6.js} +2 -2
- package/dist/{chunk-WCA2NROQ.js → chunk-2PAQNPE3.js} +2 -2
- package/dist/chunk-3QAKZ37R.js +83 -0
- package/dist/chunk-3QAKZ37R.js.map +1 -0
- package/dist/chunk-3S4BJX25.js +36 -0
- package/dist/chunk-3S4BJX25.js.map +1 -0
- package/dist/chunk-3XHOCQK4.js +118 -0
- package/dist/chunk-3XHOCQK4.js.map +1 -0
- package/dist/{chunk-4TFSM22V.js → chunk-3Y53S2SA.js} +3 -3
- package/dist/{chunk-6HPZY4ON.js → chunk-3Z2TPHC4.js} +3 -3
- package/dist/chunk-4HIL6AHQ.js +57 -0
- package/dist/chunk-4HIL6AHQ.js.map +1 -0
- package/dist/{chunk-DYECX3IX.js → chunk-7BRE6EUA.js} +2 -2
- package/dist/{chunk-DYBQG5PQ.js → chunk-7BUTTVMR.js} +2 -2
- package/dist/{chunk-KESP7GOK.js → chunk-7Q5PLD5C.js} +3 -3
- package/dist/{chunk-UA4RI7OT.js → chunk-7Z23ZFLV.js} +4 -4
- package/dist/chunk-AHPFONIL.js +59 -0
- package/dist/chunk-AHPFONIL.js.map +1 -0
- package/dist/{chunk-EGQYGYIU.js → chunk-CXSCDO5T.js} +2 -2
- package/dist/chunk-E535SAN4.js +8834 -0
- package/dist/chunk-E535SAN4.js.map +1 -0
- package/dist/{chunk-CBAHB2BF.js → chunk-EUYOGYGV.js} +6 -69
- package/dist/chunk-EUYOGYGV.js.map +1 -0
- package/dist/{chunk-OMLIZL2P.js → chunk-FAQVNJD4.js} +2 -2
- package/dist/{chunk-I6MX32UC.js → chunk-G6FRSBKK.js} +4 -4
- package/dist/{chunk-FCXOFQAJ.js → chunk-GIV6DWBG.js} +2 -2
- package/dist/{chunk-34YSDCDP.js → chunk-HXJXPZRE.js} +2 -2
- package/dist/{chunk-23TTQXVO.js → chunk-J4KLMEUL.js} +2 -2
- package/dist/{chunk-VMIO4IXG.js → chunk-JYQTXEIO.js} +5 -228
- package/dist/chunk-JYQTXEIO.js.map +1 -0
- package/dist/{chunk-NIOHFJPJ.js → chunk-LRAZDV5X.js} +6 -118
- package/dist/chunk-LRAZDV5X.js.map +1 -0
- package/dist/{chunk-P7EQ2S5O.js → chunk-MUWOSVEP.js} +2 -2
- package/dist/chunk-NWZ3I6R6.js +79 -0
- package/dist/chunk-NWZ3I6R6.js.map +1 -0
- package/dist/{chunk-HB3Z2GCR.js → chunk-OVZDFEOR.js} +2 -2
- package/dist/chunk-PFSNOPBQ.js +233 -0
- package/dist/chunk-PFSNOPBQ.js.map +1 -0
- package/dist/{chunk-UZXLQCHP.js → chunk-PLI5TV7N.js} +2 -2
- package/dist/{chunk-PA6R5ZCI.js → chunk-Q6W2CMEJ.js} +3 -3
- package/dist/{chunk-537VFZTR.js → chunk-QPEXPHJR.js} +4 -4
- package/dist/{chunk-ZNOEIM6Y.js → chunk-QXQRKXCU.js} +2 -2
- package/dist/{chunk-RD5LYKD6.js → chunk-RTZVQAJ7.js} +2 -2
- package/dist/{chunk-DPMFBCV6.js → chunk-TBKOGSYR.js} +2 -2
- package/dist/{chunk-DPMFBCV6.js.map → chunk-TBKOGSYR.js.map} +1 -1
- package/dist/chunk-UND4XIB6.js +251 -0
- package/dist/chunk-UND4XIB6.js.map +1 -0
- package/dist/{chunk-7H6DOO3E.js → chunk-VCGTOS2A.js} +211 -36
- package/dist/chunk-VCGTOS2A.js.map +1 -0
- package/dist/{chunk-MKSA2V7A.js → chunk-VE6YVP32.js} +2 -2
- package/dist/{chunk-5DWL3JBF.js → chunk-VK5EER6C.js} +2 -2
- package/dist/{chunk-MIQHZESA.js → chunk-VPSUZLOJ.js} +4 -4
- package/dist/{chunk-MIQHZESA.js.map → chunk-VPSUZLOJ.js.map} +1 -1
- package/dist/{chunk-XGSOTWYX.js → chunk-VRBCTEKQ.js} +2 -2
- package/dist/{chunk-ADQ5MQ54.js → chunk-W3XXT26A.js} +29 -1
- package/dist/{chunk-ADQ5MQ54.js.map → chunk-W3XXT26A.js.map} +1 -1
- package/dist/{chunk-2AXFIYHT.js → chunk-XG3PTSCD.js} +1 -1
- package/dist/chunk-XG3PTSCD.js.map +1 -0
- package/dist/{chunk-SIZWEV2Y.js → chunk-Y2RKOPNC.js} +4 -4
- package/dist/{chunk-SIZWEV2Y.js.map → chunk-Y2RKOPNC.js.map} +1 -1
- package/dist/{chunk-Z72JH4KG.js → chunk-YTXSFG3C.js} +4 -34
- package/dist/chunk-YTXSFG3C.js.map +1 -0
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +4 -3
- package/dist/consent/index.d.ts +4 -3
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-A7FRXYHC.js → crypto-5ZDIY3NG.js} +3 -3
- package/dist/{delegation-YBA4X4JN.js → delegation-QYXZW25W.js} +5 -5
- package/dist/derivations/index.cjs.map +1 -1
- package/dist/derivations/index.d.cts +5 -4
- package/dist/derivations/index.d.ts +5 -4
- package/dist/derivations/index.js +4 -4
- package/dist/{dev-unlock-DRwVSy2S.d.cts → dev-unlock-DQCNDfFp.d.cts} +1 -1
- package/dist/{dev-unlock-D9s-loPr.d.ts → dev-unlock-utkybTKb.d.ts} +1 -1
- package/dist/executor-AS2IDHKZ.js +11 -0
- package/dist/executor-HLXFXNFM.js +8 -0
- package/dist/executor-HN6YBHZ5.js +8 -0
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +5 -4
- package/dist/guards/index.d.ts +5 -4
- package/dist/guards/index.js +3 -3
- package/dist/{hash-DXXXusyk.d.ts → hash-DcoYWfJ_.d.ts} +1 -1
- package/dist/{hash-DtRih9MQ.d.cts → hash-jDowCrK2.d.cts} +1 -1
- package/dist/history/index.cjs +1 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +5 -4
- package/dist/history/index.d.ts +5 -4
- package/dist/history/index.js +5 -5
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +4 -3
- package/dist/i18n/index.d.ts +4 -3
- package/dist/i18n/index.js +13 -11
- package/dist/i18n/index.js.map +1 -1
- package/dist/{index-CNwA-B6-.d.ts → index-BCKdioeh.d.ts} +29 -1
- package/dist/{index-CmVgTkqk.d.cts → index-BMjrzNZr.d.cts} +29 -1
- package/dist/index.cjs +507 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -11
- package/dist/index.d.ts +12 -11
- package/dist/index.js +106 -8817
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.js +2 -2
- package/dist/issue-ORP37MVW.js +12 -0
- package/dist/{ledger-3TXNP47J.js → ledger-3IU5GMXA.js} +5 -5
- package/dist/materialized-views/index.cjs.map +1 -1
- package/dist/materialized-views/index.d.cts +6 -5
- package/dist/materialized-views/index.d.ts +6 -5
- package/dist/materialized-views/index.js +6 -6
- package/dist/noydb-5H3C24GG.js +34 -0
- package/dist/overlay-views/index.cjs.map +1 -1
- package/dist/overlay-views/index.d.cts +5 -4
- package/dist/overlay-views/index.d.ts +5 -4
- package/dist/overlay-views/index.js +6 -4
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +4 -3
- package/dist/periods/index.d.ts +4 -3
- package/dist/periods/index.js +5 -5
- package/dist/{public-envelope-PY6NKFLI.js → public-envelope-U3CMEOMV.js} +3 -3
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +3 -3
- package/dist/{registry-3L3N3PTG.js → registry-3ALP62P6.js} +3 -3
- package/dist/registry-7HE6VJGC.js +8 -0
- package/dist/registry-PSIPG2QR.js +8 -0
- package/dist/registry-PSIPG2QR.js.map +1 -0
- package/dist/revoke-KY2GB4KP.js +17 -0
- package/dist/revoke-KY2GB4KP.js.map +1 -0
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +5 -4
- package/dist/session/index.d.ts +5 -4
- package/dist/session/index.js +3 -3
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +4 -3
- package/dist/shadow/index.d.ts +4 -3
- package/dist/shadow/index.js +2 -2
- package/dist/signer-GRI5TZKH.js +18 -0
- package/dist/signer-GRI5TZKH.js.map +1 -0
- package/dist/{stale-HSC5YO2O.js → stale-OTOF3FH7.js} +2 -2
- package/dist/stale-OTOF3FH7.js.map +1 -0
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +4 -3
- package/dist/store/index.d.ts +4 -3
- package/dist/store/index.js +2 -2
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +3 -2
- package/dist/sync/index.d.ts +3 -2
- package/dist/sync/index.js +3 -3
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +4 -3
- package/dist/team/index.d.ts +4 -3
- package/dist/team/index.js +12 -10
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +4 -3
- package/dist/tx/index.d.ts +4 -3
- package/dist/tx/index.js +2 -2
- package/dist/{types-DW9RGSSs.d.ts → types-BoFFiskX.d.ts} +119 -3
- package/dist/{types-C4lwMKKF.d.cts → types-DJG8HG6F.d.cts} +119 -3
- package/dist/{index-hdFvZkBP.d.cts → ulid-BmBgooGm.d.ts} +51 -33
- package/dist/{index-4agOpzqd.d.ts → ulid-C7ms9oli.d.cts} +51 -33
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.js +1 -1
- package/dist/{with-derivation-g-pGoMzL.d.ts → with-derivation-BKXXa8Vt.d.ts} +1 -1
- package/dist/{with-derivation-C8LDlV7t.d.cts → with-derivation-BjQ7q4NE.d.cts} +1 -1
- package/dist/{with-guard-DWOCK4Ca.d.ts → with-guard-C25yNjzd.d.ts} +1 -1
- package/dist/{with-guard-jI1x9Z3k.d.cts → with-guard-DQme5DKE.d.cts} +1 -1
- package/dist/{with-materialized-view-DcTx4H3j.d.cts → with-materialized-view-BbEPFIIJ.d.cts} +1 -1
- package/dist/{with-materialized-view-DaKR-N6J.d.ts → with-materialized-view-CqnRwI2S.d.ts} +1 -1
- package/dist/{with-overlayed-view-N7jYuNOS.d.ts → with-overlayed-view-Ct1fSJt-.d.ts} +1 -1
- package/dist/{with-overlayed-view-D-6oWAgM.d.cts → with-overlayed-view-bwlmmFjx.d.cts} +1 -1
- package/package.json +15 -3
- package/dist/chunk-2AXFIYHT.js.map +0 -1
- package/dist/chunk-7H6DOO3E.js.map +0 -1
- package/dist/chunk-CBAHB2BF.js.map +0 -1
- package/dist/chunk-NIOHFJPJ.js.map +0 -1
- package/dist/chunk-VMIO4IXG.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/registry-O47PUPSY.js +0 -8
- package/dist/registry-WLLMODKN.js +0 -8
- /package/dist/{chunk-5SCJ5UEF.js.map → chunk-243PNUA6.js.map} +0 -0
- /package/dist/{chunk-WCA2NROQ.js.map → chunk-2PAQNPE3.js.map} +0 -0
- /package/dist/{chunk-4TFSM22V.js.map → chunk-3Y53S2SA.js.map} +0 -0
- /package/dist/{chunk-6HPZY4ON.js.map → chunk-3Z2TPHC4.js.map} +0 -0
- /package/dist/{chunk-DYECX3IX.js.map → chunk-7BRE6EUA.js.map} +0 -0
- /package/dist/{chunk-DYBQG5PQ.js.map → chunk-7BUTTVMR.js.map} +0 -0
- /package/dist/{chunk-KESP7GOK.js.map → chunk-7Q5PLD5C.js.map} +0 -0
- /package/dist/{chunk-UA4RI7OT.js.map → chunk-7Z23ZFLV.js.map} +0 -0
- /package/dist/{chunk-EGQYGYIU.js.map → chunk-CXSCDO5T.js.map} +0 -0
- /package/dist/{chunk-OMLIZL2P.js.map → chunk-FAQVNJD4.js.map} +0 -0
- /package/dist/{chunk-I6MX32UC.js.map → chunk-G6FRSBKK.js.map} +0 -0
- /package/dist/{chunk-FCXOFQAJ.js.map → chunk-GIV6DWBG.js.map} +0 -0
- /package/dist/{chunk-34YSDCDP.js.map → chunk-HXJXPZRE.js.map} +0 -0
- /package/dist/{chunk-23TTQXVO.js.map → chunk-J4KLMEUL.js.map} +0 -0
- /package/dist/{chunk-P7EQ2S5O.js.map → chunk-MUWOSVEP.js.map} +0 -0
- /package/dist/{chunk-HB3Z2GCR.js.map → chunk-OVZDFEOR.js.map} +0 -0
- /package/dist/{chunk-UZXLQCHP.js.map → chunk-PLI5TV7N.js.map} +0 -0
- /package/dist/{chunk-PA6R5ZCI.js.map → chunk-Q6W2CMEJ.js.map} +0 -0
- /package/dist/{chunk-537VFZTR.js.map → chunk-QPEXPHJR.js.map} +0 -0
- /package/dist/{chunk-ZNOEIM6Y.js.map → chunk-QXQRKXCU.js.map} +0 -0
- /package/dist/{chunk-RD5LYKD6.js.map → chunk-RTZVQAJ7.js.map} +0 -0
- /package/dist/{chunk-MKSA2V7A.js.map → chunk-VE6YVP32.js.map} +0 -0
- /package/dist/{chunk-5DWL3JBF.js.map → chunk-VK5EER6C.js.map} +0 -0
- /package/dist/{chunk-XGSOTWYX.js.map → chunk-VRBCTEKQ.js.map} +0 -0
- /package/dist/{crypto-A7FRXYHC.js.map → crypto-5ZDIY3NG.js.map} +0 -0
- /package/dist/{delegation-YBA4X4JN.js.map → delegation-QYXZW25W.js.map} +0 -0
- /package/dist/{executor-7E3VFGW7.js.map → executor-AS2IDHKZ.js.map} +0 -0
- /package/dist/{executor-CEWX2FQI.js.map → executor-HLXFXNFM.js.map} +0 -0
- /package/dist/{executor-X4SQ3ZLC.js.map → executor-HN6YBHZ5.js.map} +0 -0
- /package/dist/{ledger-3TXNP47J.js.map → issue-ORP37MVW.js.map} +0 -0
- /package/dist/{public-envelope-PY6NKFLI.js.map → ledger-3IU5GMXA.js.map} +0 -0
- /package/dist/{registry-3L3N3PTG.js.map → noydb-5H3C24GG.js.map} +0 -0
- /package/dist/{registry-O47PUPSY.js.map → public-envelope-U3CMEOMV.js.map} +0 -0
- /package/dist/{registry-WLLMODKN.js.map → registry-3ALP62P6.js.map} +0 -0
- /package/dist/{stale-HSC5YO2O.js.map → registry-7HE6VJGC.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BundleVersionConflictError,
|
|
3
3
|
ConflictError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-W3XXT26A.js";
|
|
5
5
|
|
|
6
6
|
// src/store/bundle-store.ts
|
|
7
7
|
var BUNDLE_STORE_VERSION = 1;
|
|
@@ -790,4 +790,4 @@ export {
|
|
|
790
790
|
withCache,
|
|
791
791
|
withHealthCheck
|
|
792
792
|
};
|
|
793
|
-
//# sourceMappingURL=chunk-
|
|
793
|
+
//# sourceMappingURL=chunk-MUWOSVEP.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureCollectionDEK
|
|
3
|
+
} from "./chunk-Q6W2CMEJ.js";
|
|
4
|
+
import {
|
|
5
|
+
NOYDB_FORMAT_VERSION
|
|
6
|
+
} from "./chunk-YS3POABP.js";
|
|
7
|
+
import {
|
|
8
|
+
decrypt,
|
|
9
|
+
encrypt
|
|
10
|
+
} from "./chunk-2PAQNPE3.js";
|
|
11
|
+
import {
|
|
12
|
+
PermissionDeniedError
|
|
13
|
+
} from "./chunk-W3XXT26A.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-NWZ3I6R6.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,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DerivationCapExceededError,
|
|
3
3
|
DerivationOutputShapeError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-W3XXT26A.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-OVZDFEOR.js.map
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NOYDB_FORMAT_VERSION
|
|
3
|
+
} from "./chunk-YS3POABP.js";
|
|
4
|
+
import {
|
|
5
|
+
encrypt
|
|
6
|
+
} from "./chunk-2PAQNPE3.js";
|
|
7
|
+
|
|
8
|
+
// src/blobs/export-blobs.ts
|
|
9
|
+
var ExportBlobsAbortedError = class extends Error {
|
|
10
|
+
constructor(reason) {
|
|
11
|
+
super(`exportBlobs aborted: ${reason}`);
|
|
12
|
+
this.name = "ExportBlobsAbortedError";
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var EXPORT_AUDIT_COLLECTION = "_export_audit";
|
|
16
|
+
function createExportBlobsHandle(actor, listAccessibleCollections, getCollection, writeAudit, options) {
|
|
17
|
+
let aborted = false;
|
|
18
|
+
const abort = () => {
|
|
19
|
+
aborted = true;
|
|
20
|
+
};
|
|
21
|
+
if (options.signal) {
|
|
22
|
+
if (options.signal.aborted) aborted = true;
|
|
23
|
+
options.signal.addEventListener("abort", () => {
|
|
24
|
+
aborted = true;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function assertLive() {
|
|
28
|
+
if (aborted) throw new ExportBlobsAbortedError("aborted by caller");
|
|
29
|
+
}
|
|
30
|
+
const allowlist = options.collections ? new Set(options.collections) : null;
|
|
31
|
+
let auditPromise = null;
|
|
32
|
+
function writeAuditOnce() {
|
|
33
|
+
if (!auditPromise) {
|
|
34
|
+
auditPromise = writeAudit({
|
|
35
|
+
id: generateBatchId(),
|
|
36
|
+
mechanism: "exportBlobs",
|
|
37
|
+
actor,
|
|
38
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
39
|
+
collections: options.collections ?? null,
|
|
40
|
+
predicate: Boolean(options.where),
|
|
41
|
+
afterBlobId: options.afterBlobId ?? null
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return auditPromise;
|
|
45
|
+
}
|
|
46
|
+
async function* generate() {
|
|
47
|
+
await writeAuditOnce();
|
|
48
|
+
assertLive();
|
|
49
|
+
const allCollections = await listAccessibleCollections();
|
|
50
|
+
const targets = allCollections.filter((name) => {
|
|
51
|
+
if (name.startsWith("_")) return false;
|
|
52
|
+
if (allowlist && !allowlist.has(name)) return false;
|
|
53
|
+
return true;
|
|
54
|
+
});
|
|
55
|
+
let resumeCursorHit = options.afterBlobId === void 0;
|
|
56
|
+
for (const collectionName of targets) {
|
|
57
|
+
if (aborted) return;
|
|
58
|
+
const coll = getCollection(collectionName);
|
|
59
|
+
const records = await coll.list().catch(() => []);
|
|
60
|
+
for (const record of records) {
|
|
61
|
+
if (aborted) return;
|
|
62
|
+
assertLive();
|
|
63
|
+
const idField = record.id;
|
|
64
|
+
if (typeof idField !== "string") continue;
|
|
65
|
+
if (options.where && !options.where(record, { collection: collectionName, id: idField })) continue;
|
|
66
|
+
const blobSet = coll.blob(idField);
|
|
67
|
+
const slots = await blobSet.list().catch(() => []);
|
|
68
|
+
for (const slot of slots) {
|
|
69
|
+
if (aborted) return;
|
|
70
|
+
if (!resumeCursorHit) {
|
|
71
|
+
if (slot.eTag === options.afterBlobId) {
|
|
72
|
+
resumeCursorHit = true;
|
|
73
|
+
}
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const bytes = await blobSet.get(slot.name);
|
|
77
|
+
if (!bytes) continue;
|
|
78
|
+
const item = {
|
|
79
|
+
blobId: slot.eTag,
|
|
80
|
+
recordRef: { collection: collectionName, id: idField, slot: slot.name },
|
|
81
|
+
bytes,
|
|
82
|
+
meta: {
|
|
83
|
+
size: slot.size,
|
|
84
|
+
filename: slot.filename,
|
|
85
|
+
...slot.mimeType !== void 0 && { mimeType: slot.mimeType },
|
|
86
|
+
...slot.uploadedAt !== void 0 && { createdAt: slot.uploadedAt }
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
yield item;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const handle = {
|
|
95
|
+
abort,
|
|
96
|
+
get aborted() {
|
|
97
|
+
return aborted;
|
|
98
|
+
},
|
|
99
|
+
[Symbol.asyncIterator]: () => generate()
|
|
100
|
+
};
|
|
101
|
+
return handle;
|
|
102
|
+
}
|
|
103
|
+
function generateBatchId() {
|
|
104
|
+
const raw = globalThis.crypto.getRandomValues(new Uint8Array(16));
|
|
105
|
+
let s = "";
|
|
106
|
+
for (const b of raw) s += b.toString(16).padStart(2, "0");
|
|
107
|
+
return `batch-${Date.now().toString(36)}-${s.slice(0, 12)}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/blobs/blob-compaction.ts
|
|
111
|
+
var BLOB_EVICTION_AUDIT_COLLECTION = "_blob_eviction_audit";
|
|
112
|
+
async function runCompaction(ctx, options = {}) {
|
|
113
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
114
|
+
const maxEvictions = options.maxEvictions ?? Infinity;
|
|
115
|
+
const dryRun = options.dryRun === true;
|
|
116
|
+
const allCollections = await ctx.listCollections();
|
|
117
|
+
const byCollection = {};
|
|
118
|
+
let evicted = 0;
|
|
119
|
+
let records = 0;
|
|
120
|
+
let auditEntries = 0;
|
|
121
|
+
let collectionsWithPolicy = 0;
|
|
122
|
+
outer: for (const collectionName of allCollections) {
|
|
123
|
+
if (collectionName.startsWith("_")) continue;
|
|
124
|
+
const config = ctx.getBlobFields(collectionName);
|
|
125
|
+
if (!config) continue;
|
|
126
|
+
const configuredSlots = Object.keys(config);
|
|
127
|
+
if (configuredSlots.length === 0) continue;
|
|
128
|
+
collectionsWithPolicy += 1;
|
|
129
|
+
byCollection[collectionName] = { records: 0, evicted: 0 };
|
|
130
|
+
const ids = await ctx.listRecords(collectionName);
|
|
131
|
+
for (const recordId of ids) {
|
|
132
|
+
if (evicted >= maxEvictions) break outer;
|
|
133
|
+
const record = await ctx.getRecord(collectionName, recordId).catch(() => null);
|
|
134
|
+
if (record === null) continue;
|
|
135
|
+
records += 1;
|
|
136
|
+
byCollection[collectionName].records += 1;
|
|
137
|
+
const slots = await ctx.listSlots(collectionName, recordId).catch(() => []);
|
|
138
|
+
for (const slot of slots) {
|
|
139
|
+
if (evicted >= maxEvictions) break outer;
|
|
140
|
+
const policy = config[slot.name];
|
|
141
|
+
if (!policy) continue;
|
|
142
|
+
const reason = evaluatePolicy(policy, record, slot, now);
|
|
143
|
+
if (!reason) continue;
|
|
144
|
+
if (!dryRun) {
|
|
145
|
+
await ctx.deleteSlot(collectionName, recordId, slot.name);
|
|
146
|
+
await writeAuditEntry(ctx, {
|
|
147
|
+
id: generateEvictionId(collectionName, recordId, slot.name),
|
|
148
|
+
collection: collectionName,
|
|
149
|
+
recordId,
|
|
150
|
+
slotName: slot.name,
|
|
151
|
+
blobHash: slot.eTag,
|
|
152
|
+
reason,
|
|
153
|
+
evictedAt: now.toISOString(),
|
|
154
|
+
actor: ctx.actor
|
|
155
|
+
});
|
|
156
|
+
auditEntries += 1;
|
|
157
|
+
}
|
|
158
|
+
evicted += 1;
|
|
159
|
+
byCollection[collectionName].evicted += 1;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
evicted,
|
|
165
|
+
records,
|
|
166
|
+
collections: collectionsWithPolicy,
|
|
167
|
+
auditEntries,
|
|
168
|
+
byCollection
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function evaluatePolicy(policy, record, slot, now) {
|
|
172
|
+
let ttlTriggered = false;
|
|
173
|
+
let predicateTriggered = false;
|
|
174
|
+
if (policy.retainDays !== void 0 && policy.retainDays > 0) {
|
|
175
|
+
const uploadedAt = Date.parse(slot.uploadedAt);
|
|
176
|
+
if (Number.isFinite(uploadedAt)) {
|
|
177
|
+
const ageMs = now.getTime() - uploadedAt;
|
|
178
|
+
const limitMs = policy.retainDays * 864e5;
|
|
179
|
+
if (ageMs > limitMs) ttlTriggered = true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (policy.evictWhen) {
|
|
183
|
+
try {
|
|
184
|
+
if (policy.evictWhen(record)) predicateTriggered = true;
|
|
185
|
+
} catch {
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (ttlTriggered && predicateTriggered) return "both";
|
|
189
|
+
if (ttlTriggered) return "ttl";
|
|
190
|
+
if (predicateTriggered) return "predicate";
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
function generateEvictionId(collection, recordId, slotName) {
|
|
194
|
+
const rand = globalThis.crypto.getRandomValues(new Uint8Array(8));
|
|
195
|
+
let suffix = "";
|
|
196
|
+
for (const b of rand) suffix += b.toString(16).padStart(2, "0");
|
|
197
|
+
return `${collection}__${recordId}__${slotName}__${suffix}`;
|
|
198
|
+
}
|
|
199
|
+
async function writeAuditEntry(ctx, entry) {
|
|
200
|
+
const json = JSON.stringify(entry);
|
|
201
|
+
let envelope;
|
|
202
|
+
if (ctx.encrypted) {
|
|
203
|
+
const dek = await ctx.getDEK(BLOB_EVICTION_AUDIT_COLLECTION);
|
|
204
|
+
const { iv, data } = await encrypt(json, dek);
|
|
205
|
+
envelope = {
|
|
206
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
207
|
+
_v: 1,
|
|
208
|
+
_ts: entry.evictedAt,
|
|
209
|
+
_iv: iv,
|
|
210
|
+
_data: data,
|
|
211
|
+
_by: entry.actor
|
|
212
|
+
};
|
|
213
|
+
} else {
|
|
214
|
+
envelope = {
|
|
215
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
216
|
+
_v: 1,
|
|
217
|
+
_ts: entry.evictedAt,
|
|
218
|
+
_iv: "",
|
|
219
|
+
_data: json,
|
|
220
|
+
_by: entry.actor
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
await ctx.adapter.put(ctx.vault, BLOB_EVICTION_AUDIT_COLLECTION, entry.id, envelope);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export {
|
|
227
|
+
ExportBlobsAbortedError,
|
|
228
|
+
EXPORT_AUDIT_COLLECTION,
|
|
229
|
+
createExportBlobsHandle,
|
|
230
|
+
BLOB_EVICTION_AUDIT_COLLECTION,
|
|
231
|
+
runCompaction
|
|
232
|
+
};
|
|
233
|
+
//# sourceMappingURL=chunk-PFSNOPBQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/blobs/export-blobs.ts","../src/blobs/blob-compaction.ts"],"sourcesContent":["/**\n * `vault.exportBlobs()` — bulk blob extraction primitive.\n *\n * Async-iterable handle over every blob attached to records in a\n * vault, optionally filtered by collection allowlist and per-record\n * predicate. Emits tuples of `{ blobId, recordRef, bytes, meta }` so\n * the consumer can pipe into any sink (zip stream, S3 multipart, USB\n * copy, cold-storage tape) without pulling the whole export into\n * memory.\n *\n * ## Auth + audit\n *\n * - Capability check runs **once** at handle creation via\n * `Vault.assertCanExport('plaintext', 'blob')`. An operator whose\n * keyring lacks that bit fails before a single byte of ciphertext\n * is decrypted.\n * - Audit entry lands in `_export_audit` at handle creation: the\n * actor, start timestamp, target collections, predicate presence,\n * and batch mechanism. **No content hashes** — per the spec\n * non-correlation invariant.\n *\n * ## Abort + resume\n *\n * - `handle.abort()` flips the internal signal; the next iteration\n * boundary throws `AbortError`. Consumers already in `for await`\n * can catch and exit cleanly.\n * - Restart after a partial failure with `{ afterBlobId }` — the\n * iterator skips tuples up to (and including) that blob id before\n * yielding again. Combined with a blob-count ceiling it supports\n * idempotent batch re-runs.\n *\n * @module\n */\n\nimport type { Collection } from '../collection.js'\nimport type { SlotInfo } from '../types.js'\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\nexport interface ExportBlobsOptions {\n /**\n * Collection allowlist. Omit to export blobs from every collection\n * the caller has read access to.\n */\n readonly collections?: readonly string[]\n /**\n * Per-record predicate. Called on the decrypted record BEFORE any\n * blob bytes are read for that record — returning false skips the\n * record and all its slots without touching their chunks.\n */\n readonly where?: (record: unknown, context: { collection: string; id: string }) => boolean\n /**\n * Resume after a specific blob id. The iterator skips tuples up to\n * and including this id, then yields. Format of the id is the same\n * as `ExportedBlob.blobId` (the HMAC-keyed eTag).\n */\n readonly afterBlobId?: string\n /**\n * External abort signal. When fired, the next iterator tick throws\n * `ExportBlobsAbortedError`. Honored alongside `handle.abort()`.\n */\n readonly signal?: AbortSignal\n}\n\nexport interface ExportedBlob {\n /** Opaque blob identifier — HMAC-keyed eTag, stable across vaults. */\n readonly blobId: string\n /** Where this blob came from in the vault. */\n readonly recordRef: {\n readonly collection: string\n readonly id: string\n readonly slot: string\n }\n /** Decrypted plaintext bytes. */\n readonly bytes: Uint8Array\n /** Best-effort metadata (from the blob slot record). */\n readonly meta: {\n readonly size: number\n /**\n * User-visible filename stored on the slot. Often equal to the\n * slot name; differs when the caller supplied an explicit\n * `filename` to `BlobSet.put()`.\n */\n readonly filename: string\n readonly mimeType?: string\n readonly createdAt?: string\n }\n}\n\nexport interface ExportBlobsHandle extends AsyncIterable<ExportedBlob> {\n /** Abort the export. Safe to call multiple times. */\n abort(): void\n /** True once `abort()` has fired or the external signal aborted. */\n readonly aborted: boolean\n}\n\nexport class ExportBlobsAbortedError extends Error {\n constructor(reason: string) {\n super(`exportBlobs aborted: ${reason}`)\n this.name = 'ExportBlobsAbortedError'\n }\n}\n\n// ─── Audit ──────────────────────────────────────────────────────────────\n\nexport const EXPORT_AUDIT_COLLECTION = '_export_audit'\n\nexport interface ExportBlobsAuditEntry {\n readonly id: string\n readonly mechanism: 'exportBlobs'\n readonly actor: string\n readonly startedAt: string\n readonly collections: readonly string[] | null\n readonly predicate: boolean\n readonly afterBlobId: string | null\n}\n\n// ─── Implementation ─────────────────────────────────────────────────────\n\n/**\n * Build the handle. Factored out of `Vault.exportBlobs` so the\n * implementation can be unit-tested without going through the\n * compartment lifecycle.\n */\nexport function createExportBlobsHandle(\n actor: string,\n listAccessibleCollections: () => Promise<string[]>,\n getCollection: <T>(name: string) => Collection<T>,\n writeAudit: (entry: ExportBlobsAuditEntry) => Promise<void>,\n options: ExportBlobsOptions,\n): ExportBlobsHandle {\n let aborted = false\n\n const abort = (): void => {\n aborted = true\n }\n\n if (options.signal) {\n if (options.signal.aborted) aborted = true\n options.signal.addEventListener('abort', () => { aborted = true })\n }\n\n function assertLive(): void {\n if (aborted) throw new ExportBlobsAbortedError('aborted by caller')\n }\n\n const allowlist = options.collections ? new Set(options.collections) : null\n\n // Write the audit entry BEFORE the first yield so a blocked\n // iteration still leaves an audit trail that the export started.\n let auditPromise: Promise<void> | null = null\n function writeAuditOnce(): Promise<void> {\n if (!auditPromise) {\n auditPromise = writeAudit({\n id: generateBatchId(),\n mechanism: 'exportBlobs',\n actor,\n startedAt: new Date().toISOString(),\n collections: options.collections ?? null,\n predicate: Boolean(options.where),\n afterBlobId: options.afterBlobId ?? null,\n })\n }\n return auditPromise\n }\n\n async function* generate(): AsyncGenerator<ExportedBlob> {\n await writeAuditOnce()\n assertLive()\n\n // Resolve target collections lazily — also keeps the call async.\n const allCollections = await listAccessibleCollections()\n const targets = allCollections.filter(name => {\n if (name.startsWith('_')) return false\n if (allowlist && !allowlist.has(name)) return false\n return true\n })\n\n let resumeCursorHit = options.afterBlobId === undefined\n\n for (const collectionName of targets) {\n if (aborted) return\n\n const coll = getCollection<Record<string, unknown>>(collectionName)\n const records = await coll.list().catch(() => [])\n for (const record of records) {\n if (aborted) return\n assertLive()\n\n const idField = (record as { id?: unknown }).id\n if (typeof idField !== 'string') continue\n\n if (options.where && !options.where(record, { collection: collectionName, id: idField })) continue\n\n const blobSet = coll.blob(idField)\n const slots = await blobSet.list().catch(() => [] as SlotInfo[])\n for (const slot of slots) {\n if (aborted) return\n\n if (!resumeCursorHit) {\n if (slot.eTag === options.afterBlobId) {\n resumeCursorHit = true\n }\n continue\n }\n\n const bytes = await blobSet.get(slot.name)\n if (!bytes) continue\n\n const item: ExportedBlob = {\n blobId: slot.eTag,\n recordRef: { collection: collectionName, id: idField, slot: slot.name },\n bytes,\n meta: {\n size: slot.size,\n filename: slot.filename,\n ...(slot.mimeType !== undefined && { mimeType: slot.mimeType }),\n ...(slot.uploadedAt !== undefined && { createdAt: slot.uploadedAt }),\n },\n }\n yield item\n }\n }\n }\n }\n\n const handle: ExportBlobsHandle = {\n abort,\n get aborted() { return aborted },\n [Symbol.asyncIterator]: () => generate(),\n }\n return handle\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────\n\nfunction generateBatchId(): string {\n // 16 bytes of crypto randomness, URL-safe base64, no padding.\n const raw = globalThis.crypto.getRandomValues(new Uint8Array(16))\n let s = ''\n for (const b of raw) s += b.toString(16).padStart(2, '0')\n return `batch-${Date.now().toString(36)}-${s.slice(0, 12)}`\n}\n","/**\n * Blob retention + compaction.\n *\n * Declarative per-collection / per-slot eviction policy. Two\n * triggers:\n *\n * - **`retainDays`** — age-based TTL. A slot uploaded more than N\n * days ago is evicted.\n * - **`evictWhen(record)`** — predicate over the **decrypted**\n * record. Lets consumers express \"the image is safe to drop once\n * the structured invoice has been reviewed and confirmed.\"\n *\n * Either trigger (or both) causes the slot to evict. Eviction removes\n * the slot entry from `_blob_slots_{collection}`, decrements the\n * blob's refCount (so unreferenced chunks can be GC'd by the next\n * sweep), and writes one entry to the `_blob_eviction_audit`\n * collection for tamper-evident record-keeping.\n *\n * The audit entry carries the eTag of the evicted blob (opaque HMAC\n * of plaintext under the vault's `_blob` DEK) — no plaintext leakage,\n * per the SPEC non-correlation invariant. Consumers reconstructing\n * \"what used to be attached\" can look up the audit entry by record\n * id.\n *\n * Compaction is **consumer-scheduled** — noy-db never runs a\n * background daemon. Call `vault.compact()` whenever your workflow\n * allows (cron, manual \"tidy\" button, cold-storage export prep, …).\n *\n * @module\n */\n\nimport type { NoydbStore, EncryptedEnvelope, SlotInfo } from '../types.js'\nimport { NOYDB_FORMAT_VERSION } from '../types.js'\nimport { encrypt } from '../crypto.js'\n\n// ─── Config types ───────────────────────────────────────────────────────\n\nexport interface BlobFieldPolicy<T = unknown> {\n /**\n * Age-based TTL in days. A slot whose `uploadedAt` is older than\n * `now - retainDays × 86400s` evicts on the next `vault.compact()`.\n * Omit to disable age-based eviction.\n */\n readonly retainDays?: number\n /**\n * Predicate evaluated against the decrypted record. When it returns\n * `true`, every matching slot on that record evicts. Omit to\n * disable predicate-based eviction.\n */\n readonly evictWhen?: (record: T) => boolean\n}\n\nexport type BlobFieldsConfig<T = unknown> = Record<string, BlobFieldPolicy<T>>\n\n// ─── Audit collection ──────────────────────────────────────────────────\n\nexport const BLOB_EVICTION_AUDIT_COLLECTION = '_blob_eviction_audit'\n\nexport interface BlobEvictionEntry {\n readonly id: string\n readonly collection: string\n readonly recordId: string\n readonly slotName: string\n readonly blobHash: string\n readonly reason: 'ttl' | 'predicate' | 'both'\n readonly evictedAt: string\n readonly actor: string\n}\n\n// ─── Compaction result ──────────────────────────────────────────────────\n\nexport interface CompactionResult {\n /** Number of blob slots evicted across all collections. */\n readonly evicted: number\n /** Number of records touched (iterated + policy checked). */\n readonly records: number\n /** Number of collections with `blobFields` configured. */\n readonly collections: number\n /** Number of audit entries written. Equal to `evicted`. */\n readonly auditEntries: number\n /** Per-collection breakdown for diagnostics. */\n readonly byCollection: Record<string, { records: number; evicted: number }>\n}\n\n// ─── Core ──────────────────────────────────────────────────────────────\n\nexport interface CompactRunOptions {\n /** Override \"now\" for deterministic testing. */\n readonly now?: Date\n /**\n * Stop after this many evictions. Useful for capped batches / cron\n * jobs that need to fit in a time window. `undefined` = unbounded.\n */\n readonly maxEvictions?: number\n /**\n * Dry-run — evaluate policies and return the counts, but do NOT\n * delete slots or write audit entries. Lets a consumer preview\n * what would happen.\n */\n readonly dryRun?: boolean\n}\n\nexport interface CompactionContext {\n readonly adapter: NoydbStore\n readonly vault: string\n readonly actor: string\n readonly encrypted: boolean\n readonly getDEK: (collection: string) => Promise<CryptoKey>\n /**\n * Resolve a collection's declared `blobFields` config. Returns an\n * empty map for collections without the config — the walk skips\n * those.\n */\n readonly getBlobFields: <T>(collection: string) => BlobFieldsConfig<T> | null\n /** List collection names in the vault. */\n readonly listCollections: () => Promise<string[]>\n /** List record ids in a collection. */\n readonly listRecords: (collection: string) => Promise<string[]>\n /** Decrypt and return the record. Null when absent. */\n readonly getRecord: <T>(collection: string, id: string) => Promise<T | null>\n /** Return the BlobSet-like handle for a record's slots. */\n readonly listSlots: (collection: string, id: string) => Promise<SlotInfo[]>\n /** Delete a slot and decrement its blob's refCount. */\n readonly deleteSlot: (collection: string, id: string, slotName: string) => Promise<void>\n}\n\nexport async function runCompaction(\n ctx: CompactionContext,\n options: CompactRunOptions = {},\n): Promise<CompactionResult> {\n const now = options.now ?? new Date()\n const maxEvictions = options.maxEvictions ?? Infinity\n const dryRun = options.dryRun === true\n\n const allCollections = await ctx.listCollections()\n const byCollection: Record<string, { records: number; evicted: number }> = {}\n let evicted = 0\n let records = 0\n let auditEntries = 0\n let collectionsWithPolicy = 0\n\n outer: for (const collectionName of allCollections) {\n if (collectionName.startsWith('_')) continue\n const config = ctx.getBlobFields(collectionName)\n if (!config) continue\n const configuredSlots = Object.keys(config)\n if (configuredSlots.length === 0) continue\n collectionsWithPolicy += 1\n byCollection[collectionName] = { records: 0, evicted: 0 }\n\n const ids = await ctx.listRecords(collectionName)\n for (const recordId of ids) {\n if (evicted >= maxEvictions) break outer\n\n const record = await ctx.getRecord(collectionName, recordId).catch(() => null)\n if (record === null) continue\n records += 1\n byCollection[collectionName].records += 1\n\n const slots = await ctx.listSlots(collectionName, recordId).catch(() => [])\n for (const slot of slots) {\n if (evicted >= maxEvictions) break outer\n const policy = config[slot.name]\n if (!policy) continue\n\n const reason = evaluatePolicy(policy, record, slot, now)\n if (!reason) continue\n\n if (!dryRun) {\n await ctx.deleteSlot(collectionName, recordId, slot.name)\n await writeAuditEntry(ctx, {\n id: generateEvictionId(collectionName, recordId, slot.name),\n collection: collectionName,\n recordId,\n slotName: slot.name,\n blobHash: slot.eTag,\n reason,\n evictedAt: now.toISOString(),\n actor: ctx.actor,\n })\n auditEntries += 1\n }\n evicted += 1\n byCollection[collectionName].evicted += 1\n }\n }\n }\n\n return {\n evicted,\n records,\n collections: collectionsWithPolicy,\n auditEntries,\n byCollection,\n }\n}\n\nfunction evaluatePolicy<T>(\n policy: BlobFieldPolicy<T>,\n record: T,\n slot: SlotInfo,\n now: Date,\n): 'ttl' | 'predicate' | 'both' | null {\n let ttlTriggered = false\n let predicateTriggered = false\n\n if (policy.retainDays !== undefined && policy.retainDays > 0) {\n const uploadedAt = Date.parse(slot.uploadedAt)\n if (Number.isFinite(uploadedAt)) {\n const ageMs = now.getTime() - uploadedAt\n const limitMs = policy.retainDays * 86_400_000\n if (ageMs > limitMs) ttlTriggered = true\n }\n }\n\n if (policy.evictWhen) {\n try {\n if (policy.evictWhen(record)) predicateTriggered = true\n } catch {\n // Predicate error → do NOT evict. Fail closed.\n }\n }\n\n if (ttlTriggered && predicateTriggered) return 'both'\n if (ttlTriggered) return 'ttl'\n if (predicateTriggered) return 'predicate'\n return null\n}\n\nfunction generateEvictionId(collection: string, recordId: string, slotName: string): string {\n const rand = globalThis.crypto.getRandomValues(new Uint8Array(8))\n let suffix = ''\n for (const b of rand) suffix += b.toString(16).padStart(2, '0')\n return `${collection}__${recordId}__${slotName}__${suffix}`\n}\n\nasync function writeAuditEntry(ctx: CompactionContext, entry: BlobEvictionEntry): Promise<void> {\n const json = JSON.stringify(entry)\n let envelope: EncryptedEnvelope\n if (ctx.encrypted) {\n const dek = await ctx.getDEK(BLOB_EVICTION_AUDIT_COLLECTION)\n const { iv, data } = await encrypt(json, dek)\n envelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: 1,\n _ts: entry.evictedAt,\n _iv: iv,\n _data: data,\n _by: entry.actor,\n }\n } else {\n envelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: 1,\n _ts: entry.evictedAt,\n _iv: '',\n _data: json,\n _by: entry.actor,\n }\n }\n await ctx.adapter.put(ctx.vault, BLOB_EVICTION_AUDIT_COLLECTION, entry.id, envelope)\n}\n"],"mappings":"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","names":[]}
|
|
@@ -30,7 +30,7 @@ async function resolveStaleMVOnRead(accessor, outputCollection) {
|
|
|
30
30
|
continue;
|
|
31
31
|
}
|
|
32
32
|
if (executor === null) {
|
|
33
|
-
({ MaterializedViewExecutor: executor } = await import("./executor-
|
|
33
|
+
({ MaterializedViewExecutor: executor } = await import("./executor-AS2IDHKZ.js"));
|
|
34
34
|
}
|
|
35
35
|
await executor.refresh(reg, {
|
|
36
36
|
getCollection: (n) => accessor.getCollection(n),
|
|
@@ -50,4 +50,4 @@ export {
|
|
|
50
50
|
resolveStaleMVOnRead,
|
|
51
51
|
clearMVStale
|
|
52
52
|
};
|
|
53
|
-
//# sourceMappingURL=chunk-
|
|
53
|
+
//# sourceMappingURL=chunk-PLI5TV7N.js.map
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
generateSalt,
|
|
13
13
|
unwrapKey,
|
|
14
14
|
wrapKey
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-2PAQNPE3.js";
|
|
16
16
|
import {
|
|
17
17
|
ConflictError,
|
|
18
18
|
DirectoryDisabledError,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
PermissionDeniedError,
|
|
25
25
|
PrivilegeEscalationError,
|
|
26
26
|
ValidationError
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-W3XXT26A.js";
|
|
28
28
|
|
|
29
29
|
// src/directory/storage.ts
|
|
30
30
|
var META_COLLECTION = "_meta";
|
|
@@ -881,4 +881,4 @@ export {
|
|
|
881
881
|
hasImportCapability,
|
|
882
882
|
evaluateImportCapability
|
|
883
883
|
};
|
|
884
|
-
//# sourceMappingURL=chunk-
|
|
884
|
+
//# sourceMappingURL=chunk-Q6W2CMEJ.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalJson,
|
|
3
3
|
sha256Hex
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XG3PTSCD.js";
|
|
5
5
|
import {
|
|
6
6
|
PeriodClosedError,
|
|
7
7
|
ValidationError
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-W3XXT26A.js";
|
|
9
9
|
|
|
10
10
|
// src/periods/periods.ts
|
|
11
11
|
var PERIODS_COLLECTION = "_periods";
|
|
@@ -56,7 +56,7 @@ function validatePeriodName(name, existing) {
|
|
|
56
56
|
}
|
|
57
57
|
async function appendPeriodLedgerEntry(ledger, actor, envelope, name) {
|
|
58
58
|
if (!ledger) return;
|
|
59
|
-
const { envelopePayloadHash } = await import("./ledger-
|
|
59
|
+
const { envelopePayloadHash } = await import("./ledger-3IU5GMXA.js");
|
|
60
60
|
await ledger.append({
|
|
61
61
|
op: "put",
|
|
62
62
|
collection: PERIODS_COLLECTION,
|
|
@@ -87,4 +87,4 @@ export {
|
|
|
87
87
|
appendPeriodLedgerEntry,
|
|
88
88
|
withPeriods
|
|
89
89
|
};
|
|
90
|
-
//# sourceMappingURL=chunk-
|
|
90
|
+
//# sourceMappingURL=chunk-QPEXPHJR.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ReadOnlyFrameError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-W3XXT26A.js";
|
|
4
4
|
|
|
5
5
|
// src/shadow/vault-frame.ts
|
|
6
6
|
var VaultFrame = class {
|
|
@@ -76,4 +76,4 @@ export {
|
|
|
76
76
|
VaultFrame,
|
|
77
77
|
CollectionFrame
|
|
78
78
|
};
|
|
79
|
-
//# sourceMappingURL=chunk-
|
|
79
|
+
//# sourceMappingURL=chunk-QXQRKXCU.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MaterializedViewConfigError,
|
|
3
3
|
ValidationError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-W3XXT26A.js";
|
|
5
5
|
|
|
6
6
|
// src/materialized-views/with-materialized-view.ts
|
|
7
7
|
function withMaterializedView(spec) {
|
|
@@ -79,4 +79,4 @@ function withMaterializedView(spec) {
|
|
|
79
79
|
export {
|
|
80
80
|
withMaterializedView
|
|
81
81
|
};
|
|
82
|
-
//# sourceMappingURL=chunk-
|
|
82
|
+
//# sourceMappingURL=chunk-RTZVQAJ7.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MaterializedViewCycleError,
|
|
3
3
|
MaterializedViewSourceUnknownError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-W3XXT26A.js";
|
|
5
5
|
|
|
6
6
|
// src/materialized-views/dependency-analyzer.ts
|
|
7
7
|
function analyzeDependencies(query) {
|
|
@@ -293,4 +293,4 @@ export {
|
|
|
293
293
|
MaterializedViewRegistry,
|
|
294
294
|
wrapDbWithPredicates
|
|
295
295
|
};
|
|
296
|
-
//# sourceMappingURL=chunk-
|
|
296
|
+
//# sourceMappingURL=chunk-TBKOGSYR.js.map
|