@noy-db/hub 0.2.0-pre.2 → 0.2.0-pre.21
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/README.md +126 -0
- package/dist/aggregate/index.cjs +643 -37
- package/dist/aggregate/index.cjs.map +1 -1
- package/dist/aggregate/index.d.cts +3 -2
- package/dist/aggregate/index.d.ts +3 -2
- package/dist/aggregate/index.js +9 -8
- package/dist/aggregate/index.js.map +1 -1
- package/dist/attestation/index.cjs.map +1 -1
- package/dist/attestation/index.d.cts +7 -5
- package/dist/attestation/index.d.ts +7 -5
- package/dist/attestation/index.js +6 -6
- package/dist/blobs/index.cjs +509 -22
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +9 -7
- package/dist/blobs/index.d.ts +9 -7
- package/dist/blobs/index.js +11 -6
- package/dist/blobs/index.js.map +1 -1
- package/dist/bundle/index.cjs +7886 -841
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +20 -18
- package/dist/bundle/index.d.ts +20 -18
- package/dist/bundle/index.js +24 -13
- package/dist/bundle/index.js.map +1 -1
- package/dist/{chunk-PFSNOPBQ.js → chunk-2XA2ZML4.js} +31 -3
- package/dist/chunk-2XA2ZML4.js.map +1 -0
- package/dist/{chunk-2PAQNPE3.js → chunk-37VGJM3T.js} +37 -2
- package/dist/chunk-37VGJM3T.js.map +1 -0
- package/dist/{chunk-7BRE6EUA.js → chunk-3HNKR65T.js} +4 -4
- package/dist/chunk-3HNKR65T.js.map +1 -0
- package/dist/{chunk-Y2RKOPNC.js → chunk-5YTXYPES.js} +46 -10
- package/dist/chunk-5YTXYPES.js.map +1 -0
- package/dist/{chunk-OVZDFEOR.js → chunk-6QAZ5O6X.js} +2 -2
- package/dist/chunk-6QAZ5O6X.js.map +1 -0
- package/dist/{chunk-RTZVQAJ7.js → chunk-6QE4DUYC.js} +19 -4
- package/dist/chunk-6QE4DUYC.js.map +1 -0
- package/dist/{chunk-7Q5PLD5C.js → chunk-7MRT7EPB.js} +3 -3
- package/dist/{chunk-E535SAN4.js → chunk-7PH4OPBZ.js} +4258 -520
- package/dist/chunk-7PH4OPBZ.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-UMLVJTYV.js → chunk-ADB7GPM3.js} +7 -4
- package/dist/chunk-ADB7GPM3.js.map +1 -0
- package/dist/{chunk-G6FRSBKK.js → chunk-AI4USDRI.js} +4 -4
- package/dist/chunk-BZW5IL43.js +151 -0
- package/dist/chunk-BZW5IL43.js.map +1 -0
- package/dist/chunk-C2RJVZZL.js +123 -0
- package/dist/chunk-C2RJVZZL.js.map +1 -0
- package/dist/{chunk-UND4XIB6.js → chunk-C6W5KVDV.js} +52 -38
- package/dist/chunk-C6W5KVDV.js.map +1 -0
- package/dist/chunk-CQYEDODS.js +125 -0
- package/dist/chunk-CQYEDODS.js.map +1 -0
- package/dist/{chunk-NWZ3I6R6.js → chunk-EYK72OTL.js} +5 -5
- package/dist/{chunk-7BUTTVMR.js → chunk-F5GWNSE2.js} +2 -2
- package/dist/{chunk-AHPFONIL.js → chunk-F5ILTHMU.js} +5 -5
- package/dist/{chunk-Q6W2CMEJ.js → chunk-FRRJIUSI.js} +18 -5
- package/dist/chunk-FRRJIUSI.js.map +1 -0
- package/dist/{chunk-YMYK7US4.js → chunk-GJTKMME7.js} +2 -2
- package/dist/chunk-GJTKMME7.js.map +1 -0
- package/dist/{chunk-EUYOGYGV.js → chunk-HYJMAV53.js} +6 -6
- package/dist/chunk-HYJMAV53.js.map +1 -0
- package/dist/{chunk-QPEXPHJR.js → chunk-I3IYTUUI.js} +4 -4
- package/dist/{chunk-3QAKZ37R.js → chunk-IVZWHIEK.js} +5 -5
- package/dist/{chunk-PLI5TV7N.js → chunk-IW4L4X65.js} +2 -2
- package/dist/chunk-IW4L4X65.js.map +1 -0
- package/dist/{chunk-3Z2TPHC4.js → chunk-IY24WS2P.js} +69 -5
- package/dist/chunk-IY24WS2P.js.map +1 -0
- package/dist/{chunk-HXJXPZRE.js → chunk-J6RGRZOY.js} +10 -3
- package/dist/chunk-J6RGRZOY.js.map +1 -0
- package/dist/{chunk-3S4BJX25.js → chunk-JBBWALNI.js} +2 -2
- package/dist/chunk-JBBWALNI.js.map +1 -0
- package/dist/{chunk-7Z23ZFLV.js → chunk-JDCPRJVS.js} +5 -5
- package/dist/chunk-JDCPRJVS.js.map +1 -0
- package/dist/{chunk-243PNUA6.js → chunk-JOK73NDT.js} +3 -3
- package/dist/chunk-JTI57WRT.js +164 -0
- package/dist/chunk-JTI57WRT.js.map +1 -0
- package/dist/{chunk-VRBCTEKQ.js → chunk-JYNH4FIM.js} +233 -11
- package/dist/chunk-JYNH4FIM.js.map +1 -0
- package/dist/{chunk-TBKOGSYR.js → chunk-KOAJ3TZM.js} +27 -5
- package/dist/chunk-KOAJ3TZM.js.map +1 -0
- package/dist/{chunk-YTXSFG3C.js → chunk-MBXKRHSS.js} +50 -20
- package/dist/chunk-MBXKRHSS.js.map +1 -0
- package/dist/{chunk-MUWOSVEP.js → chunk-NSXNXLYM.js} +10 -2
- package/dist/chunk-NSXNXLYM.js.map +1 -0
- package/dist/{chunk-J4KLMEUL.js → chunk-NV4IHBZS.js} +664 -51
- package/dist/chunk-NV4IHBZS.js.map +1 -0
- package/dist/{chunk-LRAZDV5X.js → chunk-O5XKZCUD.js} +31 -8
- package/dist/chunk-O5XKZCUD.js.map +1 -0
- package/dist/{chunk-W3XXT26A.js → chunk-OTWT6BAJ.js} +358 -3
- package/dist/chunk-OTWT6BAJ.js.map +1 -0
- package/dist/{chunk-XG3PTSCD.js → chunk-PDVP3C2I.js} +1 -1
- package/dist/chunk-PDVP3C2I.js.map +1 -0
- package/dist/{chunk-GIV6DWBG.js → chunk-S45MDEEF.js} +44 -5
- package/dist/chunk-S45MDEEF.js.map +1 -0
- package/dist/{chunk-VK5EER6C.js → chunk-SQKAECUL.js} +2 -2
- package/dist/{chunk-FAQVNJD4.js → chunk-SQOK5UM6.js} +12 -2
- package/dist/{chunk-FAQVNJD4.js.map → chunk-SQOK5UM6.js.map} +1 -1
- package/dist/chunk-STNPB3UM.js +9 -0
- package/dist/chunk-STNPB3UM.js.map +1 -0
- package/dist/{chunk-YS3POABP.js → chunk-TA6HPKWQ.js} +1 -1
- package/dist/chunk-TA6HPKWQ.js.map +1 -0
- package/dist/{chunk-4HIL6AHQ.js → chunk-TAMRU7A2.js} +4 -4
- package/dist/{chunk-QXQRKXCU.js → chunk-TGIJTNM3.js} +2 -2
- package/dist/chunk-TNH5SLCD.js +361 -0
- package/dist/chunk-TNH5SLCD.js.map +1 -0
- package/dist/{chunk-VPSUZLOJ.js → chunk-TYMDCIQM.js} +31 -5
- package/dist/chunk-TYMDCIQM.js.map +1 -0
- package/dist/chunk-U2XSUCDF.js +524 -0
- package/dist/chunk-U2XSUCDF.js.map +1 -0
- package/dist/{chunk-3Y53S2SA.js → chunk-UU6M64HI.js} +4 -4
- package/dist/{chunk-VCGTOS2A.js → chunk-WE2BUQD2.js} +3 -3
- package/dist/chunk-WE2BUQD2.js.map +1 -0
- package/dist/{chunk-JYQTXEIO.js → chunk-WWVJXBOT.js} +449 -29
- package/dist/chunk-WWVJXBOT.js.map +1 -0
- package/dist/chunk-YPIOFSN3.js +129 -0
- package/dist/chunk-YPIOFSN3.js.map +1 -0
- package/dist/chunk-ZC7J6ZYV.js +7 -0
- package/dist/chunk-ZC7J6ZYV.js.map +1 -0
- package/dist/{chunk-5ZGZ6HIZ.js → chunk-ZONKSLF2.js} +30 -7
- package/dist/chunk-ZONKSLF2.js.map +1 -0
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +8 -6
- package/dist/consent/index.d.ts +8 -6
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-5ZDIY3NG.js → crypto-456N7UVX.js} +7 -3
- package/dist/{delegation-QYXZW25W.js → delegation-DP4COTXB.js} +5 -5
- package/dist/derivations/index.cjs +124 -6
- package/dist/derivations/index.cjs.map +1 -1
- package/dist/derivations/index.d.cts +11 -9
- package/dist/derivations/index.d.ts +11 -9
- package/dist/derivations/index.js +8 -6
- package/dist/{dev-unlock-DQCNDfFp.d.cts → dev-unlock-CY0HIZA0.d.cts} +1 -1
- package/dist/{dev-unlock-utkybTKb.d.ts → dev-unlock-CpKSkl2c.d.ts} +1 -1
- package/dist/discriminant-BN9REW3o.d.cts +60 -0
- package/dist/discriminant-BN9REW3o.d.ts +60 -0
- package/dist/errors-Dkc_fi-S.d.cts +1467 -0
- package/dist/errors-Dkc_fi-S.d.ts +1467 -0
- package/dist/executor-4IEW4KG5.js +8 -0
- package/dist/executor-KYJCJCIN.js +12 -0
- package/dist/executor-W7VIBOBZ.js +8 -0
- package/dist/{fanout-sidecar-VJ52RIEY.js → fanout-sidecar-YXNAEZ33.js} +2 -2
- package/dist/fanout-sidecar-YXNAEZ33.js.map +1 -0
- package/dist/forget/index.cjs +43 -0
- package/dist/forget/index.cjs.map +1 -0
- package/dist/forget/index.d.cts +1 -0
- package/dist/forget/index.d.ts +1 -0
- package/dist/forget/index.js +14 -0
- package/dist/guards/index.cjs +144 -4
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +16 -8
- package/dist/guards/index.d.ts +16 -8
- package/dist/guards/index.js +13 -7
- package/dist/{hash-jDowCrK2.d.cts → hash-BSd0-_L8.d.cts} +1 -1
- package/dist/{hash-DcoYWfJ_.d.ts → hash-BnBQx39y.d.ts} +1 -1
- package/dist/history/index.cjs +28 -5
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +9 -7
- package/dist/history/index.d.ts +9 -7
- package/dist/history/index.js +9 -7
- package/dist/history/index.js.map +1 -1
- package/dist/i18n/index.cjs +356 -26
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +8 -6
- package/dist/i18n/index.d.ts +8 -6
- package/dist/i18n/index.js +36 -15
- package/dist/i18n/index.js.map +1 -1
- package/dist/index-BMmajblo.d.cts +362 -0
- package/dist/index-BMmajblo.d.ts +362 -0
- package/dist/{index-BCKdioeh.d.ts → index-Bm9hIY7t.d.ts} +169 -1127
- package/dist/{index-BMjrzNZr.d.cts → index-tZqVB9g5.d.cts} +169 -1127
- package/dist/index.cjs +10286 -2168
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +258 -23
- package/dist/index.d.ts +258 -23
- package/dist/index.js +443 -110
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs +97 -32
- 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-JXC6T2QR.js +12 -0
- package/dist/{lazy-builder-Rpd-V3jP.d.ts → lazy-builder-ChSqcF5t.d.ts} +2 -2
- package/dist/{lazy-builder-C-rPfWG0.d.cts → lazy-builder-eYZzLEL1.d.cts} +2 -2
- package/dist/{ledger-3IU5GMXA.js → ledger-I7JUYP4L.js} +6 -6
- package/dist/materialized-views/index.cjs +687 -13
- 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 +8 -7
- package/dist/mime-magic-BnJCGJzB.d.cts +103 -0
- package/dist/mime-magic-CjSyakO4.d.ts +103 -0
- package/dist/noydb-ZZCRF6TE.js +38 -0
- package/dist/overlay-views/index.cjs +58 -18
- package/dist/overlay-views/index.cjs.map +1 -1
- package/dist/overlay-views/index.d.cts +32 -12
- package/dist/overlay-views/index.d.ts +32 -12
- package/dist/overlay-views/index.js +6 -6
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +8 -6
- package/dist/periods/index.d.ts +8 -6
- package/dist/periods/index.js +6 -6
- package/dist/{predicate-Dnu81tsS.d.cts → predicate-BmhBSPCH.d.cts} +87 -5
- package/dist/{predicate-Dnu81tsS.d.ts → predicate-BmhBSPCH.d.ts} +87 -5
- package/dist/{public-envelope-U3CMEOMV.js → public-envelope-5XRTUNKF.js} +4 -4
- package/dist/query/index.cjs +1438 -130
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +4 -3
- package/dist/query/index.d.ts +4 -3
- package/dist/query/index.js +13 -6
- package/dist/read-only-facade-EX6WZZBP.js +7 -0
- package/dist/registry-ATRHOG5B.js +8 -0
- package/dist/registry-DKEXOJVO.js +7 -0
- package/dist/registry-LEHB26TY.js +8 -0
- package/dist/{registry-3ALP62P6.js → registry-NWHOLD5M.js} +3 -3
- package/dist/{revoke-KY2GB4KP.js → revoke-5IEK22KT.js} +6 -6
- package/dist/sealed-record/index.cjs +139 -0
- package/dist/sealed-record/index.cjs.map +1 -0
- package/dist/sealed-record/index.d.cts +123 -0
- package/dist/sealed-record/index.d.ts +123 -0
- package/dist/sealed-record/index.js +42 -0
- package/dist/sealed-record/index.js.map +1 -0
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +9 -7
- package/dist/session/index.d.ts +9 -7
- package/dist/session/index.js +3 -3
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +8 -6
- package/dist/shadow/index.d.ts +8 -6
- package/dist/shadow/index.js +2 -2
- package/dist/{signer-GRI5TZKH.js → signer-I6YARZQA.js} +5 -5
- package/dist/snapshots/index.cjs +937 -0
- package/dist/snapshots/index.cjs.map +1 -0
- package/dist/snapshots/index.d.cts +30 -0
- package/dist/snapshots/index.d.ts +30 -0
- package/dist/snapshots/index.js +152 -0
- package/dist/snapshots/index.js.map +1 -0
- package/dist/{stale-OTOF3FH7.js → stale-CPESGAPL.js} +2 -2
- package/dist/stale-CPESGAPL.js.map +1 -0
- package/dist/state-vault-JR3CFGNP.js +14 -0
- package/dist/state-vault-JR3CFGNP.js.map +1 -0
- package/dist/store/index.cjs +8 -0
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +15 -6
- package/dist/store/index.d.ts +15 -6
- package/dist/store/index.js +2 -2
- package/dist/{strategy-DSTrsZ8t.d.ts → strategy-54eIwox5.d.ts} +456 -7
- package/dist/{strategy-DSTrsZ8t.d.cts → strategy-WtB-jXYv.d.cts} +456 -7
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +7 -5
- package/dist/sync/index.d.ts +7 -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 +8 -6
- package/dist/team/index.d.ts +8 -6
- package/dist/team/index.js +8 -8
- package/dist/transition-guard-D4bfIAiW.d.ts +165 -0
- package/dist/transition-guard-Dmpqzg-_.d.cts +165 -0
- package/dist/tx/index.cjs +155 -5
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +27 -9
- package/dist/tx/index.d.ts +27 -9
- package/dist/tx/index.js +61 -4
- package/dist/tx/index.js.map +1 -1
- package/dist/{types-BoFFiskX.d.ts → types-DLfWFr6U.d.ts} +3997 -1262
- package/dist/{types-DJG8HG6F.d.cts → types-DyOI6XZ_.d.cts} +3997 -1262
- package/dist/{ulid-BmBgooGm.d.ts → ulid-B2L_aqVA.d.ts} +19 -19
- package/dist/{ulid-C7ms9oli.d.cts → ulid-LaxfH2tK.d.cts} +19 -19
- 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/vault-group-BB246VIM.js +804 -0
- package/dist/vault-group-BB246VIM.js.map +1 -0
- package/dist/{with-materialized-view-CqnRwI2S.d.ts → with-materialized-view-CeZYGJVf.d.cts} +2 -2
- package/dist/{with-materialized-view-BbEPFIIJ.d.cts → with-materialized-view-DNULSxoP.d.ts} +2 -2
- package/dist/{with-overlayed-view-Ct1fSJt-.d.ts → with-overlayed-view-C9joG7UZ.d.ts} +2 -2
- package/dist/{with-overlayed-view-bwlmmFjx.d.cts → with-overlayed-view-kdcPGHih.d.cts} +2 -2
- package/dist/with-rollup-DJDbrxjf.d.ts +47 -0
- package/dist/with-rollup-s58XAeWO.d.cts +47 -0
- package/package.json +35 -4
- package/dist/chunk-2PAQNPE3.js.map +0 -1
- package/dist/chunk-3S4BJX25.js.map +0 -1
- package/dist/chunk-3XHOCQK4.js +0 -118
- package/dist/chunk-3XHOCQK4.js.map +0 -1
- package/dist/chunk-3Z2TPHC4.js.map +0 -1
- package/dist/chunk-5ZGZ6HIZ.js.map +0 -1
- package/dist/chunk-7BRE6EUA.js.map +0 -1
- package/dist/chunk-7Z23ZFLV.js.map +0 -1
- package/dist/chunk-CXSCDO5T.js +0 -51
- package/dist/chunk-CXSCDO5T.js.map +0 -1
- package/dist/chunk-E535SAN4.js.map +0 -1
- package/dist/chunk-EUYOGYGV.js.map +0 -1
- package/dist/chunk-GIV6DWBG.js.map +0 -1
- package/dist/chunk-HXJXPZRE.js.map +0 -1
- package/dist/chunk-J4KLMEUL.js.map +0 -1
- package/dist/chunk-JYQTXEIO.js.map +0 -1
- package/dist/chunk-LRAZDV5X.js.map +0 -1
- package/dist/chunk-MRIBLZL3.js +0 -86
- package/dist/chunk-MRIBLZL3.js.map +0 -1
- package/dist/chunk-MUWOSVEP.js.map +0 -1
- package/dist/chunk-OVZDFEOR.js.map +0 -1
- package/dist/chunk-PEULZC6M.js.map +0 -1
- package/dist/chunk-PFSNOPBQ.js.map +0 -1
- package/dist/chunk-PLI5TV7N.js.map +0 -1
- package/dist/chunk-Q6W2CMEJ.js.map +0 -1
- package/dist/chunk-RTZVQAJ7.js.map +0 -1
- package/dist/chunk-TBKOGSYR.js.map +0 -1
- package/dist/chunk-UMLVJTYV.js.map +0 -1
- package/dist/chunk-UND4XIB6.js.map +0 -1
- package/dist/chunk-VCGTOS2A.js.map +0 -1
- package/dist/chunk-VE6YVP32.js +0 -19
- package/dist/chunk-VE6YVP32.js.map +0 -1
- package/dist/chunk-VPSUZLOJ.js.map +0 -1
- package/dist/chunk-VRBCTEKQ.js.map +0 -1
- package/dist/chunk-W3XXT26A.js.map +0 -1
- package/dist/chunk-XG3PTSCD.js.map +0 -1
- package/dist/chunk-Y2RKOPNC.js.map +0 -1
- package/dist/chunk-YMYK7US4.js.map +0 -1
- package/dist/chunk-YS3POABP.js.map +0 -1
- package/dist/chunk-YTXSFG3C.js.map +0 -1
- package/dist/executor-AS2IDHKZ.js +0 -11
- package/dist/executor-HLXFXNFM.js +0 -8
- package/dist/executor-HN6YBHZ5.js +0 -8
- package/dist/fanout-sidecar-VJ52RIEY.js.map +0 -1
- package/dist/issue-ORP37MVW.js +0 -12
- package/dist/mime-magic-CBBSOkjm.d.cts +0 -50
- package/dist/mime-magic-CBBSOkjm.d.ts +0 -50
- package/dist/noydb-5H3C24GG.js +0 -34
- package/dist/read-only-facade-ITU6L7BL.js +0 -7
- package/dist/registry-7HE6VJGC.js +0 -8
- package/dist/registry-PSIPG2QR.js +0 -8
- package/dist/registry-RFGGMVNJ.js +0 -7
- package/dist/with-derivation-BKXXa8Vt.d.ts +0 -13
- package/dist/with-derivation-BjQ7q4NE.d.cts +0 -13
- package/dist/with-guard-C25yNjzd.d.ts +0 -18
- package/dist/with-guard-DQme5DKE.d.cts +0 -18
- /package/dist/{chunk-7Q5PLD5C.js.map → chunk-7MRT7EPB.js.map} +0 -0
- /package/dist/{chunk-G6FRSBKK.js.map → chunk-AI4USDRI.js.map} +0 -0
- /package/dist/{chunk-NWZ3I6R6.js.map → chunk-EYK72OTL.js.map} +0 -0
- /package/dist/{chunk-7BUTTVMR.js.map → chunk-F5GWNSE2.js.map} +0 -0
- /package/dist/{chunk-AHPFONIL.js.map → chunk-F5ILTHMU.js.map} +0 -0
- /package/dist/{chunk-QPEXPHJR.js.map → chunk-I3IYTUUI.js.map} +0 -0
- /package/dist/{chunk-3QAKZ37R.js.map → chunk-IVZWHIEK.js.map} +0 -0
- /package/dist/{chunk-243PNUA6.js.map → chunk-JOK73NDT.js.map} +0 -0
- /package/dist/{chunk-VK5EER6C.js.map → chunk-SQKAECUL.js.map} +0 -0
- /package/dist/{chunk-4HIL6AHQ.js.map → chunk-TAMRU7A2.js.map} +0 -0
- /package/dist/{chunk-QXQRKXCU.js.map → chunk-TGIJTNM3.js.map} +0 -0
- /package/dist/{chunk-3Y53S2SA.js.map → chunk-UU6M64HI.js.map} +0 -0
- /package/dist/{crypto-5ZDIY3NG.js.map → crypto-456N7UVX.js.map} +0 -0
- /package/dist/{delegation-QYXZW25W.js.map → delegation-DP4COTXB.js.map} +0 -0
- /package/dist/{executor-AS2IDHKZ.js.map → executor-4IEW4KG5.js.map} +0 -0
- /package/dist/{executor-HLXFXNFM.js.map → executor-KYJCJCIN.js.map} +0 -0
- /package/dist/{executor-HN6YBHZ5.js.map → executor-W7VIBOBZ.js.map} +0 -0
- /package/dist/{issue-ORP37MVW.js.map → forget/index.js.map} +0 -0
- /package/dist/{ledger-3IU5GMXA.js.map → issue-JXC6T2QR.js.map} +0 -0
- /package/dist/{noydb-5H3C24GG.js.map → ledger-I7JUYP4L.js.map} +0 -0
- /package/dist/{public-envelope-U3CMEOMV.js.map → noydb-ZZCRF6TE.js.map} +0 -0
- /package/dist/{read-only-facade-ITU6L7BL.js.map → public-envelope-5XRTUNKF.js.map} +0 -0
- /package/dist/{registry-3ALP62P6.js.map → read-only-facade-EX6WZZBP.js.map} +0 -0
- /package/dist/{registry-7HE6VJGC.js.map → registry-ATRHOG5B.js.map} +0 -0
- /package/dist/{registry-PSIPG2QR.js.map → registry-DKEXOJVO.js.map} +0 -0
- /package/dist/{registry-RFGGMVNJ.js.map → registry-LEHB26TY.js.map} +0 -0
- /package/dist/{revoke-KY2GB4KP.js.map → registry-NWHOLD5M.js.map} +0 -0
- /package/dist/{signer-GRI5TZKH.js.map → revoke-5IEK22KT.js.map} +0 -0
- /package/dist/{stale-OTOF3FH7.js.map → signer-I6YARZQA.js.map} +0 -0
package/dist/blobs/index.cjs
CHANGED
|
@@ -90,7 +90,9 @@ __export(crypto_exports, {
|
|
|
90
90
|
generateSalt: () => generateSalt,
|
|
91
91
|
hmacSha256Hex: () => hmacSha256Hex,
|
|
92
92
|
sha256Hex: () => sha256Hex,
|
|
93
|
+
unwrapCek: () => unwrapCek,
|
|
93
94
|
unwrapKey: () => unwrapKey,
|
|
95
|
+
wrapCek: () => wrapCek,
|
|
94
96
|
wrapKey: () => wrapKey
|
|
95
97
|
});
|
|
96
98
|
async function deriveKey(passphrase, salt) {
|
|
@@ -141,6 +143,39 @@ async function unwrapKey(wrappedBase64, kek) {
|
|
|
141
143
|
throw new InvalidKeyError();
|
|
142
144
|
}
|
|
143
145
|
}
|
|
146
|
+
async function asKwKey(dek) {
|
|
147
|
+
const rawDek = await subtle.exportKey("raw", dek);
|
|
148
|
+
const hkdfKey = await subtle.importKey("raw", rawDek, "HKDF", false, ["deriveBits"]);
|
|
149
|
+
const salt = new TextEncoder().encode("noydb-cek-wrap");
|
|
150
|
+
const info = new TextEncoder().encode("v1");
|
|
151
|
+
const bits = await subtle.deriveBits(
|
|
152
|
+
{ name: "HKDF", hash: "SHA-256", salt, info },
|
|
153
|
+
hkdfKey,
|
|
154
|
+
KEY_BITS
|
|
155
|
+
);
|
|
156
|
+
return subtle.importKey("raw", bits, "AES-KW", false, ["wrapKey", "unwrapKey"]);
|
|
157
|
+
}
|
|
158
|
+
async function wrapCek(cek, dek) {
|
|
159
|
+
const kw = await asKwKey(dek);
|
|
160
|
+
const wrapped = await subtle.wrapKey("raw", cek, kw, "AES-KW");
|
|
161
|
+
return bufferToBase64(wrapped);
|
|
162
|
+
}
|
|
163
|
+
async function unwrapCek(wrappedBase64, dek) {
|
|
164
|
+
const kw = await asKwKey(dek);
|
|
165
|
+
try {
|
|
166
|
+
return await subtle.unwrapKey(
|
|
167
|
+
"raw",
|
|
168
|
+
base64ToBuffer(wrappedBase64),
|
|
169
|
+
kw,
|
|
170
|
+
"AES-KW",
|
|
171
|
+
{ name: "AES-GCM", length: KEY_BITS },
|
|
172
|
+
true,
|
|
173
|
+
["encrypt", "decrypt"]
|
|
174
|
+
);
|
|
175
|
+
} catch {
|
|
176
|
+
throw new InvalidKeyError();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
144
179
|
async function encrypt(plaintext, dek) {
|
|
145
180
|
const iv = generateIV();
|
|
146
181
|
const encoded = new TextEncoder().encode(plaintext);
|
|
@@ -362,7 +397,9 @@ __export(blobs_exports, {
|
|
|
362
397
|
createExportBlobsHandle: () => createExportBlobsHandle,
|
|
363
398
|
detectMagic: () => detectMagic,
|
|
364
399
|
detectMimeType: () => detectMimeType,
|
|
400
|
+
importExternalObjects: () => importExternalObjects,
|
|
365
401
|
isPreCompressed: () => isPreCompressed,
|
|
402
|
+
memoryObjectProjection: () => memoryObjectProjection,
|
|
366
403
|
runCompaction: () => runCompaction,
|
|
367
404
|
withBlobs: () => withBlobs
|
|
368
405
|
});
|
|
@@ -373,6 +410,11 @@ var NOYDB_FORMAT_VERSION = 1;
|
|
|
373
410
|
|
|
374
411
|
// src/blobs/blob-set.ts
|
|
375
412
|
init_crypto();
|
|
413
|
+
|
|
414
|
+
// src/record-keys/index.ts
|
|
415
|
+
init_crypto();
|
|
416
|
+
|
|
417
|
+
// src/blobs/blob-set.ts
|
|
376
418
|
init_errors();
|
|
377
419
|
|
|
378
420
|
// src/blobs/mime-magic.ts
|
|
@@ -600,11 +642,8 @@ async function compressBytes(data) {
|
|
|
600
642
|
if (typeof CompressionStream === "undefined") {
|
|
601
643
|
return { bytes: data, algorithm: "none" };
|
|
602
644
|
}
|
|
603
|
-
const
|
|
604
|
-
const
|
|
605
|
-
await writer.write(data);
|
|
606
|
-
await writer.close();
|
|
607
|
-
const buf = await new Response(cs.readable).arrayBuffer();
|
|
645
|
+
const piped = new Response(data).body.pipeThrough(new CompressionStream("gzip"));
|
|
646
|
+
const buf = await new Response(piped).arrayBuffer();
|
|
608
647
|
return { bytes: new Uint8Array(buf), algorithm: "gzip" };
|
|
609
648
|
}
|
|
610
649
|
async function decompressBytes(data) {
|
|
@@ -613,11 +652,8 @@ async function decompressBytes(data) {
|
|
|
613
652
|
"[noy-db] DecompressionStream not available \u2014 cannot decompress blob chunk"
|
|
614
653
|
);
|
|
615
654
|
}
|
|
616
|
-
const
|
|
617
|
-
const
|
|
618
|
-
await writer.write(data);
|
|
619
|
-
await writer.close();
|
|
620
|
-
const buf = await new Response(ds.readable).arrayBuffer();
|
|
655
|
+
const piped = new Response(data).body.pipeThrough(new DecompressionStream("gzip"));
|
|
656
|
+
const buf = await new Response(piped).arrayBuffer();
|
|
621
657
|
return new Uint8Array(buf);
|
|
622
658
|
}
|
|
623
659
|
function concatChunks(chunks) {
|
|
@@ -642,6 +678,10 @@ var BlobSet = class {
|
|
|
642
678
|
encrypted;
|
|
643
679
|
userId;
|
|
644
680
|
maxBlobBytes;
|
|
681
|
+
erasableBlobs;
|
|
682
|
+
debugPlaintext;
|
|
683
|
+
objectStore;
|
|
684
|
+
blobFields;
|
|
645
685
|
constructor(opts) {
|
|
646
686
|
this.store = opts.store;
|
|
647
687
|
this.vault = opts.vault;
|
|
@@ -651,6 +691,24 @@ var BlobSet = class {
|
|
|
651
691
|
this.encrypted = opts.encrypted;
|
|
652
692
|
this.userId = opts.userId;
|
|
653
693
|
this.maxBlobBytes = opts.maxBlobBytes;
|
|
694
|
+
this.erasableBlobs = opts.erasableBlobs === true;
|
|
695
|
+
this.debugPlaintext = opts.debugPlaintext === true;
|
|
696
|
+
this.objectStore = opts.objectStore;
|
|
697
|
+
this.blobFields = opts.blobFields;
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Resolve the key the blob's CHUNKS are encrypted under.
|
|
701
|
+
*
|
|
702
|
+
* - `_cek` present (erasable blob) → unwrap the per-blob content CEK under
|
|
703
|
+
* the `_blob` DEK. Deleting the BlobObject (at `refCount → 0`) makes this
|
|
704
|
+
* key unrecoverable → the chunks are crypto-shredded.
|
|
705
|
+
* - `_cek` absent (legacy) → the `_blob` DEK encrypts chunks directly.
|
|
706
|
+
* - unencrypted vault → `null` (chunks stored as plaintext base64).
|
|
707
|
+
*/
|
|
708
|
+
async resolveChunkKey(blob) {
|
|
709
|
+
if (!this.encrypted) return null;
|
|
710
|
+
const blobDEK = await this.getDEK(BLOB_COLLECTION);
|
|
711
|
+
return blob._cek !== void 0 ? await unwrapCek(blob._cek, blobDEK) : blobDEK;
|
|
654
712
|
}
|
|
655
713
|
/** The internal collection that holds slot metadata for this collection's blobs. */
|
|
656
714
|
get slotsCollection() {
|
|
@@ -768,12 +826,163 @@ var BlobSet = class {
|
|
|
768
826
|
const updated = { ...blob, refCount: blob.refCount + delta };
|
|
769
827
|
try {
|
|
770
828
|
await this.writeBlobObject(updated, version);
|
|
829
|
+
return updated.refCount;
|
|
830
|
+
} catch (err) {
|
|
831
|
+
if (err instanceof ConflictError && attempt < MAX_CAS_RETRIES - 1) continue;
|
|
832
|
+
throw err;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
throw new ConflictError(-1);
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Release `n` references to a blob and reclaim it at refCount 0 (#365 slice 4).
|
|
839
|
+
*
|
|
840
|
+
* The single reclaim choke point for every reference-drop path — slot
|
|
841
|
+
* delete/overwrite, published-version delete, and `forget()` shred — so the
|
|
842
|
+
* refCount-0 policy is uniform:
|
|
843
|
+
* - **erasable blob** (`_cek` present) → delete the `BlobObject` (the SOLE
|
|
844
|
+
* copy of the wrapped content CEK → chunks permanently undecryptable) and
|
|
845
|
+
* reclaim the chunks. The crypto-shred is EAGER on every path: GDPR erasure
|
|
846
|
+
* must not wait on orphan retention.
|
|
847
|
+
* - **legacy blob** (no `_cek`) → reclaimed only when `reclaimLegacy` (the
|
|
848
|
+
* `forget()` erasure path); otherwise left for deferred GC so the existing
|
|
849
|
+
* `BlobLifecyclePolicy.orphanRetentionDays` semantics are preserved.
|
|
850
|
+
*
|
|
851
|
+
* @returns `'shredded'` (erasable, refCount 0, chunks dead) · `'retainedShared'`
|
|
852
|
+
* (erasable, still referenced) · `'residue'` (legacy — not a crypto-shred).
|
|
853
|
+
*/
|
|
854
|
+
async releaseRef(eTag, n, reclaimLegacy) {
|
|
855
|
+
const loaded = await this.loadBlobObject(eTag);
|
|
856
|
+
if (!loaded) return "shredded";
|
|
857
|
+
const erasable = loaded.blob._cek !== void 0;
|
|
858
|
+
const remaining = await this.casUpdateRefCount(eTag, -n);
|
|
859
|
+
if (remaining > 0) return erasable ? "retainedShared" : "residue";
|
|
860
|
+
if (erasable || reclaimLegacy) {
|
|
861
|
+
await this.store.delete(this.vault, BLOB_INDEX_COLLECTION, eTag);
|
|
862
|
+
for (let i = 0; i < loaded.blob.chunkCount; i++) {
|
|
863
|
+
await this.store.delete(this.vault, BLOB_CHUNKS_COLLECTION, `${eTag}_${i}`);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return erasable ? "shredded" : "residue";
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Crypto-shred this record's blob attachments (#365 slice 2) — called by
|
|
870
|
+
* `vault.forget()`.
|
|
871
|
+
*
|
|
872
|
+
* For each distinct eTag the record references (a record may attach the same
|
|
873
|
+
* content under several slot names → several refCount holds): decrement the
|
|
874
|
+
* blob's refCount by that many. When it reaches 0:
|
|
875
|
+
* - **erasable blob** (`_cek` present) → delete the `BlobObject` (the SOLE
|
|
876
|
+
* recoverable copy of the wrapped content CEK → chunks permanently
|
|
877
|
+
* undecryptable) and reclaim the chunk bytes. This is the crypto-shred.
|
|
878
|
+
* - **legacy blob** (no `_cek`) → chunks are under the shared `_blob` DEK and
|
|
879
|
+
* stay decryptable until byte-deleted; we delete the orphaned chunks +
|
|
880
|
+
* index but report it as residue, not a cryptographic erasure.
|
|
881
|
+
* When refCount stays > 0 the content legitimately persists for its other
|
|
882
|
+
* owner — reported as `retainedShared` (or `residue` if legacy).
|
|
883
|
+
*
|
|
884
|
+
* Finally drops the record's slot map, severing the subject's link.
|
|
885
|
+
*/
|
|
886
|
+
async shredAllForRecord() {
|
|
887
|
+
const { slots } = await this.loadSlots();
|
|
888
|
+
const slotNames = Object.keys(slots);
|
|
889
|
+
const shredded = [];
|
|
890
|
+
const retainedShared = [];
|
|
891
|
+
const residue = [];
|
|
892
|
+
if (slotNames.length === 0) return { shredded, retainedShared, residue };
|
|
893
|
+
const holds = /* @__PURE__ */ new Map();
|
|
894
|
+
for (const name of slotNames) {
|
|
895
|
+
const eTag = slots[name].eTag;
|
|
896
|
+
holds.set(eTag, (holds.get(eTag) ?? 0) + 1);
|
|
897
|
+
}
|
|
898
|
+
for (const [eTag, n] of holds) {
|
|
899
|
+
const outcome = await this.releaseRef(eTag, n, true);
|
|
900
|
+
if (outcome === "shredded") shredded.push(eTag);
|
|
901
|
+
else if (outcome === "retainedShared") retainedShared.push(eTag);
|
|
902
|
+
else residue.push(eTag);
|
|
903
|
+
}
|
|
904
|
+
await this.store.delete(this.vault, this.slotsCollection, this.recordId);
|
|
905
|
+
return { shredded, retainedShared, residue };
|
|
906
|
+
}
|
|
907
|
+
/** CAS retry loop for an arbitrary BlobObject mutation. */
|
|
908
|
+
async casUpdateBlobObject(eTag, mutate) {
|
|
909
|
+
for (let attempt = 0; attempt < MAX_CAS_RETRIES; attempt++) {
|
|
910
|
+
const result = await this.loadBlobObject(eTag);
|
|
911
|
+
if (!result) throw new NotFoundError(`BlobObject ${eTag} not found`);
|
|
912
|
+
try {
|
|
913
|
+
await this.writeBlobObject(mutate(result.blob), result.version);
|
|
771
914
|
return;
|
|
772
915
|
} catch (err) {
|
|
773
916
|
if (err instanceof ConflictError && attempt < MAX_CAS_RETRIES - 1) continue;
|
|
774
917
|
throw err;
|
|
775
918
|
}
|
|
776
919
|
}
|
|
920
|
+
throw new ConflictError(-1);
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Migrate this record's LEGACY blobs (no `_cek`, chunks under the shared
|
|
924
|
+
* `_blob` DEK) to per-blob content CEKs so they become crypto-shreddable
|
|
925
|
+
* (#365 slice 3). Returns the eTags migrated vs. already-erasable.
|
|
926
|
+
*
|
|
927
|
+
* **Explicit maintenance pass** (mirrors the record-CEK migration posture):
|
|
928
|
+
* re-encrypts the existing compressed chunks IN PLACE under a fresh content
|
|
929
|
+
* CEK — preserving the eTag, chunkCount, chunkSize, and compression — then
|
|
930
|
+
* flips the `_cek` discriminant. Crash-safe + idempotent via `_cekPending`:
|
|
931
|
+
* 1. persist the wrapped content CEK in `_cekPending` (readers ignore it →
|
|
932
|
+
* the blob stays readable under the `_blob` DEK; the key survives a crash);
|
|
933
|
+
* 2. re-encrypt each chunk under the content CEK (a resume reads an
|
|
934
|
+
* already-migrated chunk under the content CEK, else under the `_blob` DEK);
|
|
935
|
+
* 3. promote `_cekPending` → `_cek` (atomic flip). Reads now use the CEK.
|
|
936
|
+
* A re-run after a crash resumes from whichever phase was reached.
|
|
937
|
+
*
|
|
938
|
+
* Dedup-safe: migrating a shared blob (refCount > 1) re-keys it for every
|
|
939
|
+
* referencer at once; a non-erasable collection still reads it (it unwraps
|
|
940
|
+
* `_cek` under the `_blob` DEK it holds).
|
|
941
|
+
*/
|
|
942
|
+
async migrate() {
|
|
943
|
+
const migrated = [];
|
|
944
|
+
const alreadyErasable = [];
|
|
945
|
+
if (!this.encrypted) return { migrated, alreadyErasable };
|
|
946
|
+
const blobDEK = await this.getDEK(BLOB_COLLECTION);
|
|
947
|
+
const { slots } = await this.loadSlots();
|
|
948
|
+
const eTags = new Set(Object.values(slots).map((s) => s.eTag));
|
|
949
|
+
for (const eTag of eTags) {
|
|
950
|
+
const loaded = await this.loadBlobObject(eTag);
|
|
951
|
+
if (!loaded) continue;
|
|
952
|
+
const blob = loaded.blob;
|
|
953
|
+
if (blob._cek !== void 0) {
|
|
954
|
+
alreadyErasable.push(eTag);
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
let contentCek;
|
|
958
|
+
if (blob._cekPending !== void 0) {
|
|
959
|
+
contentCek = await unwrapCek(blob._cekPending, blobDEK);
|
|
960
|
+
} else {
|
|
961
|
+
contentCek = await generateDEK();
|
|
962
|
+
const wrapped = await wrapCek(contentCek, blobDEK);
|
|
963
|
+
await this.casUpdateBlobObject(eTag, (b) => ({ ...b, _cekPending: wrapped }));
|
|
964
|
+
}
|
|
965
|
+
for (let i = 0; i < blob.chunkCount; i++) {
|
|
966
|
+
let raw;
|
|
967
|
+
try {
|
|
968
|
+
raw = await this.readChunk(eTag, i, blob.chunkCount, blobDEK);
|
|
969
|
+
} catch {
|
|
970
|
+
raw = await this.readChunk(eTag, i, blob.chunkCount, contentCek);
|
|
971
|
+
}
|
|
972
|
+
if (!raw) {
|
|
973
|
+
throw new NotFoundError(
|
|
974
|
+
`Blob chunk ${i}/${blob.chunkCount} missing for eTag "${eTag}" during migration`
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
await this.writeChunk(eTag, i, blob.chunkCount, raw, contentCek);
|
|
978
|
+
}
|
|
979
|
+
await this.casUpdateBlobObject(eTag, (b) => {
|
|
980
|
+
const { _cekPending, ...rest } = b;
|
|
981
|
+
return _cekPending === void 0 ? b : { ...rest, _cek: _cekPending };
|
|
982
|
+
});
|
|
983
|
+
migrated.push(eTag);
|
|
984
|
+
}
|
|
985
|
+
return { migrated, alreadyErasable };
|
|
777
986
|
}
|
|
778
987
|
// ─── Chunk I/O (with AAD binding) ─────────────────────────────────
|
|
779
988
|
async writeChunk(eTag, index, chunkCount, chunk, dek) {
|
|
@@ -845,10 +1054,10 @@ var BlobSet = class {
|
|
|
845
1054
|
}
|
|
846
1055
|
// ─── Fetch all chunks for a blob ──────────────────────────────────
|
|
847
1056
|
async fetchAllChunks(blob) {
|
|
848
|
-
const
|
|
1057
|
+
const chunkKey = await this.resolveChunkKey(blob);
|
|
849
1058
|
const chunks = [];
|
|
850
1059
|
for (let i = 0; i < blob.chunkCount; i++) {
|
|
851
|
-
const chunk = await this.readChunk(blob.eTag, i, blob.chunkCount,
|
|
1060
|
+
const chunk = await this.readChunk(blob.eTag, i, blob.chunkCount, chunkKey);
|
|
852
1061
|
if (!chunk) {
|
|
853
1062
|
throw new NotFoundError(
|
|
854
1063
|
`Blob chunk ${i}/${blob.chunkCount} missing for eTag "${blob.eTag}" on record "${this.recordId}"`
|
|
@@ -873,6 +1082,49 @@ var BlobSet = class {
|
|
|
873
1082
|
* If overwriting an existing slot, decrements the old eTag's refCount.
|
|
874
1083
|
*/
|
|
875
1084
|
async put(slotName, data, opts) {
|
|
1085
|
+
if (this.objectStore && this.blobFields?.[slotName]?.external) {
|
|
1086
|
+
const policy = this.blobFields[slotName];
|
|
1087
|
+
let contentType = opts?.mimeType;
|
|
1088
|
+
if (!contentType) {
|
|
1089
|
+
const detected = detectMagic(data.subarray(0, 16));
|
|
1090
|
+
contentType = detected?.mime ?? "application/octet-stream";
|
|
1091
|
+
}
|
|
1092
|
+
const key = `${this.collection}/${this.recordId}/${slotName}`;
|
|
1093
|
+
const isPublic = policy.public === true;
|
|
1094
|
+
const backlink = await this.buildBacklink(slotName, policy.backlink ?? "opaque-token");
|
|
1095
|
+
await this.objectStore.putObject(key, data, {
|
|
1096
|
+
contentType,
|
|
1097
|
+
public: isPublic,
|
|
1098
|
+
...backlink.userMeta ? { userMeta: backlink.userMeta } : {}
|
|
1099
|
+
});
|
|
1100
|
+
const uploaderUserId2 = opts?.uploadedBy ?? this.userId;
|
|
1101
|
+
let oldETag;
|
|
1102
|
+
await this.casUpdateSlots((slots) => {
|
|
1103
|
+
oldETag = slots[slotName]?.eTag || void 0;
|
|
1104
|
+
const prevMeta = slots[slotName]?.external?.meta;
|
|
1105
|
+
slots[slotName] = {
|
|
1106
|
+
eTag: "",
|
|
1107
|
+
external: {
|
|
1108
|
+
key,
|
|
1109
|
+
contentType,
|
|
1110
|
+
...isPublic ? { public: true } : {},
|
|
1111
|
+
...backlink.token ? { backlink: backlink.token } : {},
|
|
1112
|
+
...prevMeta ? { meta: prevMeta } : {}
|
|
1113
|
+
},
|
|
1114
|
+
filename: slotName,
|
|
1115
|
+
size: data.byteLength,
|
|
1116
|
+
mimeType: contentType,
|
|
1117
|
+
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1118
|
+
...uploaderUserId2 !== void 0 ? { uploadedBy: uploaderUserId2 } : {}
|
|
1119
|
+
};
|
|
1120
|
+
return slots;
|
|
1121
|
+
});
|
|
1122
|
+
if (oldETag) {
|
|
1123
|
+
await this.releaseRef(oldETag, 1, false).catch(() => {
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
876
1128
|
const blobDEK = this.encrypted ? await this.getDEK(BLOB_COLLECTION) : null;
|
|
877
1129
|
const eTag = blobDEK ? await hmacSha256Hex(blobDEK, data) : await plainSha256Hex(data);
|
|
878
1130
|
let mimeType = opts?.mimeType;
|
|
@@ -888,13 +1140,21 @@ var BlobSet = class {
|
|
|
888
1140
|
} else {
|
|
889
1141
|
shouldCompress = true;
|
|
890
1142
|
}
|
|
1143
|
+
if (this.debugPlaintext) shouldCompress = false;
|
|
891
1144
|
const existingBlob = await this.loadBlobObject(eTag);
|
|
892
1145
|
if (existingBlob) {
|
|
893
1146
|
await this.casUpdateRefCount(eTag, 1);
|
|
894
1147
|
} else {
|
|
895
1148
|
const { bytes: compressed, algorithm } = shouldCompress ? await compressBytes(data) : { bytes: data, algorithm: "none" };
|
|
896
|
-
const chunkSize = this.effectiveChunkSize(opts);
|
|
1149
|
+
const chunkSize = this.debugPlaintext ? Math.max(compressed.byteLength, 1) : this.effectiveChunkSize(opts);
|
|
897
1150
|
const chunkCount = Math.max(1, Math.ceil(compressed.byteLength / chunkSize));
|
|
1151
|
+
let chunkKey = blobDEK;
|
|
1152
|
+
let wrappedCek;
|
|
1153
|
+
if (blobDEK && this.erasableBlobs) {
|
|
1154
|
+
const contentCek = await generateDEK();
|
|
1155
|
+
wrappedCek = await wrapCek(contentCek, blobDEK);
|
|
1156
|
+
chunkKey = contentCek;
|
|
1157
|
+
}
|
|
898
1158
|
for (let i = 0; i < chunkCount; i++) {
|
|
899
1159
|
const start = i * chunkSize;
|
|
900
1160
|
await this.writeChunk(
|
|
@@ -902,7 +1162,7 @@ var BlobSet = class {
|
|
|
902
1162
|
i,
|
|
903
1163
|
chunkCount,
|
|
904
1164
|
compressed.subarray(start, start + chunkSize),
|
|
905
|
-
|
|
1165
|
+
chunkKey
|
|
906
1166
|
);
|
|
907
1167
|
}
|
|
908
1168
|
await this.writeBlobObject({
|
|
@@ -914,7 +1174,8 @@ var BlobSet = class {
|
|
|
914
1174
|
chunkCount,
|
|
915
1175
|
...mimeType !== void 0 ? { mimeType } : {},
|
|
916
1176
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
917
|
-
refCount: 1
|
|
1177
|
+
refCount: 1,
|
|
1178
|
+
...wrappedCek !== void 0 ? { _cek: wrappedCek } : {}
|
|
918
1179
|
});
|
|
919
1180
|
}
|
|
920
1181
|
const uploaderUserId = opts?.uploadedBy ?? this.userId;
|
|
@@ -936,7 +1197,7 @@ var BlobSet = class {
|
|
|
936
1197
|
if (this._deferredRefDecrement) {
|
|
937
1198
|
const oldETag = this._deferredRefDecrement;
|
|
938
1199
|
this._deferredRefDecrement = void 0;
|
|
939
|
-
await this.
|
|
1200
|
+
await this.releaseRef(oldETag, 1, false).catch(() => {
|
|
940
1201
|
});
|
|
941
1202
|
}
|
|
942
1203
|
}
|
|
@@ -950,10 +1211,114 @@ var BlobSet = class {
|
|
|
950
1211
|
const { slots } = await this.loadSlots();
|
|
951
1212
|
const slot = slots[slotName];
|
|
952
1213
|
if (!slot) return null;
|
|
1214
|
+
if (slot.external) {
|
|
1215
|
+
if (!this.objectStore) {
|
|
1216
|
+
throw new NotFoundError(`Blob slot "${slotName}" is external but no objectStore is configured`);
|
|
1217
|
+
}
|
|
1218
|
+
return this.objectStore.getObject(slot.external.key);
|
|
1219
|
+
}
|
|
953
1220
|
const result = await this.loadBlobObject(slot.eTag);
|
|
954
1221
|
if (!result) return null;
|
|
955
1222
|
return this.fetchAllChunks(result.blob);
|
|
956
1223
|
}
|
|
1224
|
+
/**
|
|
1225
|
+
* A URL to fetch an `external` slot's object directly — presigned
|
|
1226
|
+
* (time-limited) or public, per the projection. Returns `null` if the slot
|
|
1227
|
+
* does not exist. Throws for a non-external slot (use `get()`/`response()`).
|
|
1228
|
+
*/
|
|
1229
|
+
async url(slotName, opts) {
|
|
1230
|
+
const { slots } = await this.loadSlots();
|
|
1231
|
+
const slot = slots[slotName];
|
|
1232
|
+
if (!slot) return null;
|
|
1233
|
+
if (!slot.external) {
|
|
1234
|
+
throw new NotFoundError(`Blob slot "${slotName}" is not external \u2014 url() is only for external fields`);
|
|
1235
|
+
}
|
|
1236
|
+
if (!this.objectStore) {
|
|
1237
|
+
throw new NotFoundError(`Blob slot "${slotName}" is external but no objectStore is configured`);
|
|
1238
|
+
}
|
|
1239
|
+
return this.objectStore.objectUrl(slot.external.key, opts);
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Build the backlink stamped onto an external object's metadata — the
|
|
1243
|
+
* self-describing "secondary store" reference back to this record. See
|
|
1244
|
+
* {@link BlobFieldPolicy.backlink}. Returns the `userMeta` to attach and, for
|
|
1245
|
+
* `opaque-token`, the `token` to record on the slot.
|
|
1246
|
+
*/
|
|
1247
|
+
async buildBacklink(slotName, mode) {
|
|
1248
|
+
if (mode === "none") return {};
|
|
1249
|
+
const ref = { vault: this.vault, collection: this.collection, record: this.recordId, field: slotName };
|
|
1250
|
+
if (mode === "plain") {
|
|
1251
|
+
return {
|
|
1252
|
+
userMeta: {
|
|
1253
|
+
"noydb-vault": ref.vault,
|
|
1254
|
+
"noydb-collection": ref.collection,
|
|
1255
|
+
"noydb-record": ref.record,
|
|
1256
|
+
"noydb-field": ref.field
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
if (mode === "encrypted" && this.encrypted) {
|
|
1261
|
+
const dek = await this.getDEK(BLOB_COLLECTION);
|
|
1262
|
+
const { iv, data } = await encrypt(JSON.stringify(ref), dek);
|
|
1263
|
+
return { userMeta: { "noydb-backlink-enc": `${iv}.${data}` } };
|
|
1264
|
+
}
|
|
1265
|
+
const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16));
|
|
1266
|
+
const token = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
1267
|
+
return { userMeta: { "noydb-backlink": token }, token };
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Adopt an EXISTING object in the projection into this slot **without
|
|
1271
|
+
* re-uploading** — used by import/bootstrap to anchor objects already in the
|
|
1272
|
+
* bucket. Writes the external slot record (the catalog entry).
|
|
1273
|
+
*/
|
|
1274
|
+
async adoptExternal(slotName, ref) {
|
|
1275
|
+
const uploaderUserId = this.userId;
|
|
1276
|
+
await this.casUpdateSlots((slots) => {
|
|
1277
|
+
slots[slotName] = {
|
|
1278
|
+
eTag: "",
|
|
1279
|
+
external: {
|
|
1280
|
+
key: ref.key,
|
|
1281
|
+
...ref.contentType ? { contentType: ref.contentType } : {},
|
|
1282
|
+
...ref.public ? { public: true } : {},
|
|
1283
|
+
...ref.backlink ? { backlink: ref.backlink } : {},
|
|
1284
|
+
...ref.meta ? { meta: ref.meta } : {}
|
|
1285
|
+
},
|
|
1286
|
+
filename: slotName,
|
|
1287
|
+
size: ref.size ?? 0,
|
|
1288
|
+
...ref.contentType ? { mimeType: ref.contentType } : {},
|
|
1289
|
+
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1290
|
+
...uploaderUserId !== void 0 ? { uploadedBy: uploaderUserId } : {}
|
|
1291
|
+
};
|
|
1292
|
+
return slots;
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Merge derived metadata (video `duration`, image `width`/`height`, arbitrary
|
|
1297
|
+
* metatags) into an external slot's secondary metadata store. Typically called
|
|
1298
|
+
* from an AWS-side processing callback (MediaConvert / ffprobe / Rekognition)
|
|
1299
|
+
* once it has probed the object. No-op for a missing or non-external slot.
|
|
1300
|
+
*/
|
|
1301
|
+
async setExternalMeta(slotName, meta) {
|
|
1302
|
+
await this.casUpdateSlots((slots) => {
|
|
1303
|
+
const slot = slots[slotName];
|
|
1304
|
+
if (!slot?.external) return null;
|
|
1305
|
+
slots[slotName] = {
|
|
1306
|
+
...slot,
|
|
1307
|
+
external: { ...slot.external, meta: { ...slot.external.meta, ...meta } }
|
|
1308
|
+
};
|
|
1309
|
+
return slots;
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Read an external slot's synced derived metadata (the secondary store).
|
|
1314
|
+
* Returns `null` for a missing or non-external slot, `{}` if none synced yet.
|
|
1315
|
+
*/
|
|
1316
|
+
async externalMeta(slotName) {
|
|
1317
|
+
const { slots } = await this.loadSlots();
|
|
1318
|
+
const slot = slots[slotName];
|
|
1319
|
+
if (!slot?.external) return null;
|
|
1320
|
+
return slot.external.meta ?? {};
|
|
1321
|
+
}
|
|
957
1322
|
/**
|
|
958
1323
|
* List all slot entries for this record.
|
|
959
1324
|
* Returns metadata only — no chunk data is loaded.
|
|
@@ -967,15 +1332,22 @@ var BlobSet = class {
|
|
|
967
1332
|
* Decrements refCount on the blob. Chunks are GC'd by `vault.blobGC()`.
|
|
968
1333
|
*/
|
|
969
1334
|
async delete(slotName) {
|
|
970
|
-
let
|
|
1335
|
+
let eTagToRelease;
|
|
1336
|
+
let externalKeyToDelete;
|
|
971
1337
|
await this.casUpdateSlots((slots) => {
|
|
972
1338
|
if (!(slotName in slots)) return null;
|
|
973
|
-
|
|
1339
|
+
const slot = slots[slotName];
|
|
1340
|
+
if (slot.external) externalKeyToDelete = slot.external.key;
|
|
1341
|
+
else eTagToRelease = slot.eTag;
|
|
974
1342
|
delete slots[slotName];
|
|
975
1343
|
return slots;
|
|
976
1344
|
});
|
|
977
|
-
if (
|
|
978
|
-
await this.
|
|
1345
|
+
if (externalKeyToDelete && this.objectStore) {
|
|
1346
|
+
await this.objectStore.deleteObject(externalKeyToDelete).catch(() => {
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
if (eTagToRelease) {
|
|
1350
|
+
await this.releaseRef(eTagToRelease, 1, false).catch(() => {
|
|
979
1351
|
});
|
|
980
1352
|
}
|
|
981
1353
|
}
|
|
@@ -1058,7 +1430,7 @@ var BlobSet = class {
|
|
|
1058
1430
|
await this.writeVersionRecord(slotName, record);
|
|
1059
1431
|
await this.casUpdateRefCount(slot.eTag, 1);
|
|
1060
1432
|
if (existing && existing.eTag !== slot.eTag) {
|
|
1061
|
-
await this.
|
|
1433
|
+
await this.releaseRef(existing.eTag, 1, false).catch(() => {
|
|
1062
1434
|
});
|
|
1063
1435
|
}
|
|
1064
1436
|
}
|
|
@@ -1101,7 +1473,7 @@ var BlobSet = class {
|
|
|
1101
1473
|
const record = await this.loadVersionRecord(slotName, label);
|
|
1102
1474
|
if (!record) return;
|
|
1103
1475
|
await this.deleteVersionRecord(slotName, label);
|
|
1104
|
-
await this.
|
|
1476
|
+
await this.releaseRef(record.eTag, 1, false).catch(() => {
|
|
1105
1477
|
});
|
|
1106
1478
|
}
|
|
1107
1479
|
/**
|
|
@@ -1240,6 +1612,91 @@ function withBlobs() {
|
|
|
1240
1612
|
};
|
|
1241
1613
|
}
|
|
1242
1614
|
|
|
1615
|
+
// src/blobs/object-projection.ts
|
|
1616
|
+
function memoryObjectProjection(opts = {}) {
|
|
1617
|
+
const base = opts.baseUrl ?? "memory://objects";
|
|
1618
|
+
const store = /* @__PURE__ */ new Map();
|
|
1619
|
+
return {
|
|
1620
|
+
name: "memory",
|
|
1621
|
+
async putObject(key, bytes, o) {
|
|
1622
|
+
store.set(key, {
|
|
1623
|
+
bytes,
|
|
1624
|
+
contentType: o.contentType,
|
|
1625
|
+
public: o.public === true,
|
|
1626
|
+
...o.userMeta ? { userMeta: o.userMeta } : {}
|
|
1627
|
+
});
|
|
1628
|
+
},
|
|
1629
|
+
async getObject(key) {
|
|
1630
|
+
return store.get(key)?.bytes ?? null;
|
|
1631
|
+
},
|
|
1632
|
+
async deleteObject(key) {
|
|
1633
|
+
store.delete(key);
|
|
1634
|
+
},
|
|
1635
|
+
async headObject(key) {
|
|
1636
|
+
const e = store.get(key);
|
|
1637
|
+
if (!e) return null;
|
|
1638
|
+
return {
|
|
1639
|
+
size: e.bytes.byteLength,
|
|
1640
|
+
contentType: e.contentType,
|
|
1641
|
+
...e.userMeta ? { userMeta: e.userMeta } : {}
|
|
1642
|
+
};
|
|
1643
|
+
},
|
|
1644
|
+
async objectUrl(key, o) {
|
|
1645
|
+
const e = store.get(key);
|
|
1646
|
+
if (e?.public) return `${base}/${key}`;
|
|
1647
|
+
return `${base}/${key}?sig=memory&expires=${o?.expiresInSeconds ?? 900}`;
|
|
1648
|
+
},
|
|
1649
|
+
async putUrl(key, o) {
|
|
1650
|
+
return `${base}/${key}?upload=memory&ct=${encodeURIComponent(o.contentType)}`;
|
|
1651
|
+
},
|
|
1652
|
+
async listPrefix(prefix) {
|
|
1653
|
+
const out = [];
|
|
1654
|
+
for (const [key, e] of store) {
|
|
1655
|
+
if (!key.startsWith(prefix)) continue;
|
|
1656
|
+
out.push({
|
|
1657
|
+
key,
|
|
1658
|
+
meta: { size: e.bytes.byteLength, contentType: e.contentType, ...e.userMeta ? { userMeta: e.userMeta } : {} }
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
return out;
|
|
1662
|
+
}
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// src/blobs/import-external.ts
|
|
1667
|
+
function defaultDeriveRecordId(key) {
|
|
1668
|
+
const parts = key.split("/");
|
|
1669
|
+
return parts.length >= 2 ? parts[parts.length - 2] ?? null : null;
|
|
1670
|
+
}
|
|
1671
|
+
async function importExternalObjects(args) {
|
|
1672
|
+
const { collection, objectStore, field } = args;
|
|
1673
|
+
const opts = args.options ?? {};
|
|
1674
|
+
const derive = opts.deriveRecordId ?? defaultDeriveRecordId;
|
|
1675
|
+
const makeRecord = opts.makeRecord ?? ((id) => ({ id }));
|
|
1676
|
+
const objects = await objectStore.listPrefix(opts.prefix ?? "");
|
|
1677
|
+
const recordIds = [];
|
|
1678
|
+
let imported = 0;
|
|
1679
|
+
let skipped = 0;
|
|
1680
|
+
for (const { key, meta } of objects) {
|
|
1681
|
+
const recordId = derive(key);
|
|
1682
|
+
if (!recordId) {
|
|
1683
|
+
skipped++;
|
|
1684
|
+
continue;
|
|
1685
|
+
}
|
|
1686
|
+
if (await collection.get(recordId) == null) {
|
|
1687
|
+
await collection.put(recordId, makeRecord(recordId));
|
|
1688
|
+
}
|
|
1689
|
+
await collection.blob(recordId).adoptExternal(field, {
|
|
1690
|
+
key,
|
|
1691
|
+
...meta.size !== void 0 ? { size: meta.size } : {},
|
|
1692
|
+
...meta.contentType ? { contentType: meta.contentType } : {}
|
|
1693
|
+
});
|
|
1694
|
+
recordIds.push(recordId);
|
|
1695
|
+
imported++;
|
|
1696
|
+
}
|
|
1697
|
+
return { imported, skipped, recordIds };
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1243
1700
|
// src/blobs/blob-compaction.ts
|
|
1244
1701
|
init_crypto();
|
|
1245
1702
|
var BLOB_EVICTION_AUDIT_COLLECTION = "_blob_eviction_audit";
|
|
@@ -1252,6 +1709,7 @@ async function runCompaction(ctx, options = {}) {
|
|
|
1252
1709
|
let evicted = 0;
|
|
1253
1710
|
let records = 0;
|
|
1254
1711
|
let auditEntries = 0;
|
|
1712
|
+
let held = 0;
|
|
1255
1713
|
let collectionsWithPolicy = 0;
|
|
1256
1714
|
outer: for (const collectionName of allCollections) {
|
|
1257
1715
|
if (collectionName.startsWith("_")) continue;
|
|
@@ -1275,6 +1733,10 @@ async function runCompaction(ctx, options = {}) {
|
|
|
1275
1733
|
if (!policy) continue;
|
|
1276
1734
|
const reason = evaluatePolicy(policy, record, slot, now);
|
|
1277
1735
|
if (!reason) continue;
|
|
1736
|
+
if (isHeld(policy, record, now)) {
|
|
1737
|
+
held += 1;
|
|
1738
|
+
continue;
|
|
1739
|
+
}
|
|
1278
1740
|
if (!dryRun) {
|
|
1279
1741
|
await ctx.deleteSlot(collectionName, recordId, slot.name);
|
|
1280
1742
|
await writeAuditEntry(ctx, {
|
|
@@ -1299,9 +1761,32 @@ async function runCompaction(ctx, options = {}) {
|
|
|
1299
1761
|
records,
|
|
1300
1762
|
collections: collectionsWithPolicy,
|
|
1301
1763
|
auditEntries,
|
|
1764
|
+
held,
|
|
1302
1765
|
byCollection
|
|
1303
1766
|
};
|
|
1304
1767
|
}
|
|
1768
|
+
function isHeld(policy, record, now) {
|
|
1769
|
+
if (policy.legalHold) {
|
|
1770
|
+
try {
|
|
1771
|
+
if (policy.legalHold(record)) return true;
|
|
1772
|
+
} catch {
|
|
1773
|
+
return true;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
if (policy.retainUntil) {
|
|
1777
|
+
try {
|
|
1778
|
+
const until = policy.retainUntil(record);
|
|
1779
|
+
if (until !== null && until !== void 0) {
|
|
1780
|
+
const t = until instanceof Date ? until.getTime() : typeof until === "number" ? until : Date.parse(String(until));
|
|
1781
|
+
if (!Number.isFinite(t)) return true;
|
|
1782
|
+
if (t > now.getTime()) return true;
|
|
1783
|
+
}
|
|
1784
|
+
} catch {
|
|
1785
|
+
return true;
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
return false;
|
|
1789
|
+
}
|
|
1305
1790
|
function evaluatePolicy(policy, record, slot, now) {
|
|
1306
1791
|
let ttlTriggered = false;
|
|
1307
1792
|
let predicateTriggered = false;
|
|
@@ -1473,7 +1958,9 @@ function generateBatchId() {
|
|
|
1473
1958
|
createExportBlobsHandle,
|
|
1474
1959
|
detectMagic,
|
|
1475
1960
|
detectMimeType,
|
|
1961
|
+
importExternalObjects,
|
|
1476
1962
|
isPreCompressed,
|
|
1963
|
+
memoryObjectProjection,
|
|
1477
1964
|
runCompaction,
|
|
1478
1965
|
withBlobs
|
|
1479
1966
|
});
|