@noy-db/hub 0.2.0-pre.23 → 0.2.0-pre.24
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.d.cts +3 -3
- package/dist/aggregate/index.d.ts +3 -3
- package/dist/aggregate/index.js +5 -5
- package/dist/attestation/index.cjs.map +1 -1
- package/dist/attestation/index.d.cts +4 -4
- package/dist/attestation/index.d.ts +4 -4
- package/dist/attestation/index.js +6 -6
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +6 -6
- package/dist/blobs/index.d.ts +6 -6
- package/dist/blobs/index.js +6 -6
- package/dist/bundle/index.cjs +617 -1202
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +15 -6
- package/dist/bundle/index.d.ts +15 -6
- package/dist/bundle/index.js +58 -193
- package/dist/bundle/index.js.map +1 -1
- package/dist/{chunk-CQYEDODS.js → chunk-35U5YNRR.js} +3 -3
- package/dist/{chunk-NV4IHBZS.js → chunk-3XJU3OHE.js} +5 -5
- package/dist/{chunk-OTWT6BAJ.js → chunk-4BB4T3O7.js} +12 -2
- package/dist/chunk-4BB4T3O7.js.map +1 -0
- package/dist/{chunk-IVZWHIEK.js → chunk-4HEGG5NJ.js} +5 -5
- package/dist/{chunk-WE2BUQD2.js → chunk-4TCMCCC3.js} +5 -3
- package/dist/{chunk-5YTXYPES.js → chunk-5A2FVGHT.js} +5 -5
- package/dist/{chunk-NSXNXLYM.js → chunk-5GZC2ZM3.js} +2 -2
- package/dist/{chunk-JYNH4FIM.js → chunk-77WF53XY.js} +4 -4
- package/dist/{chunk-O5XKZCUD.js → chunk-7X4EF35A.js} +5 -5
- package/dist/{chunk-SQKAECUL.js → chunk-7ZCTUI26.js} +2 -2
- package/dist/{chunk-J6RGRZOY.js → chunk-AO3QSMCU.js} +2 -2
- package/dist/{chunk-JDCPRJVS.js → chunk-AONK5GCC.js} +4 -4
- package/dist/{chunk-FRRJIUSI.js → chunk-B5CSNGSE.js} +17 -9
- package/dist/chunk-B5CSNGSE.js.map +1 -0
- package/dist/{chunk-IY24WS2P.js → chunk-BCMHJYVT.js} +4 -4
- package/dist/{chunk-IY24WS2P.js.map → chunk-BCMHJYVT.js.map} +1 -1
- package/dist/{chunk-TYMDCIQM.js → chunk-C472BRJ4.js} +4 -4
- package/dist/{chunk-MBXKRHSS.js → chunk-CCNRFAL3.js} +2 -2
- package/dist/{chunk-BZW5IL43.js → chunk-DCA2BDHA.js} +4 -4
- package/dist/{chunk-JBBWALNI.js → chunk-DCICHSRS.js} +2 -2
- package/dist/{chunk-2XA2ZML4.js → chunk-FG6IQ3ZL.js} +3 -3
- package/dist/{chunk-C2RJVZZL.js → chunk-G4GW5VOS.js} +2 -2
- package/dist/{chunk-U2XSUCDF.js → chunk-GEWIFM4J.js} +2 -2
- package/dist/{chunk-TNH5SLCD.js → chunk-HD4QCT2O.js} +2 -2
- package/dist/{chunk-I3IYTUUI.js → chunk-HHJ5DZCZ.js} +3 -3
- package/dist/{chunk-6QAZ5O6X.js → chunk-IEIADIPM.js} +2 -2
- package/dist/{chunk-YPIOFSN3.js → chunk-IHAISFXP.js} +2 -2
- package/dist/{chunk-GJTKMME7.js → chunk-JKM2AVVH.js} +2 -2
- package/dist/{chunk-EYK72OTL.js → chunk-JRMOSIH4.js} +5 -5
- package/dist/chunk-JRMOSIH4.js.map +1 -0
- package/dist/{chunk-S45MDEEF.js → chunk-LMWVNF6X.js} +2 -2
- package/dist/{chunk-TA6HPKWQ.js → chunk-LR7CODVN.js} +1 -1
- package/dist/chunk-LR7CODVN.js.map +1 -0
- package/dist/{chunk-TAMRU7A2.js → chunk-OKV7S356.js} +4 -4
- package/dist/{chunk-HYJMAV53.js → chunk-OWAMTSAI.js} +93 -93
- package/dist/chunk-OWAMTSAI.js.map +1 -0
- package/dist/{chunk-IW4L4X65.js → chunk-P5A4E53B.js} +2 -2
- package/dist/{chunk-JOK73NDT.js → chunk-P7OL22JP.js} +3 -3
- package/dist/{chunk-P65YMN5V.js → chunk-QOXZM3L2.js} +407 -162
- package/dist/chunk-QOXZM3L2.js.map +1 -0
- package/dist/chunk-R43KS34V.js +399 -0
- package/dist/chunk-R43KS34V.js.map +1 -0
- package/dist/{chunk-TGIJTNM3.js → chunk-R5ZECURV.js} +2 -2
- package/dist/{chunk-KOAJ3TZM.js → chunk-RFEXGW3L.js} +2 -2
- package/dist/{chunk-F5ILTHMU.js → chunk-RNQPDV75.js} +5 -5
- package/dist/{chunk-WWVJXBOT.js → chunk-SGM7CK7R.js} +5 -5
- package/dist/{chunk-7MRT7EPB.js → chunk-SOQE5DUV.js} +3 -3
- package/dist/{chunk-F5GWNSE2.js → chunk-TOMSCJRV.js} +3 -3
- package/dist/{chunk-F5GWNSE2.js.map → chunk-TOMSCJRV.js.map} +1 -1
- package/dist/{chunk-ZONKSLF2.js → chunk-TQMQZOMX.js} +2 -2
- package/dist/{chunk-3HNKR65T.js → chunk-U6LTLN7O.js} +3 -3
- package/dist/{chunk-UU6M64HI.js → chunk-UAK2AMO2.js} +4 -4
- package/dist/{chunk-37VGJM3T.js → chunk-WQ3KAGOV.js} +2 -2
- package/dist/{chunk-C6W5KVDV.js → chunk-XC32SZPW.js} +35 -35
- package/dist/chunk-XC32SZPW.js.map +1 -0
- package/dist/{chunk-AI4USDRI.js → chunk-XQO4TAJS.js} +4 -4
- package/dist/{chunk-SQOK5UM6.js → chunk-ZBENTRFS.js} +2 -2
- package/dist/{chunk-6QE4DUYC.js → chunk-ZDITTESU.js} +2 -2
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +5 -5
- package/dist/consent/index.d.ts +5 -5
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-456N7UVX.js → crypto-2LU6XUFF.js} +3 -3
- package/dist/{delegation-DP4COTXB.js → delegation-6ABSJGXV.js} +5 -5
- package/dist/derivations/index.cjs.map +1 -1
- package/dist/derivations/index.d.cts +6 -6
- package/dist/derivations/index.d.ts +6 -6
- package/dist/derivations/index.js +4 -4
- package/dist/{dev-unlock-DzDzLTdZ.d.ts → dev-unlock-BlhRHr6p.d.ts} +1 -1
- package/dist/{dev-unlock-Bw7iBD1D.d.cts → dev-unlock-DURe4IvF.d.cts} +1 -1
- package/dist/{errors-Dkc_fi-S.d.cts → errors-B2tUcRPg.d.cts} +19 -5
- package/dist/{errors-Dkc_fi-S.d.ts → errors-B2tUcRPg.d.ts} +19 -5
- package/dist/executor-JKMSEB34.js +8 -0
- package/dist/executor-UYXSQB4D.js +12 -0
- package/dist/executor-VJSCTBWY.js +8 -0
- package/dist/{fanout-sidecar-YXNAEZ33.js → fanout-sidecar-ZQT4Y7PF.js} +2 -2
- package/dist/forget/index.js +4 -4
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +6 -6
- package/dist/guards/index.d.ts +6 -6
- package/dist/guards/index.js +6 -6
- package/dist/{hash-C52X_-m5.d.cts → hash-CqRZfDZH.d.cts} +1 -1
- package/dist/{hash-DepR-xVc.d.ts → hash-cF4iWaBV.d.ts} +1 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +6 -6
- package/dist/history/index.d.ts +6 -6
- package/dist/history/index.js +5 -5
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +5 -5
- package/dist/i18n/index.d.ts +5 -5
- package/dist/i18n/index.js +6 -6
- package/dist/index-B8MoIS7B.d.ts +70 -0
- package/dist/{index-Bm9hIY7t.d.ts → index-BLff_E35.d.ts} +2 -2
- package/dist/{index-tZqVB9g5.d.cts → index-BthnP2MA.d.cts} +2 -2
- package/dist/index-da0M3NnR.d.cts +70 -0
- package/dist/index.cjs +25907 -25557
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +135 -80
- package/dist/index.d.ts +135 -80
- package/dist/index.js +78 -51
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.js +4 -4
- package/dist/issue-KLRMW5DH.js +12 -0
- package/dist/kernel/index.cjs +657 -0
- package/dist/kernel/index.cjs.map +1 -0
- package/dist/kernel/index.d.cts +11 -0
- package/dist/kernel/index.d.ts +11 -0
- package/dist/kernel/index.js +40 -0
- package/dist/{ledger-I7JUYP4L.js → ledger-VOS2X3WJ.js} +5 -5
- package/dist/materialized-views/index.cjs.map +1 -1
- package/dist/materialized-views/index.d.cts +6 -6
- package/dist/materialized-views/index.d.ts +6 -6
- package/dist/materialized-views/index.js +8 -8
- package/dist/{mime-magic-Dejetix_.d.ts → mime-magic-BswIvWkR.d.ts} +1 -1
- package/dist/{mime-magic-Cxf9B_Dm.d.cts → mime-magic-CCrP-iXJ.d.cts} +1 -1
- package/dist/{ulid-Bg-IBJyA.d.cts → multi-bundle-6s5nKAZX.d.ts} +114 -58
- package/dist/{ulid-Dwt3JEcy.d.ts → multi-bundle-WhYiJEgV.d.cts} +114 -58
- package/dist/noydb-2PI2ZBX6.js +38 -0
- package/dist/overlay-views/index.cjs.map +1 -1
- package/dist/overlay-views/index.d.cts +6 -6
- package/dist/overlay-views/index.d.ts +6 -6
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +5 -5
- package/dist/periods/index.d.ts +5 -5
- package/dist/periods/index.js +5 -5
- package/dist/{public-envelope-5XRTUNKF.js → public-envelope-IJJMWSTJ.js} +4 -4
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +3 -3
- package/dist/query/index.d.ts +3 -3
- package/dist/query/index.js +7 -7
- package/dist/registry-GAIFVWXF.js +8 -0
- package/dist/registry-J77ZUQ7G.js +8 -0
- package/dist/{registry-NWHOLD5M.js → registry-JGEVJ6YC.js} +3 -3
- package/dist/{revoke-5IEK22KT.js → revoke-WUY4AYRJ.js} +6 -6
- package/dist/sealed-record/index.cjs.map +1 -1
- package/dist/sealed-record/index.d.cts +1 -1
- package/dist/sealed-record/index.d.ts +1 -1
- package/dist/sealed-record/index.js +2 -2
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +6 -6
- package/dist/session/index.d.ts +6 -6
- package/dist/session/index.js +3 -3
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +5 -5
- package/dist/shadow/index.d.ts +5 -5
- package/dist/shadow/index.js +2 -2
- package/dist/{signer-I6YARZQA.js → signer-UJF3CFDC.js} +5 -5
- package/dist/snapshots/index.cjs.map +1 -1
- package/dist/snapshots/index.d.cts +5 -5
- package/dist/snapshots/index.d.ts +5 -5
- package/dist/snapshots/index.js +4 -4
- package/dist/{stale-CPESGAPL.js → stale-PW6VBGSP.js} +2 -2
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +5 -5
- package/dist/store/index.d.ts +5 -5
- package/dist/store/index.js +2 -2
- package/dist/{strategy-WtB-jXYv.d.cts → strategy-BWmgRPA2.d.cts} +1 -1
- package/dist/{strategy-54eIwox5.d.ts → strategy-D47TC5X6.d.ts} +1 -1
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +4 -4
- package/dist/sync/index.d.ts +4 -4
- package/dist/sync/index.js +4 -4
- package/dist/team/index.cjs +10 -3
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +5 -5
- package/dist/team/index.d.ts +5 -5
- package/dist/team/index.js +8 -8
- package/dist/{transition-guard-BcLyTGYq.d.cts → transition-guard-C3NxfVKk.d.cts} +1 -1
- package/dist/{transition-guard-Ctxapq1b.d.ts → transition-guard-CQH5263l.d.ts} +1 -1
- package/dist/tx/index.cjs +1 -1
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +5 -5
- package/dist/tx/index.d.ts +5 -5
- package/dist/tx/index.js +3 -3
- package/dist/{types-DONgts0n.d.ts → types-BGRX6sPT.d.ts} +288 -578
- package/dist/{types-Bhs2i_Ll.d.cts → types-COQ6qJZh.d.cts} +288 -578
- package/dist/ulid-DRH25k3y.d.cts +66 -0
- package/dist/ulid-DRH25k3y.d.ts +66 -0
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.js +1 -1
- package/dist/{with-materialized-view-CyVLOr09.d.ts → with-materialized-view-Cj-6fuav.d.ts} +1 -1
- package/dist/{with-materialized-view-BYb3p9wT.d.cts → with-materialized-view-D4U-KrBH.d.cts} +1 -1
- package/dist/{with-overlayed-view-LGrQ984e.d.cts → with-overlayed-view-BKjdUPRx.d.cts} +1 -1
- package/dist/{with-overlayed-view-BhLRxqwI.d.ts → with-overlayed-view-COp_7EEy.d.ts} +1 -1
- package/dist/{with-rollup-CO8ibRcK.d.ts → with-rollup-B1_ZjG02.d.ts} +1 -1
- package/dist/{with-rollup-Bj8c7ttB.d.cts → with-rollup-C-Bok_o2.d.cts} +1 -1
- package/package.json +13 -3
- package/dist/chunk-C6W5KVDV.js.map +0 -1
- package/dist/chunk-EYK72OTL.js.map +0 -1
- package/dist/chunk-FRRJIUSI.js.map +0 -1
- package/dist/chunk-HYJMAV53.js.map +0 -1
- package/dist/chunk-JTI57WRT.js +0 -164
- package/dist/chunk-JTI57WRT.js.map +0 -1
- package/dist/chunk-OTWT6BAJ.js.map +0 -1
- package/dist/chunk-P65YMN5V.js.map +0 -1
- package/dist/chunk-TA6HPKWQ.js.map +0 -1
- package/dist/chunk-ZC7J6ZYV.js +0 -7
- package/dist/chunk-ZC7J6ZYV.js.map +0 -1
- package/dist/executor-4IEW4KG5.js +0 -8
- package/dist/executor-KYJCJCIN.js +0 -12
- package/dist/executor-W7VIBOBZ.js +0 -8
- package/dist/issue-JXC6T2QR.js +0 -12
- package/dist/noydb-VGR2HLDB.js +0 -39
- package/dist/registry-ATRHOG5B.js +0 -8
- package/dist/registry-LEHB26TY.js +0 -8
- package/dist/state-vault-JR3CFGNP.js +0 -14
- package/dist/vault-group-BB246VIM.js +0 -804
- package/dist/vault-group-BB246VIM.js.map +0 -1
- /package/dist/{chunk-CQYEDODS.js.map → chunk-35U5YNRR.js.map} +0 -0
- /package/dist/{chunk-NV4IHBZS.js.map → chunk-3XJU3OHE.js.map} +0 -0
- /package/dist/{chunk-IVZWHIEK.js.map → chunk-4HEGG5NJ.js.map} +0 -0
- /package/dist/{chunk-WE2BUQD2.js.map → chunk-4TCMCCC3.js.map} +0 -0
- /package/dist/{chunk-5YTXYPES.js.map → chunk-5A2FVGHT.js.map} +0 -0
- /package/dist/{chunk-NSXNXLYM.js.map → chunk-5GZC2ZM3.js.map} +0 -0
- /package/dist/{chunk-JYNH4FIM.js.map → chunk-77WF53XY.js.map} +0 -0
- /package/dist/{chunk-O5XKZCUD.js.map → chunk-7X4EF35A.js.map} +0 -0
- /package/dist/{chunk-SQKAECUL.js.map → chunk-7ZCTUI26.js.map} +0 -0
- /package/dist/{chunk-J6RGRZOY.js.map → chunk-AO3QSMCU.js.map} +0 -0
- /package/dist/{chunk-JDCPRJVS.js.map → chunk-AONK5GCC.js.map} +0 -0
- /package/dist/{chunk-TYMDCIQM.js.map → chunk-C472BRJ4.js.map} +0 -0
- /package/dist/{chunk-MBXKRHSS.js.map → chunk-CCNRFAL3.js.map} +0 -0
- /package/dist/{chunk-BZW5IL43.js.map → chunk-DCA2BDHA.js.map} +0 -0
- /package/dist/{chunk-JBBWALNI.js.map → chunk-DCICHSRS.js.map} +0 -0
- /package/dist/{chunk-2XA2ZML4.js.map → chunk-FG6IQ3ZL.js.map} +0 -0
- /package/dist/{chunk-C2RJVZZL.js.map → chunk-G4GW5VOS.js.map} +0 -0
- /package/dist/{chunk-U2XSUCDF.js.map → chunk-GEWIFM4J.js.map} +0 -0
- /package/dist/{chunk-TNH5SLCD.js.map → chunk-HD4QCT2O.js.map} +0 -0
- /package/dist/{chunk-I3IYTUUI.js.map → chunk-HHJ5DZCZ.js.map} +0 -0
- /package/dist/{chunk-6QAZ5O6X.js.map → chunk-IEIADIPM.js.map} +0 -0
- /package/dist/{chunk-YPIOFSN3.js.map → chunk-IHAISFXP.js.map} +0 -0
- /package/dist/{chunk-GJTKMME7.js.map → chunk-JKM2AVVH.js.map} +0 -0
- /package/dist/{chunk-S45MDEEF.js.map → chunk-LMWVNF6X.js.map} +0 -0
- /package/dist/{chunk-TAMRU7A2.js.map → chunk-OKV7S356.js.map} +0 -0
- /package/dist/{chunk-IW4L4X65.js.map → chunk-P5A4E53B.js.map} +0 -0
- /package/dist/{chunk-JOK73NDT.js.map → chunk-P7OL22JP.js.map} +0 -0
- /package/dist/{chunk-TGIJTNM3.js.map → chunk-R5ZECURV.js.map} +0 -0
- /package/dist/{chunk-KOAJ3TZM.js.map → chunk-RFEXGW3L.js.map} +0 -0
- /package/dist/{chunk-F5ILTHMU.js.map → chunk-RNQPDV75.js.map} +0 -0
- /package/dist/{chunk-WWVJXBOT.js.map → chunk-SGM7CK7R.js.map} +0 -0
- /package/dist/{chunk-7MRT7EPB.js.map → chunk-SOQE5DUV.js.map} +0 -0
- /package/dist/{chunk-ZONKSLF2.js.map → chunk-TQMQZOMX.js.map} +0 -0
- /package/dist/{chunk-3HNKR65T.js.map → chunk-U6LTLN7O.js.map} +0 -0
- /package/dist/{chunk-UU6M64HI.js.map → chunk-UAK2AMO2.js.map} +0 -0
- /package/dist/{chunk-37VGJM3T.js.map → chunk-WQ3KAGOV.js.map} +0 -0
- /package/dist/{chunk-AI4USDRI.js.map → chunk-XQO4TAJS.js.map} +0 -0
- /package/dist/{chunk-SQOK5UM6.js.map → chunk-ZBENTRFS.js.map} +0 -0
- /package/dist/{chunk-6QE4DUYC.js.map → chunk-ZDITTESU.js.map} +0 -0
- /package/dist/{crypto-456N7UVX.js.map → crypto-2LU6XUFF.js.map} +0 -0
- /package/dist/{delegation-DP4COTXB.js.map → delegation-6ABSJGXV.js.map} +0 -0
- /package/dist/{executor-4IEW4KG5.js.map → executor-JKMSEB34.js.map} +0 -0
- /package/dist/{executor-KYJCJCIN.js.map → executor-UYXSQB4D.js.map} +0 -0
- /package/dist/{executor-W7VIBOBZ.js.map → executor-VJSCTBWY.js.map} +0 -0
- /package/dist/{fanout-sidecar-YXNAEZ33.js.map → fanout-sidecar-ZQT4Y7PF.js.map} +0 -0
- /package/dist/{issue-JXC6T2QR.js.map → issue-KLRMW5DH.js.map} +0 -0
- /package/dist/{ledger-I7JUYP4L.js.map → kernel/index.js.map} +0 -0
- /package/dist/{noydb-VGR2HLDB.js.map → ledger-VOS2X3WJ.js.map} +0 -0
- /package/dist/{public-envelope-5XRTUNKF.js.map → noydb-2PI2ZBX6.js.map} +0 -0
- /package/dist/{registry-ATRHOG5B.js.map → public-envelope-IJJMWSTJ.js.map} +0 -0
- /package/dist/{registry-LEHB26TY.js.map → registry-GAIFVWXF.js.map} +0 -0
- /package/dist/{registry-NWHOLD5M.js.map → registry-J77ZUQ7G.js.map} +0 -0
- /package/dist/{revoke-5IEK22KT.js.map → registry-JGEVJ6YC.js.map} +0 -0
- /package/dist/{signer-I6YARZQA.js.map → revoke-WUY4AYRJ.js.map} +0 -0
- /package/dist/{stale-CPESGAPL.js.map → signer-UJF3CFDC.js.map} +0 -0
- /package/dist/{state-vault-JR3CFGNP.js.map → stale-PW6VBGSP.js.map} +0 -0
package/dist/bundle/index.cjs
CHANGED
|
@@ -203,7 +203,7 @@ var init_format = __esm({
|
|
|
203
203
|
});
|
|
204
204
|
|
|
205
205
|
// src/errors.ts
|
|
206
|
-
var NoydbError, DebugPlaintextError, DebugReservedFieldError, DecryptionError, TamperedError, InvalidKeyError, KeyringCorruptError, NoAccessError, ReadOnlyError, PermissionDeniedError, ExportCapabilityError, KeyringExpiredError, ImportCapabilityError, StoreCapabilityError, PrivilegeEscalationError,
|
|
206
|
+
var NoydbError, DebugPlaintextError, DebugReservedFieldError, DecryptionError, TamperedError, InvalidKeyError, KeyringCorruptError, NoAccessError, ReadOnlyError, PermissionDeniedError, ExportCapabilityError, KeyringExpiredError, ImportCapabilityError, StoreCapabilityError, PrivilegeEscalationError, FieldFrozenError, InvariantError, AmendmentForbiddenError, TierNotGrantedError, ElevationExpiredError, AlreadyElevatedError, TierDemoteDeniedError, DelegationTargetMissingError, ConflictError, LedgerContentionError, SequenceContentionError, SequenceOfflineError, NumberingUncertaintyError, BundleVersionConflictError, ValidationError, SchemaValidationError, SchemaUpdateError, SchemaFenceError, MigrationRequiredError, QuiesceTimeoutError, GroupCardinalityError, IndexRequiredError, UniqueConstraintError, UnsupportedIndexOptionError, IndexWriteFailureError, BundleIntegrityError, BundleSealMismatchError, ReservedCollectionNameError, LocaleNotSpecifiedError, StaticDictReadonlyError, UnknownDictCodeError, TranslatorNotConfiguredError, BackupLedgerError, BackupCorruptedError, PartitionExtractionError, TransferSealError, AdoptionStateError, AttestationError, JoinTooLargeError, CrossJoinTooLargeError, CrossJoinSourceUnknownError, DanglingReferenceError, DerivationCycleError, DerivationOutputShapeError, DerivationCapExceededError, MaterializedViewCycleError, MaterializedViewSourceUnknownError, MaterializedViewTooLargeError, OverlayBaseIsVirtualError, OverlayCollectionUnavailableError, OverlayNameCollisionError, OverlayIdMismatchError, FederationMovedError, ForgetStrategyNotConfiguredError, RecordCekNotFoundError;
|
|
207
207
|
var init_errors = __esm({
|
|
208
208
|
"src/errors.ts"() {
|
|
209
209
|
"use strict";
|
|
@@ -342,18 +342,6 @@ var init_errors = __esm({
|
|
|
342
342
|
this.offendingCollection = offendingCollection;
|
|
343
343
|
}
|
|
344
344
|
};
|
|
345
|
-
ReservedVaultNameError = class extends NoydbError {
|
|
346
|
-
/** The rejected vault name. */
|
|
347
|
-
vaultName;
|
|
348
|
-
constructor(vaultName) {
|
|
349
|
-
super(
|
|
350
|
-
"RESERVED_VAULT_NAME",
|
|
351
|
-
`"${vaultName}" is a reserved internal vault name and cannot be used as a group name or partition key`
|
|
352
|
-
);
|
|
353
|
-
this.name = "ReservedVaultNameError";
|
|
354
|
-
this.vaultName = vaultName;
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
345
|
FieldFrozenError = class extends NoydbError {
|
|
358
346
|
collection;
|
|
359
347
|
id;
|
|
@@ -941,58 +929,13 @@ Resolutions:
|
|
|
941
929
|
this.expected = expected;
|
|
942
930
|
}
|
|
943
931
|
};
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
constructor(partitionKey, groupName) {
|
|
947
|
-
super(
|
|
948
|
-
"SHARD_UNKNOWN",
|
|
949
|
-
`No shard for partition key "${partitionKey}" in vault group "${groupName}" and autoCreate is disabled. Call group.createShard(${JSON.stringify(partitionKey)}) first, or enable sharding.autoCreate.`
|
|
950
|
-
);
|
|
951
|
-
this.name = "UnknownShardError";
|
|
952
|
-
this.partitionKey = partitionKey;
|
|
953
|
-
}
|
|
954
|
-
};
|
|
955
|
-
ShardProvisioningError = class extends NoydbError {
|
|
956
|
-
vaultId;
|
|
957
|
-
constructor(vaultId, partitionKey) {
|
|
958
|
-
super(
|
|
959
|
-
"SHARD_PROVISIONING",
|
|
960
|
-
`Registry has a row for partition "${partitionKey}" (vault "${vaultId}") but that vault is not provisioned in the store. Refusing to recreate it \u2014 the registry and store have diverged. Investigate before retrying.`
|
|
961
|
-
);
|
|
962
|
-
this.name = "ShardProvisioningError";
|
|
963
|
-
this.vaultId = vaultId;
|
|
964
|
-
}
|
|
965
|
-
};
|
|
966
|
-
DataResidencyError = class extends NoydbError {
|
|
967
|
-
vaultId;
|
|
968
|
-
requiredRegion;
|
|
969
|
-
backendRegion;
|
|
970
|
-
constructor(vaultId, requiredRegion, backendRegion) {
|
|
971
|
-
super(
|
|
972
|
-
"DATA_RESIDENCY",
|
|
973
|
-
`Shard "${vaultId}" requires region "${requiredRegion}" but its placement backend declares region ${backendRegion === void 0 ? "(none)" : `"${backendRegion}"`}. Refusing to provision \u2014 route this shard to a region-correct backend via routeStore({ vaultRoutes }) (e.g. a region-encoded partition key) before retrying.`
|
|
974
|
-
);
|
|
975
|
-
this.name = "DataResidencyError";
|
|
976
|
-
this.vaultId = vaultId;
|
|
977
|
-
this.requiredRegion = requiredRegion;
|
|
978
|
-
this.backendRegion = backendRegion;
|
|
979
|
-
}
|
|
980
|
-
};
|
|
981
|
-
CrossShardJoinError = class extends NoydbError {
|
|
982
|
-
constructor(message) {
|
|
983
|
-
super("CROSS_SHARD_JOIN", message);
|
|
984
|
-
this.name = "CrossShardJoinError";
|
|
985
|
-
}
|
|
986
|
-
};
|
|
987
|
-
VaultTemplateNotFoundError = class extends NoydbError {
|
|
988
|
-
templateName;
|
|
989
|
-
constructor(templateName) {
|
|
932
|
+
FederationMovedError = class extends NoydbError {
|
|
933
|
+
constructor(api) {
|
|
990
934
|
super(
|
|
991
|
-
"
|
|
992
|
-
|
|
935
|
+
"FEDERATION_MOVED",
|
|
936
|
+
`${api} has moved to @klum-db/lobby. Install @klum-db/lobby, then: import { createLobby } from '@klum-db/lobby'; const lobby = createLobby(db); await lobby.${api}(...)`
|
|
993
937
|
);
|
|
994
|
-
this.name = "
|
|
995
|
-
this.templateName = templateName;
|
|
938
|
+
this.name = "FederationMovedError";
|
|
996
939
|
}
|
|
997
940
|
};
|
|
998
941
|
ForgetStrategyNotConfiguredError = class extends NoydbError {
|
|
@@ -1730,6 +1673,17 @@ function parsePrefixAndHeader(bytes) {
|
|
|
1730
1673
|
function readNoydbBundleHeader(bytes) {
|
|
1731
1674
|
return parsePrefixAndHeader(bytes).header;
|
|
1732
1675
|
}
|
|
1676
|
+
function readNoydbBundlePublicEnvelope(bytes, opts = {}) {
|
|
1677
|
+
const header = parsePrefixAndHeader(bytes).header;
|
|
1678
|
+
const env = header.publicEnvelope;
|
|
1679
|
+
if (!env) return void 0;
|
|
1680
|
+
if (opts.locale === void 0) return env;
|
|
1681
|
+
return {
|
|
1682
|
+
...env,
|
|
1683
|
+
...env.name !== void 0 ? { name: pickLocale(env.name, opts.locale, env.defaultLocale) } : {},
|
|
1684
|
+
...env.description !== void 0 ? { description: pickLocale(env.description, opts.locale, env.defaultLocale) } : {}
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1733
1687
|
async function readNoydbBundle(bytes, opts = {}) {
|
|
1734
1688
|
const { header, bodyOffset, algo } = parsePrefixAndHeader(bytes);
|
|
1735
1689
|
const body = bytes.slice(bodyOffset);
|
|
@@ -1771,6 +1725,7 @@ var init_bundle = __esm({
|
|
|
1771
1725
|
"use strict";
|
|
1772
1726
|
init_format();
|
|
1773
1727
|
init_errors();
|
|
1728
|
+
init_storage();
|
|
1774
1729
|
cachedBrotliSupport = null;
|
|
1775
1730
|
}
|
|
1776
1731
|
});
|
|
@@ -2518,12 +2473,14 @@ var init_user_envelope = __esm({
|
|
|
2518
2473
|
// src/team/keyring.ts
|
|
2519
2474
|
function canGrant(callerRole, targetRole) {
|
|
2520
2475
|
if (callerRole === "owner") return true;
|
|
2476
|
+
if (callerRole === "custodian") return false;
|
|
2521
2477
|
if (callerRole === "admin") return ADMIN_GRANTABLE_TARGETS.includes(targetRole);
|
|
2522
2478
|
return false;
|
|
2523
2479
|
}
|
|
2524
2480
|
function canRevoke(callerRole, targetRole) {
|
|
2525
2481
|
if (targetRole === "owner") return false;
|
|
2526
2482
|
if (callerRole === "owner") return true;
|
|
2483
|
+
if (callerRole === "custodian") return false;
|
|
2527
2484
|
if (callerRole === "admin") return ADMIN_GRANTABLE_TARGETS.includes(targetRole);
|
|
2528
2485
|
return false;
|
|
2529
2486
|
}
|
|
@@ -2687,7 +2644,7 @@ async function grant(adapter, vault, callerKeyring, options) {
|
|
|
2687
2644
|
wrappedDeks[collName] = await wrapKey(dek, newKek);
|
|
2688
2645
|
}
|
|
2689
2646
|
}
|
|
2690
|
-
if (options.role === "owner" || options.role === "admin" || options.role === "viewer") {
|
|
2647
|
+
if (options.role === "owner" || options.role === "admin" || options.role === "custodian" || options.role === "viewer") {
|
|
2691
2648
|
for (const [collName, dek] of callerKeyring.deks) {
|
|
2692
2649
|
if (!(collName in wrappedDeks)) {
|
|
2693
2650
|
wrappedDeks[collName] = await wrapKey(dek, newKek);
|
|
@@ -2835,6 +2792,11 @@ async function updateKeyringIdentity(adapter, vault, callerKeyring, options) {
|
|
|
2835
2792
|
await writeKeyringFile(adapter, vault, options.userId, next);
|
|
2836
2793
|
}
|
|
2837
2794
|
async function rotateKeys(adapter, vault, callerKeyring, collections) {
|
|
2795
|
+
if (callerKeyring.role === "custodian") {
|
|
2796
|
+
throw new PermissionDeniedError(
|
|
2797
|
+
"custodian cannot rotate keys (FR-6: re-key is an owner-only meta-capability; use the Deed owner)"
|
|
2798
|
+
);
|
|
2799
|
+
}
|
|
2838
2800
|
const newDeks = /* @__PURE__ */ new Map();
|
|
2839
2801
|
for (const collName of collections) {
|
|
2840
2802
|
newDeks.set(collName, await generateDEK());
|
|
@@ -2944,7 +2906,7 @@ async function buildRecipientKeyringFile(callerKeyring, recipient) {
|
|
|
2944
2906
|
wrappedDeks[collName] = await wrapKey(dek, newKek);
|
|
2945
2907
|
}
|
|
2946
2908
|
}
|
|
2947
|
-
if (role === "owner" || role === "admin" || role === "viewer") {
|
|
2909
|
+
if (role === "owner" || role === "admin" || role === "custodian" || role === "viewer") {
|
|
2948
2910
|
for (const [collName, dek] of callerKeyring.deks) {
|
|
2949
2911
|
if (!(collName in wrappedDeks)) {
|
|
2950
2912
|
wrappedDeks[collName] = await wrapKey(dek, newKek);
|
|
@@ -3018,12 +2980,13 @@ async function ensureCollectionDEK(adapter, vault, keyring) {
|
|
|
3018
2980
|
};
|
|
3019
2981
|
}
|
|
3020
2982
|
function hasWritePermission(keyring, collectionName) {
|
|
3021
|
-
if (keyring.role === "owner" || keyring.role === "admin") return true;
|
|
2983
|
+
if (keyring.role === "owner" || keyring.role === "admin" || keyring.role === "custodian") return true;
|
|
3022
2984
|
if (keyring.role === "viewer" || keyring.role === "client") return false;
|
|
3023
2985
|
return keyring.permissions[collectionName] === "rw";
|
|
3024
2986
|
}
|
|
3025
2987
|
function hasAccess(keyring, collectionName) {
|
|
3026
|
-
if (keyring.role === "owner" || keyring.role === "admin" || keyring.role === "viewer")
|
|
2988
|
+
if (keyring.role === "owner" || keyring.role === "admin" || keyring.role === "custodian" || keyring.role === "viewer")
|
|
2989
|
+
return true;
|
|
3027
2990
|
return collectionName in keyring.permissions;
|
|
3028
2991
|
}
|
|
3029
2992
|
async function persistKeyring(adapter, vault, keyring) {
|
|
@@ -3075,7 +3038,7 @@ function hasImportCapability(keyring, tier, format) {
|
|
|
3075
3038
|
return cap?.bundle === true;
|
|
3076
3039
|
}
|
|
3077
3040
|
function resolvePermissions(role, explicit) {
|
|
3078
|
-
if (role === "owner" || role === "admin" || role === "viewer") return {};
|
|
3041
|
+
if (role === "owner" || role === "admin" || role === "custodian" || role === "viewer") return {};
|
|
3079
3042
|
return explicit ?? {};
|
|
3080
3043
|
}
|
|
3081
3044
|
async function writeKeyringFile(adapter, vault, userId, keyringFile) {
|
|
@@ -3781,15 +3744,6 @@ var init_store = __esm({
|
|
|
3781
3744
|
}
|
|
3782
3745
|
});
|
|
3783
3746
|
|
|
3784
|
-
// src/federation/constants.ts
|
|
3785
|
-
var STATE_VAULT_NAME;
|
|
3786
|
-
var init_constants2 = __esm({
|
|
3787
|
-
"src/federation/constants.ts"() {
|
|
3788
|
-
"use strict";
|
|
3789
|
-
STATE_VAULT_NAME = "__noydb_state__";
|
|
3790
|
-
}
|
|
3791
|
-
});
|
|
3792
|
-
|
|
3793
3747
|
// src/policy/errors.ts
|
|
3794
3748
|
var PolicyDeniedError, RecoveryNotEnrolledError, ManagedRecoveryNotEnrolledError, RecoveryProfileNotImplementedError;
|
|
3795
3749
|
var init_errors2 = __esm({
|
|
@@ -4992,10 +4946,10 @@ function shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAf
|
|
|
4992
4946
|
}
|
|
4993
4947
|
}
|
|
4994
4948
|
function parseToScaledInt(input, scale, rounding) {
|
|
4995
|
-
const
|
|
4996
|
-
if (
|
|
4997
|
-
const negative =
|
|
4998
|
-
const unsigned = negative ?
|
|
4949
|
+
const canonical = toCanonicalDecimalString(input);
|
|
4950
|
+
if (canonical === null) return { ok: false, reason: "nonfinite" };
|
|
4951
|
+
const negative = canonical.startsWith("-");
|
|
4952
|
+
const unsigned = negative ? canonical.slice(1) : canonical;
|
|
4999
4953
|
const dot = unsigned.indexOf(".");
|
|
5000
4954
|
const intPart = dot === -1 ? unsigned : unsigned.slice(0, dot);
|
|
5001
4955
|
const fracPart = dot === -1 ? "" : unsigned.slice(dot + 1);
|
|
@@ -5491,7 +5445,7 @@ function dekKey(collection, tier) {
|
|
|
5491
5445
|
}
|
|
5492
5446
|
function assertTierAccess(keyring, collection, tier) {
|
|
5493
5447
|
if (tier <= 0) return;
|
|
5494
|
-
if (keyring.role === "owner" || keyring.role === "admin") return;
|
|
5448
|
+
if (keyring.role === "owner" || keyring.role === "admin" || keyring.role === "custodian") return;
|
|
5495
5449
|
if (!keyring.deks.has(dekKey(collection, tier))) {
|
|
5496
5450
|
throw new TierNotGrantedError(collection, tier);
|
|
5497
5451
|
}
|
|
@@ -6562,7 +6516,7 @@ function serializeClause(clause) {
|
|
|
6562
6516
|
}
|
|
6563
6517
|
function canonicalCtxHash(ctx) {
|
|
6564
6518
|
if (ctx === void 0) return "";
|
|
6565
|
-
const
|
|
6519
|
+
const canonical = JSON.stringify(ctx, (_key, value) => {
|
|
6566
6520
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
6567
6521
|
const sorted = {};
|
|
6568
6522
|
for (const k of Object.keys(value).sort()) {
|
|
@@ -6573,8 +6527,8 @@ function canonicalCtxHash(ctx) {
|
|
|
6573
6527
|
return value;
|
|
6574
6528
|
});
|
|
6575
6529
|
let h = 5381;
|
|
6576
|
-
for (let i = 0; i <
|
|
6577
|
-
h = (h << 5) + h ^
|
|
6530
|
+
for (let i = 0; i < canonical.length; i++) {
|
|
6531
|
+
h = (h << 5) + h ^ canonical.charCodeAt(i);
|
|
6578
6532
|
}
|
|
6579
6533
|
return (h >>> 0).toString(16).padStart(8, "0");
|
|
6580
6534
|
}
|
|
@@ -7283,29 +7237,6 @@ var init_builder = __esm({
|
|
|
7283
7237
|
}
|
|
7284
7238
|
});
|
|
7285
7239
|
|
|
7286
|
-
// src/aggregate/aggregation.ts
|
|
7287
|
-
function reduceRecords(records, spec) {
|
|
7288
|
-
const state = {};
|
|
7289
|
-
for (const key of Object.keys(spec)) {
|
|
7290
|
-
state[key] = spec[key].init();
|
|
7291
|
-
}
|
|
7292
|
-
for (const record of records) {
|
|
7293
|
-
for (const key of Object.keys(spec)) {
|
|
7294
|
-
state[key] = spec[key].step(state[key], record);
|
|
7295
|
-
}
|
|
7296
|
-
}
|
|
7297
|
-
const result = {};
|
|
7298
|
-
for (const key of Object.keys(spec)) {
|
|
7299
|
-
result[key] = spec[key].finalize(state[key]);
|
|
7300
|
-
}
|
|
7301
|
-
return result;
|
|
7302
|
-
}
|
|
7303
|
-
var init_aggregation = __esm({
|
|
7304
|
-
"src/aggregate/aggregation.ts"() {
|
|
7305
|
-
"use strict";
|
|
7306
|
-
}
|
|
7307
|
-
});
|
|
7308
|
-
|
|
7309
7240
|
// src/aggregate/canonical-key.ts
|
|
7310
7241
|
function canonicalGroupKey(fields, row) {
|
|
7311
7242
|
const sorted = [...fields].sort();
|
|
@@ -8534,7 +8465,7 @@ var init_transaction = __esm({
|
|
|
8534
8465
|
const v = this._db.vault(name);
|
|
8535
8466
|
if (this._amendment && !this._amendmentVaults.has(name)) {
|
|
8536
8467
|
const role = v.role;
|
|
8537
|
-
if (role !== "admin" && role !== "owner") {
|
|
8468
|
+
if (role !== "admin" && role !== "owner" && role !== "custodian") {
|
|
8538
8469
|
throw new AmendmentForbiddenError(v.userId, role);
|
|
8539
8470
|
}
|
|
8540
8471
|
const reg = v._getGuardRegistry();
|
|
@@ -8904,12 +8835,12 @@ var init_dependency_analyzer = __esm({
|
|
|
8904
8835
|
|
|
8905
8836
|
// src/materialized-views/query-hash.ts
|
|
8906
8837
|
async function computeQueryHash(mvName, dependencies, queryPlanSummary) {
|
|
8907
|
-
const
|
|
8838
|
+
const canonical = JSON.stringify({
|
|
8908
8839
|
mvName,
|
|
8909
8840
|
dependencies: [...dependencies].sort(),
|
|
8910
8841
|
queryPlanSummary
|
|
8911
8842
|
});
|
|
8912
|
-
const bytes = new TextEncoder().encode(
|
|
8843
|
+
const bytes = new TextEncoder().encode(canonical);
|
|
8913
8844
|
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
8914
8845
|
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
8915
8846
|
}
|
|
@@ -9722,6 +9653,13 @@ var init_collection = __esm({
|
|
|
9722
9653
|
* flag) still decrypts CEK records.
|
|
9723
9654
|
*/
|
|
9724
9655
|
perRecordCek;
|
|
9656
|
+
/**
|
|
9657
|
+
* Per-record provenance opt-in (`provenance: true`). When set, `put()` calls
|
|
9658
|
+
* that supply a `source` option stamp `_source`/`_sourceTs` onto the
|
|
9659
|
+
* unencrypted envelope metadata. Off by default — zero cost for collections
|
|
9660
|
+
* that don't need lineage tracking (FR-5, #445).
|
|
9661
|
+
*/
|
|
9662
|
+
provenance;
|
|
9725
9663
|
/**
|
|
9726
9664
|
* Session-scoped `(id) → CEK` cache for this collection. Lets updates
|
|
9727
9665
|
* reuse a record's stable CEK and lets repeated reads skip the AES-KW
|
|
@@ -9881,6 +9819,7 @@ var init_collection = __esm({
|
|
|
9881
9819
|
}
|
|
9882
9820
|
this.perRecordCek = opts.perRecordKeys === true;
|
|
9883
9821
|
this.cekCache = this.perRecordCek ? new Lru({ maxRecords: 4096 }) : null;
|
|
9822
|
+
this.provenance = opts.provenance === true;
|
|
9884
9823
|
if (opts.crdt && opts.onRegisterConflictResolver) {
|
|
9885
9824
|
const crdtMode = opts.crdt;
|
|
9886
9825
|
const crdtResolver = async (id, local, remote) => {
|
|
@@ -10068,6 +10007,33 @@ var init_collection = __esm({
|
|
|
10068
10007
|
if (json === null) return null;
|
|
10069
10008
|
return JSON.parse(json);
|
|
10070
10009
|
}
|
|
10010
|
+
/**
|
|
10011
|
+
* Read a record's unencrypted envelope metadata (version, timestamps,
|
|
10012
|
+
* provenance) without decrypting the body.
|
|
10013
|
+
*
|
|
10014
|
+
* Returns `null` when no envelope exists for `id` (record absent or never
|
|
10015
|
+
* written). Only `_source`/`_sourceTs` fields are populated when the
|
|
10016
|
+
* collection was opened with `provenance: true` AND the record was written
|
|
10017
|
+
* with a `source` option — but this method works on any collection because
|
|
10018
|
+
* it reads the raw envelope directly.
|
|
10019
|
+
*
|
|
10020
|
+
* @returns `{ version, timestamp, by?, source?, sourceTs? }` or `null`.
|
|
10021
|
+
*
|
|
10022
|
+
* @example
|
|
10023
|
+
* const meta = await clients.getMetadata('c1')
|
|
10024
|
+
* if (meta) console.log(meta.source, meta.timestamp)
|
|
10025
|
+
*/
|
|
10026
|
+
async getMetadata(id) {
|
|
10027
|
+
const env = await this.adapter.get(this.vault, this.name, id);
|
|
10028
|
+
if (!env) return null;
|
|
10029
|
+
return {
|
|
10030
|
+
version: env._v,
|
|
10031
|
+
timestamp: env._ts,
|
|
10032
|
+
...env._by !== void 0 ? { by: env._by } : {},
|
|
10033
|
+
...env._source !== void 0 ? { source: env._source } : {},
|
|
10034
|
+
...env._sourceTs !== void 0 ? { sourceTs: env._sourceTs } : {}
|
|
10035
|
+
};
|
|
10036
|
+
}
|
|
10071
10037
|
/**
|
|
10072
10038
|
* Return a presence handle for this collection.
|
|
10073
10039
|
*
|
|
@@ -10105,6 +10071,14 @@ var init_collection = __esm({
|
|
|
10105
10071
|
* `reason` is stamped onto the resulting ledger entry
|
|
10106
10072
|
* so audit consumers can filter via
|
|
10107
10073
|
* `entries.filter(e => e.reason?.startsWith('import:'))`.
|
|
10074
|
+
* `source` is an opaque source id (e.g. `'crm-sync'`, `'firm-A'`)
|
|
10075
|
+
* stamped onto the envelope as `_source`/`_sourceTs` when
|
|
10076
|
+
* the collection has `provenance: true`. Ignored otherwise
|
|
10077
|
+
* (zero cost). (FR-5, #445)
|
|
10078
|
+
* `sourceTs` is an optional ISO-8601 origin timestamp override;
|
|
10079
|
+
* when supplied together with `source` on a provenance collection,
|
|
10080
|
+
* replaces the machine-stamped `now()` so re-merges preserve the
|
|
10081
|
+
* ORIGIN refresh time across vaults. (FR-4)
|
|
10108
10082
|
*/
|
|
10109
10083
|
async put(id, record, options) {
|
|
10110
10084
|
await this.schemaUpdateGate?.assertWritable();
|
|
@@ -10136,6 +10110,20 @@ var init_collection = __esm({
|
|
|
10136
10110
|
if (busAfterPut) await this.subsystemBus.dispatch("afterPut", event);
|
|
10137
10111
|
}
|
|
10138
10112
|
}
|
|
10113
|
+
/**
|
|
10114
|
+
* Validate a record against this collection's schema WITHOUT writing it.
|
|
10115
|
+
* Returns the (possibly coerced) record on success; throws
|
|
10116
|
+
* {@link SchemaValidationError} (direction: `'input'`) on violation.
|
|
10117
|
+
* A no-op pass-through when no schema is declared.
|
|
10118
|
+
*
|
|
10119
|
+
* Used by FR-8 migrate-then-merge to pre-validate all staged records
|
|
10120
|
+
* before `mergeDecryptedRecords` writes anything — so a failed upgrade
|
|
10121
|
+
* never half-writes the receiver.
|
|
10122
|
+
*/
|
|
10123
|
+
async validateInput(record) {
|
|
10124
|
+
if (this.schema === void 0) return record;
|
|
10125
|
+
return validateSchemaInput(this.schema, record, `validateInput(${this.name})`);
|
|
10126
|
+
}
|
|
10139
10127
|
/** @internal — true when hooks should fire for this write (handlers exist, not re-entrant). */
|
|
10140
10128
|
#hooksActive() {
|
|
10141
10129
|
return this.writeHooks !== void 0 && this.writeHooks.hasHandlers && !this.writeHooks.suppressed;
|
|
@@ -10293,7 +10281,7 @@ var init_collection = __esm({
|
|
|
10293
10281
|
}
|
|
10294
10282
|
const version2 = existingVersion + 1;
|
|
10295
10283
|
const cek2 = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
|
|
10296
|
-
const envelope2 = await this.encryptJsonString(JSON.stringify(crdtState), version2, cek2);
|
|
10284
|
+
const envelope2 = await this.encryptJsonString(JSON.stringify(crdtState), version2, cek2, options?.source, options?.sourceTs);
|
|
10297
10285
|
await this.adapter.put(this.vault, this.name, id, envelope2);
|
|
10298
10286
|
const resolvedRecord = this.crdtStrategy.resolveCrdtSnapshot(crdtState);
|
|
10299
10287
|
const existingResolvedRecord = existingEnvelope ? await this.decryptRecord(existingEnvelope, { skipValidation: true }) : null;
|
|
@@ -10372,7 +10360,7 @@ var init_collection = __esm({
|
|
|
10372
10360
|
});
|
|
10373
10361
|
}
|
|
10374
10362
|
}
|
|
10375
|
-
const envelope = await this.encryptRecord(record, version, cek);
|
|
10363
|
+
const envelope = await this.encryptRecord(record, version, cek, options?.source, options?.sourceTs);
|
|
10376
10364
|
await this.adapter.put(this.vault, this.name, id, envelope);
|
|
10377
10365
|
if (this.ledger) {
|
|
10378
10366
|
const appendInput = {
|
|
@@ -10665,7 +10653,7 @@ var init_collection = __esm({
|
|
|
10665
10653
|
priorEnvelope
|
|
10666
10654
|
});
|
|
10667
10655
|
}
|
|
10668
|
-
await outputCollection.put(entry.key, entry.value);
|
|
10656
|
+
await outputCollection.put(entry.key, entry.value, { source: "derived" });
|
|
10669
10657
|
}
|
|
10670
10658
|
await saveFanoutSidecar2(this.adapter, this.vault, {
|
|
10671
10659
|
source: spec.source,
|
|
@@ -10698,7 +10686,7 @@ var init_collection = __esm({
|
|
|
10698
10686
|
priorEnvelope: prior
|
|
10699
10687
|
});
|
|
10700
10688
|
}
|
|
10701
|
-
await outputCollection.put(run.runId, patched);
|
|
10689
|
+
await outputCollection.put(run.runId, patched, { source: "derived" });
|
|
10702
10690
|
continue;
|
|
10703
10691
|
}
|
|
10704
10692
|
if (txCtx !== null) {
|
|
@@ -10713,7 +10701,7 @@ var init_collection = __esm({
|
|
|
10713
10701
|
priorEnvelope: prior
|
|
10714
10702
|
});
|
|
10715
10703
|
}
|
|
10716
|
-
await outputCollection.put(run.runId, out.value);
|
|
10704
|
+
await outputCollection.put(run.runId, out.value, { source: "derived" });
|
|
10717
10705
|
}
|
|
10718
10706
|
}
|
|
10719
10707
|
}
|
|
@@ -12376,7 +12364,7 @@ var init_collection = __esm({
|
|
|
12376
12364
|
* (see {@link encryptRecord}). Rejects `_`-prefixed record fields, which
|
|
12377
12365
|
* would collide with the reserved metadata namespace.
|
|
12378
12366
|
*/
|
|
12379
|
-
buildDebugEnvelope(record, version) {
|
|
12367
|
+
buildDebugEnvelope(record, version, source, sourceTs) {
|
|
12380
12368
|
const rec = record;
|
|
12381
12369
|
for (const key of Object.keys(rec)) {
|
|
12382
12370
|
if (key.startsWith("_")) throw new DebugReservedFieldError(this.name, key);
|
|
@@ -12389,11 +12377,13 @@ var init_collection = __esm({
|
|
|
12389
12377
|
_data: "",
|
|
12390
12378
|
_by: this.keyring.userId,
|
|
12391
12379
|
_debug: NOYDB_FORMAT_VERSION,
|
|
12380
|
+
...this.provenance && source !== void 0 ? { _source: source, _sourceTs: sourceTs ?? (/* @__PURE__ */ new Date()).toISOString() } : {},
|
|
12392
12381
|
...rec
|
|
12393
12382
|
};
|
|
12394
12383
|
}
|
|
12395
|
-
async encryptJsonString(json, version, cek) {
|
|
12384
|
+
async encryptJsonString(json, version, cek, source, sourceTs) {
|
|
12396
12385
|
const by = this.keyring.userId;
|
|
12386
|
+
const provenanceFields = this.provenance && source !== void 0 ? { _source: source, _sourceTs: sourceTs ?? (/* @__PURE__ */ new Date()).toISOString() } : {};
|
|
12397
12387
|
if (!this.encrypted) {
|
|
12398
12388
|
return {
|
|
12399
12389
|
_noydb: NOYDB_FORMAT_VERSION,
|
|
@@ -12401,7 +12391,8 @@ var init_collection = __esm({
|
|
|
12401
12391
|
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12402
12392
|
_iv: "",
|
|
12403
12393
|
_data: json,
|
|
12404
|
-
_by: by
|
|
12394
|
+
_by: by,
|
|
12395
|
+
...provenanceFields
|
|
12405
12396
|
};
|
|
12406
12397
|
}
|
|
12407
12398
|
const dek = await this.getDEK(this.name);
|
|
@@ -12415,7 +12406,8 @@ var init_collection = __esm({
|
|
|
12415
12406
|
_iv: iv2,
|
|
12416
12407
|
_data: data2,
|
|
12417
12408
|
_by: by,
|
|
12418
|
-
_cek: wrapped
|
|
12409
|
+
_cek: wrapped,
|
|
12410
|
+
...provenanceFields
|
|
12419
12411
|
};
|
|
12420
12412
|
}
|
|
12421
12413
|
const { iv, data } = await encrypt(json, dek);
|
|
@@ -12425,14 +12417,15 @@ var init_collection = __esm({
|
|
|
12425
12417
|
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12426
12418
|
_iv: iv,
|
|
12427
12419
|
_data: data,
|
|
12428
|
-
_by: by
|
|
12420
|
+
_by: by,
|
|
12421
|
+
...provenanceFields
|
|
12429
12422
|
};
|
|
12430
12423
|
}
|
|
12431
|
-
async encryptRecord(record, version, cek) {
|
|
12424
|
+
async encryptRecord(record, version, cek, source, sourceTs) {
|
|
12432
12425
|
if (!this.encrypted && this.keyring.debugPlaintext === true && !this.name.startsWith("_")) {
|
|
12433
|
-
return this.buildDebugEnvelope(record, version);
|
|
12426
|
+
return this.buildDebugEnvelope(record, version, source, sourceTs);
|
|
12434
12427
|
}
|
|
12435
|
-
const base = await this.encryptJsonString(JSON.stringify(record), version, cek);
|
|
12428
|
+
const base = await this.encryptJsonString(JSON.stringify(record), version, cek, source, sourceTs);
|
|
12436
12429
|
if (!this.deterministicFields || !this.encrypted) return base;
|
|
12437
12430
|
const dek = await this.getDEK(this.name);
|
|
12438
12431
|
const rec = record;
|
|
@@ -12566,7 +12559,8 @@ var init_collection = __esm({
|
|
|
12566
12559
|
_iv: iv,
|
|
12567
12560
|
_data: data,
|
|
12568
12561
|
_by: this.keyring.userId,
|
|
12569
|
-
...tier > 0 && { _tier: tier }
|
|
12562
|
+
...tier > 0 && { _tier: tier },
|
|
12563
|
+
...this.provenance && opts?.source !== void 0 ? { _source: opts.source, _sourceTs: opts.sourceTs ?? (/* @__PURE__ */ new Date()).toISOString() } : {}
|
|
12570
12564
|
};
|
|
12571
12565
|
await this.adapter.put(this.vault, this.name, id, envelope);
|
|
12572
12566
|
if (tier > 0) {
|
|
@@ -12873,43 +12867,49 @@ function randomId() {
|
|
|
12873
12867
|
const b = globalThis.crypto.getRandomValues(new Uint8Array(12));
|
|
12874
12868
|
return Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
12875
12869
|
}
|
|
12876
|
-
async function
|
|
12870
|
+
async function freezeSnapshotOnly(vault, collections, opts) {
|
|
12877
12871
|
const { name: vaultName, adapter } = vault._introspectState();
|
|
12878
12872
|
const closure = [];
|
|
12879
12873
|
for (const c of collections) {
|
|
12880
12874
|
for (const id of await adapter.list(vaultName, c)) closure.push({ collection: c, id });
|
|
12881
12875
|
}
|
|
12882
|
-
|
|
12883
|
-
|
|
12884
|
-
const withdrawalId = opts.withdrawalId ?? `wd-${randomId()}`;
|
|
12885
|
-
const snap = {};
|
|
12886
|
-
for (const { collection, id } of closure) {
|
|
12887
|
-
const env = await adapter.get(vaultName, collection, id);
|
|
12888
|
-
if (env) (snap[collection] ??= {})[id] = env;
|
|
12889
|
-
}
|
|
12890
|
-
const frozenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12891
|
-
const body = JSON.stringify({ withdrawalId, frozenAt, by: opts.actorUserId, collections: snap });
|
|
12892
|
-
const sha = await sha256Hex2(ENC.encode(body));
|
|
12893
|
-
await adapter.put(
|
|
12894
|
-
vaultName,
|
|
12895
|
-
FROZEN_SNAPSHOTS_COLLECTION,
|
|
12896
|
-
withdrawalId,
|
|
12897
|
-
{ _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: frozenAt, _iv: "", _data: body, _by: opts.actorUserId },
|
|
12898
|
-
0
|
|
12899
|
-
);
|
|
12900
|
-
await vault._getLedgerOrNull()?.append({
|
|
12901
|
-
op: "lifecycle",
|
|
12902
|
-
collection: "",
|
|
12903
|
-
id: "",
|
|
12904
|
-
version: 0,
|
|
12905
|
-
actor: opts.actorUserId,
|
|
12906
|
-
payloadHash: "",
|
|
12907
|
-
reason: `withdrawal-frozen-snapshot:${withdrawalId}:${sha}`
|
|
12908
|
-
});
|
|
12909
|
-
snapshot = { withdrawalId, sha256: sha, recordCount: closure.length, frozenAt };
|
|
12910
|
-
}
|
|
12876
|
+
const withdrawalId = opts.withdrawalId ?? `wd-${randomId()}`;
|
|
12877
|
+
const snap = {};
|
|
12911
12878
|
for (const { collection, id } of closure) {
|
|
12912
|
-
await
|
|
12879
|
+
const env = await adapter.get(vaultName, collection, id);
|
|
12880
|
+
if (env) (snap[collection] ??= {})[id] = env;
|
|
12881
|
+
}
|
|
12882
|
+
const frozenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12883
|
+
const body = JSON.stringify({ withdrawalId, frozenAt, by: opts.actorUserId, collections: snap });
|
|
12884
|
+
const sha = await sha256Hex2(ENC.encode(body));
|
|
12885
|
+
await adapter.put(
|
|
12886
|
+
vaultName,
|
|
12887
|
+
FROZEN_SNAPSHOTS_COLLECTION,
|
|
12888
|
+
withdrawalId,
|
|
12889
|
+
{ _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: frozenAt, _iv: "", _data: body, _by: opts.actorUserId },
|
|
12890
|
+
0
|
|
12891
|
+
);
|
|
12892
|
+
await vault._getLedgerOrNull()?.append({
|
|
12893
|
+
op: "lifecycle",
|
|
12894
|
+
collection: "",
|
|
12895
|
+
id: "",
|
|
12896
|
+
version: 0,
|
|
12897
|
+
actor: opts.actorUserId,
|
|
12898
|
+
payloadHash: "",
|
|
12899
|
+
reason: `withdrawal-frozen-snapshot:${withdrawalId}:${sha}`
|
|
12900
|
+
});
|
|
12901
|
+
return { withdrawalId, sha256: sha, recordCount: closure.length, frozenAt };
|
|
12902
|
+
}
|
|
12903
|
+
async function freezeAndDeleteClosure(vault, collections, opts) {
|
|
12904
|
+
const snapshot = opts.disposition === "freeze" ? await freezeSnapshotOnly(vault, collections, {
|
|
12905
|
+
actorUserId: opts.actorUserId,
|
|
12906
|
+
...opts.withdrawalId ? { withdrawalId: opts.withdrawalId } : {}
|
|
12907
|
+
}) : void 0;
|
|
12908
|
+
const { name: vaultName, adapter } = vault._introspectState();
|
|
12909
|
+
for (const c of collections) {
|
|
12910
|
+
for (const id of await adapter.list(vaultName, c)) {
|
|
12911
|
+
await vault.collection(c).delete(id);
|
|
12912
|
+
}
|
|
12913
12913
|
}
|
|
12914
12914
|
return snapshot;
|
|
12915
12915
|
}
|
|
@@ -12921,6 +12921,11 @@ async function withdrawAccessibleData(vault, opts) {
|
|
|
12921
12921
|
"unilateralWithdrawal is the scoped self-service path; an owner/admin should use extractPartition"
|
|
12922
12922
|
);
|
|
12923
12923
|
}
|
|
12924
|
+
if (keyring.role === "custodian") {
|
|
12925
|
+
throw new ReadOnlyError(
|
|
12926
|
+
"a custodian cannot destructively withdraw/sever; use vault.custody.liberate for an audited ownership claim"
|
|
12927
|
+
);
|
|
12928
|
+
}
|
|
12924
12929
|
if (keyring.role === "client" || keyring.role === "viewer") {
|
|
12925
12930
|
throw new ReadOnlyError(
|
|
12926
12931
|
"read-only role cannot self-serve a destructive withdrawal \u2014 use requestWithdrawal (two-party)"
|
|
@@ -14819,6 +14824,157 @@ var init_api = __esm({
|
|
|
14819
14824
|
}
|
|
14820
14825
|
});
|
|
14821
14826
|
|
|
14827
|
+
// src/custody/index.ts
|
|
14828
|
+
var CustodyApi;
|
|
14829
|
+
var init_custody = __esm({
|
|
14830
|
+
"src/custody/index.ts"() {
|
|
14831
|
+
"use strict";
|
|
14832
|
+
CustodyApi = class {
|
|
14833
|
+
constructor(_grantCustodian, _revokeCustodian, _liberate) {
|
|
14834
|
+
this._grantCustodian = _grantCustodian;
|
|
14835
|
+
this._revokeCustodian = _revokeCustodian;
|
|
14836
|
+
this._liberate = _liberate;
|
|
14837
|
+
}
|
|
14838
|
+
_grantCustodian;
|
|
14839
|
+
_revokeCustodian;
|
|
14840
|
+
_liberate;
|
|
14841
|
+
/**
|
|
14842
|
+
* Owner-only: grant the FR-6 `custodian` role. The custodian operates every
|
|
14843
|
+
* collection (rw + access) but is provably unable to grant / revoke / rotate /
|
|
14844
|
+
* extract-and-sever. Defended in depth (gate + owner-only role check) inside
|
|
14845
|
+
* the injected `Noydb.grantCustodian`.
|
|
14846
|
+
*/
|
|
14847
|
+
async grantCustodian(options, factors) {
|
|
14848
|
+
return this._grantCustodian(options, factors);
|
|
14849
|
+
}
|
|
14850
|
+
/** Owner-only: revoke a custodian. */
|
|
14851
|
+
async revokeCustodian(options, factors) {
|
|
14852
|
+
return this._revokeCustodian(options, factors);
|
|
14853
|
+
}
|
|
14854
|
+
/**
|
|
14855
|
+
* Custodian-only: the audited claim of ownership over a sealed-owner (Deed)
|
|
14856
|
+
* vault. Mints a DISTINCT new owner re-wrapping the incumbent DEKs under a
|
|
14857
|
+
* fresh KEK (the latent owner is never impersonated), ledger-audited. See
|
|
14858
|
+
* {@link liberateVault}.
|
|
14859
|
+
*/
|
|
14860
|
+
async liberate(opts) {
|
|
14861
|
+
return this._liberate(opts);
|
|
14862
|
+
}
|
|
14863
|
+
};
|
|
14864
|
+
}
|
|
14865
|
+
});
|
|
14866
|
+
|
|
14867
|
+
// src/team/deed.ts
|
|
14868
|
+
async function loadDeedMarker(store, vault) {
|
|
14869
|
+
const envelope = await store.get(vault, "_meta", DEED_RECORD_ID);
|
|
14870
|
+
if (!envelope) return null;
|
|
14871
|
+
let payload;
|
|
14872
|
+
try {
|
|
14873
|
+
payload = JSON.parse(envelope._data);
|
|
14874
|
+
} catch {
|
|
14875
|
+
return null;
|
|
14876
|
+
}
|
|
14877
|
+
if (typeof payload !== "object" || payload === null) return null;
|
|
14878
|
+
const r = payload;
|
|
14879
|
+
if (r._noydb_deed !== 1) return null;
|
|
14880
|
+
if (typeof r.ownerUserId !== "string" || typeof r.sealedUnder !== "string" || r.latent !== true || typeof r.issuedAt !== "string") {
|
|
14881
|
+
return null;
|
|
14882
|
+
}
|
|
14883
|
+
const marker = {
|
|
14884
|
+
ownerUserId: r.ownerUserId,
|
|
14885
|
+
sealedUnder: r.sealedUnder,
|
|
14886
|
+
latent: true,
|
|
14887
|
+
issuedAt: r.issuedAt,
|
|
14888
|
+
...typeof r.liberatedAt === "string" ? { liberatedAt: r.liberatedAt } : {}
|
|
14889
|
+
};
|
|
14890
|
+
return marker;
|
|
14891
|
+
}
|
|
14892
|
+
async function saveDeedMarker(store, vault, marker) {
|
|
14893
|
+
const persisted = { _noydb_deed: 1, ...marker };
|
|
14894
|
+
const prior = await store.get(vault, "_meta", DEED_RECORD_ID);
|
|
14895
|
+
const env = {
|
|
14896
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
14897
|
+
_v: (prior?._v ?? 0) + 1,
|
|
14898
|
+
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14899
|
+
// AES-GCM bypassed — the marker is plaintext audit metadata.
|
|
14900
|
+
_iv: "",
|
|
14901
|
+
_data: JSON.stringify(persisted)
|
|
14902
|
+
};
|
|
14903
|
+
await store.put(vault, "_meta", DEED_RECORD_ID, env);
|
|
14904
|
+
}
|
|
14905
|
+
var DEED_RECORD_ID;
|
|
14906
|
+
var init_deed = __esm({
|
|
14907
|
+
"src/team/deed.ts"() {
|
|
14908
|
+
"use strict";
|
|
14909
|
+
init_types();
|
|
14910
|
+
DEED_RECORD_ID = "deed";
|
|
14911
|
+
}
|
|
14912
|
+
});
|
|
14913
|
+
|
|
14914
|
+
// src/custody/liberate.ts
|
|
14915
|
+
async function liberateVault(vault, opts) {
|
|
14916
|
+
await vault.noydb.checkGate(vault.name, "liberate-vault", opts.factors);
|
|
14917
|
+
const { name: vaultName, adapter, keyring } = vault._introspectState();
|
|
14918
|
+
if (keyring.role !== "custodian") {
|
|
14919
|
+
throw new PermissionDeniedError(
|
|
14920
|
+
"liberation is claimed only by the custodian (the de-facto authority holding the DEKs)"
|
|
14921
|
+
);
|
|
14922
|
+
}
|
|
14923
|
+
const existing = await adapter.get(vaultName, "_keyring", opts.newOwnerId);
|
|
14924
|
+
if (existing) {
|
|
14925
|
+
throw new PermissionDeniedError(
|
|
14926
|
+
`liberateVault: newOwnerId "${opts.newOwnerId}" already exists as a principal; choose a fresh id (liberation mints a distinct owner, it never overwrites an existing keyring)`
|
|
14927
|
+
);
|
|
14928
|
+
}
|
|
14929
|
+
const collections = await listOperationalCollections(vault);
|
|
14930
|
+
const snapshot = await freezeSnapshotOnly(vault, collections, { actorUserId: keyring.userId });
|
|
14931
|
+
const newOwner = await createOwnerKeyring(adapter, vaultName, opts.newOwnerId, opts.newOwnerPassphrase);
|
|
14932
|
+
if (!newOwner.kek) {
|
|
14933
|
+
throw new PermissionDeniedError(
|
|
14934
|
+
`new owner keyring for "${opts.newOwnerId}" has no KEK to re-wrap the incumbent DEKs under`
|
|
14935
|
+
);
|
|
14936
|
+
}
|
|
14937
|
+
const env = await adapter.get(vaultName, "_keyring", opts.newOwnerId);
|
|
14938
|
+
if (!env) {
|
|
14939
|
+
throw new PermissionDeniedError(`new owner keyring for "${opts.newOwnerId}" did not persist`);
|
|
14940
|
+
}
|
|
14941
|
+
const keyringFile = JSON.parse(env._data);
|
|
14942
|
+
const mergedDeks = { ...keyringFile.deks };
|
|
14943
|
+
for (const [collection, dek] of keyring.deks) {
|
|
14944
|
+
mergedDeks[collection] = await wrapKey(dek, newOwner.kek);
|
|
14945
|
+
}
|
|
14946
|
+
const mergedFile = { ...keyringFile, deks: mergedDeks };
|
|
14947
|
+
await adapter.put(vaultName, "_keyring", opts.newOwnerId, { ...env, _data: JSON.stringify(mergedFile) });
|
|
14948
|
+
await vault._getLedgerOrNull()?.append({
|
|
14949
|
+
op: "lifecycle",
|
|
14950
|
+
collection: "",
|
|
14951
|
+
id: "",
|
|
14952
|
+
version: 0,
|
|
14953
|
+
actor: opts.newOwnerId,
|
|
14954
|
+
payloadHash: "",
|
|
14955
|
+
reason: `liberation-claimed:${opts.newOwnerId}:${opts.legalBasis}`
|
|
14956
|
+
});
|
|
14957
|
+
const marker = await loadDeedMarker(adapter, vaultName);
|
|
14958
|
+
if (marker) {
|
|
14959
|
+
await saveDeedMarker(adapter, vaultName, { ...marker, liberatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
14960
|
+
}
|
|
14961
|
+
return { snapshot };
|
|
14962
|
+
}
|
|
14963
|
+
async function listOperationalCollections(vault) {
|
|
14964
|
+
const { keyring } = vault._introspectState();
|
|
14965
|
+
return [...keyring.deks.keys()].filter((c) => !c.startsWith("_"));
|
|
14966
|
+
}
|
|
14967
|
+
var init_liberate = __esm({
|
|
14968
|
+
"src/custody/liberate.ts"() {
|
|
14969
|
+
"use strict";
|
|
14970
|
+
init_errors();
|
|
14971
|
+
init_crypto();
|
|
14972
|
+
init_keyring();
|
|
14973
|
+
init_withdraw_accessible();
|
|
14974
|
+
init_deed();
|
|
14975
|
+
}
|
|
14976
|
+
});
|
|
14977
|
+
|
|
14822
14978
|
// src/persisted-schemas/canonicalize.ts
|
|
14823
14979
|
function canonicalize(value) {
|
|
14824
14980
|
if (value === null || typeof value !== "object") {
|
|
@@ -14868,8 +15024,8 @@ async function derivePersistedSchema(validator) {
|
|
|
14868
15024
|
if (kind === "Zod") {
|
|
14869
15025
|
const convert = await loadZodConverter();
|
|
14870
15026
|
const jsonSchema = convert(validator);
|
|
14871
|
-
const
|
|
14872
|
-
const hash = await sha256Hex2(new TextEncoder().encode(
|
|
15027
|
+
const canonical = canonicalize(jsonSchema);
|
|
15028
|
+
const hash = await sha256Hex2(new TextEncoder().encode(canonical));
|
|
14873
15029
|
return { _noydb_schema: 1, kind, jsonSchema, hash, derivedAt };
|
|
14874
15030
|
}
|
|
14875
15031
|
return {
|
|
@@ -15912,7 +16068,7 @@ var init_read_only_facade = __esm({
|
|
|
15912
16068
|
|
|
15913
16069
|
// src/derivations/strategy-hash.ts
|
|
15914
16070
|
async function computeStrategyHash(source, outputKeys, derive, sources) {
|
|
15915
|
-
const
|
|
16071
|
+
const canonical = JSON.stringify({
|
|
15916
16072
|
source,
|
|
15917
16073
|
outputs: [...outputKeys].sort(),
|
|
15918
16074
|
derive: derive.toString(),
|
|
@@ -15921,7 +16077,7 @@ async function computeStrategyHash(source, outputKeys, derive, sources) {
|
|
|
15921
16077
|
// so strategies without siblings keep their existing hash.
|
|
15922
16078
|
...sources?.length ? { sources: [...sources].sort() } : {}
|
|
15923
16079
|
});
|
|
15924
|
-
const bytes = new TextEncoder().encode(
|
|
16080
|
+
const bytes = new TextEncoder().encode(canonical);
|
|
15925
16081
|
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
15926
16082
|
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
15927
16083
|
}
|
|
@@ -16254,6 +16410,8 @@ var init_vault = __esm({
|
|
|
16254
16410
|
init_blob_compaction();
|
|
16255
16411
|
init_magic_link_grant();
|
|
16256
16412
|
init_api();
|
|
16413
|
+
init_custody();
|
|
16414
|
+
init_liberate();
|
|
16257
16415
|
init_register();
|
|
16258
16416
|
init_gate();
|
|
16259
16417
|
init_fence_controller();
|
|
@@ -16361,6 +16519,18 @@ var init_vault = __esm({
|
|
|
16361
16519
|
* @see docs/superpowers/specs/2026-05-05-user-envelope-design.md
|
|
16362
16520
|
*/
|
|
16363
16521
|
user;
|
|
16522
|
+
/**
|
|
16523
|
+
* FR-6 custody API — the sovereign-custody surface, mirroring `vault.user.*`.
|
|
16524
|
+
*
|
|
16525
|
+
* - `grantCustodian(opts)` / `revokeCustodian(opts)` — owner-only: mint /
|
|
16526
|
+
* remove a `custodian` who operates the vault fully but can never grant /
|
|
16527
|
+
* rotate / sever / extract.
|
|
16528
|
+
* - `liberate(opts)` — custodian-only: the audited claim of ownership over a
|
|
16529
|
+
* sealed-owner (Deed) vault (mints a DISTINCT new owner; ledger-audited).
|
|
16530
|
+
*
|
|
16531
|
+
* @see docs/superpowers/specs/2026-06-17-fr6-deed-custodian-liberate-design.md
|
|
16532
|
+
*/
|
|
16533
|
+
custody;
|
|
16364
16534
|
/**
|
|
16365
16535
|
* Optional callback that re-derives an UnlockedKeyring from the
|
|
16366
16536
|
* adapter using the active user's passphrase. Called by `load()`
|
|
@@ -16571,6 +16741,11 @@ var init_vault = __esm({
|
|
|
16571
16741
|
(requestId, opts2) => approveWithdrawal(this, requestId, opts2),
|
|
16572
16742
|
(requestId, opts2) => rejectWithdrawal(this, requestId, opts2)
|
|
16573
16743
|
);
|
|
16744
|
+
this.custody = new CustodyApi(
|
|
16745
|
+
(options, factors) => this.noydb.grantCustodian(this.name, options, factors),
|
|
16746
|
+
(options, factors) => this.noydb.revokeCustodian(this.name, options, factors),
|
|
16747
|
+
(opts2) => liberateVault(this, opts2)
|
|
16748
|
+
);
|
|
16574
16749
|
}
|
|
16575
16750
|
/**
|
|
16576
16751
|
* Construct (or reconstruct) the lazy DEK resolver. Captures the
|
|
@@ -16798,6 +16973,7 @@ var init_vault = __esm({
|
|
|
16798
16973
|
}
|
|
16799
16974
|
collOpts.perRecordKeys = true;
|
|
16800
16975
|
}
|
|
16976
|
+
if (options?.provenance !== void 0) collOpts.provenance = options.provenance;
|
|
16801
16977
|
if (options?.tiers !== void 0) collOpts.tiers = options.tiers;
|
|
16802
16978
|
if (options?.tierMode !== void 0) collOpts.tierMode = options.tierMode;
|
|
16803
16979
|
collOpts.onCrossTierAccess = (event) => this.emitCrossTier(event);
|
|
@@ -18620,7 +18796,7 @@ var init_vault = __esm({
|
|
|
18620
18796
|
if (this.activeElevation) {
|
|
18621
18797
|
throw new AlreadyElevatedError(this.activeElevation.tier);
|
|
18622
18798
|
}
|
|
18623
|
-
if (this.keyring.role !== "owner" && this.keyring.role !== "admin") {
|
|
18799
|
+
if (this.keyring.role !== "owner" && this.keyring.role !== "admin" && this.keyring.role !== "custodian") {
|
|
18624
18800
|
const suffix = `#${tier}`;
|
|
18625
18801
|
let found = false;
|
|
18626
18802
|
for (const k of this.keyring.deks.keys()) {
|
|
@@ -20822,997 +20998,6 @@ var init_executor3 = __esm({
|
|
|
20822
20998
|
}
|
|
20823
20999
|
});
|
|
20824
21000
|
|
|
20825
|
-
// src/federation/schema-manifest.ts
|
|
20826
|
-
function captureBlueprint(configure) {
|
|
20827
|
-
const recorded = [];
|
|
20828
|
-
const collectionStub = new Proxy(
|
|
20829
|
-
{},
|
|
20830
|
-
{
|
|
20831
|
-
get: () => () => collectionStub
|
|
20832
|
-
}
|
|
20833
|
-
);
|
|
20834
|
-
const proxy = new Proxy(
|
|
20835
|
-
{},
|
|
20836
|
-
{
|
|
20837
|
-
get: (_t, prop) => {
|
|
20838
|
-
if (prop === "collection") {
|
|
20839
|
-
return (name, opts) => {
|
|
20840
|
-
recorded.push({
|
|
20841
|
-
name,
|
|
20842
|
-
indexes: opts?.indexes ?? [],
|
|
20843
|
-
persistJsonSchema: !!opts?.persistJsonSchema
|
|
20844
|
-
});
|
|
20845
|
-
return collectionStub;
|
|
20846
|
-
};
|
|
20847
|
-
}
|
|
20848
|
-
return () => proxy;
|
|
20849
|
-
}
|
|
20850
|
-
}
|
|
20851
|
-
);
|
|
20852
|
-
configure(proxy);
|
|
20853
|
-
const sorted = [...recorded].sort((a, b) => a.name.localeCompare(b.name));
|
|
20854
|
-
const indexes = {};
|
|
20855
|
-
const persistJsonSchema = [];
|
|
20856
|
-
for (const c of sorted) {
|
|
20857
|
-
indexes[c.name] = c.indexes;
|
|
20858
|
-
if (c.persistJsonSchema) persistJsonSchema.push(c.name);
|
|
20859
|
-
}
|
|
20860
|
-
return {
|
|
20861
|
-
// `persistJsonSchema` is already name-sorted: it is populated while
|
|
20862
|
-
// iterating `sorted` (collections in name order).
|
|
20863
|
-
collections: sorted.map((c) => c.name),
|
|
20864
|
-
indexes,
|
|
20865
|
-
persistJsonSchema
|
|
20866
|
-
};
|
|
20867
|
-
}
|
|
20868
|
-
function canonical(value) {
|
|
20869
|
-
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
20870
|
-
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
20871
|
-
const obj = value;
|
|
20872
|
-
const keys = Object.keys(obj).sort();
|
|
20873
|
-
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(obj[k])}`).join(",")}}`;
|
|
20874
|
-
}
|
|
20875
|
-
async function fingerprintBlueprint(bp) {
|
|
20876
|
-
return sha256Hex2(new TextEncoder().encode(canonical(bp)));
|
|
20877
|
-
}
|
|
20878
|
-
var init_schema_manifest = __esm({
|
|
20879
|
-
"src/federation/schema-manifest.ts"() {
|
|
20880
|
-
"use strict";
|
|
20881
|
-
init_crypto();
|
|
20882
|
-
}
|
|
20883
|
-
});
|
|
20884
|
-
|
|
20885
|
-
// src/federation/state-vault.ts
|
|
20886
|
-
var state_vault_exports = {};
|
|
20887
|
-
__export(state_vault_exports, {
|
|
20888
|
-
STATE_VAULT_NAME: () => STATE_VAULT_NAME,
|
|
20889
|
-
StateManagementVault: () => StateManagementVault
|
|
20890
|
-
});
|
|
20891
|
-
var REGISTRY, MANIFEST, EVENTS, MIGRATION_STATUS, StateManagementVault;
|
|
20892
|
-
var init_state_vault = __esm({
|
|
20893
|
-
"src/federation/state-vault.ts"() {
|
|
20894
|
-
"use strict";
|
|
20895
|
-
init_schema_manifest();
|
|
20896
|
-
init_constants2();
|
|
20897
|
-
init_ulid();
|
|
20898
|
-
init_constants2();
|
|
20899
|
-
REGISTRY = "vaultRegistry";
|
|
20900
|
-
MANIFEST = "schemaManifest";
|
|
20901
|
-
EVENTS = "deploymentEvents";
|
|
20902
|
-
MIGRATION_STATUS = "migrationStatus";
|
|
20903
|
-
StateManagementVault = class _StateManagementVault {
|
|
20904
|
-
constructor(registry, schemaManifest, events, migrationStatus) {
|
|
20905
|
-
this.registry = registry;
|
|
20906
|
-
this.schemaManifest = schemaManifest;
|
|
20907
|
-
this.#events = events;
|
|
20908
|
-
this.#migrationStatus = migrationStatus;
|
|
20909
|
-
}
|
|
20910
|
-
registry;
|
|
20911
|
-
schemaManifest;
|
|
20912
|
-
/**
|
|
20913
|
-
* The append-only deployment-events log is kept truly private so the raw
|
|
20914
|
-
* mutable Collection is never surfaced — events may only be written via
|
|
20915
|
-
* `appendEvent` and read via `queryEvents`. (`registry` and
|
|
20916
|
-
* `schemaManifest` are deliberately public: consumers read and write them.)
|
|
20917
|
-
*/
|
|
20918
|
-
#events;
|
|
20919
|
-
/** Per-shard fleet-migration progress (#271). Surfaced via typed methods only. */
|
|
20920
|
-
#migrationStatus;
|
|
20921
|
-
/** Idempotently open the reserved state vault and bind the control-plane collections. */
|
|
20922
|
-
static async open(db) {
|
|
20923
|
-
const vault = await db.openVault(STATE_VAULT_NAME);
|
|
20924
|
-
return new _StateManagementVault(
|
|
20925
|
-
vault.collection(REGISTRY),
|
|
20926
|
-
vault.collection(MANIFEST),
|
|
20927
|
-
vault.collection(EVENTS),
|
|
20928
|
-
vault.collection(MIGRATION_STATUS)
|
|
20929
|
-
);
|
|
20930
|
-
}
|
|
20931
|
-
/** Read one shard's migration status (or null). */
|
|
20932
|
-
async getMigrationStatus(vaultId) {
|
|
20933
|
-
return this.#migrationStatus.get(vaultId);
|
|
20934
|
-
}
|
|
20935
|
-
/** All migration-status rows (hydrates first). */
|
|
20936
|
-
async listMigrationStatus() {
|
|
20937
|
-
await this.#migrationStatus.list();
|
|
20938
|
-
return this.#migrationStatus.query().toArray();
|
|
20939
|
-
}
|
|
20940
|
-
/** Upsert one shard's migration status (keyed by vaultId). */
|
|
20941
|
-
async upsertMigrationStatus(row) {
|
|
20942
|
-
await this.#migrationStatus.put(row.vaultId, row);
|
|
20943
|
-
}
|
|
20944
|
-
/** Read-only query over the append-only deployment-events log. */
|
|
20945
|
-
queryEvents() {
|
|
20946
|
-
return this.#events.query();
|
|
20947
|
-
}
|
|
20948
|
-
/**
|
|
20949
|
-
* Append a deployment event with a fresh unique (ULID) id. This is the
|
|
20950
|
-
* only write path to the events log; no update/delete is exposed.
|
|
20951
|
-
* Callers should treat failures as non-fatal — this method does not
|
|
20952
|
-
* swallow errors, so wrap the call site in try/catch where appropriate.
|
|
20953
|
-
*/
|
|
20954
|
-
async appendEvent(event) {
|
|
20955
|
-
const ts = event.ts ?? Date.now();
|
|
20956
|
-
const id = generateULID();
|
|
20957
|
-
await this.#events.put(id, { ...event, id, ts });
|
|
20958
|
-
}
|
|
20959
|
-
/**
|
|
20960
|
-
* Ensure a manifest row exists for `(templateName, template.version)`.
|
|
20961
|
-
* Safe to call repeatedly: the `fingerprint` is a deterministic hash of
|
|
20962
|
-
* the template's declared shape (stable across calls), though each call
|
|
20963
|
-
* refreshes `recordedAt`.
|
|
20964
|
-
*/
|
|
20965
|
-
async recordManifest(templateName, template) {
|
|
20966
|
-
const bp = captureBlueprint(template.configure);
|
|
20967
|
-
const fingerprint = await fingerprintBlueprint(bp);
|
|
20968
|
-
await this.schemaManifest.put(`${templateName}:${template.version}`, {
|
|
20969
|
-
templateName,
|
|
20970
|
-
version: template.version,
|
|
20971
|
-
collections: bp.collections,
|
|
20972
|
-
indexes: bp.indexes,
|
|
20973
|
-
persistJsonSchema: bp.persistJsonSchema,
|
|
20974
|
-
fingerprint,
|
|
20975
|
-
recordedAt: Date.now()
|
|
20976
|
-
});
|
|
20977
|
-
return fingerprint;
|
|
20978
|
-
}
|
|
20979
|
-
/**
|
|
20980
|
-
* True when `template`'s current declared shape does not match the recorded
|
|
20981
|
-
* manifest for `(templateName, template.version)`. Because shards carry no
|
|
20982
|
-
* schema state independent of their template, this catches "a template's
|
|
20983
|
-
* shape changed without bumping `version`" — not independent per-shard drift.
|
|
20984
|
-
* A missing manifest is treated as drift (nothing to verify against).
|
|
20985
|
-
*/
|
|
20986
|
-
async detectDrift(templateName, template) {
|
|
20987
|
-
const row = await this.schemaManifest.get(`${templateName}:${template.version}`);
|
|
20988
|
-
if (!row) return true;
|
|
20989
|
-
const current = await fingerprintBlueprint(captureBlueprint(template.configure));
|
|
20990
|
-
return current !== row.fingerprint;
|
|
20991
|
-
}
|
|
20992
|
-
};
|
|
20993
|
-
}
|
|
20994
|
-
});
|
|
20995
|
-
|
|
20996
|
-
// src/federation/classify-skip.ts
|
|
20997
|
-
function classifyShardSkip(err) {
|
|
20998
|
-
return err instanceof NoAccessError ? "no-grant" : "error";
|
|
20999
|
-
}
|
|
21000
|
-
var init_classify_skip = __esm({
|
|
21001
|
-
"src/federation/classify-skip.ts"() {
|
|
21002
|
-
"use strict";
|
|
21003
|
-
init_errors();
|
|
21004
|
-
}
|
|
21005
|
-
});
|
|
21006
|
-
|
|
21007
|
-
// src/federation/cross-shard-join.ts
|
|
21008
|
-
function coerceKey(value) {
|
|
21009
|
-
if (value === null || value === void 0) return null;
|
|
21010
|
-
if (typeof value === "string") return value;
|
|
21011
|
-
if (typeof value === "number" || typeof value === "bigint") return String(value);
|
|
21012
|
-
return null;
|
|
21013
|
-
}
|
|
21014
|
-
function warnOnceBroadcastMiss(field, as, key) {
|
|
21015
|
-
const dedup = `${field}\u2192${as}:${key}`;
|
|
21016
|
-
if (warnedBroadcastKeys.has(dedup)) return;
|
|
21017
|
-
warnedBroadcastKeys.add(dedup);
|
|
21018
|
-
console.warn(
|
|
21019
|
-
`[noy-db] broadcastJoin: no "${as}" dimension row for ${field}="${key}". Attaching null. Use mode: 'cascade' to silence.`
|
|
21020
|
-
);
|
|
21021
|
-
}
|
|
21022
|
-
async function applyBroadcastLegs(rows, legs) {
|
|
21023
|
-
if (legs.length === 0) return [...rows];
|
|
21024
|
-
const indexes = [];
|
|
21025
|
-
for (const leg of legs) {
|
|
21026
|
-
const map = /* @__PURE__ */ new Map();
|
|
21027
|
-
for (const rec of await leg.from.list()) {
|
|
21028
|
-
const k = coerceKey(readPath(rec, leg.on));
|
|
21029
|
-
if (k !== null && !map.has(k)) map.set(k, rec);
|
|
21030
|
-
}
|
|
21031
|
-
indexes.push({ leg, map });
|
|
21032
|
-
}
|
|
21033
|
-
return rows.map((row) => {
|
|
21034
|
-
const out = { ...row };
|
|
21035
|
-
for (const { leg, map } of indexes) {
|
|
21036
|
-
const key = coerceKey(readPath(row, leg.field));
|
|
21037
|
-
const match = key === null ? null : map.get(key) ?? null;
|
|
21038
|
-
if (match === null && leg.mode === "warn") {
|
|
21039
|
-
warnOnceBroadcastMiss(leg.field, leg.as, key ?? "<null>");
|
|
21040
|
-
}
|
|
21041
|
-
out[leg.as] = match;
|
|
21042
|
-
}
|
|
21043
|
-
return out;
|
|
21044
|
-
});
|
|
21045
|
-
}
|
|
21046
|
-
var warnedBroadcastKeys;
|
|
21047
|
-
var init_cross_shard_join = __esm({
|
|
21048
|
-
"src/federation/cross-shard-join.ts"() {
|
|
21049
|
-
"use strict";
|
|
21050
|
-
init_predicate();
|
|
21051
|
-
warnedBroadcastKeys = /* @__PURE__ */ new Set();
|
|
21052
|
-
}
|
|
21053
|
-
});
|
|
21054
|
-
|
|
21055
|
-
// src/federation/cross-vault-live.ts
|
|
21056
|
-
var CrossVaultLive;
|
|
21057
|
-
var init_cross_vault_live = __esm({
|
|
21058
|
-
"src/federation/cross-vault-live.ts"() {
|
|
21059
|
-
"use strict";
|
|
21060
|
-
CrossVaultLive = class {
|
|
21061
|
-
snapshot;
|
|
21062
|
-
error = null;
|
|
21063
|
-
ready;
|
|
21064
|
-
subs = /* @__PURE__ */ new Set();
|
|
21065
|
-
unsubChange;
|
|
21066
|
-
opts;
|
|
21067
|
-
stopped = false;
|
|
21068
|
-
computing = false;
|
|
21069
|
-
dirty = false;
|
|
21070
|
-
scheduled = false;
|
|
21071
|
-
timer = null;
|
|
21072
|
-
resolveReady;
|
|
21073
|
-
settledOnce = false;
|
|
21074
|
-
constructor(opts) {
|
|
21075
|
-
this.opts = opts;
|
|
21076
|
-
this.snapshot = opts.initialSnapshot;
|
|
21077
|
-
this.ready = new Promise((res) => {
|
|
21078
|
-
this.resolveReady = res;
|
|
21079
|
-
});
|
|
21080
|
-
this.unsubChange = opts.subscribeToChanges((e) => {
|
|
21081
|
-
if (this.stopped || !opts.isRelevant(e)) return;
|
|
21082
|
-
this.schedule();
|
|
21083
|
-
});
|
|
21084
|
-
this.schedule();
|
|
21085
|
-
}
|
|
21086
|
-
subscribe(cb) {
|
|
21087
|
-
if (this.stopped) return () => {
|
|
21088
|
-
};
|
|
21089
|
-
this.subs.add(cb);
|
|
21090
|
-
return () => this.subs.delete(cb);
|
|
21091
|
-
}
|
|
21092
|
-
stop() {
|
|
21093
|
-
if (this.stopped) return;
|
|
21094
|
-
this.stopped = true;
|
|
21095
|
-
this.unsubChange();
|
|
21096
|
-
if (this.timer !== null) clearTimeout(this.timer);
|
|
21097
|
-
this.subs.clear();
|
|
21098
|
-
if (!this.settledOnce) this.resolveReady();
|
|
21099
|
-
}
|
|
21100
|
-
schedule() {
|
|
21101
|
-
if (this.stopped) return;
|
|
21102
|
-
if (this.computing) {
|
|
21103
|
-
this.dirty = true;
|
|
21104
|
-
return;
|
|
21105
|
-
}
|
|
21106
|
-
if (this.scheduled) return;
|
|
21107
|
-
this.scheduled = true;
|
|
21108
|
-
const run = () => {
|
|
21109
|
-
this.scheduled = false;
|
|
21110
|
-
void this.runCompute();
|
|
21111
|
-
};
|
|
21112
|
-
const ms = this.opts.debounceMs ?? 0;
|
|
21113
|
-
if (ms > 0) this.timer = setTimeout(run, ms);
|
|
21114
|
-
else queueMicrotask(run);
|
|
21115
|
-
}
|
|
21116
|
-
async runCompute() {
|
|
21117
|
-
if (this.stopped) return;
|
|
21118
|
-
this.computing = true;
|
|
21119
|
-
this.dirty = false;
|
|
21120
|
-
try {
|
|
21121
|
-
const next = await this.opts.compute();
|
|
21122
|
-
if (this.stopped) return;
|
|
21123
|
-
this.snapshot = next;
|
|
21124
|
-
this.error = null;
|
|
21125
|
-
} catch (err) {
|
|
21126
|
-
if (this.stopped) return;
|
|
21127
|
-
this.error = err instanceof Error ? err : new Error(String(err));
|
|
21128
|
-
} finally {
|
|
21129
|
-
this.computing = false;
|
|
21130
|
-
if (!this.stopped) {
|
|
21131
|
-
if (!this.settledOnce) {
|
|
21132
|
-
this.settledOnce = true;
|
|
21133
|
-
this.resolveReady();
|
|
21134
|
-
}
|
|
21135
|
-
for (const cb of this.subs) cb();
|
|
21136
|
-
if (this.dirty) this.schedule();
|
|
21137
|
-
}
|
|
21138
|
-
}
|
|
21139
|
-
}
|
|
21140
|
-
};
|
|
21141
|
-
}
|
|
21142
|
-
});
|
|
21143
|
-
|
|
21144
|
-
// src/federation/aggregate-across.ts
|
|
21145
|
-
var CrossVaultAggregation, CrossVaultGroupedAggregation;
|
|
21146
|
-
var init_aggregate_across = __esm({
|
|
21147
|
-
"src/federation/aggregate-across.ts"() {
|
|
21148
|
-
"use strict";
|
|
21149
|
-
init_aggregation();
|
|
21150
|
-
init_groupby();
|
|
21151
|
-
init_cross_vault_live();
|
|
21152
|
-
CrossVaultAggregation = class {
|
|
21153
|
-
constructor(src, spec, bind) {
|
|
21154
|
-
this.src = src;
|
|
21155
|
-
this.spec = spec;
|
|
21156
|
-
this.bind = bind;
|
|
21157
|
-
}
|
|
21158
|
-
src;
|
|
21159
|
-
spec;
|
|
21160
|
-
bind;
|
|
21161
|
-
async run(options = {}) {
|
|
21162
|
-
const { records, skippedVaults } = await this.src.fanoutRecords(options);
|
|
21163
|
-
return { result: reduceRecords(records, this.spec), skippedVaults };
|
|
21164
|
-
}
|
|
21165
|
-
live(options = {}) {
|
|
21166
|
-
if (!this.bind) throw new Error("CrossVaultAggregation: live() requires a LiveBinding \u2014 use ShardedQuery.aggregate()");
|
|
21167
|
-
const spec = this.spec;
|
|
21168
|
-
const src = this.src;
|
|
21169
|
-
const core = new CrossVaultLive({
|
|
21170
|
-
subscribeToChanges: this.bind.subscribeToChanges,
|
|
21171
|
-
isRelevant: this.bind.isRelevant,
|
|
21172
|
-
compute: async () => {
|
|
21173
|
-
const { records, skippedVaults } = await src.fanoutRecords(options);
|
|
21174
|
-
return { value: reduceRecords(records, spec), skipped: skippedVaults };
|
|
21175
|
-
},
|
|
21176
|
-
initialSnapshot: { value: void 0, skipped: [] },
|
|
21177
|
-
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
21178
|
-
});
|
|
21179
|
-
return {
|
|
21180
|
-
get value() {
|
|
21181
|
-
return core.snapshot.value;
|
|
21182
|
-
},
|
|
21183
|
-
get skippedVaults() {
|
|
21184
|
-
return core.snapshot.skipped;
|
|
21185
|
-
},
|
|
21186
|
-
get error() {
|
|
21187
|
-
return core.error;
|
|
21188
|
-
},
|
|
21189
|
-
ready: core.ready,
|
|
21190
|
-
subscribe: (cb) => core.subscribe(cb),
|
|
21191
|
-
stop: () => core.stop()
|
|
21192
|
-
};
|
|
21193
|
-
}
|
|
21194
|
-
};
|
|
21195
|
-
CrossVaultGroupedAggregation = class {
|
|
21196
|
-
constructor(src, field, spec, bind) {
|
|
21197
|
-
this.src = src;
|
|
21198
|
-
this.field = field;
|
|
21199
|
-
this.spec = spec;
|
|
21200
|
-
this.bind = bind;
|
|
21201
|
-
}
|
|
21202
|
-
src;
|
|
21203
|
-
field;
|
|
21204
|
-
spec;
|
|
21205
|
-
bind;
|
|
21206
|
-
async run(options = {}) {
|
|
21207
|
-
const { records, skippedVaults } = await this.src.fanoutRecords(options);
|
|
21208
|
-
return {
|
|
21209
|
-
results: groupAndReduce(records, this.field, this.spec),
|
|
21210
|
-
skippedVaults
|
|
21211
|
-
};
|
|
21212
|
-
}
|
|
21213
|
-
live(options = {}) {
|
|
21214
|
-
if (!this.bind) throw new Error("CrossVaultGroupedAggregation: live() requires a LiveBinding \u2014 use ShardedQuery.groupBy().aggregate()");
|
|
21215
|
-
const field = this.field;
|
|
21216
|
-
const spec = this.spec;
|
|
21217
|
-
const src = this.src;
|
|
21218
|
-
const core = new CrossVaultLive({
|
|
21219
|
-
subscribeToChanges: this.bind.subscribeToChanges,
|
|
21220
|
-
isRelevant: this.bind.isRelevant,
|
|
21221
|
-
compute: async () => {
|
|
21222
|
-
const { records, skippedVaults } = await src.fanoutRecords(options);
|
|
21223
|
-
return {
|
|
21224
|
-
records: groupAndReduce(records, field, spec),
|
|
21225
|
-
skipped: skippedVaults
|
|
21226
|
-
};
|
|
21227
|
-
},
|
|
21228
|
-
initialSnapshot: { records: [], skipped: [] },
|
|
21229
|
-
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
21230
|
-
});
|
|
21231
|
-
return {
|
|
21232
|
-
get value() {
|
|
21233
|
-
return core.snapshot.records;
|
|
21234
|
-
},
|
|
21235
|
-
get skippedVaults() {
|
|
21236
|
-
return core.snapshot.skipped;
|
|
21237
|
-
},
|
|
21238
|
-
get error() {
|
|
21239
|
-
return core.error;
|
|
21240
|
-
},
|
|
21241
|
-
ready: core.ready,
|
|
21242
|
-
subscribe: (cb) => core.subscribe(cb),
|
|
21243
|
-
stop: () => core.stop()
|
|
21244
|
-
};
|
|
21245
|
-
}
|
|
21246
|
-
};
|
|
21247
|
-
}
|
|
21248
|
-
});
|
|
21249
|
-
|
|
21250
|
-
// src/federation/vault-group.ts
|
|
21251
|
-
var vault_group_exports = {};
|
|
21252
|
-
__export(vault_group_exports, {
|
|
21253
|
-
ShardedCollection: () => ShardedCollection,
|
|
21254
|
-
ShardedGroupedQuery: () => ShardedGroupedQuery,
|
|
21255
|
-
ShardedQuery: () => ShardedQuery,
|
|
21256
|
-
VaultGroup: () => VaultGroup
|
|
21257
|
-
});
|
|
21258
|
-
function assertSafePartitionKey(partitionKey) {
|
|
21259
|
-
if (partitionKey.length === 0) {
|
|
21260
|
-
throw new ValidationError("partitionKey must be a non-empty string");
|
|
21261
|
-
}
|
|
21262
|
-
if (partitionKey === STATE_VAULT_NAME) {
|
|
21263
|
-
throw new ReservedVaultNameError(partitionKey);
|
|
21264
|
-
}
|
|
21265
|
-
if (!SAFE_PARTITION_KEY.test(partitionKey)) {
|
|
21266
|
-
throw new ValidationError(
|
|
21267
|
-
`partitionKey "${partitionKey}" contains characters outside [A-Za-z0-9._-]. Map your records to a store-safe key in sharding.keyOf.`
|
|
21268
|
-
);
|
|
21269
|
-
}
|
|
21270
|
-
if (partitionKey.includes(SHARD_SEPARATOR)) {
|
|
21271
|
-
throw new ValidationError(
|
|
21272
|
-
`partitionKey "${partitionKey}" must not contain "--" \u2014 it is reserved as the shard vault-id separator and would risk shard-id collisions.`
|
|
21273
|
-
);
|
|
21274
|
-
}
|
|
21275
|
-
}
|
|
21276
|
-
var SHARD_SEPARATOR, SAFE_PARTITION_KEY, VaultGroup, ShardedCollection, ShardedQuery, ShardedGroupedQuery;
|
|
21277
|
-
var init_vault_group = __esm({
|
|
21278
|
-
"src/federation/vault-group.ts"() {
|
|
21279
|
-
"use strict";
|
|
21280
|
-
init_state_vault();
|
|
21281
|
-
init_errors();
|
|
21282
|
-
init_constants2();
|
|
21283
|
-
init_classify_skip();
|
|
21284
|
-
init_cross_shard_join();
|
|
21285
|
-
init_cross_vault_live();
|
|
21286
|
-
init_aggregate_across();
|
|
21287
|
-
SHARD_SEPARATOR = "--";
|
|
21288
|
-
SAFE_PARTITION_KEY = /^[A-Za-z0-9._-]+$/;
|
|
21289
|
-
VaultGroup = class {
|
|
21290
|
-
constructor(db, name, registry, sharding, template, migrateOnOpen = false) {
|
|
21291
|
-
this.db = db;
|
|
21292
|
-
this.name = name;
|
|
21293
|
-
this.registry = registry;
|
|
21294
|
-
this.sharding = sharding;
|
|
21295
|
-
this.template = template;
|
|
21296
|
-
this.migrateOnOpen = migrateOnOpen;
|
|
21297
|
-
if (name.includes(SHARD_SEPARATOR)) {
|
|
21298
|
-
throw new ValidationError(
|
|
21299
|
-
`VaultGroup name "${name}" must not contain "--" (reserved shard vault-id separator).`
|
|
21300
|
-
);
|
|
21301
|
-
}
|
|
21302
|
-
}
|
|
21303
|
-
db;
|
|
21304
|
-
name;
|
|
21305
|
-
registry;
|
|
21306
|
-
sharding;
|
|
21307
|
-
template;
|
|
21308
|
-
migrateOnOpen;
|
|
21309
|
-
/** @internal — set when the group is managed (no explicit registry). */
|
|
21310
|
-
stateVault;
|
|
21311
|
-
/** @internal */
|
|
21312
|
-
_attachStateVault(sv) {
|
|
21313
|
-
this.stateVault = sv;
|
|
21314
|
-
}
|
|
21315
|
-
/** Deterministic vault name for a partition key, namespaced by the group. */
|
|
21316
|
-
shardVaultId(partitionKey) {
|
|
21317
|
-
assertSafePartitionKey(partitionKey);
|
|
21318
|
-
return `${this.name}${SHARD_SEPARATOR}${partitionKey}`;
|
|
21319
|
-
}
|
|
21320
|
-
/**
|
|
21321
|
-
* @internal — group-qualified registry record key (avoids cross-group key
|
|
21322
|
-
* collisions). Identical to the shard vault id by design — the registry row
|
|
21323
|
-
* for a shard is keyed by that shard's vault id — so it delegates to
|
|
21324
|
-
* `shardVaultId`, reusing its partition-key validation.
|
|
21325
|
-
*/
|
|
21326
|
-
registryId(partitionKey) {
|
|
21327
|
-
return this.shardVaultId(partitionKey);
|
|
21328
|
-
}
|
|
21329
|
-
/**
|
|
21330
|
-
* Registry rows for THIS group (hydrates the registry collection first).
|
|
21331
|
-
* The registry may be shared across groups (the auto-wired StateManagement
|
|
21332
|
-
* vault holds one `vaultRegistry` for the whole instance), so rows are
|
|
21333
|
-
* filtered by `group` — without this, a group's fan-out reads would leak
|
|
21334
|
-
* across into other groups' shards. Mirrors the `${group}--` scoping that
|
|
21335
|
-
* `liveBinding().isRelevant` already applies to the reactive path.
|
|
21336
|
-
*/
|
|
21337
|
-
async allRows() {
|
|
21338
|
-
await this.registry.list();
|
|
21339
|
-
const rows = this.registry.query().toArray();
|
|
21340
|
-
return rows.filter((r) => r.group === this.name);
|
|
21341
|
-
}
|
|
21342
|
-
/**
|
|
21343
|
-
* Open an existing shard and apply the template. When `migrateOnOpen` is set
|
|
21344
|
-
* (#271) and the shard's registry version is behind the template, its cutover
|
|
21345
|
-
* runs inline first — so a behind shard never surfaces a stale handle.
|
|
21346
|
-
*/
|
|
21347
|
-
async openShard(partitionKey) {
|
|
21348
|
-
if (this.migrateOnOpen) {
|
|
21349
|
-
const row = await this.registry.get(this.registryId(partitionKey));
|
|
21350
|
-
if (row && row.schemaVersion < this.template.version) {
|
|
21351
|
-
await this.migrateShard(partitionKey);
|
|
21352
|
-
}
|
|
21353
|
-
}
|
|
21354
|
-
return this._openShardRaw(partitionKey);
|
|
21355
|
-
}
|
|
21356
|
-
/** @internal — open + configure with no migrate-on-open hook (used by the migration path itself to avoid recursion). */
|
|
21357
|
-
async _openShardRaw(partitionKey) {
|
|
21358
|
-
const vault = await this.db.openVault(this.shardVaultId(partitionKey), { create: false });
|
|
21359
|
-
this.template.configure(vault);
|
|
21360
|
-
return vault;
|
|
21361
|
-
}
|
|
21362
|
-
/**
|
|
21363
|
-
* Idempotently provision a shard for `partitionKey`. Returns the
|
|
21364
|
-
* configured vault handle.
|
|
21365
|
-
*
|
|
21366
|
-
* - row + vault present → no-op, return handle
|
|
21367
|
-
* - row present, vault gone → ShardProvisioningError
|
|
21368
|
-
* - row absent (vault present or not) → open-or-create, configure, write row
|
|
21369
|
-
*
|
|
21370
|
-
* When `region` is given (the routing `put` passes `sharding.regionOf(record)`),
|
|
21371
|
-
* the candidate backend's `capabilities.region` must match or this throws
|
|
21372
|
-
* `DataResidencyError` BEFORE provisioning (#271 data-residency guard).
|
|
21373
|
-
*/
|
|
21374
|
-
async createShard(partitionKey, region) {
|
|
21375
|
-
const vaultId = this.shardVaultId(partitionKey);
|
|
21376
|
-
const row = await this.registry.get(this.registryId(partitionKey));
|
|
21377
|
-
const provisioned = await this.db._shardVaultProvisioned(vaultId);
|
|
21378
|
-
if (row && !provisioned) throw new ShardProvisioningError(vaultId, partitionKey);
|
|
21379
|
-
if (row && provisioned) return this.openShard(partitionKey);
|
|
21380
|
-
if (region !== void 0) {
|
|
21381
|
-
const backendRegion = this.db._resolveBackend(vaultId).capabilities?.region;
|
|
21382
|
-
if (backendRegion !== region) throw new DataResidencyError(vaultId, region, backendRegion);
|
|
21383
|
-
}
|
|
21384
|
-
const vault = await this.db.openVault(vaultId);
|
|
21385
|
-
this.template.configure(vault);
|
|
21386
|
-
await this.registry.put(this.registryId(partitionKey), {
|
|
21387
|
-
vaultId,
|
|
21388
|
-
partitionKey,
|
|
21389
|
-
templateName: this.sharding.vaultTemplate,
|
|
21390
|
-
schemaVersion: this.template.version,
|
|
21391
|
-
createdAt: Date.now(),
|
|
21392
|
-
group: this.name
|
|
21393
|
-
});
|
|
21394
|
-
if (this.stateVault) {
|
|
21395
|
-
try {
|
|
21396
|
-
await this.stateVault.appendEvent({
|
|
21397
|
-
type: "shard-created",
|
|
21398
|
-
group: this.name,
|
|
21399
|
-
vaultId,
|
|
21400
|
-
templateName: this.sharding.vaultTemplate,
|
|
21401
|
-
version: this.template.version
|
|
21402
|
-
});
|
|
21403
|
-
} catch {
|
|
21404
|
-
}
|
|
21405
|
-
}
|
|
21406
|
-
return vault;
|
|
21407
|
-
}
|
|
21408
|
-
/**
|
|
21409
|
-
* Drill down to a single shard's full Collection API. Throws if the shard is unknown.
|
|
21410
|
-
* Also throws ShardProvisioningError if the registry row exists but the vault has been deleted
|
|
21411
|
-
* (registry/store divergence).
|
|
21412
|
-
*/
|
|
21413
|
-
async shard(partitionKey) {
|
|
21414
|
-
const vaultId = this.shardVaultId(partitionKey);
|
|
21415
|
-
const row = await this.registry.get(this.registryId(partitionKey));
|
|
21416
|
-
if (!row) throw new UnknownShardError(partitionKey, this.name);
|
|
21417
|
-
const provisioned = await this.db._shardVaultProvisioned(vaultId);
|
|
21418
|
-
if (!provisioned) throw new ShardProvisioningError(vaultId, partitionKey);
|
|
21419
|
-
return this.openShard(partitionKey);
|
|
21420
|
-
}
|
|
21421
|
-
/** A sharded view over one logical collection across all shards. */
|
|
21422
|
-
collection(collectionName) {
|
|
21423
|
-
return new ShardedCollection(this, collectionName);
|
|
21424
|
-
}
|
|
21425
|
-
/** @internal — eligible (openable-candidate) rows + drift/divergence skips. */
|
|
21426
|
-
async resolveEligible(options = {}) {
|
|
21427
|
-
const rows = await this.allRows();
|
|
21428
|
-
const skipped = [];
|
|
21429
|
-
const versionOk = [];
|
|
21430
|
-
for (const row of rows) {
|
|
21431
|
-
if (options.minVersion !== void 0 && row.schemaVersion < options.minVersion) {
|
|
21432
|
-
skipped.push({ vaultId: row.vaultId, reason: "schema-drift" });
|
|
21433
|
-
} else versionOk.push(row);
|
|
21434
|
-
}
|
|
21435
|
-
const provisioned = await Promise.all(versionOk.map((r) => this.db._shardVaultProvisioned(r.vaultId)));
|
|
21436
|
-
const eligible = [];
|
|
21437
|
-
versionOk.forEach((row, i) => {
|
|
21438
|
-
if (provisioned[i]) eligible.push(row);
|
|
21439
|
-
else skipped.push({ vaultId: row.vaultId, reason: "error", error: new ShardProvisioningError(row.vaultId, row.partitionKey) });
|
|
21440
|
-
});
|
|
21441
|
-
return { eligible, skipped };
|
|
21442
|
-
}
|
|
21443
|
-
/** @internal — registered push-model cross-vault derivations (#271 Insight Vault). */
|
|
21444
|
-
crossVaultDerivations = [];
|
|
21445
|
-
/**
|
|
21446
|
-
* Register a push-model cross-vault derivation — the Insight Vault pattern
|
|
21447
|
-
* (#271, Layer 4). Drive it with {@link refreshInsights}.
|
|
21448
|
-
*
|
|
21449
|
-
* For each shard, `derive(records, ctx)` runs on that shard's `source`
|
|
21450
|
-
* records and its return value is written into the analytics
|
|
21451
|
-
* (`target.vault` / `target.collection`) vault, keyed by partition key —
|
|
21452
|
-
* one summary row per shard. The derivation runs in-process under THIS
|
|
21453
|
-
* group's `Noydb` (which already holds both the shard and Insight Vault
|
|
21454
|
-
* keyrings); the shard's decrypted records are reduced to a summary that is
|
|
21455
|
-
* re-encrypted under the Insight Vault's own DEK, so no shard ciphertext
|
|
21456
|
-
* crosses a DEK boundary.
|
|
21457
|
-
*
|
|
21458
|
-
* **Zero-knowledge note:** the Insight Vault backend sees aggregated
|
|
21459
|
-
* structure (totals, counts, timestamps) drawn from many shards — a weaker
|
|
21460
|
-
* ZK profile than the per-shard vaults. Opt-in; keep summaries to aggregate
|
|
21461
|
-
* scalars (no embeddings / no raw records).
|
|
21462
|
-
*
|
|
21463
|
-
* v1 is explicit-refresh (no write-path push); call `refreshInsights()`
|
|
21464
|
-
* after a batch of writes, or on a schedule.
|
|
21465
|
-
*
|
|
21466
|
-
* The `target.vault` must NOT be the group itself or one of its shards —
|
|
21467
|
-
* a summary writing back into client-shard data would breach the Insight
|
|
21468
|
-
* Vault's separate-DEK-boundary contract. Such a target throws a
|
|
21469
|
-
* `ValidationError` at registration (#271 Insight-write isolation).
|
|
21470
|
-
*/
|
|
21471
|
-
withCrossVaultDerivation(spec) {
|
|
21472
|
-
const target = spec.target.vault;
|
|
21473
|
-
if (target === this.name || target.startsWith(`${this.name}${SHARD_SEPARATOR}`)) {
|
|
21474
|
-
throw new ValidationError(
|
|
21475
|
-
`withCrossVaultDerivation: target.vault "${target}" is the "${this.name}" group itself or one of its shards \u2014 an Insight summary must target a SEPARATE analytics vault, never write back into client-shard data (it would breach the per-shard DEK boundary). Use a distinct vault name.`
|
|
21476
|
-
);
|
|
21477
|
-
}
|
|
21478
|
-
this.crossVaultDerivations.push(spec);
|
|
21479
|
-
}
|
|
21480
|
-
/**
|
|
21481
|
-
* Run every registered {@link withCrossVaultDerivation}: read each eligible
|
|
21482
|
-
* shard's source records, derive a per-shard summary, and write it into the
|
|
21483
|
-
* Insight Vault keyed by partition key. Shards behind `minVersion`,
|
|
21484
|
-
* unprovisioned, or whose read errors are reported in `skippedVaults` and
|
|
21485
|
-
* are not written (a stale summary is never left behind for a failed shard).
|
|
21486
|
-
*/
|
|
21487
|
-
async refreshInsights(options = {}) {
|
|
21488
|
-
if (this.crossVaultDerivations.length === 0) return { written: 0, skippedVaults: [] };
|
|
21489
|
-
const { eligible, skipped } = await this.resolveEligible(
|
|
21490
|
-
options.minVersion !== void 0 ? { minVersion: options.minVersion } : {}
|
|
21491
|
-
);
|
|
21492
|
-
let written = 0;
|
|
21493
|
-
for (const spec of this.crossVaultDerivations) {
|
|
21494
|
-
const results = await this.db.queryAcross(
|
|
21495
|
-
eligible.map((r) => r.vaultId),
|
|
21496
|
-
async (vault) => {
|
|
21497
|
-
this.template.configure(vault);
|
|
21498
|
-
return vault.collection(spec.source).list();
|
|
21499
|
-
},
|
|
21500
|
-
{ create: false, ...options.concurrency !== void 0 ? { concurrency: options.concurrency } : {} }
|
|
21501
|
-
);
|
|
21502
|
-
const insight = await this.db.openVault(spec.target.vault);
|
|
21503
|
-
const out = insight.collection(spec.target.collection);
|
|
21504
|
-
for (let i = 0; i < eligible.length; i++) {
|
|
21505
|
-
const row = eligible[i];
|
|
21506
|
-
const res = results[i];
|
|
21507
|
-
if (!res || res.result === void 0) {
|
|
21508
|
-
skipped.push({ vaultId: row.vaultId, reason: "error", ...res?.error ? { error: res.error } : {} });
|
|
21509
|
-
continue;
|
|
21510
|
-
}
|
|
21511
|
-
const ctx = {
|
|
21512
|
-
vaultId: row.vaultId,
|
|
21513
|
-
partitionKey: row.partitionKey,
|
|
21514
|
-
schemaVersion: row.schemaVersion
|
|
21515
|
-
};
|
|
21516
|
-
const summary = spec.derive(res.result, ctx);
|
|
21517
|
-
await out.put(row.partitionKey, summary);
|
|
21518
|
-
written++;
|
|
21519
|
-
}
|
|
21520
|
-
}
|
|
21521
|
-
return { written, skippedVaults: skipped };
|
|
21522
|
-
}
|
|
21523
|
-
/** @internal — the control-plane vault for migration status; lazily opened. */
|
|
21524
|
-
async ensureStateVault() {
|
|
21525
|
-
if (!this.stateVault) this.stateVault = await StateManagementVault.open(this.db);
|
|
21526
|
-
return this.stateVault;
|
|
21527
|
-
}
|
|
21528
|
-
/**
|
|
21529
|
-
* Migrate ONE shard to the template's current version (#271 fleet runner,
|
|
21530
|
-
* per-shard step). Opens the shard (applying the template, which arms the
|
|
21531
|
-
* M12 cutover), drains schema-write detection, runs `vault.runSchemaCutover()`
|
|
21532
|
-
* (the per-vault drain-barrier-transform protocol), then advances the
|
|
21533
|
-
* registry row's `schemaVersion` and records `migration-status`. A shard
|
|
21534
|
-
* already at the template version is a no-op (`status: 'done'`, migrated 0).
|
|
21535
|
-
* Never throws on a cutover failure — it records `status: 'failed'` and
|
|
21536
|
-
* returns the row, so a fleet run continues past a bad shard.
|
|
21537
|
-
*/
|
|
21538
|
-
async migrateShard(partitionKey) {
|
|
21539
|
-
const vaultId = this.shardVaultId(partitionKey);
|
|
21540
|
-
const row = await this.registry.get(this.registryId(partitionKey));
|
|
21541
|
-
if (!row) throw new UnknownShardError(partitionKey, this.name);
|
|
21542
|
-
const target = this.template.version;
|
|
21543
|
-
const sv = await this.ensureStateVault();
|
|
21544
|
-
const base = { vaultId, group: this.name, currentVersion: row.schemaVersion, targetVersion: target };
|
|
21545
|
-
if (row.schemaVersion >= target) {
|
|
21546
|
-
const done = { ...base, status: "done", migrated: 0, finishedAt: Date.now() };
|
|
21547
|
-
await sv.upsertMigrationStatus(done);
|
|
21548
|
-
return done;
|
|
21549
|
-
}
|
|
21550
|
-
await sv.upsertMigrationStatus({ ...base, status: "running", startedAt: Date.now() });
|
|
21551
|
-
try {
|
|
21552
|
-
await sv.appendEvent({ type: "migration-started", group: this.name, vaultId, version: target });
|
|
21553
|
-
} catch {
|
|
21554
|
-
}
|
|
21555
|
-
try {
|
|
21556
|
-
const vault = await this._openShardRaw(partitionKey);
|
|
21557
|
-
await vault._drainPendingSchemaWrites();
|
|
21558
|
-
const { migrated } = await vault.runSchemaCutover();
|
|
21559
|
-
await this.registry.put(this.registryId(partitionKey), { ...row, schemaVersion: target });
|
|
21560
|
-
const done = { ...base, currentVersion: target, status: "done", migrated, finishedAt: Date.now() };
|
|
21561
|
-
await sv.upsertMigrationStatus(done);
|
|
21562
|
-
try {
|
|
21563
|
-
await sv.appendEvent({ type: "migration-completed", group: this.name, vaultId, version: target });
|
|
21564
|
-
} catch {
|
|
21565
|
-
}
|
|
21566
|
-
return done;
|
|
21567
|
-
} catch (err) {
|
|
21568
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
21569
|
-
const failed = { ...base, status: "failed", error, finishedAt: Date.now() };
|
|
21570
|
-
await sv.upsertMigrationStatus(failed);
|
|
21571
|
-
try {
|
|
21572
|
-
await sv.appendEvent({ type: "migration-failed", group: this.name, vaultId, version: target, detail: error });
|
|
21573
|
-
} catch {
|
|
21574
|
-
}
|
|
21575
|
-
return failed;
|
|
21576
|
-
}
|
|
21577
|
-
}
|
|
21578
|
-
/**
|
|
21579
|
-
* Active batch runner (#271): migrate every shard behind the template version
|
|
21580
|
-
* to it, in controlled batches. **Resumable + crash-safe** — shards already at
|
|
21581
|
-
* the target are skipped (the registry version is the source of truth), so a
|
|
21582
|
-
* re-run after a crash only picks up the unfinished + previously-failed shards.
|
|
21583
|
-
*
|
|
21584
|
-
* - `cohort` — restrict to these partition keys (the staged / canary rollout:
|
|
21585
|
-
* migrate a small cohort, verify the Insight Vault, then run the rest).
|
|
21586
|
-
* - `batchSize` — max shards migrated concurrently per batch (back-pressure).
|
|
21587
|
-
* Default 4. Batches run sequentially; shards within a batch run in parallel.
|
|
21588
|
-
*/
|
|
21589
|
-
async migrateFleet(options = {}) {
|
|
21590
|
-
const target = this.template.version;
|
|
21591
|
-
const rows = await this.allRows();
|
|
21592
|
-
const cohort = options.cohort;
|
|
21593
|
-
const todo = rows.filter(
|
|
21594
|
-
(r) => r.schemaVersion < target && (cohort === void 0 || cohort.includes(r.partitionKey))
|
|
21595
|
-
);
|
|
21596
|
-
const batchSize = Math.max(1, options.batchSize ?? 4);
|
|
21597
|
-
const migrated = [];
|
|
21598
|
-
const failed = [];
|
|
21599
|
-
for (let i = 0; i < todo.length; i += batchSize) {
|
|
21600
|
-
const batch = todo.slice(i, i + batchSize);
|
|
21601
|
-
const settled = await Promise.all(batch.map((r) => this.migrateShard(r.partitionKey)));
|
|
21602
|
-
for (const res of settled) {
|
|
21603
|
-
if (res.status === "done") migrated.push(res.vaultId);
|
|
21604
|
-
else failed.push({ vaultId: res.vaultId, error: res.error ?? "unknown" });
|
|
21605
|
-
}
|
|
21606
|
-
}
|
|
21607
|
-
return { target, migrated, failed };
|
|
21608
|
-
}
|
|
21609
|
-
};
|
|
21610
|
-
ShardedCollection = class {
|
|
21611
|
-
constructor(group, collectionName) {
|
|
21612
|
-
this.group = group;
|
|
21613
|
-
this.collectionName = collectionName;
|
|
21614
|
-
}
|
|
21615
|
-
group;
|
|
21616
|
-
collectionName;
|
|
21617
|
-
/** Route a write to the shard owning `keyOf(record)`. */
|
|
21618
|
-
async put(id, record) {
|
|
21619
|
-
const key = this.group.sharding.keyOf(record);
|
|
21620
|
-
const row = await this.group.registry.get(this.group.registryId(key));
|
|
21621
|
-
let vault;
|
|
21622
|
-
if (!row) {
|
|
21623
|
-
if (this.group.sharding.autoCreate === false) {
|
|
21624
|
-
throw new UnknownShardError(key, this.group.name);
|
|
21625
|
-
}
|
|
21626
|
-
vault = await this.group.createShard(key, this.group.sharding.regionOf?.(record));
|
|
21627
|
-
} else {
|
|
21628
|
-
vault = await this.group.openShard(key);
|
|
21629
|
-
}
|
|
21630
|
-
await vault.collection(this.collectionName).put(id, record);
|
|
21631
|
-
}
|
|
21632
|
-
/** Begin a cross-shard fan-out query. */
|
|
21633
|
-
query() {
|
|
21634
|
-
return new ShardedQuery(this.group, this.collectionName, []);
|
|
21635
|
-
}
|
|
21636
|
-
};
|
|
21637
|
-
ShardedQuery = class _ShardedQuery {
|
|
21638
|
-
constructor(group, collectionName, clauses, coPartitionedLegs = [], broadcastLegs = []) {
|
|
21639
|
-
this.group = group;
|
|
21640
|
-
this.collectionName = collectionName;
|
|
21641
|
-
this.clauses = clauses;
|
|
21642
|
-
this.coPartitionedLegs = coPartitionedLegs;
|
|
21643
|
-
this.broadcastLegs = broadcastLegs;
|
|
21644
|
-
}
|
|
21645
|
-
group;
|
|
21646
|
-
collectionName;
|
|
21647
|
-
clauses;
|
|
21648
|
-
coPartitionedLegs;
|
|
21649
|
-
broadcastLegs;
|
|
21650
|
-
where(field, op, value) {
|
|
21651
|
-
return new _ShardedQuery(
|
|
21652
|
-
this.group,
|
|
21653
|
-
this.collectionName,
|
|
21654
|
-
[...this.clauses, { field, op, value }],
|
|
21655
|
-
this.coPartitionedLegs,
|
|
21656
|
-
this.broadcastLegs
|
|
21657
|
-
);
|
|
21658
|
-
}
|
|
21659
|
-
/** Co-partitioned join: each shard joins its own same-vault right collection (resolved via ref()), then union. */
|
|
21660
|
-
crossShardJoin(field, opts) {
|
|
21661
|
-
const leg = { field, as: opts.as, maxRows: opts.maxRows, strategy: opts.strategy };
|
|
21662
|
-
return new _ShardedQuery(
|
|
21663
|
-
this.group,
|
|
21664
|
-
this.collectionName,
|
|
21665
|
-
this.clauses,
|
|
21666
|
-
[...this.coPartitionedLegs, leg],
|
|
21667
|
-
this.broadcastLegs
|
|
21668
|
-
);
|
|
21669
|
-
}
|
|
21670
|
-
/** Broadcast dimension join: enrich every merged row from a single shared collection. */
|
|
21671
|
-
broadcastJoin(field, opts) {
|
|
21672
|
-
const leg = {
|
|
21673
|
-
field,
|
|
21674
|
-
as: opts.as,
|
|
21675
|
-
from: opts.from,
|
|
21676
|
-
on: opts.on ?? "id",
|
|
21677
|
-
mode: opts.mode ?? "warn"
|
|
21678
|
-
};
|
|
21679
|
-
return new _ShardedQuery(
|
|
21680
|
-
this.group,
|
|
21681
|
-
this.collectionName,
|
|
21682
|
-
this.clauses,
|
|
21683
|
-
this.coPartitionedLegs,
|
|
21684
|
-
[...this.broadcastLegs, leg]
|
|
21685
|
-
);
|
|
21686
|
-
}
|
|
21687
|
-
/** @internal — fan out the where-filtered records across eligible shards. */
|
|
21688
|
-
async fanoutRecords(options = {}) {
|
|
21689
|
-
const { eligible, skipped } = await this.group.resolveEligible(options);
|
|
21690
|
-
const probeRow = eligible[0];
|
|
21691
|
-
if (this.coPartitionedLegs.length > 0 && probeRow) {
|
|
21692
|
-
const probe = await this.group.openShard(probeRow.partitionKey);
|
|
21693
|
-
this.group.template.configure(probe);
|
|
21694
|
-
for (const leg of this.coPartitionedLegs) {
|
|
21695
|
-
if (!probe.resolveRef(this.collectionName, leg.field)) {
|
|
21696
|
-
throw new CrossShardJoinError(
|
|
21697
|
-
`crossShardJoin("${leg.field}"): no ref() declared for "${leg.field}" on collection "${this.collectionName}" in template "${this.group.sharding.vaultTemplate}". Add refs: { ${leg.field}: ref('<target>') } to the template's collection options.`
|
|
21698
|
-
);
|
|
21699
|
-
}
|
|
21700
|
-
}
|
|
21701
|
-
}
|
|
21702
|
-
const across = await this.group.db.queryAcross(
|
|
21703
|
-
eligible.map((r) => r.vaultId),
|
|
21704
|
-
async (vault) => {
|
|
21705
|
-
this.group.template.configure(vault);
|
|
21706
|
-
const coll = vault.collection(this.collectionName);
|
|
21707
|
-
await coll.list();
|
|
21708
|
-
for (const leg of this.coPartitionedLegs) {
|
|
21709
|
-
const desc = vault.resolveRef(this.collectionName, leg.field);
|
|
21710
|
-
if (desc) await vault.collection(desc.target).list();
|
|
21711
|
-
}
|
|
21712
|
-
let q = coll.query();
|
|
21713
|
-
for (const c of this.clauses) q = q.where(c.field, c.op, c.value);
|
|
21714
|
-
for (const leg of this.coPartitionedLegs) {
|
|
21715
|
-
q = q.join(leg.field, {
|
|
21716
|
-
as: leg.as,
|
|
21717
|
-
...leg.maxRows !== void 0 ? { maxRows: leg.maxRows } : {},
|
|
21718
|
-
...leg.strategy ? { strategy: leg.strategy } : {}
|
|
21719
|
-
});
|
|
21720
|
-
}
|
|
21721
|
-
return q.toArray();
|
|
21722
|
-
},
|
|
21723
|
-
{ concurrency: options.concurrency ?? 1, create: false }
|
|
21724
|
-
);
|
|
21725
|
-
const results = [];
|
|
21726
|
-
for (const r of across) {
|
|
21727
|
-
if (r.error) skipped.push({ vaultId: r.vault, reason: classifyShardSkip(r.error), error: r.error });
|
|
21728
|
-
else for (const item of r.result) results.push(item);
|
|
21729
|
-
}
|
|
21730
|
-
return { records: results, skippedVaults: skipped };
|
|
21731
|
-
}
|
|
21732
|
-
/** Fan out across eligible shards, merge, then apply any broadcast dimension legs. */
|
|
21733
|
-
async toArray(options = {}) {
|
|
21734
|
-
const { records, skippedVaults } = await this.fanoutRecords(options);
|
|
21735
|
-
const results = await applyBroadcastLegs(records, this.broadcastLegs);
|
|
21736
|
-
return { results, skippedVaults };
|
|
21737
|
-
}
|
|
21738
|
-
/** @internal — build the change-subscription + relevance binding for this query's group+collection. */
|
|
21739
|
-
liveBinding() {
|
|
21740
|
-
const group = this.group;
|
|
21741
|
-
const collectionName = this.collectionName;
|
|
21742
|
-
return {
|
|
21743
|
-
subscribeToChanges: (h) => {
|
|
21744
|
-
group.db.on("change", h);
|
|
21745
|
-
return () => group.db.off("change", h);
|
|
21746
|
-
},
|
|
21747
|
-
isRelevant: (e) => e.collection === collectionName && e.vault.startsWith(`${group.name}--`)
|
|
21748
|
-
};
|
|
21749
|
-
}
|
|
21750
|
-
/** @internal — joined queries don't support reactive/aggregate surfaces in v1. */
|
|
21751
|
-
assertNoJoinLegs(surface) {
|
|
21752
|
-
if (this.coPartitionedLegs.length || this.broadcastLegs.length) {
|
|
21753
|
-
throw new CrossShardJoinError(
|
|
21754
|
-
`${surface}() is not supported on a ShardedQuery with crossShardJoin/broadcastJoin legs in v1. Use toArray() for joined cross-shard queries.`
|
|
21755
|
-
);
|
|
21756
|
-
}
|
|
21757
|
-
}
|
|
21758
|
-
/** Returns a reactive cross-shard live query — a facade over CrossVaultLive. */
|
|
21759
|
-
live(options = {}) {
|
|
21760
|
-
this.assertNoJoinLegs("live");
|
|
21761
|
-
const bind = this.liveBinding();
|
|
21762
|
-
const core = new CrossVaultLive({
|
|
21763
|
-
...bind,
|
|
21764
|
-
compute: async () => {
|
|
21765
|
-
const { records, skippedVaults } = await this.fanoutRecords(options);
|
|
21766
|
-
return { records, skipped: skippedVaults };
|
|
21767
|
-
},
|
|
21768
|
-
initialSnapshot: { records: [], skipped: [] },
|
|
21769
|
-
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
21770
|
-
});
|
|
21771
|
-
return {
|
|
21772
|
-
get value() {
|
|
21773
|
-
return core.snapshot.records;
|
|
21774
|
-
},
|
|
21775
|
-
get skippedVaults() {
|
|
21776
|
-
return core.snapshot.skipped;
|
|
21777
|
-
},
|
|
21778
|
-
get error() {
|
|
21779
|
-
return core.error;
|
|
21780
|
-
},
|
|
21781
|
-
ready: core.ready,
|
|
21782
|
-
subscribe: (cb) => core.subscribe(cb),
|
|
21783
|
-
stop: () => core.stop()
|
|
21784
|
-
};
|
|
21785
|
-
}
|
|
21786
|
-
/** One-shot distributed aggregate — central reduce over all shard records. */
|
|
21787
|
-
aggregate(spec) {
|
|
21788
|
-
this.assertNoJoinLegs("aggregate");
|
|
21789
|
-
return new CrossVaultAggregation(this, spec, this.liveBinding());
|
|
21790
|
-
}
|
|
21791
|
-
/** Begin a grouped cross-shard aggregate. */
|
|
21792
|
-
groupBy(field) {
|
|
21793
|
-
this.assertNoJoinLegs("groupBy");
|
|
21794
|
-
return new ShardedGroupedQuery(this, field);
|
|
21795
|
-
}
|
|
21796
|
-
};
|
|
21797
|
-
ShardedGroupedQuery = class {
|
|
21798
|
-
constructor(query, field) {
|
|
21799
|
-
this.query = query;
|
|
21800
|
-
this.field = field;
|
|
21801
|
-
}
|
|
21802
|
-
query;
|
|
21803
|
-
field;
|
|
21804
|
-
aggregate(spec) {
|
|
21805
|
-
return new CrossVaultGroupedAggregation(
|
|
21806
|
-
{ fanoutRecords: (o) => this.query.fanoutRecords(o) },
|
|
21807
|
-
this.field,
|
|
21808
|
-
spec,
|
|
21809
|
-
this.query.liveBinding()
|
|
21810
|
-
);
|
|
21811
|
-
}
|
|
21812
|
-
};
|
|
21813
|
-
}
|
|
21814
|
-
});
|
|
21815
|
-
|
|
21816
21001
|
// src/noydb.ts
|
|
21817
21002
|
var noydb_exports = {};
|
|
21818
21003
|
__export(noydb_exports, {
|
|
@@ -21873,7 +21058,6 @@ var init_noydb = __esm({
|
|
|
21873
21058
|
"src/noydb.ts"() {
|
|
21874
21059
|
"use strict";
|
|
21875
21060
|
init_errors();
|
|
21876
|
-
init_constants2();
|
|
21877
21061
|
init_storage3();
|
|
21878
21062
|
init_rotate_recover();
|
|
21879
21063
|
init_peer_recover();
|
|
@@ -21907,6 +21091,12 @@ var init_noydb = __esm({
|
|
|
21907
21091
|
client: 1,
|
|
21908
21092
|
viewer: 2,
|
|
21909
21093
|
operator: 3,
|
|
21094
|
+
// FR-6: custodian is operationally admin-rank (rw + access on every
|
|
21095
|
+
// collection) — it ranks alongside admin for "how much can this
|
|
21096
|
+
// principal see/operate." It is NOT above admin, and explicitly below
|
|
21097
|
+
// owner: a custodian can never grant/revoke/rotate/sever (those are
|
|
21098
|
+
// owner meta-capabilities), so it must not outrank or equal the owner.
|
|
21099
|
+
custodian: 4,
|
|
21910
21100
|
admin: 4,
|
|
21911
21101
|
owner: 5
|
|
21912
21102
|
};
|
|
@@ -21955,7 +21145,6 @@ var init_noydb = __esm({
|
|
|
21955
21145
|
writeRelay;
|
|
21956
21146
|
/** Per-vault policy enforcers. */
|
|
21957
21147
|
policyEnforcers = /* @__PURE__ */ new Map();
|
|
21958
|
-
vaultTemplates = /* @__PURE__ */ new Map();
|
|
21959
21148
|
txStrategy;
|
|
21960
21149
|
forgetStrategy;
|
|
21961
21150
|
sessionStrategy;
|
|
@@ -22405,6 +21594,37 @@ var init_noydb = __esm({
|
|
|
22405
21594
|
const keyring = await this.getKeyringInternal(vault);
|
|
22406
21595
|
await revoke(this.options.store, vault, keyring, options);
|
|
22407
21596
|
}
|
|
21597
|
+
/**
|
|
21598
|
+
* Grant the FR-6 `custodian` role to a user (owner-only custody API).
|
|
21599
|
+
*
|
|
21600
|
+
* A custodian operates every collection (rw + access) but is provably
|
|
21601
|
+
* unable to grant / revoke / rotate / extract-and-sever. Only the Deed
|
|
21602
|
+
* owner may mint one. Defended in depth: the `grant-custodian` gate
|
|
21603
|
+
* (fail-closed) AND an explicit `keyring.role !== 'owner'` check — the
|
|
21604
|
+
* gate enforces host policy, the role check enforces the cryptographic
|
|
21605
|
+
* owner-only invariant even if a host mis-configures the gate.
|
|
21606
|
+
*/
|
|
21607
|
+
async grantCustodian(vault, options, factors) {
|
|
21608
|
+
this.checkPolicyOperation(vault, "grant");
|
|
21609
|
+
await this.checkGate(vault, "grant-custodian", factors);
|
|
21610
|
+
const keyring = await this.getKeyringInternal(vault);
|
|
21611
|
+
if (keyring.role !== "owner") throw new PermissionDeniedError("only the Deed owner can grant a custodian");
|
|
21612
|
+
await grant(this.options.store, vault, keyring, { ...options, role: "custodian" });
|
|
21613
|
+
}
|
|
21614
|
+
/**
|
|
21615
|
+
* Revoke a custodian (owner-only custody API).
|
|
21616
|
+
*
|
|
21617
|
+
* Mirrors {@link revoke} but pins the caller to the Deed owner: defended
|
|
21618
|
+
* in depth by the `revoke-user` gate AND an explicit `keyring.role !==
|
|
21619
|
+
* 'owner'` check, so an admin cannot unwind a custodianship.
|
|
21620
|
+
*/
|
|
21621
|
+
async revokeCustodian(vault, options, factors) {
|
|
21622
|
+
this.checkPolicyOperation(vault, "revoke");
|
|
21623
|
+
await this.checkGate(vault, "revoke-user", factors);
|
|
21624
|
+
const keyring = await this.getKeyringInternal(vault);
|
|
21625
|
+
if (keyring.role !== "owner") throw new PermissionDeniedError("only the Deed owner can revoke a custodian");
|
|
21626
|
+
await revoke(this.options.store, vault, keyring, options);
|
|
21627
|
+
}
|
|
22408
21628
|
/**
|
|
22409
21629
|
* Mutate post-grant identity fields on an existing keyring — `role`,
|
|
22410
21630
|
* `displayName`, and/or `permissions`. Pure plaintext-header rewrite:
|
|
@@ -22674,52 +21894,24 @@ var init_noydb = __esm({
|
|
|
22674
21894
|
return results;
|
|
22675
21895
|
}
|
|
22676
21896
|
/**
|
|
22677
|
-
*
|
|
22678
|
-
*
|
|
21897
|
+
* @internal True once `close()` has been called. Read by
|
|
21898
|
+
* `@klum-db/lobby`'s Lobby entry points (which can't see the private
|
|
21899
|
+
* `closed` field).
|
|
22679
21900
|
*/
|
|
22680
|
-
|
|
22681
|
-
this.
|
|
21901
|
+
get isClosed() {
|
|
21902
|
+
return this.closed;
|
|
22682
21903
|
}
|
|
22683
|
-
/**
|
|
22684
|
-
|
|
22685
|
-
|
|
22686
|
-
* collection.
|
|
22687
|
-
*/
|
|
22688
|
-
async openVaultGroup(name, opts) {
|
|
22689
|
-
if (this.closed) throw new ValidationError("Instance is closed");
|
|
22690
|
-
if (name === STATE_VAULT_NAME) throw new ReservedVaultNameError(name);
|
|
22691
|
-
const template = this.vaultTemplates.get(opts.sharding.vaultTemplate);
|
|
22692
|
-
if (!template) throw new VaultTemplateNotFoundError(opts.sharding.vaultTemplate);
|
|
22693
|
-
const { VaultGroup: VaultGroup2 } = await Promise.resolve().then(() => (init_vault_group(), vault_group_exports));
|
|
22694
|
-
const { StateManagementVault: StateManagementVault2 } = await Promise.resolve().then(() => (init_state_vault(), state_vault_exports));
|
|
22695
|
-
const stateVault = opts.registry ? void 0 : await StateManagementVault2.open(this);
|
|
22696
|
-
const registry = opts.registry ?? stateVault.registry;
|
|
22697
|
-
const group = new VaultGroup2(this, name, registry, opts.sharding, template, opts.migrateOnOpen ?? false);
|
|
22698
|
-
if (stateVault) {
|
|
22699
|
-
group._attachStateVault(stateVault);
|
|
22700
|
-
await stateVault.recordManifest(opts.sharding.vaultTemplate, template);
|
|
22701
|
-
try {
|
|
22702
|
-
await stateVault.appendEvent({
|
|
22703
|
-
type: "manifest-recorded",
|
|
22704
|
-
group: name,
|
|
22705
|
-
templateName: opts.sharding.vaultTemplate,
|
|
22706
|
-
version: template.version
|
|
22707
|
-
});
|
|
22708
|
-
await stateVault.appendEvent({ type: "group-opened", group: name });
|
|
22709
|
-
} catch {
|
|
22710
|
-
}
|
|
22711
|
-
}
|
|
22712
|
-
return group;
|
|
21904
|
+
/** @deprecated Federation moved to @klum-db/lobby. Use `createLobby(db).withVaultTemplate(...)`. */
|
|
21905
|
+
withVaultTemplate() {
|
|
21906
|
+
throw new FederationMovedError("withVaultTemplate");
|
|
22713
21907
|
}
|
|
22714
|
-
/**
|
|
22715
|
-
|
|
22716
|
-
|
|
22717
|
-
|
|
22718
|
-
|
|
21908
|
+
/** @deprecated Federation moved to @klum-db/lobby. Use `createLobby(db).openVaultGroup(...)`. */
|
|
21909
|
+
async openVaultGroup() {
|
|
21910
|
+
throw new FederationMovedError("openVaultGroup");
|
|
21911
|
+
}
|
|
21912
|
+
/** @deprecated Federation moved to @klum-db/lobby. Use `createLobby(db).openStateManagementVault()`. */
|
|
22719
21913
|
async openStateManagementVault() {
|
|
22720
|
-
|
|
22721
|
-
const { StateManagementVault: StateManagementVault2 } = await Promise.resolve().then(() => (init_state_vault(), state_vault_exports));
|
|
22722
|
-
return StateManagementVault2.open(this);
|
|
21914
|
+
throw new FederationMovedError("openStateManagementVault");
|
|
22723
21915
|
}
|
|
22724
21916
|
/**
|
|
22725
21917
|
* @internal — true when an encrypted shard vault is provisioned
|
|
@@ -24211,21 +23403,30 @@ __export(bundle_exports, {
|
|
|
24211
23403
|
NOYDB_BUNDLE_FORMAT_VERSION: () => NOYDB_BUNDLE_FORMAT_VERSION,
|
|
24212
23404
|
NOYDB_BUNDLE_MAGIC: () => NOYDB_BUNDLE_MAGIC,
|
|
24213
23405
|
NOYDB_BUNDLE_PREFIX_BYTES: () => NOYDB_BUNDLE_PREFIX_BYTES,
|
|
23406
|
+
NOYDB_MULTI_BUNDLE_MAGIC: () => NOYDB_MULTI_BUNDLE_MAGIC,
|
|
23407
|
+
NOYDB_MULTI_BUNDLE_PREFIX_BYTES: () => NOYDB_MULTI_BUNDLE_PREFIX_BYTES,
|
|
23408
|
+
NOYDB_MULTI_BUNDLE_VERSION: () => NOYDB_MULTI_BUNDLE_VERSION,
|
|
24214
23409
|
PartitionExtractionError: () => PartitionExtractionError,
|
|
24215
23410
|
TransferSealError: () => TransferSealError,
|
|
24216
23411
|
adoptPartition: () => adoptPartition,
|
|
24217
23412
|
createOwnerOnAdoptedPartition: () => createOwnerOnAdoptedPartition,
|
|
23413
|
+
decodeMultiBundle: () => decodeMultiBundle,
|
|
23414
|
+
decryptExtractedPartition: () => decryptExtractedPartition,
|
|
24218
23415
|
describeExtraction: () => describeExtraction,
|
|
24219
23416
|
encodeBundleHeader: () => encodeBundleHeader,
|
|
23417
|
+
encodeMultiBundle: () => encodeMultiBundle,
|
|
24220
23418
|
extractPartition: () => extractPartition,
|
|
24221
23419
|
generateULID: () => generateULID,
|
|
24222
23420
|
isULID: () => isULID,
|
|
23421
|
+
readMultiVaultBundleCompartment: () => readMultiVaultBundleCompartment,
|
|
24223
23422
|
readNoydbBundle: () => readNoydbBundle,
|
|
24224
23423
|
readNoydbBundleHeader: () => readNoydbBundleHeader,
|
|
23424
|
+
readNoydbBundleManifest: () => readNoydbBundleManifest,
|
|
24225
23425
|
resetBrotliSupportCache: () => resetBrotliSupportCache,
|
|
24226
23426
|
unsealDeks: () => unsealDeks,
|
|
24227
23427
|
validateBundleHeader: () => validateBundleHeader,
|
|
24228
23428
|
walkClosure: () => walkClosure,
|
|
23429
|
+
writeMultiVaultBundle: () => writeMultiVaultBundle,
|
|
24229
23430
|
writeNoydbBundle: () => writeNoydbBundle
|
|
24230
23431
|
});
|
|
24231
23432
|
module.exports = __toCommonJS(bundle_exports);
|
|
@@ -24233,6 +23434,160 @@ init_bundle();
|
|
|
24233
23434
|
init_format();
|
|
24234
23435
|
init_ulid();
|
|
24235
23436
|
|
|
23437
|
+
// src/bundle/multi-bundle.ts
|
|
23438
|
+
init_crypto();
|
|
23439
|
+
init_ulid();
|
|
23440
|
+
init_format();
|
|
23441
|
+
init_bundle();
|
|
23442
|
+
var NOYDB_MULTI_BUNDLE_MAGIC = new Uint8Array([78, 68, 66, 77]);
|
|
23443
|
+
var NOYDB_MULTI_BUNDLE_PREFIX_BYTES = 10;
|
|
23444
|
+
var NOYDB_MULTI_BUNDLE_VERSION = 1;
|
|
23445
|
+
function encodeMultiBundle(manifest, inner) {
|
|
23446
|
+
validateManifest(manifest);
|
|
23447
|
+
if (manifest.compartments.length !== inner.length) {
|
|
23448
|
+
throw new Error(`multi-bundle: manifest has ${manifest.compartments.length} compartments but ${inner.length} inner bundles were provided.`);
|
|
23449
|
+
}
|
|
23450
|
+
for (let i = 0; i < inner.length; i++) {
|
|
23451
|
+
if (manifest.compartments[i].innerBytes !== inner[i].length) {
|
|
23452
|
+
throw new Error(`multi-bundle: compartment ${i} declares innerBytes ${manifest.compartments[i].innerBytes} but ${inner[i].length} bytes were provided.`);
|
|
23453
|
+
}
|
|
23454
|
+
}
|
|
23455
|
+
const manifestBytes = new TextEncoder().encode(JSON.stringify(manifest));
|
|
23456
|
+
const bodyLen = inner.reduce((n, b) => n + b.length, 0);
|
|
23457
|
+
const out = new Uint8Array(NOYDB_MULTI_BUNDLE_PREFIX_BYTES + manifestBytes.length + bodyLen);
|
|
23458
|
+
out.set(NOYDB_MULTI_BUNDLE_MAGIC, 0);
|
|
23459
|
+
out[4] = NOYDB_MULTI_BUNDLE_VERSION;
|
|
23460
|
+
out[5] = 0;
|
|
23461
|
+
writeUint32BE(out, 6, manifestBytes.length);
|
|
23462
|
+
out.set(manifestBytes, NOYDB_MULTI_BUNDLE_PREFIX_BYTES);
|
|
23463
|
+
let off = NOYDB_MULTI_BUNDLE_PREFIX_BYTES + manifestBytes.length;
|
|
23464
|
+
for (const b of inner) {
|
|
23465
|
+
out.set(b, off);
|
|
23466
|
+
off += b.length;
|
|
23467
|
+
}
|
|
23468
|
+
return out;
|
|
23469
|
+
}
|
|
23470
|
+
function hasMultiMagic(bytes) {
|
|
23471
|
+
if (bytes.length < NOYDB_MULTI_BUNDLE_MAGIC.length) return false;
|
|
23472
|
+
for (let i = 0; i < NOYDB_MULTI_BUNDLE_MAGIC.length; i++) if (bytes[i] !== NOYDB_MULTI_BUNDLE_MAGIC[i]) return false;
|
|
23473
|
+
return true;
|
|
23474
|
+
}
|
|
23475
|
+
function validateManifest(parsed) {
|
|
23476
|
+
if (parsed === null || typeof parsed !== "object") throw new Error("multi-bundle manifest must be a JSON object.");
|
|
23477
|
+
const m = parsed;
|
|
23478
|
+
if (m["multiFormatVersion"] !== NOYDB_MULTI_BUNDLE_VERSION) throw new Error(`multi-bundle manifest.multiFormatVersion must be ${NOYDB_MULTI_BUNDLE_VERSION}, got ${String(m["multiFormatVersion"])}.`);
|
|
23479
|
+
if (typeof m["handle"] !== "string" || m["handle"].length === 0) throw new Error("multi-bundle manifest.handle must be a non-empty string.");
|
|
23480
|
+
if (!Array.isArray(m["compartments"])) throw new Error("multi-bundle manifest.compartments must be an array.");
|
|
23481
|
+
const seenHandles = /* @__PURE__ */ new Set();
|
|
23482
|
+
for (const c of m["compartments"]) {
|
|
23483
|
+
if (c === null || typeof c !== "object") throw new Error("multi-bundle compartment must be an object.");
|
|
23484
|
+
const e = c;
|
|
23485
|
+
if (typeof e["handle"] !== "string" || e["handle"].length === 0) throw new Error("multi-bundle compartment.handle must be a non-empty string.");
|
|
23486
|
+
if (seenHandles.has(e["handle"])) {
|
|
23487
|
+
throw new Error(`multi-bundle manifest has a duplicate compartment handle "${e["handle"]}".`);
|
|
23488
|
+
}
|
|
23489
|
+
seenHandles.add(e["handle"]);
|
|
23490
|
+
if (typeof e["innerBytes"] !== "number" || !Number.isInteger(e["innerBytes"]) || e["innerBytes"] < 0) throw new Error("multi-bundle compartment.innerBytes must be a non-negative integer.");
|
|
23491
|
+
if (typeof e["innerSha256"] !== "string" || !/^[0-9a-f]{64}$/.test(e["innerSha256"])) throw new Error("multi-bundle compartment.innerSha256 must be 64-char lowercase hex.");
|
|
23492
|
+
}
|
|
23493
|
+
}
|
|
23494
|
+
function decodeMultiBundle(bytes) {
|
|
23495
|
+
if (!hasMultiMagic(bytes)) throw new Error("not a NOYDB multi-bundle: missing NDBM magic.");
|
|
23496
|
+
if (bytes.length < NOYDB_MULTI_BUNDLE_PREFIX_BYTES) throw new Error("multi-bundle truncated: shorter than the fixed prefix.");
|
|
23497
|
+
if (bytes[4] !== NOYDB_MULTI_BUNDLE_VERSION) throw new Error(`unsupported multi-bundle version ${String(bytes[4])}.`);
|
|
23498
|
+
const manifestLen = readUint32BE(bytes, 6);
|
|
23499
|
+
const manifestEnd = NOYDB_MULTI_BUNDLE_PREFIX_BYTES + manifestLen;
|
|
23500
|
+
if (manifestEnd > bytes.length) throw new Error("multi-bundle truncated: manifest length overruns the buffer.");
|
|
23501
|
+
const manifestJson = new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(NOYDB_MULTI_BUNDLE_PREFIX_BYTES, manifestEnd));
|
|
23502
|
+
let parsed;
|
|
23503
|
+
try {
|
|
23504
|
+
parsed = JSON.parse(manifestJson);
|
|
23505
|
+
} catch (err) {
|
|
23506
|
+
throw new Error(`multi-bundle manifest is not valid JSON: ${err.message}`);
|
|
23507
|
+
}
|
|
23508
|
+
validateManifest(parsed);
|
|
23509
|
+
const inner = [];
|
|
23510
|
+
let off = manifestEnd;
|
|
23511
|
+
for (const c of parsed.compartments) {
|
|
23512
|
+
const end = off + c.innerBytes;
|
|
23513
|
+
if (end > bytes.length) throw new Error(`multi-bundle truncated: compartment "${c.handle}" innerBytes overruns the buffer.`);
|
|
23514
|
+
inner.push(bytes.subarray(off, end));
|
|
23515
|
+
off = end;
|
|
23516
|
+
}
|
|
23517
|
+
if (off !== bytes.length) {
|
|
23518
|
+
throw new Error(`multi-bundle: ${bytes.length - off} trailing byte(s) after the last compartment \u2014 buffer may be corrupt.`);
|
|
23519
|
+
}
|
|
23520
|
+
return { manifest: parsed, inner };
|
|
23521
|
+
}
|
|
23522
|
+
async function writeMultiVaultBundle(compartments, opts = {}) {
|
|
23523
|
+
if (compartments.length === 0) throw new Error("writeMultiVaultBundle: at least one compartment is required.");
|
|
23524
|
+
const inner = [];
|
|
23525
|
+
const entries = [];
|
|
23526
|
+
for (const c of compartments) {
|
|
23527
|
+
const innerBytes = await writeNoydbBundle(c.vault, c.bundleOptions ?? {});
|
|
23528
|
+
const header = readNoydbBundleHeader(innerBytes);
|
|
23529
|
+
const entry = {
|
|
23530
|
+
handle: header.handle,
|
|
23531
|
+
exportedAt: c.exportedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
23532
|
+
innerBytes: innerBytes.length,
|
|
23533
|
+
innerSha256: await sha256Hex2(innerBytes)
|
|
23534
|
+
};
|
|
23535
|
+
if (c.roleTag !== void 0) entry.roleTag = c.roleTag;
|
|
23536
|
+
if (c.disclose?.name !== void 0 && c.disclose.name !== false) {
|
|
23537
|
+
entry.name = c.disclose.name === true ? c.vault.name : c.disclose.name;
|
|
23538
|
+
}
|
|
23539
|
+
if (c.disclose?.collections === true) {
|
|
23540
|
+
const names = await c.vault.collections();
|
|
23541
|
+
entry.collections = await Promise.all(
|
|
23542
|
+
names.map(async (n) => ({ name: n, count: await c.vault.collection(n).count() }))
|
|
23543
|
+
);
|
|
23544
|
+
}
|
|
23545
|
+
if (c.disclose?.publicEnvelope === true) {
|
|
23546
|
+
const env = readNoydbBundlePublicEnvelope(innerBytes);
|
|
23547
|
+
if (env !== void 0) entry.publicEnvelope = env;
|
|
23548
|
+
}
|
|
23549
|
+
const fence = await c.vault.schemaFenceState();
|
|
23550
|
+
entry.schemaVersion = fence.currentSchemaVersion;
|
|
23551
|
+
inner.push(innerBytes);
|
|
23552
|
+
entries.push(entry);
|
|
23553
|
+
}
|
|
23554
|
+
const manifest = {
|
|
23555
|
+
multiFormatVersion: NOYDB_MULTI_BUNDLE_VERSION,
|
|
23556
|
+
handle: opts.handle ?? generateULID(),
|
|
23557
|
+
compartments: entries
|
|
23558
|
+
};
|
|
23559
|
+
return encodeMultiBundle(manifest, inner);
|
|
23560
|
+
}
|
|
23561
|
+
async function readNoydbBundleManifest(bytes) {
|
|
23562
|
+
if (hasMultiMagic(bytes)) return [...decodeMultiBundle(bytes).manifest.compartments];
|
|
23563
|
+
if (hasNoydbBundleMagic(bytes)) {
|
|
23564
|
+
const header = readNoydbBundleHeader(bytes);
|
|
23565
|
+
const env = readNoydbBundlePublicEnvelope(bytes);
|
|
23566
|
+
const entry = {
|
|
23567
|
+
handle: header.handle,
|
|
23568
|
+
innerBytes: bytes.length,
|
|
23569
|
+
innerSha256: await sha256Hex2(bytes)
|
|
23570
|
+
};
|
|
23571
|
+
if (env !== void 0) entry.publicEnvelope = env;
|
|
23572
|
+
return [entry];
|
|
23573
|
+
}
|
|
23574
|
+
throw new Error("readNoydbBundleManifest: not a NOYDB bundle (no NDB1 or NDBM magic).");
|
|
23575
|
+
}
|
|
23576
|
+
function readMultiVaultBundleCompartment(bytes, selector) {
|
|
23577
|
+
if (typeof selector === "number" && !Number.isInteger(selector)) {
|
|
23578
|
+
throw new Error(`readMultiVaultBundleCompartment: numeric selector must be an integer, got ${selector}.`);
|
|
23579
|
+
}
|
|
23580
|
+
if (hasNoydbBundleMagic(bytes) && !hasMultiMagic(bytes)) {
|
|
23581
|
+
const header = readNoydbBundleHeader(bytes);
|
|
23582
|
+
if (selector === 0 || selector === header.handle) return bytes;
|
|
23583
|
+
throw new Error(`readMultiVaultBundleCompartment: single v1 bundle has only compartment "${header.handle}".`);
|
|
23584
|
+
}
|
|
23585
|
+
const { manifest, inner } = decodeMultiBundle(bytes);
|
|
23586
|
+
const idx = typeof selector === "number" ? selector : manifest.compartments.findIndex((c) => c.handle === selector);
|
|
23587
|
+
if (idx < 0 || idx >= inner.length) throw new Error(`readMultiVaultBundleCompartment: no compartment ${typeof selector === "number" ? `at index ${selector}` : `"${selector}"`}.`);
|
|
23588
|
+
return inner[idx];
|
|
23589
|
+
}
|
|
23590
|
+
|
|
24236
23591
|
// src/bundle/walk-closure.ts
|
|
24237
23592
|
init_errors();
|
|
24238
23593
|
async function walkClosure(vault, opts) {
|
|
@@ -24388,7 +23743,7 @@ init_constants();
|
|
|
24388
23743
|
init_entry();
|
|
24389
23744
|
init_hash();
|
|
24390
23745
|
init_bundle();
|
|
24391
|
-
async function reKeyClosure(vault, closure) {
|
|
23746
|
+
async function reKeyClosure(vault, closure, fieldProjection) {
|
|
24392
23747
|
const { name: vaultName, adapter, getDEK } = vault._introspectState();
|
|
24393
23748
|
const collections = {};
|
|
24394
23749
|
const deks = /* @__PURE__ */ new Map();
|
|
@@ -24397,29 +23752,40 @@ async function reKeyClosure(vault, closure) {
|
|
|
24397
23752
|
const destDek = await generateDEK();
|
|
24398
23753
|
deks.set(collectionName, destDek);
|
|
24399
23754
|
const out = {};
|
|
23755
|
+
const projList = fieldProjection?.[collectionName];
|
|
23756
|
+
const proj = projList ? new Set(projList) : void 0;
|
|
23757
|
+
const project = (plaintext) => {
|
|
23758
|
+
if (!proj) return plaintext;
|
|
23759
|
+
const rec = JSON.parse(plaintext);
|
|
23760
|
+
const kept = {};
|
|
23761
|
+
if ("id" in rec) kept["id"] = rec["id"];
|
|
23762
|
+
for (const f of proj) if (f in rec) kept[f] = rec[f];
|
|
23763
|
+
return JSON.stringify(kept);
|
|
23764
|
+
};
|
|
24400
23765
|
for (const id of ids) {
|
|
24401
23766
|
const env = await adapter.get(vaultName, collectionName, id);
|
|
24402
23767
|
if (!env) continue;
|
|
24403
23768
|
if (env._cek !== void 0) {
|
|
24404
23769
|
const cek = await unwrapCek(env._cek, srcDek);
|
|
24405
23770
|
const plaintext2 = await decrypt(env._iv, env._data, cek);
|
|
24406
|
-
const { iv: iv2, data: data2 } = await encrypt(plaintext2, cek);
|
|
23771
|
+
const { iv: iv2, data: data2 } = await encrypt(project(plaintext2), cek);
|
|
24407
23772
|
const wrapped = await wrapCek(cek, destDek);
|
|
24408
23773
|
out[id] = { ...env, _iv: iv2, _data: data2, _cek: wrapped };
|
|
24409
23774
|
continue;
|
|
24410
23775
|
}
|
|
24411
23776
|
const plaintext = await decrypt(env._iv, env._data, srcDek);
|
|
24412
|
-
const { iv, data } = await encrypt(plaintext, destDek);
|
|
23777
|
+
const { iv, data } = await encrypt(project(plaintext), destDek);
|
|
24413
23778
|
out[id] = { ...env, _iv: iv, _data: data };
|
|
24414
23779
|
}
|
|
24415
23780
|
collections[collectionName] = out;
|
|
24416
23781
|
}
|
|
24417
23782
|
return { collections, deks };
|
|
24418
23783
|
}
|
|
24419
|
-
async function reKeySchemas(vault, closure, destDeks) {
|
|
23784
|
+
async function reKeySchemas(vault, closure, destDeks, fieldProjection) {
|
|
24420
23785
|
const { name: vaultName, adapter, getDEK } = vault._introspectState();
|
|
24421
23786
|
const out = {};
|
|
24422
23787
|
for (const collectionName of closure.keys()) {
|
|
23788
|
+
if (fieldProjection?.[collectionName]) continue;
|
|
24423
23789
|
const env = await adapter.get(vaultName, SCHEMAS_COLLECTION, collectionName);
|
|
24424
23790
|
if (!env) continue;
|
|
24425
23791
|
const destDek = destDeks.get(collectionName);
|
|
@@ -24511,6 +23877,11 @@ async function sealDeks(deks) {
|
|
|
24511
23877
|
};
|
|
24512
23878
|
}
|
|
24513
23879
|
async function extractPartition(vault, opts) {
|
|
23880
|
+
if (vault.role === "custodian") {
|
|
23881
|
+
throw new PartitionExtractionError(
|
|
23882
|
+
"extractPartition is owner-only; a custodian cannot extract-and-sever (FR-6: producing a re-keyed standalone partition is an ownership operation; use the Deed owner)."
|
|
23883
|
+
);
|
|
23884
|
+
}
|
|
24514
23885
|
if (vault.role !== "owner") {
|
|
24515
23886
|
throw new PartitionExtractionError(
|
|
24516
23887
|
`extractPartition requires the 'owner' role on the source vault; caller is '${vault.role}'. Producing a re-keyed standalone partition is an ownership operation.`
|
|
@@ -24518,7 +23889,7 @@ async function extractPartition(vault, opts) {
|
|
|
24518
23889
|
}
|
|
24519
23890
|
if (opts.carrySchemas) await vault._drainPendingSchemaWrites();
|
|
24520
23891
|
const { closure } = await walkClosure(vault, opts);
|
|
24521
|
-
const { collections, deks } = await reKeyClosure(vault, closure);
|
|
23892
|
+
const { collections, deks } = await reKeyClosure(vault, closure, opts.fieldProjection);
|
|
24522
23893
|
let ledgerHead;
|
|
24523
23894
|
let ledgerEntries;
|
|
24524
23895
|
if (opts.carryLedger && vault._getLedgerOrNull() !== null) {
|
|
@@ -24530,7 +23901,7 @@ async function extractPartition(vault, opts) {
|
|
|
24530
23901
|
deks.set(LEDGER_COLLECTION, ledgerDek);
|
|
24531
23902
|
}
|
|
24532
23903
|
}
|
|
24533
|
-
const internalSchemas = opts.carrySchemas ? await reKeySchemas(vault, closure, deks) : {};
|
|
23904
|
+
const internalSchemas = opts.carrySchemas ? await reKeySchemas(vault, closure, deks, opts.fieldProjection) : {};
|
|
24534
23905
|
const internal = {};
|
|
24535
23906
|
if (Object.keys(internalSchemas).length > 0) internal[SCHEMAS_COLLECTION] = internalSchemas;
|
|
24536
23907
|
if (ledgerEntries) internal[LEDGER_COLLECTION] = ledgerEntries;
|
|
@@ -24745,6 +24116,41 @@ async function createOwnerOnAdoptedPartition(store, vaultName, opts) {
|
|
|
24745
24116
|
return { vaultName, userId };
|
|
24746
24117
|
}
|
|
24747
24118
|
|
|
24119
|
+
// src/bundle/decrypt-partition.ts
|
|
24120
|
+
init_crypto();
|
|
24121
|
+
init_record_keys();
|
|
24122
|
+
init_bundle();
|
|
24123
|
+
async function decryptExtractedPartition(bundleBytes, transferKey) {
|
|
24124
|
+
const header = readNoydbBundleHeader(bundleBytes);
|
|
24125
|
+
if (header.bundleKind !== "extracted-partition" || header.transferSeal === void 0) {
|
|
24126
|
+
throw new Error("decryptExtractedPartition: bundle is not an extracted-partition.");
|
|
24127
|
+
}
|
|
24128
|
+
const { dumpJson } = await readNoydbBundle(bundleBytes);
|
|
24129
|
+
const { dump, seal } = parseExtractedPartitionBody(dumpJson);
|
|
24130
|
+
const deks = await unsealDeks(seal, transferKey);
|
|
24131
|
+
const backup = JSON.parse(dump);
|
|
24132
|
+
const out = {};
|
|
24133
|
+
for (const [collection, byId] of Object.entries(backup.collections)) {
|
|
24134
|
+
const dek = deks.get(collection);
|
|
24135
|
+
if (dek === void 0) continue;
|
|
24136
|
+
const recs = [];
|
|
24137
|
+
for (const [id, env] of Object.entries(byId)) {
|
|
24138
|
+
const plaintext = env._cek !== void 0 ? await decrypt(env._iv, env._data, await unwrapCek(env._cek, dek)) : await decrypt(env._iv, env._data, dek);
|
|
24139
|
+
const body = JSON.parse(plaintext);
|
|
24140
|
+
recs.push({
|
|
24141
|
+
id,
|
|
24142
|
+
record: { ...body, id },
|
|
24143
|
+
ts: env._ts,
|
|
24144
|
+
version: env._v,
|
|
24145
|
+
...env._source !== void 0 ? { source: env._source } : {},
|
|
24146
|
+
...env._sourceTs !== void 0 ? { sourceTs: env._sourceTs } : {}
|
|
24147
|
+
});
|
|
24148
|
+
}
|
|
24149
|
+
out[collection] = recs;
|
|
24150
|
+
}
|
|
24151
|
+
return out;
|
|
24152
|
+
}
|
|
24153
|
+
|
|
24748
24154
|
// src/bundle/index.ts
|
|
24749
24155
|
init_errors();
|
|
24750
24156
|
init_errors();
|
|
@@ -24764,21 +24170,30 @@ init_errors();
|
|
|
24764
24170
|
NOYDB_BUNDLE_FORMAT_VERSION,
|
|
24765
24171
|
NOYDB_BUNDLE_MAGIC,
|
|
24766
24172
|
NOYDB_BUNDLE_PREFIX_BYTES,
|
|
24173
|
+
NOYDB_MULTI_BUNDLE_MAGIC,
|
|
24174
|
+
NOYDB_MULTI_BUNDLE_PREFIX_BYTES,
|
|
24175
|
+
NOYDB_MULTI_BUNDLE_VERSION,
|
|
24767
24176
|
PartitionExtractionError,
|
|
24768
24177
|
TransferSealError,
|
|
24769
24178
|
adoptPartition,
|
|
24770
24179
|
createOwnerOnAdoptedPartition,
|
|
24180
|
+
decodeMultiBundle,
|
|
24181
|
+
decryptExtractedPartition,
|
|
24771
24182
|
describeExtraction,
|
|
24772
24183
|
encodeBundleHeader,
|
|
24184
|
+
encodeMultiBundle,
|
|
24773
24185
|
extractPartition,
|
|
24774
24186
|
generateULID,
|
|
24775
24187
|
isULID,
|
|
24188
|
+
readMultiVaultBundleCompartment,
|
|
24776
24189
|
readNoydbBundle,
|
|
24777
24190
|
readNoydbBundleHeader,
|
|
24191
|
+
readNoydbBundleManifest,
|
|
24778
24192
|
resetBrotliSupportCache,
|
|
24779
24193
|
unsealDeks,
|
|
24780
24194
|
validateBundleHeader,
|
|
24781
24195
|
walkClosure,
|
|
24196
|
+
writeMultiVaultBundle,
|
|
24782
24197
|
writeNoydbBundle
|
|
24783
24198
|
});
|
|
24784
24199
|
//# sourceMappingURL=index.cjs.map
|