@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tx/transaction.ts"],"sourcesContent":["/**\n * Multi-record atomic transactions.\n *\n * Lets an application stage writes across two or more collections (or\n * vaults) and commit them all-or-nothing.\n *\n * ```ts\n * await db.transaction(async (tx) => {\n * const inv = tx.vault('acme').collection<Invoice>('invoices')\n * const pay = tx.vault('acme').collection<Payment>('payments')\n * await inv.put(invoiceId, { ...invoice, status: 'paid' })\n * await pay.put(paymentId, { invoiceId, amount, paidAt })\n * })\n * // If the body throws before returning: nothing persisted.\n * // If the body returns: all puts committed; any CAS mismatch rolls\n * // the batch back and surfaces as ConflictError.\n * ```\n *\n * ## Atomicity semantics\n *\n * Ops are buffered during the body. On body-return the hub:\n *\n * 1. **Pre-flight** — re-reads every touched envelope and enforces\n * any caller-supplied `expectedVersion`. A mismatch throws\n * `ConflictError` with *no* writes performed.\n * 2. **Execute** — calls `Collection.put()` / `.delete()` for each\n * staged op in declaration order. History snapshots, ledger\n * appends, and change events fire as normal per op.\n * 3. **Unwind on failure** — if step 2 throws mid-batch, each\n * already-committed op is reverted via the raw store (restoring\n * the captured prior envelope, or deleting if none existed). The\n * ledger is NOT rewritten — audit history preserves the partial\n * commit and the revert.\n *\n * **Crash window.** Steps 2–3 are not a storage-layer transaction —\n * if the process dies between two executed ops, the on-disk state is\n * partial. True all-or-nothing atomicity requires a store that\n * implements `NoydbStore.tx()` (DynamoDB `TransactWriteItems`,\n * IndexedDB `readwrite` transaction, …). This executor declares\n * that future integration point via the `tx?()` method + the\n * `StoreCapabilities.txAtomic` bit, but does not yet delegate\n * to it — the cascade into `Fork · Stores` tracks the per-adapter\n * wire-up.\n *\n * ## Not covered\n *\n * - Cross-sync-peer atomicity. Transactions commit against the\n * primary store only; the sync engine pushes on its normal\n * schedule. For cross-peer two-phase commit use `SyncTransaction`\n * via `db.transaction(vaultName)`.\n * - Read-your-writes within the body. `tx.collection().get(id)`\n * returns the most-recently-staged value for that id when one\n * exists; if no staged op has touched the id, it reads the current\n * committed state. Version numbers returned by `get` reflect the\n * pre-transaction state (staged puts have no version yet).\n *\n * @module\n */\n\nimport type { Noydb } from '../noydb.js'\nimport type { Vault } from '../vault.js'\nimport type { Collection } from '../collection.js'\nimport type { EncryptedEnvelope } from '../types.js'\nimport {\n AmendmentForbiddenError,\n ConflictError,\n InvariantError,\n ValidationError,\n} from '../errors.js'\nimport { generateULID } from '../bundle/ulid.js'\nimport type { GuardExecutor as GuardExecutorModule } from '../guards/executor.js'\nimport type { LedgerEntry } from '../history/ledger/entry.js'\nimport type { TransactionInvariant } from './invariants.js'\nimport type { GuardChange, GuardContext, ReadOnlyVaultFacade } from '../guards/types.js'\n\n/** One op buffered inside a running `TxContext`. @internal */\nexport interface StagedOp {\n type: 'put' | 'delete'\n vaultName: string\n collectionName: string\n id: string\n record?: unknown\n expectedVersion?: number\n /**\n * Optional human-readable tag forwarded to the resulting ledger\n * entry's `reason` field. Set by callers via\n * `tx.vault(v).collection(c).put(id, record, { reason })`.\n */\n reason?: string\n}\n\n/**\n * One executed op (main staged op or recursive side-effect like a\n * derivation output) paired with the envelope captured before the write.\n * `revertExecuted` walks this array in reverse on rollback.\n * @internal\n */\nexport interface ExecutedOp {\n op: StagedOp\n priorEnvelope: EncryptedEnvelope | null\n}\n\n/**\n * Options accepted by `db.transaction({ amendment, reason }, fn)`.\n * Only the amendment variant uses these — a plain `db.transaction(fn)`\n * never sees this shape.\n */\nexport interface AmendmentTxOptions {\n /** Opt into amendment mode. Required to be `true`. */\n readonly amendment: true\n /** Human-readable rationale recorded in the ledger entry. Required. */\n readonly reason: string\n}\n\n/**\n * Transaction handle passed to the user's body. Use\n * `tx.vault(name).collection<T>(name)` to get a per-collection\n * facade; its `put`/`delete`/`get` calls stage ops against the tx.\n */\nexport class TxContext {\n /** Stable id for this transaction; shared by all writes it performs. */\n readonly txId: string = generateULID()\n /** @internal */\n readonly _ops: StagedOp[] = []\n /**\n * @internal — write log built up in Phase 2. Each entry records the\n * envelope captured BEFORE the write so a mid-batch failure can\n * restore prior state via `revertExecuted`. Side-effect writes (e.g.\n * recursive derivation outputs fired inside `Collection.put`) are\n * appended here in execution order so they roll back alongside the\n * main staged ops.\n */\n readonly _executed: ExecutedOp[] = []\n /** @internal */\n readonly _db: Noydb\n /**\n * @internal — true when this TxContext was opened in amendment\n * mode. Toggles the lazy-`beginAmendment` + role-check path on first\n * `tx.vault(name)` and unlocks the post-Phase-2 invariant + audit run.\n */\n readonly _amendment: boolean\n /** @internal — vaults that have already had `beginAmendment` called. */\n readonly _amendmentVaults = new Map<string, Vault>()\n\n /** @internal */\n constructor(db: Noydb, amendment = false) {\n this._db = db\n this._amendment = amendment\n }\n\n /** Scope subsequent `collection()` calls to the named vault. */\n vault(name: string): TxVault {\n const v = this._db.vault(name)\n if (this._amendment && !this._amendmentVaults.has(name)) {\n // Role check is per-vault. The task spec (\"only admin or owner\n // can open an amendment\") is implemented lazy-on-first-touch\n // because the role lives on the vault's keyring, and `tx.vault()`\n // is the first place we know which vault we're addressing. The\n // observable effect is identical to an eager check in the single-\n // vault case the tests exercise; multi-vault amendments check\n // each touched vault as they first appear.\n const role = v.role\n if (role !== 'admin' && role !== 'owner') {\n throw new AmendmentForbiddenError(v.userId, role)\n }\n // Amendments require an initialised guard registry — they\n // produce a structured invariant + change-set audit. A vault\n // opened without `guardStrategies` (or via the sync fallback\n // path) has a null registry and cannot run an amendment.\n const reg = v._getGuardRegistry()\n if (reg === null) {\n throw new ValidationError(\n `Vault \"${name}\": amendment mode requires at least one ` +\n `guardStrategy registered via createNoydb({ guardStrategies }). ` +\n `Open the vault with guardStrategies before calling ` +\n `db.transaction({ amendment: true }).`,\n )\n }\n reg.beginAmendment()\n this._amendmentVaults.set(name, v)\n }\n return new TxVault(this, v)\n }\n}\n\n/** Per-vault facade inside a running transaction. */\nexport class TxVault {\n /** @internal */\n readonly _ctx: TxContext\n /** @internal */\n readonly _vault: Vault\n\n /** @internal */\n constructor(ctx: TxContext, vault: Vault) {\n this._ctx = ctx\n this._vault = vault\n }\n\n /** Scope subsequent op calls to the named collection. */\n collection<T>(name: string): TxCollection<T> {\n const c = this._vault.collection<T>(name)\n return new TxCollection<T>(this._ctx, this._vault, c, name)\n }\n}\n\n/** Per-collection facade inside a running transaction. */\nexport class TxCollection<T> {\n /** @internal */\n readonly _ctx: TxContext\n /** @internal */\n readonly _vault: Vault\n /** @internal */\n readonly _coll: Collection<T>\n /** @internal */\n readonly _name: string\n\n /** @internal */\n constructor(ctx: TxContext, vault: Vault, coll: Collection<T>, name: string) {\n this._ctx = ctx\n this._vault = vault\n this._coll = coll\n this._name = name\n }\n\n /**\n * Read the current committed value, or the most-recently-staged\n * value from the same transaction if one exists.\n */\n async get(id: string): Promise<T | null> {\n for (let i = this._ctx._ops.length - 1; i >= 0; i--) {\n const op = this._ctx._ops[i]!\n if (\n op.vaultName === this._vault.name &&\n op.collectionName === this._name &&\n op.id === id\n ) {\n if (op.type === 'delete') return null\n return op.record as T\n }\n }\n return this._coll.get(id)\n }\n\n /**\n * Stage a put. Does not write until the transaction body returns.\n * Supply `{ expectedVersion }` to enforce optimistic concurrency\n * during the commit pre-flight.\n */\n put(id: string, record: T, options?: { expectedVersion?: number; reason?: string }): void {\n const op: StagedOp = {\n type: 'put',\n vaultName: this._vault.name,\n collectionName: this._name,\n id,\n record,\n }\n if (options?.expectedVersion !== undefined) op.expectedVersion = options.expectedVersion\n if (options?.reason !== undefined) op.reason = options.reason\n this._ctx._ops.push(op)\n }\n\n /**\n * Stage a delete. Does not write until the transaction body returns.\n * Supply `{ expectedVersion }` to enforce optimistic concurrency\n * during the commit pre-flight.\n */\n delete(id: string, options?: { expectedVersion?: number }): void {\n const op: StagedOp = {\n type: 'delete',\n vaultName: this._vault.name,\n collectionName: this._name,\n id,\n }\n if (options?.expectedVersion !== undefined) op.expectedVersion = options.expectedVersion\n this._ctx._ops.push(op)\n }\n}\n\n/**\n * Commit plan: pre-flight check + execution + revert plan.\n *\n * @internal — driven by `withTransactions()` (via `tx/active.ts`) for\n * user-facing `db.transaction(...)` calls and by the `amendment` path\n * in `noydb.ts`. `Collection.putManyAtomic` runs its own Phase 2 loop\n * but shares the `_activeTxContext` mechanism (and the `revertExecuted`\n * helper) so nested side-effect derivation writes get registered for\n * revert alongside the bulk-put source ops.\n */\nexport async function runTransaction<T>(\n db: Noydb,\n fn: (tx: TxContext) => Promise<T> | T,\n options?: AmendmentTxOptions,\n txInvariants?: ReadonlyArray<TransactionInvariant>,\n): Promise<T> {\n // ─── Amendment-mode pre-flight ───────────────────────────────\n // `reason` is the only thing we can validate before the body runs;\n // the per-vault role check happens lazily on first `tx.vault(name)`\n // because we don't know which vaults the body will touch ahead of\n // time. Throwing here keeps the failure mode close to the call site\n // so the developer doesn't have to walk an async stack to find the\n // missing-reason mistake.\n if (options?.amendment) {\n if (typeof options.reason !== 'string' || options.reason.trim().length === 0) {\n throw new ValidationError(\n 'db.transaction({ amendment: true }) requires a non-empty `reason` string.',\n )\n }\n }\n\n const ctx = new TxContext(db, options?.amendment === true)\n const bodyResult = await fn(ctx)\n\n if (ctx._ops.length === 0) {\n // Body produced no ops. If amendment mode was active we still\n // need to close any opened windows so a subsequent (unrelated)\n // write doesn't surprise-collect into a stale change-set. Each\n // `beginAmendment` is matched by exactly one `consumeChanges`.\n if (ctx._amendment) {\n for (const v of ctx._amendmentVaults.values()) {\n // Registry is guaranteed non-null here — `tx.vault(name)`\n // threw above if it was null before adding to\n // `_amendmentVaults`.\n const reg = v._getGuardRegistry()\n if (reg !== null) {\n reg.consumeChanges()\n reg.consumeMeta()\n }\n }\n }\n return bodyResult\n }\n\n // Phase 1 — pre-flight: snapshot every touched envelope and enforce\n // any caller-supplied expectedVersion. Same (vault, coll, id) touched\n // more than once in one tx snapshots only the *initial* committed\n // state; the in-order replay in Phase 2 takes care of successor ops.\n const priorEnvelopes = new Map<string, EncryptedEnvelope | null>()\n const store = db._store\n\n // Commit-time changeset invariants (#342) need PLAINTEXT prior records\n // for `before`, but `priorEnvelopes` holds ENCRYPTED envelopes. So for\n // ops in a watched scope we additionally decrypt the prior record here,\n // in Phase 1, BEFORE Phase 2 overwrites it. Snapshots only the initial\n // committed state per (vault, coll, id), matching the envelope snapshot.\n const invariants = txInvariants ?? []\n const watchedScopes = new Set(invariants.map(i => i.scope))\n const plainBefore = new Map<string, unknown>()\n\n for (const op of ctx._ops) {\n const key = keyOf(op)\n if (!priorEnvelopes.has(key)) {\n const env = await store.get(op.vaultName, op.collectionName, op.id)\n priorEnvelopes.set(key, env)\n }\n if (watchedScopes.has(op.collectionName) && !plainBefore.has(key)) {\n const prior = await db\n .vault(op.vaultName)\n .collection(op.collectionName)\n .get(op.id)\n plainBefore.set(key, prior ?? null)\n }\n if (op.expectedVersion !== undefined) {\n const env = priorEnvelopes.get(key) ?? null\n const actual = env?._v ?? 0\n if (actual !== op.expectedVersion) {\n throw new ConflictError(\n actual,\n `Transaction pre-flight: ${op.vaultName}/${op.collectionName}/${op.id} ` +\n `expected v${op.expectedVersion}, found v${actual}`,\n )\n }\n }\n }\n\n // Phase 2 — execute via the Collection layer so history snapshots,\n // ledger entries, and change events fire normally. We capture each\n // successful op so a mid-batch throw can revert in Phase 3.\n //\n // `_activeTxContext` is published on the Noydb instance for the\n // duration of Phase 2 so recursive writes triggered inside\n // `Collection.put` (today: eager derivation outputs) can register\n // their own envelopes onto `ctx._executed` and roll back alongside\n // the main staged ops. The `finally` clears it before the\n // amendment commit phase runs.\n db._setActiveTxContext(ctx)\n try {\n try {\n for (const op of ctx._ops) {\n const coll = db.vault(op.vaultName).collection(op.collectionName)\n const key = keyOf(op)\n const prior = priorEnvelopes.get(key) ?? null\n // Record the revert plan BEFORE the call so a mid-`coll.put` throw\n // (e.g. strict-mode derivation failure firing after `store.put`\n // has already committed the envelope) still has its source write\n // reverted. `revertExecuted` is best-effort: putting prior back is\n // idempotent when the failing op never actually wrote, and\n // `_invalidateCacheEntry` is a no-op when the collection isn't\n // hydrated.\n ctx._executed.push({ op, priorEnvelope: prior })\n if (op.type === 'put') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await coll.put(op.id, op.record as any, op.reason !== undefined ? { reason: op.reason } : undefined)\n } else {\n await coll.delete(op.id)\n }\n }\n } catch (err) {\n // Phase 3 — best-effort revert. See helper docstring.\n await revertExecuted(ctx._executed, store, db)\n // Drain amendment windows so the next transaction starts clean.\n if (ctx._amendment) {\n for (const v of ctx._amendmentVaults.values()) {\n const reg = v._getGuardRegistry()\n if (reg !== null) {\n reg.consumeChanges()\n reg.consumeMeta()\n }\n }\n }\n throw err\n }\n } finally {\n db._clearActiveTxContext(ctx)\n }\n\n // ─── Amendment commit phase (only if amendment === true) ────\n // Body succeeded — now run each touched vault's invariants over the\n // collected change-set, then append a structured ledger entry. If\n // any invariant throws, treat it exactly like a mid-Phase-2 failure:\n // revert every executed op and re-throw the InvariantError.\n if (ctx._amendment) {\n // Lazy-load GuardExecutor at the dispatch site — keeps the floor\n // bundle free of the guards subsystem when amendments aren't used.\n // Mirrors the deferred-load pattern from elsewhere in this module.\n const { GuardExecutor } = (await import('../guards/executor.js')) as {\n GuardExecutor: typeof GuardExecutorModule\n }\n try {\n for (const [vaultName, v] of ctx._amendmentVaults) {\n const registry = v._getGuardRegistry()\n // Registry is guaranteed non-null at this point — the\n // `tx.vault(name)` path that populates `_amendmentVaults`\n // throws if the registry is null. The defensive check here\n // is for TypeScript's narrowing.\n if (registry === null) continue\n const changesByCollection = registry.consumeChanges()\n const meta = registry.consumeMeta()\n if (changesByCollection.size === 0) continue\n\n const readOnlyVault = v._getReadOnlyFacade()\n if (readOnlyVault === null) continue\n\n // Build the invariant ctx once per vault — it's the same shape\n // every guard sees on the normal `check` path, just with a\n // synthetic `existing: null` (invariants get the full change\n // set in their first parameter; `existing` is a per-record\n // concept that doesn't apply here).\n const invariantsPassed: string[] = []\n for (const [collection, changes] of changesByCollection) {\n const guards = registry.guardsFor(collection).filter(g => g.amendment !== undefined)\n for (const guard of guards) {\n await GuardExecutor.runInvariant(guard, changes, {\n existing: null,\n vault: readOnlyVault,\n userId: v.userId,\n role: v.role,\n })\n }\n if (guards.length > 0) invariantsPassed.push(collection)\n }\n\n // Append the audit ledger entry. Silent no-op when the\n // history strategy isn't configured — the records still\n // committed, only the multi-record summary is unavailable.\n const ledger = v._getLedgerOrNull()\n if (ledger) {\n const role = v.role as 'admin' | 'owner'\n const amendment: NonNullable<LedgerEntry['amendment']> = {\n reason: options!.reason,\n role,\n changes: meta,\n invariantsPassed,\n }\n await ledger.append({\n op: 'amendment',\n collection: '',\n id: '',\n version: 0,\n actor: v.userId,\n // No payload to hash — the per-record entries already\n // captured `payloadHash` at their own append time. We use\n // a sha256 of the canonical reason string so the field is\n // populated with something deterministic and non-empty.\n payloadHash: '',\n amendment,\n })\n }\n void vaultName\n }\n } catch (err) {\n await revertExecuted(ctx._executed, store, db)\n throw err instanceof InvariantError ? err : new InvariantError(\n err instanceof Error ? err.message : `invariant violated: ${String(err)}`,\n )\n }\n }\n\n // ─── Commit-time changeset invariant phase (#342) ───────────\n // Runs for BOTH ordinary and amendment transactions (placed after the\n // amendment phase so an amendment commit is still subject to these\n // set-level constraints). Assemble the changeset from the executed\n // staged ops, deduped to the LAST write per (vault, coll, id) while\n // preserving write order, then group `GuardChange` by collection\n // (scope) and run each matching invariant. A throw mirrors the\n // amendment-phase failure mode exactly: revert every executed op and\n // re-throw as `InvariantError`.\n if (invariants.length > 0) {\n // Dedup ctx._ops to the last write per key, preserving first-seen\n // (write) order so the changeset is stable and order-meaningful.\n const lastOp = new Map<string, StagedOp>()\n const order: string[] = []\n for (const op of ctx._ops) {\n const key = keyOf(op)\n if (!lastOp.has(key)) order.push(key)\n lastOp.set(key, op)\n }\n\n // Group {before, after} pairs by collection name (the invariant\n // scope). `before` is the plaintext prior captured in Phase 1 (null\n // for inserts / unwatched — only watched scopes were captured, and\n // every grouped key belongs to a watched scope). `after` is the\n // written record, null for a delete.\n const changesByScope = new Map<string, GuardChange<unknown>[]>()\n // Parallel map: scope → the vault name of its (last-seen) op, used to\n // build the per-invariant read-only ctx (facade + userId + role).\n const scopeVault = new Map<string, string>()\n for (const key of order) {\n const op = lastOp.get(key)!\n if (!watchedScopes.has(op.collectionName)) continue\n const before = plainBefore.get(key) ?? null\n const after = op.type === 'delete' ? null : (op.record ?? null)\n const change = { before, after } as GuardChange<unknown>\n const arr = changesByScope.get(op.collectionName)\n if (arr) arr.push(change)\n else changesByScope.set(op.collectionName, [change])\n\n // Stash the vault name alongside so we can build a per-vault ctx.\n // (All ops in a scope group could span vaults; we resolve the\n // vault per change below via a parallel map keyed the same way.)\n scopeVault.set(op.collectionName, op.vaultName)\n }\n\n try {\n for (const inv of invariants) {\n const changes = changesByScope.get(inv.scope)\n if (changes === undefined || changes.length === 0) continue\n const vaultName = scopeVault.get(inv.scope)!\n const v = db.vault(vaultName)\n // Prefer the real read-only facade so the invariant can read\n // sibling collections; fall back to a minimal read-only stub.\n const facade: ReadOnlyVaultFacade =\n v._getReadOnlyFacade() ?? {\n collection<R = unknown>(name: string) {\n const c = v.collection<R>(name)\n return {\n get: (id: string) => c.get(id),\n list: () => c.list(),\n query: () => c.query(),\n }\n },\n }\n const ctxForInv: GuardContext<unknown> = {\n existing: null,\n vault: facade,\n userId: v.userId,\n role: v.role,\n }\n await inv.check(changes, ctxForInv)\n }\n } catch (err) {\n await revertExecuted(ctx._executed, store, db)\n throw err instanceof InvariantError ? err : new InvariantError(\n err instanceof Error ? err.message : `invariant violated: ${String(err)}`,\n )\n }\n }\n\n return bodyResult\n}\n\n/**\n * Phase 3 helper — restore captured prior envelopes via the raw store\n * to avoid re-firing Collection-level side effects (we don't want a\n * cascade of change events undoing themselves). The ledger is left\n * as-is: each committed op appended an entry; the revert is\n * deliberately NOT recorded as a compensating entry because the\n * caller-facing contract is \"atomic or not at all,\" not \"every write\n * visible in the audit trail.\" Auditors who need the intermediate\n * state can still reconstruct it by walking the ledger through the\n * failed-tx timestamp.\n *\n * @internal — shared between `runTransaction` and\n * `Collection.putManyAtomic`. Both register source ops + nested\n * derivation side-effect ops onto `_executed`; this helper unwinds the\n * combined list in reverse on rollback.\n */\nexport async function revertExecuted(\n executed: ReadonlyArray<ExecutedOp>,\n store: Noydb['_store'],\n db?: Noydb,\n): Promise<void> {\n for (const { op, priorEnvelope } of executed.slice().reverse()) {\n try {\n if (priorEnvelope) {\n await store.put(op.vaultName, op.collectionName, op.id, priorEnvelope)\n } else {\n await store.delete(op.vaultName, op.collectionName, op.id)\n }\n // Sync the Collection-layer cache with what we just wrote at\n // the raw store. Without this, eager-mode `get` would still\n // return the rolled-back record from its in-memory map. The\n // Collection's `_invalidateCacheEntry` is a no-op when the\n // collection hasn't yet been hydrated.\n if (db) {\n const coll = db.vault(op.vaultName).collection(op.collectionName)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await (coll as any)._invalidateCacheEntry(op.id)\n }\n } catch {\n // swallow — best-effort. Surfacing the revert error would mask\n // the original one that triggered the rollback.\n }\n }\n}\n\nfunction keyOf(op: StagedOp): string {\n return `${op.vaultName}\\x00${op.collectionName}\\x00${op.id}`\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/tx/transaction.ts"],"sourcesContent":["/**\n * Multi-record atomic transactions.\n *\n * Lets an application stage writes across two or more collections (or\n * vaults) and commit them all-or-nothing.\n *\n * ```ts\n * await db.transaction(async (tx) => {\n * const inv = tx.vault('acme').collection<Invoice>('invoices')\n * const pay = tx.vault('acme').collection<Payment>('payments')\n * await inv.put(invoiceId, { ...invoice, status: 'paid' })\n * await pay.put(paymentId, { invoiceId, amount, paidAt })\n * })\n * // If the body throws before returning: nothing persisted.\n * // If the body returns: all puts committed; any CAS mismatch rolls\n * // the batch back and surfaces as ConflictError.\n * ```\n *\n * ## Atomicity semantics\n *\n * Ops are buffered during the body. On body-return the hub:\n *\n * 1. **Pre-flight** — re-reads every touched envelope and enforces\n * any caller-supplied `expectedVersion`. A mismatch throws\n * `ConflictError` with *no* writes performed.\n * 2. **Execute** — calls `Collection.put()` / `.delete()` for each\n * staged op in declaration order. History snapshots, ledger\n * appends, and change events fire as normal per op.\n * 3. **Unwind on failure** — if step 2 throws mid-batch, each\n * already-committed op is reverted via the raw store (restoring\n * the captured prior envelope, or deleting if none existed). The\n * ledger is NOT rewritten — audit history preserves the partial\n * commit and the revert.\n *\n * **Crash window.** Steps 2–3 are not a storage-layer transaction —\n * if the process dies between two executed ops, the on-disk state is\n * partial. True all-or-nothing atomicity requires a store that\n * implements `NoydbStore.tx()` (DynamoDB `TransactWriteItems`,\n * IndexedDB `readwrite` transaction, …). This executor declares\n * that future integration point via the `tx?()` method + the\n * `StoreCapabilities.txAtomic` bit, but does not yet delegate\n * to it — the cascade into `Fork · Stores` tracks the per-adapter\n * wire-up.\n *\n * ## Not covered\n *\n * - Cross-sync-peer atomicity. Transactions commit against the\n * primary store only; the sync engine pushes on its normal\n * schedule. For cross-peer two-phase commit use `SyncTransaction`\n * via `db.transaction(vaultName)`.\n * - Read-your-writes within the body. `tx.collection().get(id)`\n * returns the most-recently-staged value for that id when one\n * exists; if no staged op has touched the id, it reads the current\n * committed state. Version numbers returned by `get` reflect the\n * pre-transaction state (staged puts have no version yet).\n *\n * @module\n */\n\nimport type { Noydb } from '../noydb.js'\nimport type { Vault } from '../vault.js'\nimport type { Collection } from '../collection.js'\nimport type { EncryptedEnvelope } from '../types.js'\nimport {\n AmendmentForbiddenError,\n ConflictError,\n InvariantError,\n ValidationError,\n} from '../errors.js'\nimport { generateULID } from '../bundle/ulid.js'\nimport type { GuardExecutor as GuardExecutorModule } from '../guards/executor.js'\nimport type { LedgerEntry } from '../history/ledger/entry.js'\nimport type { TransactionInvariant } from './invariants.js'\nimport type { GuardChange, GuardContext, ReadOnlyVaultFacade } from '../guards/types.js'\n\n/** One op buffered inside a running `TxContext`. @internal */\nexport interface StagedOp {\n type: 'put' | 'delete'\n vaultName: string\n collectionName: string\n id: string\n record?: unknown\n expectedVersion?: number\n /**\n * Optional human-readable tag forwarded to the resulting ledger\n * entry's `reason` field. Set by callers via\n * `tx.vault(v).collection(c).put(id, record, { reason })`.\n */\n reason?: string\n}\n\n/**\n * One executed op (main staged op or recursive side-effect like a\n * derivation output) paired with the envelope captured before the write.\n * `revertExecuted` walks this array in reverse on rollback.\n * @internal\n */\nexport interface ExecutedOp {\n op: StagedOp\n priorEnvelope: EncryptedEnvelope | null\n}\n\n/**\n * Options accepted by `db.transaction({ amendment, reason }, fn)`.\n * Only the amendment variant uses these — a plain `db.transaction(fn)`\n * never sees this shape.\n */\nexport interface AmendmentTxOptions {\n /** Opt into amendment mode. Required to be `true`. */\n readonly amendment: true\n /** Human-readable rationale recorded in the ledger entry. Required. */\n readonly reason: string\n}\n\n/**\n * Transaction handle passed to the user's body. Use\n * `tx.vault(name).collection<T>(name)` to get a per-collection\n * facade; its `put`/`delete`/`get` calls stage ops against the tx.\n */\nexport class TxContext {\n /** Stable id for this transaction; shared by all writes it performs. */\n readonly txId: string = generateULID()\n /** @internal */\n readonly _ops: StagedOp[] = []\n /**\n * @internal — write log built up in Phase 2. Each entry records the\n * envelope captured BEFORE the write so a mid-batch failure can\n * restore prior state via `revertExecuted`. Side-effect writes (e.g.\n * recursive derivation outputs fired inside `Collection.put`) are\n * appended here in execution order so they roll back alongside the\n * main staged ops.\n */\n readonly _executed: ExecutedOp[] = []\n /** @internal */\n readonly _db: Noydb\n /**\n * @internal — true when this TxContext was opened in amendment\n * mode. Toggles the lazy-`beginAmendment` + role-check path on first\n * `tx.vault(name)` and unlocks the post-Phase-2 invariant + audit run.\n */\n readonly _amendment: boolean\n /** @internal — vaults that have already had `beginAmendment` called. */\n readonly _amendmentVaults = new Map<string, Vault>()\n\n /** @internal */\n constructor(db: Noydb, amendment = false) {\n this._db = db\n this._amendment = amendment\n }\n\n /** Scope subsequent `collection()` calls to the named vault. */\n vault(name: string): TxVault {\n const v = this._db.vault(name)\n if (this._amendment && !this._amendmentVaults.has(name)) {\n // Role check is per-vault. The task spec (\"only admin or owner\n // can open an amendment\") is implemented lazy-on-first-touch\n // because the role lives on the vault's keyring, and `tx.vault()`\n // is the first place we know which vault we're addressing. The\n // observable effect is identical to an eager check in the single-\n // vault case the tests exercise; multi-vault amendments check\n // each touched vault as they first appear.\n const role = v.role\n // FR-6: custodian is admin-rank for operational mutations, and an\n // amendment is an operational (data-correcting) act — not an ownership\n // meta-capability — so custodian is allowed alongside owner/admin.\n if (role !== 'admin' && role !== 'owner' && role !== 'custodian') {\n throw new AmendmentForbiddenError(v.userId, role)\n }\n // Amendments require an initialised guard registry — they\n // produce a structured invariant + change-set audit. A vault\n // opened without `guardStrategies` (or via the sync fallback\n // path) has a null registry and cannot run an amendment.\n const reg = v._getGuardRegistry()\n if (reg === null) {\n throw new ValidationError(\n `Vault \"${name}\": amendment mode requires at least one ` +\n `guardStrategy registered via createNoydb({ guardStrategies }). ` +\n `Open the vault with guardStrategies before calling ` +\n `db.transaction({ amendment: true }).`,\n )\n }\n reg.beginAmendment()\n this._amendmentVaults.set(name, v)\n }\n return new TxVault(this, v)\n }\n}\n\n/** Per-vault facade inside a running transaction. */\nexport class TxVault {\n /** @internal */\n readonly _ctx: TxContext\n /** @internal */\n readonly _vault: Vault\n\n /** @internal */\n constructor(ctx: TxContext, vault: Vault) {\n this._ctx = ctx\n this._vault = vault\n }\n\n /** Scope subsequent op calls to the named collection. */\n collection<T>(name: string): TxCollection<T> {\n const c = this._vault.collection<T>(name)\n return new TxCollection<T>(this._ctx, this._vault, c, name)\n }\n}\n\n/** Per-collection facade inside a running transaction. */\nexport class TxCollection<T> {\n /** @internal */\n readonly _ctx: TxContext\n /** @internal */\n readonly _vault: Vault\n /** @internal */\n readonly _coll: Collection<T>\n /** @internal */\n readonly _name: string\n\n /** @internal */\n constructor(ctx: TxContext, vault: Vault, coll: Collection<T>, name: string) {\n this._ctx = ctx\n this._vault = vault\n this._coll = coll\n this._name = name\n }\n\n /**\n * Read the current committed value, or the most-recently-staged\n * value from the same transaction if one exists.\n */\n async get(id: string): Promise<T | null> {\n for (let i = this._ctx._ops.length - 1; i >= 0; i--) {\n const op = this._ctx._ops[i]!\n if (\n op.vaultName === this._vault.name &&\n op.collectionName === this._name &&\n op.id === id\n ) {\n if (op.type === 'delete') return null\n return op.record as T\n }\n }\n return this._coll.get(id)\n }\n\n /**\n * Stage a put. Does not write until the transaction body returns.\n * Supply `{ expectedVersion }` to enforce optimistic concurrency\n * during the commit pre-flight.\n */\n put(id: string, record: T, options?: { expectedVersion?: number; reason?: string }): void {\n const op: StagedOp = {\n type: 'put',\n vaultName: this._vault.name,\n collectionName: this._name,\n id,\n record,\n }\n if (options?.expectedVersion !== undefined) op.expectedVersion = options.expectedVersion\n if (options?.reason !== undefined) op.reason = options.reason\n this._ctx._ops.push(op)\n }\n\n /**\n * Stage a delete. Does not write until the transaction body returns.\n * Supply `{ expectedVersion }` to enforce optimistic concurrency\n * during the commit pre-flight.\n */\n delete(id: string, options?: { expectedVersion?: number }): void {\n const op: StagedOp = {\n type: 'delete',\n vaultName: this._vault.name,\n collectionName: this._name,\n id,\n }\n if (options?.expectedVersion !== undefined) op.expectedVersion = options.expectedVersion\n this._ctx._ops.push(op)\n }\n}\n\n/**\n * Commit plan: pre-flight check + execution + revert plan.\n *\n * @internal — driven by `withTransactions()` (via `tx/active.ts`) for\n * user-facing `db.transaction(...)` calls and by the `amendment` path\n * in `noydb.ts`. `Collection.putManyAtomic` runs its own Phase 2 loop\n * but shares the `_activeTxContext` mechanism (and the `revertExecuted`\n * helper) so nested side-effect derivation writes get registered for\n * revert alongside the bulk-put source ops.\n */\nexport async function runTransaction<T>(\n db: Noydb,\n fn: (tx: TxContext) => Promise<T> | T,\n options?: AmendmentTxOptions,\n txInvariants?: ReadonlyArray<TransactionInvariant>,\n): Promise<T> {\n // ─── Amendment-mode pre-flight ───────────────────────────────\n // `reason` is the only thing we can validate before the body runs;\n // the per-vault role check happens lazily on first `tx.vault(name)`\n // because we don't know which vaults the body will touch ahead of\n // time. Throwing here keeps the failure mode close to the call site\n // so the developer doesn't have to walk an async stack to find the\n // missing-reason mistake.\n if (options?.amendment) {\n if (typeof options.reason !== 'string' || options.reason.trim().length === 0) {\n throw new ValidationError(\n 'db.transaction({ amendment: true }) requires a non-empty `reason` string.',\n )\n }\n }\n\n const ctx = new TxContext(db, options?.amendment === true)\n const bodyResult = await fn(ctx)\n\n if (ctx._ops.length === 0) {\n // Body produced no ops. If amendment mode was active we still\n // need to close any opened windows so a subsequent (unrelated)\n // write doesn't surprise-collect into a stale change-set. Each\n // `beginAmendment` is matched by exactly one `consumeChanges`.\n if (ctx._amendment) {\n for (const v of ctx._amendmentVaults.values()) {\n // Registry is guaranteed non-null here — `tx.vault(name)`\n // threw above if it was null before adding to\n // `_amendmentVaults`.\n const reg = v._getGuardRegistry()\n if (reg !== null) {\n reg.consumeChanges()\n reg.consumeMeta()\n }\n }\n }\n return bodyResult\n }\n\n // Phase 1 — pre-flight: snapshot every touched envelope and enforce\n // any caller-supplied expectedVersion. Same (vault, coll, id) touched\n // more than once in one tx snapshots only the *initial* committed\n // state; the in-order replay in Phase 2 takes care of successor ops.\n const priorEnvelopes = new Map<string, EncryptedEnvelope | null>()\n const store = db._store\n\n // Commit-time changeset invariants (#342) need PLAINTEXT prior records\n // for `before`, but `priorEnvelopes` holds ENCRYPTED envelopes. So for\n // ops in a watched scope we additionally decrypt the prior record here,\n // in Phase 1, BEFORE Phase 2 overwrites it. Snapshots only the initial\n // committed state per (vault, coll, id), matching the envelope snapshot.\n const invariants = txInvariants ?? []\n const watchedScopes = new Set(invariants.map(i => i.scope))\n const plainBefore = new Map<string, unknown>()\n\n for (const op of ctx._ops) {\n const key = keyOf(op)\n if (!priorEnvelopes.has(key)) {\n const env = await store.get(op.vaultName, op.collectionName, op.id)\n priorEnvelopes.set(key, env)\n }\n if (watchedScopes.has(op.collectionName) && !plainBefore.has(key)) {\n const prior = await db\n .vault(op.vaultName)\n .collection(op.collectionName)\n .get(op.id)\n plainBefore.set(key, prior ?? null)\n }\n if (op.expectedVersion !== undefined) {\n const env = priorEnvelopes.get(key) ?? null\n const actual = env?._v ?? 0\n if (actual !== op.expectedVersion) {\n throw new ConflictError(\n actual,\n `Transaction pre-flight: ${op.vaultName}/${op.collectionName}/${op.id} ` +\n `expected v${op.expectedVersion}, found v${actual}`,\n )\n }\n }\n }\n\n // Phase 2 — execute via the Collection layer so history snapshots,\n // ledger entries, and change events fire normally. We capture each\n // successful op so a mid-batch throw can revert in Phase 3.\n //\n // `_activeTxContext` is published on the Noydb instance for the\n // duration of Phase 2 so recursive writes triggered inside\n // `Collection.put` (today: eager derivation outputs) can register\n // their own envelopes onto `ctx._executed` and roll back alongside\n // the main staged ops. The `finally` clears it before the\n // amendment commit phase runs.\n db._setActiveTxContext(ctx)\n try {\n try {\n for (const op of ctx._ops) {\n const coll = db.vault(op.vaultName).collection(op.collectionName)\n const key = keyOf(op)\n const prior = priorEnvelopes.get(key) ?? null\n // Record the revert plan BEFORE the call so a mid-`coll.put` throw\n // (e.g. strict-mode derivation failure firing after `store.put`\n // has already committed the envelope) still has its source write\n // reverted. `revertExecuted` is best-effort: putting prior back is\n // idempotent when the failing op never actually wrote, and\n // `_invalidateCacheEntry` is a no-op when the collection isn't\n // hydrated.\n ctx._executed.push({ op, priorEnvelope: prior })\n if (op.type === 'put') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await coll.put(op.id, op.record as any, op.reason !== undefined ? { reason: op.reason } : undefined)\n } else {\n await coll.delete(op.id)\n }\n }\n } catch (err) {\n // Phase 3 — best-effort revert. See helper docstring.\n await revertExecuted(ctx._executed, store, db)\n // Drain amendment windows so the next transaction starts clean.\n if (ctx._amendment) {\n for (const v of ctx._amendmentVaults.values()) {\n const reg = v._getGuardRegistry()\n if (reg !== null) {\n reg.consumeChanges()\n reg.consumeMeta()\n }\n }\n }\n throw err\n }\n } finally {\n db._clearActiveTxContext(ctx)\n }\n\n // ─── Amendment commit phase (only if amendment === true) ────\n // Body succeeded — now run each touched vault's invariants over the\n // collected change-set, then append a structured ledger entry. If\n // any invariant throws, treat it exactly like a mid-Phase-2 failure:\n // revert every executed op and re-throw the InvariantError.\n if (ctx._amendment) {\n // Lazy-load GuardExecutor at the dispatch site — keeps the floor\n // bundle free of the guards subsystem when amendments aren't used.\n // Mirrors the deferred-load pattern from elsewhere in this module.\n const { GuardExecutor } = (await import('../guards/executor.js')) as {\n GuardExecutor: typeof GuardExecutorModule\n }\n try {\n for (const [vaultName, v] of ctx._amendmentVaults) {\n const registry = v._getGuardRegistry()\n // Registry is guaranteed non-null at this point — the\n // `tx.vault(name)` path that populates `_amendmentVaults`\n // throws if the registry is null. The defensive check here\n // is for TypeScript's narrowing.\n if (registry === null) continue\n const changesByCollection = registry.consumeChanges()\n const meta = registry.consumeMeta()\n if (changesByCollection.size === 0) continue\n\n const readOnlyVault = v._getReadOnlyFacade()\n if (readOnlyVault === null) continue\n\n // Build the invariant ctx once per vault — it's the same shape\n // every guard sees on the normal `check` path, just with a\n // synthetic `existing: null` (invariants get the full change\n // set in their first parameter; `existing` is a per-record\n // concept that doesn't apply here).\n const invariantsPassed: string[] = []\n for (const [collection, changes] of changesByCollection) {\n const guards = registry.guardsFor(collection).filter(g => g.amendment !== undefined)\n for (const guard of guards) {\n await GuardExecutor.runInvariant(guard, changes, {\n existing: null,\n vault: readOnlyVault,\n userId: v.userId,\n role: v.role,\n })\n }\n if (guards.length > 0) invariantsPassed.push(collection)\n }\n\n // Append the audit ledger entry. Silent no-op when the\n // history strategy isn't configured — the records still\n // committed, only the multi-record summary is unavailable.\n const ledger = v._getLedgerOrNull()\n if (ledger) {\n const role = v.role as 'admin' | 'owner'\n const amendment: NonNullable<LedgerEntry['amendment']> = {\n reason: options!.reason,\n role,\n changes: meta,\n invariantsPassed,\n }\n await ledger.append({\n op: 'amendment',\n collection: '',\n id: '',\n version: 0,\n actor: v.userId,\n // No payload to hash — the per-record entries already\n // captured `payloadHash` at their own append time. We use\n // a sha256 of the canonical reason string so the field is\n // populated with something deterministic and non-empty.\n payloadHash: '',\n amendment,\n })\n }\n void vaultName\n }\n } catch (err) {\n await revertExecuted(ctx._executed, store, db)\n throw err instanceof InvariantError ? err : new InvariantError(\n err instanceof Error ? err.message : `invariant violated: ${String(err)}`,\n )\n }\n }\n\n // ─── Commit-time changeset invariant phase (#342) ───────────\n // Runs for BOTH ordinary and amendment transactions (placed after the\n // amendment phase so an amendment commit is still subject to these\n // set-level constraints). Assemble the changeset from the executed\n // staged ops, deduped to the LAST write per (vault, coll, id) while\n // preserving write order, then group `GuardChange` by collection\n // (scope) and run each matching invariant. A throw mirrors the\n // amendment-phase failure mode exactly: revert every executed op and\n // re-throw as `InvariantError`.\n if (invariants.length > 0) {\n // Dedup ctx._ops to the last write per key, preserving first-seen\n // (write) order so the changeset is stable and order-meaningful.\n const lastOp = new Map<string, StagedOp>()\n const order: string[] = []\n for (const op of ctx._ops) {\n const key = keyOf(op)\n if (!lastOp.has(key)) order.push(key)\n lastOp.set(key, op)\n }\n\n // Group {before, after} pairs by collection name (the invariant\n // scope). `before` is the plaintext prior captured in Phase 1 (null\n // for inserts / unwatched — only watched scopes were captured, and\n // every grouped key belongs to a watched scope). `after` is the\n // written record, null for a delete.\n const changesByScope = new Map<string, GuardChange<unknown>[]>()\n // Parallel map: scope → the vault name of its (last-seen) op, used to\n // build the per-invariant read-only ctx (facade + userId + role).\n const scopeVault = new Map<string, string>()\n for (const key of order) {\n const op = lastOp.get(key)!\n if (!watchedScopes.has(op.collectionName)) continue\n const before = plainBefore.get(key) ?? null\n const after = op.type === 'delete' ? null : (op.record ?? null)\n const change = { before, after } as GuardChange<unknown>\n const arr = changesByScope.get(op.collectionName)\n if (arr) arr.push(change)\n else changesByScope.set(op.collectionName, [change])\n\n // Stash the vault name alongside so we can build a per-vault ctx.\n // (All ops in a scope group could span vaults; we resolve the\n // vault per change below via a parallel map keyed the same way.)\n scopeVault.set(op.collectionName, op.vaultName)\n }\n\n try {\n for (const inv of invariants) {\n const changes = changesByScope.get(inv.scope)\n if (changes === undefined || changes.length === 0) continue\n const vaultName = scopeVault.get(inv.scope)!\n const v = db.vault(vaultName)\n // Prefer the real read-only facade so the invariant can read\n // sibling collections; fall back to a minimal read-only stub.\n const facade: ReadOnlyVaultFacade =\n v._getReadOnlyFacade() ?? {\n collection<R = unknown>(name: string) {\n const c = v.collection<R>(name)\n return {\n get: (id: string) => c.get(id),\n list: () => c.list(),\n query: () => c.query(),\n }\n },\n }\n const ctxForInv: GuardContext<unknown> = {\n existing: null,\n vault: facade,\n userId: v.userId,\n role: v.role,\n }\n await inv.check(changes, ctxForInv)\n }\n } catch (err) {\n await revertExecuted(ctx._executed, store, db)\n throw err instanceof InvariantError ? err : new InvariantError(\n err instanceof Error ? err.message : `invariant violated: ${String(err)}`,\n )\n }\n }\n\n return bodyResult\n}\n\n/**\n * Phase 3 helper — restore captured prior envelopes via the raw store\n * to avoid re-firing Collection-level side effects (we don't want a\n * cascade of change events undoing themselves). The ledger is left\n * as-is: each committed op appended an entry; the revert is\n * deliberately NOT recorded as a compensating entry because the\n * caller-facing contract is \"atomic or not at all,\" not \"every write\n * visible in the audit trail.\" Auditors who need the intermediate\n * state can still reconstruct it by walking the ledger through the\n * failed-tx timestamp.\n *\n * @internal — shared between `runTransaction` and\n * `Collection.putManyAtomic`. Both register source ops + nested\n * derivation side-effect ops onto `_executed`; this helper unwinds the\n * combined list in reverse on rollback.\n */\nexport async function revertExecuted(\n executed: ReadonlyArray<ExecutedOp>,\n store: Noydb['_store'],\n db?: Noydb,\n): Promise<void> {\n for (const { op, priorEnvelope } of executed.slice().reverse()) {\n try {\n if (priorEnvelope) {\n await store.put(op.vaultName, op.collectionName, op.id, priorEnvelope)\n } else {\n await store.delete(op.vaultName, op.collectionName, op.id)\n }\n // Sync the Collection-layer cache with what we just wrote at\n // the raw store. Without this, eager-mode `get` would still\n // return the rolled-back record from its in-memory map. The\n // Collection's `_invalidateCacheEntry` is a no-op when the\n // collection hasn't yet been hydrated.\n if (db) {\n const coll = db.vault(op.vaultName).collection(op.collectionName)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await (coll as any)._invalidateCacheEntry(op.id)\n }\n } catch {\n // swallow — best-effort. Surfacing the revert error would mask\n // the original one that triggered the rollback.\n }\n }\n}\n\nfunction keyOf(op: StagedOp): string {\n return `${op.vaultName}\\x00${op.collectionName}\\x00${op.id}`\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NOYDB_FORMAT_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LR7CODVN.js";
|
|
4
4
|
import {
|
|
5
5
|
decrypt
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-WQ3KAGOV.js";
|
|
7
7
|
import {
|
|
8
8
|
ReadOnlyAtInstantError
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-4BB4T3O7.js";
|
|
10
10
|
|
|
11
11
|
// src/history/history.ts
|
|
12
12
|
var HISTORY_COLLECTION = "_history";
|
|
@@ -335,4 +335,4 @@ export {
|
|
|
335
335
|
diff,
|
|
336
336
|
formatDiff
|
|
337
337
|
};
|
|
338
|
-
//# sourceMappingURL=chunk-
|
|
338
|
+
//# sourceMappingURL=chunk-C472BRJ4.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
OverlayIdMismatchError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4BB4T3O7.js";
|
|
4
4
|
|
|
5
5
|
// src/overlay-views/virtual-collection.ts
|
|
6
6
|
var OverlayedCollection = class {
|
|
@@ -206,4 +206,4 @@ var OverlayedCollection = class {
|
|
|
206
206
|
export {
|
|
207
207
|
OverlayedCollection
|
|
208
208
|
};
|
|
209
|
-
//# sourceMappingURL=chunk-
|
|
209
|
+
//# sourceMappingURL=chunk-CCNRFAL3.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NOYDB_FORMAT_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LR7CODVN.js";
|
|
4
4
|
import {
|
|
5
5
|
bufferToBase64,
|
|
6
6
|
decrypt,
|
|
@@ -8,11 +8,11 @@ import {
|
|
|
8
8
|
generateDEK,
|
|
9
9
|
unwrapCek,
|
|
10
10
|
wrapCek
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-WQ3KAGOV.js";
|
|
12
12
|
import {
|
|
13
13
|
RecordCekNotFoundError,
|
|
14
14
|
ValidationError
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-4BB4T3O7.js";
|
|
16
16
|
|
|
17
17
|
// src/record-keys/tombstone.ts
|
|
18
18
|
function isTombstone(envelope, encrypted) {
|
|
@@ -148,4 +148,4 @@ export {
|
|
|
148
148
|
revokeSealedRecord,
|
|
149
149
|
rotateRecordCek
|
|
150
150
|
};
|
|
151
|
-
//# sourceMappingURL=chunk-
|
|
151
|
+
//# sourceMappingURL=chunk-DCA2BDHA.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ValidationError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4BB4T3O7.js";
|
|
4
4
|
|
|
5
5
|
// src/overlay-views/with-overlayed-view.ts
|
|
6
6
|
function withOverlayedView(spec) {
|
|
@@ -33,4 +33,4 @@ function withOverlayedView(spec) {
|
|
|
33
33
|
export {
|
|
34
34
|
withOverlayedView
|
|
35
35
|
};
|
|
36
|
-
//# sourceMappingURL=chunk-
|
|
36
|
+
//# sourceMappingURL=chunk-DCICHSRS.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NOYDB_FORMAT_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LR7CODVN.js";
|
|
4
4
|
import {
|
|
5
5
|
encrypt
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-WQ3KAGOV.js";
|
|
7
7
|
|
|
8
8
|
// src/blobs/export-blobs.ts
|
|
9
9
|
var ExportBlobsAbortedError = class extends Error {
|
|
@@ -258,4 +258,4 @@ export {
|
|
|
258
258
|
BLOB_EVICTION_AUDIT_COLLECTION,
|
|
259
259
|
runCompaction
|
|
260
260
|
};
|
|
261
|
-
//# sourceMappingURL=chunk-
|
|
261
|
+
//# sourceMappingURL=chunk-FG6IQ3ZL.js.map
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
IllegalTransitionError,
|
|
3
3
|
RecordLockedError,
|
|
4
4
|
ValidationError
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-4BB4T3O7.js";
|
|
6
6
|
|
|
7
7
|
// src/guards/with-guard.ts
|
|
8
8
|
function withGuard(strategy) {
|
|
@@ -120,4 +120,4 @@ export {
|
|
|
120
120
|
immutableGuard,
|
|
121
121
|
transitionGuard
|
|
122
122
|
};
|
|
123
|
-
//# sourceMappingURL=chunk-
|
|
123
|
+
//# sourceMappingURL=chunk-G4GW5VOS.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NoydbError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4BB4T3O7.js";
|
|
4
4
|
|
|
5
5
|
// src/money/iso4217.ts
|
|
6
6
|
var MINOR_UNITS = {
|
|
@@ -521,4 +521,4 @@ export {
|
|
|
521
521
|
evaluateClause,
|
|
522
522
|
hasFnClause
|
|
523
523
|
};
|
|
524
|
-
//# sourceMappingURL=chunk-
|
|
524
|
+
//# sourceMappingURL=chunk-GEWIFM4J.js.map
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
LocaleNotSpecifiedError,
|
|
3
3
|
MissingTranslationError,
|
|
4
4
|
ScriptViolationError
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-4BB4T3O7.js";
|
|
6
6
|
|
|
7
7
|
// src/i18n/policy.ts
|
|
8
8
|
function resolvePolicy(onMissing, layer) {
|
|
@@ -358,4 +358,4 @@ export {
|
|
|
358
358
|
setAtPathInPlace,
|
|
359
359
|
applyI18nLocale
|
|
360
360
|
};
|
|
361
|
-
//# sourceMappingURL=chunk-
|
|
361
|
+
//# sourceMappingURL=chunk-HD4QCT2O.js.map
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
PeriodClosedError,
|
|
7
7
|
ValidationError
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-4BB4T3O7.js";
|
|
9
9
|
|
|
10
10
|
// src/periods/periods.ts
|
|
11
11
|
var PERIODS_COLLECTION = "_periods";
|
|
@@ -56,7 +56,7 @@ function validatePeriodName(name, existing) {
|
|
|
56
56
|
}
|
|
57
57
|
async function appendPeriodLedgerEntry(ledger, actor, envelope, name) {
|
|
58
58
|
if (!ledger) return;
|
|
59
|
-
const { envelopePayloadHash } = await import("./ledger-
|
|
59
|
+
const { envelopePayloadHash } = await import("./ledger-VOS2X3WJ.js");
|
|
60
60
|
await ledger.append({
|
|
61
61
|
op: "put",
|
|
62
62
|
collection: PERIODS_COLLECTION,
|
|
@@ -87,4 +87,4 @@ export {
|
|
|
87
87
|
appendPeriodLedgerEntry,
|
|
88
88
|
withPeriods
|
|
89
89
|
};
|
|
90
|
-
//# sourceMappingURL=chunk-
|
|
90
|
+
//# sourceMappingURL=chunk-HHJ5DZCZ.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DerivationCapExceededError,
|
|
3
3
|
DerivationOutputShapeError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4BB4T3O7.js";
|
|
5
5
|
|
|
6
6
|
// src/derivations/executor.ts
|
|
7
7
|
var DerivationExecutor = {
|
|
@@ -121,4 +121,4 @@ var DerivationExecutor = {
|
|
|
121
121
|
export {
|
|
122
122
|
DerivationExecutor
|
|
123
123
|
};
|
|
124
|
-
//# sourceMappingURL=chunk-
|
|
124
|
+
//# sourceMappingURL=chunk-IEIADIPM.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ValidationError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4BB4T3O7.js";
|
|
4
4
|
|
|
5
5
|
// src/derivations/with-derivation.ts
|
|
6
6
|
function withDerivation(spec) {
|
|
@@ -126,4 +126,4 @@ export {
|
|
|
126
126
|
withDerivation,
|
|
127
127
|
withRollup
|
|
128
128
|
};
|
|
129
|
-
//# sourceMappingURL=chunk-
|
|
129
|
+
//# sourceMappingURL=chunk-IHAISFXP.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readPath
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GEWIFM4J.js";
|
|
4
4
|
|
|
5
5
|
// src/indexing/eager-indexes.ts
|
|
6
6
|
var CollectionIndexes = class {
|
|
@@ -129,4 +129,4 @@ function removeFromIndex(idx, id, record) {
|
|
|
129
129
|
export {
|
|
130
130
|
CollectionIndexes
|
|
131
131
|
};
|
|
132
|
-
//# sourceMappingURL=chunk-
|
|
132
|
+
//# sourceMappingURL=chunk-JKM2AVVH.js.map
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ensureCollectionDEK
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-B5CSNGSE.js";
|
|
4
4
|
import {
|
|
5
5
|
NOYDB_FORMAT_VERSION
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-LR7CODVN.js";
|
|
7
7
|
import {
|
|
8
8
|
decrypt,
|
|
9
9
|
encrypt
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-WQ3KAGOV.js";
|
|
11
11
|
import {
|
|
12
12
|
PermissionDeniedError
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-4BB4T3O7.js";
|
|
14
14
|
|
|
15
15
|
// src/team/sync-credentials.ts
|
|
16
16
|
var SYNC_CREDENTIALS_COLLECTION = "_sync_credentials";
|
|
@@ -76,4 +76,4 @@ export {
|
|
|
76
76
|
listCredentials,
|
|
77
77
|
credentialStatus
|
|
78
78
|
};
|
|
79
|
-
//# sourceMappingURL=chunk-
|
|
79
|
+
//# sourceMappingURL=chunk-JRMOSIH4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/team/sync-credentials.ts"],"sourcesContent":["/**\n * _sync_credentials reserved collection —\n *\n * Stores per-adapter OAuth tokens (and any other long-lived sync secrets) as\n * encrypted records inside the vault itself. Tokens are wrapped with the\n * compartment's own DEK, live on disk as ciphertext like any other record, and\n * are accessed only through the dedicated API in this module — never via\n * `vault.collection('_sync_credentials')`.\n *\n * Design decisions\n * ────────────────\n *\n * **Why a reserved collection, not a separate store?**\n * The compartment's existing encryption stack (AES-256-GCM + collection DEK)\n * is exactly the right primitive for protecting OAuth tokens at rest. Using a\n * separate store would require a new encryption surface, new adapter calls,\n * and a new backup/restore path — all of which already exist for collections.\n *\n * **Why not exposed as a regular collection?**\n * The same reason `_keyring` and `_ledger` aren't: they have invariants that\n * must be enforced (naming scheme, no cross-user leakage, no schema\n * validation, no history/ledger writes for privacy). Routing through a\n * dedicated API enforces those invariants.\n *\n * **Token lifecycle:**\n * - `putCredential(vault, adapterId, token)` — store or overwrite\n * - `getCredential(vault, adapterId)` — load and decrypt\n * - `deleteCredential(vault, adapterId)` — remove\n * - `listCredentials(vault)` — enumerate adapter IDs (not tokens)\n *\n * The `adapterId` is the record ID within the `_sync_credentials` collection.\n * It should be a stable, human-readable identifier for the adapter instance\n * (e.g. `'google-drive'`, `'dropbox'`, `'s3-prod'`).\n *\n * **ACL:** only `owner` and `admin` roles can read/write sync credentials.\n * Operators, viewers, and clients cannot call this API. The check is made\n * against the caller's keyring role at call time.\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../types.js'\nimport { NOYDB_FORMAT_VERSION } from '../types.js'\nimport type { UnlockedKeyring } from './keyring.js'\nimport { encrypt, decrypt } from '../crypto.js'\nimport { ensureCollectionDEK } from './keyring.js'\nimport { PermissionDeniedError } from '../errors.js'\n\n/** The reserved collection name. Never collides with user collections. */\nexport const SYNC_CREDENTIALS_COLLECTION = '_sync_credentials'\n\n// ─── Token types ──────────────────────────────────────────────────────\n\n/**\n * An OAuth/auth token stored in `_sync_credentials`.\n *\n * Fields mirror the OAuth2 token response shape. `customData` is an escape\n * hatch for adapter-specific secrets (API keys, connection strings, etc.)\n * that don't fit the OAuth2 shape.\n */\nexport interface SyncCredential {\n /** Stable identifier for the adapter instance (e.g. 'google-drive'). */\n readonly adapterId: string\n /** OAuth token type, usually 'Bearer'. */\n readonly tokenType: string\n /** The access token. Expires at `expiresAt` if set. */\n readonly accessToken: string\n /** Long-lived refresh token for renewing the access token. */\n readonly refreshToken?: string\n /** ISO timestamp when `accessToken` expires. Absent means \"no expiry\". */\n readonly expiresAt?: string\n /** Space-separated OAuth scopes. */\n readonly scopes?: string\n /** Adapter-specific opaque data (API keys, endpoints, etc.). */\n readonly customData?: Record<string, string>\n}\n\n// ─── Access check ─────────────────────────────────────────────────────\n\nfunction requireAdminAccess(keyring: UnlockedKeyring): void {\n // FR-6: custodian is INTENTIONALLY excluded. Sync credentials are the\n // firm's hosting/infrastructure secrets (OAuth tokens, connection strings) —\n // not the custodian's operational scope. A custodian operates the DATA but\n // must never mint or read transport credentials that could be used to\n // re-home or impersonate the firm's vault. Do NOT add 'custodian' here.\n if (keyring.role !== 'owner' && keyring.role !== 'admin') {\n throw new PermissionDeniedError(\n `Sync credentials require owner or admin role. Current role: \"${keyring.role}\"`,\n )\n }\n}\n\n// ─── Public API ────────────────────────────────────────────────────────\n\n/**\n * Store or overwrite a sync credential for the given adapter.\n *\n * The credential is encrypted with the `_sync_credentials` collection DEK\n * (auto-generated on first use). The record ID is the `adapterId`.\n *\n * Requires owner or admin role.\n */\nexport async function putCredential(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n credential: SyncCredential,\n): Promise<void> {\n requireAdminAccess(keyring)\n\n const getDek = await ensureCollectionDEK(adapter, vault, keyring)\n const dek = await getDek(SYNC_CREDENTIALS_COLLECTION)\n\n const { iv, data } = await encrypt(JSON.stringify(credential), dek)\n\n const existing = await adapter.get(vault, SYNC_CREDENTIALS_COLLECTION, credential.adapterId)\n const version = existing ? existing._v + 1 : 1\n\n const envelope: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: version,\n _ts: new Date().toISOString(),\n _iv: iv,\n _data: data,\n _by: keyring.userId,\n }\n\n await adapter.put(\n vault,\n SYNC_CREDENTIALS_COLLECTION,\n credential.adapterId,\n envelope,\n existing ? existing._v : undefined,\n )\n}\n\n/**\n * Load and decrypt a sync credential for the given adapter ID.\n *\n * Returns `null` if no credential exists for this adapter.\n * Requires owner or admin role.\n */\nexport async function getCredential(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n adapterId: string,\n): Promise<SyncCredential | null> {\n requireAdminAccess(keyring)\n\n const getDek = await ensureCollectionDEK(adapter, vault, keyring)\n const dek = await getDek(SYNC_CREDENTIALS_COLLECTION)\n\n const envelope = await adapter.get(vault, SYNC_CREDENTIALS_COLLECTION, adapterId)\n if (!envelope) return null\n\n const plaintext = await decrypt(envelope._iv, envelope._data, dek)\n return JSON.parse(plaintext) as SyncCredential\n}\n\n/**\n * Delete a sync credential by adapter ID.\n *\n * No-op if the credential doesn't exist. Requires owner or admin role.\n */\nexport async function deleteCredential(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n adapterId: string,\n): Promise<void> {\n requireAdminAccess(keyring)\n await adapter.delete(vault, SYNC_CREDENTIALS_COLLECTION, adapterId)\n}\n\n/**\n * List all adapter IDs that have stored credentials.\n *\n * Returns only the IDs, never the credential payloads. Useful for\n * displaying \"connected adapters\" in UI without decrypting tokens.\n * Requires owner or admin role.\n */\nexport async function listCredentials(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n): Promise<string[]> {\n requireAdminAccess(keyring)\n return adapter.list(vault, SYNC_CREDENTIALS_COLLECTION)\n}\n\n/**\n * Check whether a credential exists and whether its access token has expired.\n *\n * Returns `{ exists: false }` if no credential is stored, or\n * `{ exists: true, expired: boolean }` based on the `expiresAt` field.\n * Requires owner or admin role.\n */\nexport async function credentialStatus(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n adapterId: string,\n): Promise<{ exists: false } | { exists: true; expired: boolean }> {\n const credential = await getCredential(adapter, vault, keyring, adapterId)\n if (!credential) return { exists: false }\n\n const expired = credential.expiresAt\n ? Date.now() > new Date(credential.expiresAt).getTime()\n : false\n\n return { exists: true, expired }\n}\n"],"mappings":";;;;;;;;;;;;;;;AA+CO,IAAM,8BAA8B;AA8B3C,SAAS,mBAAmB,SAAgC;AAM1D,MAAI,QAAQ,SAAS,WAAW,QAAQ,SAAS,SAAS;AACxD,UAAM,IAAI;AAAA,MACR,gEAAgE,QAAQ,IAAI;AAAA,IAC9E;AAAA,EACF;AACF;AAYA,eAAsB,cACpB,SACA,OACA,SACA,YACe;AACf,qBAAmB,OAAO;AAE1B,QAAM,SAAS,MAAM,oBAAoB,SAAS,OAAO,OAAO;AAChE,QAAM,MAAM,MAAM,OAAO,2BAA2B;AAEpD,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,QAAQ,KAAK,UAAU,UAAU,GAAG,GAAG;AAElE,QAAM,WAAW,MAAM,QAAQ,IAAI,OAAO,6BAA6B,WAAW,SAAS;AAC3F,QAAM,UAAU,WAAW,SAAS,KAAK,IAAI;AAE7C,QAAM,WAA8B;AAAA,IAClC,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAK,oBAAI,KAAK,GAAE,YAAY;AAAA,IAC5B,KAAK;AAAA,IACL,OAAO;AAAA,IACP,KAAK,QAAQ;AAAA,EACf;AAEA,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,WAAW,SAAS,KAAK;AAAA,EAC3B;AACF;AAQA,eAAsB,cACpB,SACA,OACA,SACA,WACgC;AAChC,qBAAmB,OAAO;AAE1B,QAAM,SAAS,MAAM,oBAAoB,SAAS,OAAO,OAAO;AAChE,QAAM,MAAM,MAAM,OAAO,2BAA2B;AAEpD,QAAM,WAAW,MAAM,QAAQ,IAAI,OAAO,6BAA6B,SAAS;AAChF,MAAI,CAAC,SAAU,QAAO;AAEtB,QAAM,YAAY,MAAM,QAAQ,SAAS,KAAK,SAAS,OAAO,GAAG;AACjE,SAAO,KAAK,MAAM,SAAS;AAC7B;AAOA,eAAsB,iBACpB,SACA,OACA,SACA,WACe;AACf,qBAAmB,OAAO;AAC1B,QAAM,QAAQ,OAAO,OAAO,6BAA6B,SAAS;AACpE;AASA,eAAsB,gBACpB,SACA,OACA,SACmB;AACnB,qBAAmB,OAAO;AAC1B,SAAO,QAAQ,KAAK,OAAO,2BAA2B;AACxD;AASA,eAAsB,iBACpB,SACA,OACA,SACA,WACiE;AACjE,QAAM,aAAa,MAAM,cAAc,SAAS,OAAO,SAAS,SAAS;AACzE,MAAI,CAAC,WAAY,QAAO,EAAE,QAAQ,MAAM;AAExC,QAAM,UAAU,WAAW,YACvB,KAAK,IAAI,IAAI,IAAI,KAAK,WAAW,SAAS,EAAE,QAAQ,IACpD;AAEJ,SAAO,EAAE,QAAQ,MAAM,QAAQ;AACjC;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DerivationCycleError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4BB4T3O7.js";
|
|
4
4
|
|
|
5
5
|
// src/derivations/strategy-hash.ts
|
|
6
6
|
async function computeStrategyHash(source, outputKeys, derive, sources) {
|
|
@@ -115,4 +115,4 @@ var DerivationRegistry = class {
|
|
|
115
115
|
export {
|
|
116
116
|
DerivationRegistry
|
|
117
117
|
};
|
|
118
|
-
//# sourceMappingURL=chunk-
|
|
118
|
+
//# sourceMappingURL=chunk-LMWVNF6X.js.map
|