@noy-db/hub 0.8.0-pre.0 → 0.9.0-pre.0
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/CHANGELOG.md +78 -0
- package/README.md +8 -8
- package/codemods/0.4.0-pre.json +1 -1
- package/codemods/0.8.0-pre.json +1 -1
- package/dist/api-MSJXVYQG.js +18 -0
- package/dist/as/index.js +34 -25
- package/dist/as/index.js.map +1 -1
- package/dist/at/index.js +9 -10
- package/dist/attestation/index.js +39 -28
- package/dist/attestation/index.js.map +1 -1
- package/dist/{backup-7TFRVFLH.js → backup-7RKNZMQO.js} +15 -16
- package/dist/backup-7RKNZMQO.js.map +1 -0
- package/dist/blobs/index.js +13 -14
- package/dist/blobs/index.js.map +1 -1
- package/dist/broker/index.js +20 -13
- package/dist/broker/index.js.map +1 -1
- package/dist/by/index.js +2 -2
- package/dist/capsule/contract.d.ts +159 -0
- package/dist/capsule/contract.js +95 -0
- package/dist/capsule/enclave-aes/bound.d.ts +52 -0
- package/dist/{kernel/enclave → capsule/enclave-aes}/broker/proof.d.ts +12 -3
- package/dist/capsule/enclave-aes/capabilities.d.ts +2 -0
- package/dist/capsule/enclave-aes/classify/compare.d.ts +20 -0
- package/dist/{kernel/enclave → capsule/enclave-aes}/classify/find.d.ts +1 -1
- package/dist/{kernel/enclave → capsule/enclave-aes}/classify/reveal.d.ts +1 -1
- package/dist/{kernel/enclave → capsule/enclave-aes}/classify/verify.d.ts +2 -2
- package/dist/{kernel/enclave → capsule/enclave-aes}/classify/write.d.ts +1 -1
- package/dist/{kernel/enclave → capsule/enclave-aes}/crypto.d.ts +33 -129
- package/dist/capsule/enclave-aes/index.d.ts +113 -0
- package/dist/capsule/enclave-aes/index.js +361 -0
- package/dist/capsule/enclave-aes/index.js.map +1 -0
- package/dist/capsule/enclave-aes/primitives.d.ts +29 -0
- package/dist/capsule/index.d.ts +27 -0
- package/dist/capsule/plumbing/ct-equal.d.ts +11 -0
- package/dist/{kernel/enclave/record-keys → capsule/plumbing}/deterministic.d.ts +8 -21
- package/dist/capsule/plumbing/digest.d.ts +122 -0
- package/dist/{kernel/enclave/record-keys → capsule/plumbing}/envelope-body.d.ts +44 -48
- package/dist/capsule/plumbing/index.d.ts +100 -0
- package/dist/capsule/plumbing/lifecycle.d.ts +50 -0
- package/dist/{kernel/enclave/record-keys → capsule/plumbing}/record-codec.d.ts +62 -20
- package/dist/{kernel/enclave → capsule/plumbing}/record-envelope.d.ts +1 -1
- package/dist/capsule/plumbing/rekey-blob.d.ts +33 -0
- package/dist/capsule/plumbing/rekey.d.ts +44 -0
- package/dist/capsule/plumbing/sealed-slot.d.ts +22 -0
- package/dist/capsule/plumbing/sealed-slots.d.ts +37 -0
- package/dist/capsule/plumbing/sealing.d.ts +29 -0
- package/dist/capsule/plumbing/sign.d.ts +9 -0
- package/dist/{kernel/enclave/record-keys → capsule/plumbing}/tombstone.d.ts +2 -2
- package/dist/capsule-57BDK7LG.js +209 -0
- package/dist/cargo/index.js +36 -36
- package/dist/{chunk-BUDAGWL5.js → chunk-226V6XXJ.js} +41 -6
- package/dist/chunk-226V6XXJ.js.map +1 -0
- package/dist/{chunk-QWGU66MP.js → chunk-2A56DSRI.js} +5 -7
- package/dist/chunk-2A56DSRI.js.map +1 -0
- package/dist/chunk-2DX4JCRI.js +1 -0
- package/dist/{chunk-ODVPTDPK.js → chunk-2ELOCEXE.js} +1 -1
- package/dist/chunk-2ELOCEXE.js.map +1 -0
- package/dist/{chunk-CKNGS5MH.js → chunk-2WJKJZSO.js} +3 -5
- package/dist/chunk-2WJKJZSO.js.map +1 -0
- package/dist/{chunk-ZSX4S5XB.js → chunk-2ZFD2UJC.js} +9 -9
- package/dist/{chunk-YSK2CU2Q.js → chunk-3V6LZHUO.js} +3 -3
- package/dist/chunk-3V6LZHUO.js.map +1 -0
- package/dist/{chunk-HT2AHGQI.js → chunk-3Y5JLSXS.js} +2 -2
- package/dist/{chunk-M7NI3IWE.js → chunk-45VHWE4C.js} +67 -60
- package/dist/chunk-45VHWE4C.js.map +1 -0
- package/dist/{chunk-LGNDFRYC.js → chunk-4CFHL5RY.js} +1 -1
- package/dist/chunk-4CFHL5RY.js.map +1 -0
- package/dist/{enclave-27SMF6GF.js → chunk-4J3MNVVX.js} +83 -56
- package/dist/chunk-4J3MNVVX.js.map +1 -0
- package/dist/{chunk-SI3V7D4G.js → chunk-4UIP3QNW.js} +2 -2
- package/dist/{chunk-WNJ4AVGE.js → chunk-55P6OBB7.js} +2 -2
- package/dist/{chunk-6LNL42F5.js → chunk-5HNW4ZTA.js} +12 -15
- package/dist/chunk-5HNW4ZTA.js.map +1 -0
- package/dist/{chunk-SYVZW77S.js → chunk-5LMCVBMY.js} +6 -8
- package/dist/chunk-5LMCVBMY.js.map +1 -0
- package/dist/{chunk-MKXXMNJH.js → chunk-5UUJE4FP.js} +3 -3
- package/dist/chunk-5XONHAFD.js +12 -0
- package/dist/chunk-5XONHAFD.js.map +1 -0
- package/dist/{chunk-X4773RBR.js → chunk-6QLCS7ET.js} +2 -2
- package/dist/chunk-6QLCS7ET.js.map +1 -0
- package/dist/{chunk-FW22ZCXT.js → chunk-6WS35E6X.js} +2 -2
- package/dist/{chunk-FCKWQE2H.js → chunk-6Y5E4SJU.js} +7 -8
- package/dist/chunk-6Y5E4SJU.js.map +1 -0
- package/dist/{chunk-4AF3TUMP.js → chunk-72XBVN4J.js} +8 -2
- package/dist/{chunk-4AF3TUMP.js.map → chunk-72XBVN4J.js.map} +1 -1
- package/dist/{chunk-QBBUZ3I3.js → chunk-7BMOS4JQ.js} +11 -11
- package/dist/chunk-7BMOS4JQ.js.map +1 -0
- package/dist/{chunk-5FDLC6G7.js → chunk-7CT6SX4M.js} +3 -3
- package/dist/chunk-7CT6SX4M.js.map +1 -0
- package/dist/{chunk-LQ4ZHOKP.js → chunk-ABU6A4Z5.js} +36 -27
- package/dist/chunk-ABU6A4Z5.js.map +1 -0
- package/dist/{chunk-WX5ADGTZ.js → chunk-AJFPAHMW.js} +3 -3
- package/dist/chunk-AJFPAHMW.js.map +1 -0
- package/dist/{chunk-ZE4CHIED.js → chunk-APFFJN5T.js} +39 -37
- package/dist/chunk-APFFJN5T.js.map +1 -0
- package/dist/{chunk-6OWXYZK2.js → chunk-B65CC6Z6.js} +2 -2
- package/dist/{chunk-F2TDU7MQ.js → chunk-BJH77UY2.js} +2 -2
- package/dist/{chunk-L7DOS3TE.js → chunk-BMHQHH7Q.js} +3 -3
- package/dist/chunk-BMHQHH7Q.js.map +1 -0
- package/dist/{chunk-BJ2IXQ5Q.js → chunk-BNFNQNEP.js} +2 -2
- package/dist/chunk-BU2ABHFW.js +74 -0
- package/dist/chunk-BU2ABHFW.js.map +1 -0
- package/dist/{chunk-HLEK5VAN.js → chunk-BUE7R3B5.js} +2 -2
- package/dist/{chunk-ISZQJBML.js → chunk-BVF3SXD7.js} +6 -6
- package/dist/chunk-BVF3SXD7.js.map +1 -0
- package/dist/{chunk-M522UYZ2.js → chunk-BYHCKX73.js} +6 -10
- package/dist/chunk-BYHCKX73.js.map +1 -0
- package/dist/{chunk-EOVMT6TB.js → chunk-C53ER7AG.js} +2 -2
- package/dist/{chunk-K6W7BVZ6.js → chunk-CAD7N6HQ.js} +3 -3
- package/dist/chunk-CB4467G3.js +565 -0
- package/dist/chunk-CB4467G3.js.map +1 -0
- package/dist/{chunk-NWHBJQAL.js → chunk-D4V5JJAK.js} +4 -4
- package/dist/chunk-D4V5JJAK.js.map +1 -0
- package/dist/{chunk-QWFHIOD2.js → chunk-D5A3GCZ4.js} +11 -12
- package/dist/chunk-D5A3GCZ4.js.map +1 -0
- package/dist/{chunk-5O7D6ZTW.js → chunk-DHU2AQ6T.js} +2 -2
- package/dist/{chunk-DC5QVPV7.js → chunk-DL6BZKP3.js} +3 -3
- package/dist/chunk-DL6BZKP3.js.map +1 -0
- package/dist/{chunk-FQ54DWUQ.js → chunk-EA4VDI5D.js} +4 -4
- package/dist/chunk-EA4VDI5D.js.map +1 -0
- package/dist/{chunk-IT22PWKV.js → chunk-ECGXA4LT.js} +2 -2
- package/dist/{chunk-BP6DARYA.js → chunk-EMAWOGVH.js} +4 -4
- package/dist/chunk-EMAWOGVH.js.map +1 -0
- package/dist/{chunk-3F7GLNAE.js → chunk-EUD3HQM4.js} +7 -11
- package/dist/chunk-EUD3HQM4.js.map +1 -0
- package/dist/{chunk-T2LBQ5DV.js → chunk-FSQNGPHH.js} +335 -250
- package/dist/chunk-FSQNGPHH.js.map +1 -0
- package/dist/{chunk-7VCXZDA4.js → chunk-FZXRLNJO.js} +7 -11
- package/dist/chunk-FZXRLNJO.js.map +1 -0
- package/dist/{chunk-6LMQ5R26.js → chunk-G46JSSVX.js} +7 -7
- package/dist/{chunk-6LMQ5R26.js.map → chunk-G46JSSVX.js.map} +1 -1
- package/dist/{chunk-GF5BZ7D4.js → chunk-G5EDYVSE.js} +11 -13
- package/dist/chunk-G5EDYVSE.js.map +1 -0
- package/dist/chunk-G66YXUTY.js +60 -0
- package/dist/chunk-G66YXUTY.js.map +1 -0
- package/dist/{chunk-FFWI7UH7.js → chunk-GAGT3YZN.js} +1 -1
- package/dist/chunk-GAGT3YZN.js.map +1 -0
- package/dist/{chunk-4MXI3ODD.js → chunk-GCUMOKTN.js} +3 -3
- package/dist/{chunk-FH2L2E34.js → chunk-GETUTTZU.js} +4 -4
- package/dist/{chunk-FH2L2E34.js.map → chunk-GETUTTZU.js.map} +1 -1
- package/dist/chunk-GMKGGY7V.js +234 -0
- package/dist/chunk-GMKGGY7V.js.map +1 -0
- package/dist/{chunk-B6SJIBQK.js → chunk-GOJQ5DZA.js} +4 -4
- package/dist/{chunk-EIPVYUEP.js → chunk-GQ4MF3CM.js} +3 -3
- package/dist/chunk-GQ4MF3CM.js.map +1 -0
- package/dist/{chunk-PDCG6PR5.js → chunk-GRS2HIV6.js} +2 -2
- package/dist/chunk-GRS2HIV6.js.map +1 -0
- package/dist/{chunk-PEOZ34EM.js → chunk-GSAK56VF.js} +1 -1
- package/dist/chunk-GSAK56VF.js.map +1 -0
- package/dist/{chunk-3H5KLDZN.js → chunk-H3YM66EI.js} +4 -8
- package/dist/chunk-H3YM66EI.js.map +1 -0
- package/dist/{chunk-NRT6QZQB.js → chunk-IVMFIKNA.js} +6 -6
- package/dist/chunk-IVMFIKNA.js.map +1 -0
- package/dist/{chunk-PSIV7CJV.js → chunk-J4KAPKBJ.js} +2 -2
- package/dist/chunk-J4KAPKBJ.js.map +1 -0
- package/dist/{chunk-N5DNADLQ.js → chunk-JA25QG5M.js} +224 -34
- package/dist/chunk-JA25QG5M.js.map +1 -0
- package/dist/{chunk-T7IR4N2S.js → chunk-JBMMWYOH.js} +3 -1
- package/dist/{chunk-T7IR4N2S.js.map → chunk-JBMMWYOH.js.map} +1 -1
- package/dist/{chunk-YNIUIERI.js → chunk-JPSL5GIZ.js} +18 -59
- package/dist/chunk-JPSL5GIZ.js.map +1 -0
- package/dist/{chunk-TKUOEU2F.js → chunk-JSWKVZCR.js} +2 -2
- package/dist/chunk-JZ4EDEIP.js +22 -0
- package/dist/chunk-JZ4EDEIP.js.map +1 -0
- package/dist/{chunk-QOOUUR4A.js → chunk-K673OO6B.js} +10 -4
- package/dist/chunk-K673OO6B.js.map +1 -0
- package/dist/{chunk-NNARWO72.js → chunk-K7RW7FI4.js} +13 -12
- package/dist/chunk-K7RW7FI4.js.map +1 -0
- package/dist/{chunk-ZXABPVM6.js → chunk-L2F3CFCH.js} +9 -13
- package/dist/chunk-L2F3CFCH.js.map +1 -0
- package/dist/{chunk-X25NWUZB.js → chunk-L5A3KR7R.js} +3 -3
- package/dist/{chunk-T52GX4SA.js → chunk-LGGIFKJP.js} +4 -4
- package/dist/{chunk-ID6Z6E66.js → chunk-MQ7YWBQ5.js} +2 -2
- package/dist/{chunk-FXQBGFOX.js → chunk-MRJDLXI3.js} +14 -8
- package/dist/{chunk-FXQBGFOX.js.map → chunk-MRJDLXI3.js.map} +1 -1
- package/dist/chunk-NH2UOV3B.js +18 -0
- package/dist/chunk-NH2UOV3B.js.map +1 -0
- package/dist/{chunk-FN5EBVTQ.js → chunk-NIXRB5BP.js} +2 -2
- package/dist/{chunk-RHVMZSUU.js → chunk-NV4OESHC.js} +1 -1
- package/dist/chunk-NV4OESHC.js.map +1 -0
- package/dist/{chunk-HBRLNOHL.js → chunk-NYJBYVBG.js} +4 -4
- package/dist/{chunk-DF73H4UN.js → chunk-NZULV7LK.js} +5 -5
- package/dist/chunk-NZULV7LK.js.map +1 -0
- package/dist/{chunk-HK67PX4J.js → chunk-OFD2HD6F.js} +1 -1
- package/dist/chunk-OFD2HD6F.js.map +1 -0
- package/dist/{chunk-WY2XXTET.js → chunk-OJU67C23.js} +4 -4
- package/dist/{chunk-WY2XXTET.js.map → chunk-OJU67C23.js.map} +1 -1
- package/dist/{chunk-ZUVBC32X.js → chunk-OK57EATL.js} +2 -2
- package/dist/{chunk-KJFW62P4.js → chunk-OM3SEDI4.js} +652 -662
- package/dist/chunk-OM3SEDI4.js.map +1 -0
- package/dist/{chunk-TOPYQ6GA.js → chunk-PCYPXLEX.js} +6 -6
- package/dist/{chunk-GYGLAVCX.js → chunk-PFCTSXHU.js} +6 -2
- package/dist/chunk-PFCTSXHU.js.map +1 -0
- package/dist/{chunk-VNNRUGMM.js → chunk-PGC35KXE.js} +2 -2
- package/dist/{chunk-4I522ERM.js → chunk-PT2YCQVK.js} +4 -4
- package/dist/{chunk-Z5HTYMMQ.js → chunk-PTJY5R3O.js} +2 -2
- package/dist/chunk-PUBI455O.js +157 -0
- package/dist/chunk-PUBI455O.js.map +1 -0
- package/dist/{chunk-MTL7MXGR.js → chunk-QDHHHI5Q.js} +16 -17
- package/dist/chunk-QDHHHI5Q.js.map +1 -0
- package/dist/{chunk-F24ZZTHQ.js → chunk-QGFEBNHO.js} +4 -4
- package/dist/{chunk-A6IJU4TK.js → chunk-QGSNJD3B.js} +2 -2
- package/dist/chunk-QJNAHPVT.js +91 -0
- package/dist/chunk-QJNAHPVT.js.map +1 -0
- package/dist/{chunk-BK2HHQGN.js → chunk-QSHATAXJ.js} +2 -2
- package/dist/{chunk-ANWPSTZU.js → chunk-QXJXG7DN.js} +57 -30
- package/dist/chunk-QXJXG7DN.js.map +1 -0
- package/dist/{chunk-HBPPGWUM.js → chunk-RDOEOPOI.js} +14 -9
- package/dist/chunk-RDOEOPOI.js.map +1 -0
- package/dist/{chunk-JCN44NCY.js → chunk-RIZTZJX6.js} +2 -2
- package/dist/{chunk-MVZ2TPVP.js → chunk-RNACRT5H.js} +2 -2
- package/dist/chunk-RWFQVNXR.js +24 -0
- package/dist/chunk-RWFQVNXR.js.map +1 -0
- package/dist/{chunk-TROR3ALR.js → chunk-SFCD6OU7.js} +2 -2
- package/dist/{chunk-S57HTZ6X.js → chunk-SNM7LSYG.js} +5 -8
- package/dist/chunk-SNM7LSYG.js.map +1 -0
- package/dist/chunk-SPPOUJLN.js +40 -0
- package/dist/chunk-SPPOUJLN.js.map +1 -0
- package/dist/{chunk-3ELRMVJK.js → chunk-SWXLZ4HX.js} +31 -35
- package/dist/chunk-SWXLZ4HX.js.map +1 -0
- package/dist/{chunk-6B3VNR4P.js → chunk-TFWFPZRU.js} +26 -35
- package/dist/chunk-TFWFPZRU.js.map +1 -0
- package/dist/{chunk-F7XGHL5O.js → chunk-TMIJAR2X.js} +3 -3
- package/dist/{chunk-F7XGHL5O.js.map → chunk-TMIJAR2X.js.map} +1 -1
- package/dist/{chunk-I3JDRCMW.js → chunk-TZYTMZZY.js} +10 -10
- package/dist/{chunk-AOWGXU6D.js → chunk-UAKY6E4N.js} +2 -2
- package/dist/chunk-UHFGK6YA.js +40 -0
- package/dist/chunk-UHFGK6YA.js.map +1 -0
- package/dist/{chunk-3JWHAR6E.js → chunk-UNSQZSZP.js} +2 -2
- package/dist/{chunk-FQ5VRHIB.js → chunk-UOZOWN73.js} +5 -9
- package/dist/chunk-UOZOWN73.js.map +1 -0
- package/dist/{chunk-GHLTDZRS.js → chunk-V55JASXO.js} +25 -4
- package/dist/chunk-V55JASXO.js.map +1 -0
- package/dist/{chunk-244VXYPG.js → chunk-VSUQI5Q6.js} +45 -4
- package/dist/chunk-VSUQI5Q6.js.map +1 -0
- package/dist/{chunk-6K7DPM7D.js → chunk-WFWFXDNS.js} +2 -2
- package/dist/{chunk-CF5UU4S7.js → chunk-WHOKGHJ3.js} +7 -7
- package/dist/{chunk-LGRLME4M.js → chunk-X2RP4TO5.js} +16 -16
- package/dist/chunk-X2RP4TO5.js.map +1 -0
- package/dist/{chunk-VNPEWFPD.js → chunk-XA34THLF.js} +8 -6
- package/dist/chunk-XA34THLF.js.map +1 -0
- package/dist/{chunk-HVSCEIYL.js → chunk-XITHENBI.js} +12 -14
- package/dist/chunk-XITHENBI.js.map +1 -0
- package/dist/{chunk-ENAG3IY7.js → chunk-XKBTJGFP.js} +26 -28
- package/dist/chunk-XKBTJGFP.js.map +1 -0
- package/dist/{chunk-32YV3TJT.js → chunk-XXNWU5QZ.js} +2 -2
- package/dist/{chunk-B57AFBY2.js → chunk-XXQ5XIYY.js} +2 -2
- package/dist/{chunk-ORDURZBU.js → chunk-YFFMPZPY.js} +2 -2
- package/dist/{chunk-MDWKM2XC.js → chunk-YKGJ44Z7.js} +3 -3
- package/dist/{chunk-IUWCVHQ7.js → chunk-YQFBH5ZJ.js} +4 -4
- package/dist/{chunk-IUWCVHQ7.js.map → chunk-YQFBH5ZJ.js.map} +1 -1
- package/dist/{chunk-UPD5NWI5.js → chunk-Z3ZXVB2Y.js} +29 -16
- package/dist/chunk-Z3ZXVB2Y.js.map +1 -0
- package/dist/chunk-ZBUAX4EF.js +27 -0
- package/dist/chunk-ZBUAX4EF.js.map +1 -0
- package/dist/{chunk-MLPHLJIQ.js → chunk-ZHQJAV6D.js} +2 -2
- package/dist/{chunk-UD5EVGZ7.js → chunk-ZMVSKYTJ.js} +56 -96
- package/dist/chunk-ZMVSKYTJ.js.map +1 -0
- package/dist/{chunk-7JE7BDYJ.js → chunk-ZVSKSUGI.js} +5 -5
- package/dist/{chunk-J6UOHJY5.js → chunk-ZWF3EWZA.js} +4 -8
- package/dist/chunk-ZWF3EWZA.js.map +1 -0
- package/dist/classified/index.js +5 -5
- package/dist/{classified-marker-W3USLL5M.js → classified-marker-KHDOYIJB.js} +3 -3
- package/dist/classified-marker-KHDOYIJB.js.map +1 -0
- package/dist/collection-facade-LZIF7GWD.js +41 -0
- package/dist/{computed-TPFBX7U4.js → computed-XMFTHAXV.js} +3 -3
- package/dist/consent/index.js +10 -11
- package/dist/consent/index.js.map +1 -1
- package/dist/cover/index.js +9 -10
- package/dist/coverage/index.js +1 -1
- package/dist/custody/index.js +13 -13
- package/dist/{dead-filter-U2UYKSZL.js → dead-filter-CXV5NS67.js} +11 -3
- package/dist/dead-filter-CXV5NS67.js.map +1 -0
- package/dist/debug/index.js +11 -2
- package/dist/debug/index.js.map +1 -1
- package/dist/{delegation-S3AEPW2T.js → delegation-ONLM4JFI.js} +52 -31
- package/dist/delegation-ONLM4JFI.js.map +1 -0
- package/dist/derivations/index.js +25 -25
- package/dist/derive-3YSIS36W.js +20 -0
- package/dist/directory/index.js +10 -11
- package/dist/{dispatch-RH4LHKBH.js → dispatch-2MQFMEGY.js} +22 -23
- package/dist/dispatch-2MQFMEGY.js.map +1 -0
- package/dist/{dispatch-3CQDJV5Q.js → dispatch-OFDA4WQV.js} +5 -5
- package/dist/executor-E6YDJ7YA.js +8 -0
- package/dist/executor-NJX3YQQ4.js +35 -0
- package/dist/executor-WWM3DD4P.js +8 -0
- package/dist/export-accessible-RLHU5G4W.js +22 -0
- package/dist/extract-partition-QII2EDCS.js +35 -0
- package/dist/{fanout-sidecar-OT6RYLOL.js → fanout-sidecar-BO67PCPO.js} +13 -15
- package/dist/fanout-sidecar-BO67PCPO.js.map +1 -0
- package/dist/find-6M7GYDUZ.js +10 -0
- package/dist/forget/index.js +26 -10
- package/dist/forget/index.js.map +1 -1
- package/dist/guards/index.js +4 -4
- package/dist/history/index.js +13 -14
- package/dist/history/index.js.map +1 -1
- package/dist/i18n/index.js +14 -15
- package/dist/i18n/index.js.map +1 -1
- package/dist/index.d.ts +15 -13
- package/dist/index.js +198 -187
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +6 -6
- package/dist/introspection/index.js +16 -13
- package/dist/issue-LPEEEMPP.js +19 -0
- package/dist/kernel/attestation-types.d.ts +62 -0
- package/dist/kernel/cache/index.d.ts +1 -1
- package/dist/kernel/collection-config.d.ts +1 -48
- package/dist/kernel/collection.d.ts +2 -1
- package/dist/kernel/cutover-transform.d.ts +1 -1
- package/dist/kernel/errors.d.ts +63 -6
- package/dist/kernel/export-scope.d.ts +40 -0
- package/dist/kernel/merge-authority.d.ts +1 -1
- package/dist/kernel/noydb.d.ts +3 -1
- package/dist/kernel/paths.d.ts +29 -5
- package/dist/kernel/prepared-write.d.ts +1 -1
- package/dist/kernel/put-many-atomic.d.ts +1 -1
- package/dist/kernel/query/live/live.d.ts +2 -1
- package/dist/kernel/query/sort-key.d.ts +0 -2
- package/dist/kernel/secret-mode.d.ts +1 -1
- package/dist/kernel/tx-write-gate.d.ts +2 -4
- package/dist/kernel/types.d.ts +135 -11
- package/dist/kernel/validation.d.ts +0 -10
- package/dist/kernel/vault.d.ts +6 -2
- package/dist/kernel/via/reconcile.d.ts +1 -42
- package/dist/kernel/write-queue.d.ts +17 -0
- package/dist/{ledger-KZPCMVZY.js → ledger-MSJGWKGW.js} +11 -12
- package/dist/liberate-LEKDYKTC.js +25 -0
- package/dist/link-set-QXXSPPHL.js +29 -0
- package/dist/{managed-secret-S3MQ6SH6.js → managed-secret-VSK7ANQI.js} +10 -11
- package/dist/materialized-views/index.js +27 -28
- package/dist/{migrate-cek-V7GKJZ7O.js → migrate-cek-GEBFYBDC.js} +2 -2
- package/dist/money/index.js +5 -5
- package/dist/noydb-F2AUF6KL.js +98 -0
- package/dist/on/index.js +25 -0
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.js +12 -13
- package/dist/pod/index.js +76 -74
- package/dist/{pod-handle-WGDFB4C2.js → pod-handle-YE7CT66T.js} +10 -11
- package/dist/pod-handle-YE7CT66T.js.map +1 -0
- package/dist/policy/index.js +10 -11
- package/dist/port/as/active.d.ts +4 -0
- package/dist/port/as/index.d.ts +1 -0
- package/dist/port/by/index.d.ts +14 -5
- package/dist/port/on/index.d.ts +27 -1
- package/dist/port/to/index.d.ts +1 -1
- package/dist/port/with/blob-strategy.d.ts +1 -1
- package/dist/port/with/broker-strategy.d.ts +14 -0
- package/dist/port/with/classified-strategy.d.ts +1 -1
- package/dist/port/with/collection-options.d.ts +1 -1
- package/dist/port/with/collection-registries.d.ts +1 -1
- package/dist/port/with/coverage-strategy.d.ts +17 -1
- package/dist/port/with/formats-strategy.d.ts +34 -1
- package/dist/port/with/lookup-strategy.d.ts +87 -6
- package/dist/port/with/service-bus.d.ts +2 -1
- package/dist/portability/index.js +8 -8
- package/dist/{post-register-SLSXIPBV.js → post-register-QC5TEAW3.js} +6 -6
- package/dist/post-register-QC5TEAW3.js.map +1 -0
- package/dist/query/all/index.js +16 -16
- package/dist/query/index.js +8 -8
- package/dist/query/live/index.js +7 -7
- package/dist/query/reduce/index.js +10 -10
- package/dist/query/relate/index.js +10 -10
- package/dist/reduce/index.js +8 -8
- package/dist/register-SN72QALV.js +30 -0
- package/dist/registry-253FF7C6.js +35 -0
- package/dist/registry-3AZWQTDT.js +23 -0
- package/dist/registry-7UEBTHWG.js +8 -0
- package/dist/registry-IVJ67NFQ.js +8 -0
- package/dist/request-withdrawal-UKVJZKX2.js +28 -0
- package/dist/{reveal-4BCKPEC2.js → reveal-YQTRNQFH.js} +10 -9
- package/dist/reveal-YQTRNQFH.js.map +1 -0
- package/dist/revoke-G2KXJQA5.js +24 -0
- package/dist/satellites/index.js +1 -1
- package/dist/schema-update/index.js +2 -2
- package/dist/sealed-record/index.js +16 -18
- package/dist/sealed-record/index.js.map +1 -1
- package/dist/search/index.js +4 -4
- package/dist/{seed-4JS2RJ4A.js → seed-JMQTRKRE.js} +125 -34
- package/dist/seed-JMQTRKRE.js.map +1 -0
- package/dist/sequence/index.js +9 -10
- package/dist/session/index.js +9 -10
- package/dist/session/index.js.map +1 -1
- package/dist/shadow/index.js +2 -2
- package/dist/signer-IUIYR3CX.js +23 -0
- package/dist/snapshots/index.js +11 -11
- package/dist/stale-SKV33PBU.js +28 -0
- package/dist/storage-NPBYP2SM.js +21 -0
- package/dist/store/index.js +1 -1
- package/dist/{store-coordination-provider-2O2VUHKW.js → store-coordination-provider-Z6HKPOGM.js} +11 -12
- package/dist/store-coordination-provider-Z6HKPOGM.js.map +1 -0
- package/dist/sync/index.js +18 -13
- package/dist/sync/index.js.map +1 -1
- package/dist/team/index.js +18 -17
- package/dist/tiers/index.js +11 -12
- package/dist/to/index.js +2 -2
- package/dist/transactions/index.js +3 -3
- package/dist/util/index.js +1 -1
- package/dist/vault-head/index.js +11 -14
- package/dist/vault-head/index.js.map +1 -1
- package/dist/{verify-RVT67IMH.js → verify-KWQIWLGH.js} +14 -12
- package/dist/verify-KWQIWLGH.js.map +1 -0
- package/dist/via/classified/binding.d.ts +2 -2
- package/dist/via/classified/config-drift.d.ts +2 -2
- package/dist/via/classified/errors.d.ts +1 -1
- package/dist/via/classified/presets.d.ts +29 -0
- package/dist/via/classified/resolve.d.ts +7 -0
- package/dist/via/geo/binding.d.ts +0 -4
- package/dist/via/lookup/handle.d.ts +4 -4
- package/dist/via/lookup/index.d.ts +32 -0
- package/dist/via/money/binding.d.ts +0 -9
- package/dist/walk-F4OLXI5H.js +20 -0
- package/dist/with-audit/attestation/index.d.ts +2 -1
- package/dist/with-audit/attestation/issue.d.ts +2 -2
- package/dist/with-audit/attestation/revoke.d.ts +2 -2
- package/dist/with-audit/attestation/scheme.d.ts +7 -0
- package/dist/with-audit/attestation/signer.d.ts +1 -1
- package/dist/with-audit/attestation/strategy.d.ts +4 -3
- package/dist/with-audit/attestation/types.d.ts +10 -0
- package/dist/with-audit/attestation/vault-facade.d.ts +9 -2
- package/dist/with-audit/attestation/verifier.d.ts +6 -0
- package/dist/with-audit/consent/consent.d.ts +1 -1
- package/dist/with-audit/consent/strategy.d.ts +1 -1
- package/dist/with-audit/forget/completeness.d.ts +66 -0
- package/dist/with-audit/forget/index.d.ts +2 -0
- package/dist/with-audit/forget/strategy.d.ts +65 -0
- package/dist/with-audit/forget/subject-index.d.ts +1 -1
- package/dist/with-audit/periods/periods.d.ts +0 -9
- package/dist/with-audit/periods/reserved-collections.d.ts +7 -0
- package/dist/with-audit/periods/vault-facade.d.ts +1 -1
- package/dist/with-audit/portability/request-withdrawal.d.ts +0 -1
- package/dist/with-audit/portability/withdraw-accessible.d.ts +0 -1
- package/dist/with-audit/sealed-record/index.d.ts +2 -2
- package/dist/with-audit/sealed-record/strategy.d.ts +1 -1
- package/dist/with-audit/tiers/index.d.ts +3 -3
- package/dist/with-audit/tiers/strategy.d.ts +19 -0
- package/dist/with-cargo/adopt-partition.d.ts +1 -1
- package/dist/with-cargo/extract-partition.d.ts +1 -47
- package/dist/with-cargo/floor.d.ts +1 -1
- package/dist/with-commit/history/history.d.ts +2 -2
- package/dist/with-commit/history/index.d.ts +1 -1
- package/dist/with-commit/history/ledger/store.d.ts +1 -1
- package/dist/with-commit/history/strategy.d.ts +1 -1
- package/dist/with-commit/history/time-machine.d.ts +1 -1
- package/dist/with-commit/numbering/index.d.ts +1 -3
- package/dist/with-commit/sequence/index.d.ts +1 -1
- package/dist/with-commit/tx/transaction.d.ts +2 -24
- package/dist/with-commit/vault-head/head.d.ts +1 -1
- package/dist/with-commit/vault-head/index.d.ts +1 -1
- package/dist/with-commit/vault-head/strategy.d.ts +8 -1
- package/dist/with-commit/vault-head/verify.d.ts +1 -1
- package/dist/with-fork/snapshots/noydb-facade.d.ts +7 -0
- package/dist/with-formula/derivations/dispatch.d.ts +1 -1
- package/dist/with-formula/derivations/fanout-sidecar.d.ts +1 -1
- package/dist/with-formula/derivations/trigger-match.d.ts +8 -5
- package/dist/with-formula/materialized-views/emit-memo.d.ts +0 -2
- package/dist/with-lookup/embeddings/index.d.ts +1 -2
- package/dist/with-lookup/indexing/collection-facade.d.ts +2 -16
- package/dist/with-lookup/indexing/eager-indexes.d.ts +2 -1
- package/dist/with-lookup/search/collection-facade.d.ts +1 -13
- package/dist/with-party/broker/index.d.ts +4 -4
- package/dist/with-party/broker/seed.d.ts +25 -10
- package/dist/with-party/directory/user-envelope/storage.d.ts +1 -1
- package/dist/with-party/policy/noydb-facade.d.ts +1 -31
- package/dist/with-party/team/delegation.d.ts +59 -3
- package/dist/with-party/team/echo-secret.d.ts +2 -4
- package/dist/with-party/team/index.d.ts +1 -1
- package/dist/with-party/team/keyring.d.ts +2 -22
- package/dist/with-party/team/magic-link-grant.d.ts +12 -2
- package/dist/with-party/team/noydb-facade.d.ts +5 -0
- package/dist/with-party/team/recovery.d.ts +1 -1
- package/dist/with-party/team/reserved-secret-collections.d.ts +10 -8
- package/dist/with-party/team/roster-tag.d.ts +43 -2
- package/dist/with-party/team/rotate-recover.d.ts +1 -1
- package/dist/with-party/team/tiers.d.ts +36 -5
- package/dist/with-party/team/wrapped-deks.d.ts +1 -1
- package/dist/with-pod/index.d.ts +1 -1
- package/dist/with-shape/blobs/blob-compaction.d.ts +1 -1
- package/dist/with-shape/blobs/blob-intent.d.ts +1 -1
- package/dist/with-shape/blobs/blob-set.d.ts +25 -2
- package/dist/with-shape/blobs/legacy-sweep.d.ts +17 -1
- package/dist/with-shape/introspection/describe.d.ts +2 -3
- package/dist/with-shape/introspection/index.d.ts +1 -0
- package/dist/with-shape/introspection/types.d.ts +2 -1
- package/dist/with-shape/introspection/walk.d.ts +1 -1
- package/dist/with-shape/links/lazy-handle.d.ts +1 -1
- package/dist/with-shape/links/link-set.d.ts +1 -1
- package/dist/with-shape/manifest/derive.d.ts +1 -1
- package/dist/with-shape/manifest/storage.d.ts +1 -1
- package/dist/with-shape/persisted-schemas/register.d.ts +1 -1
- package/dist/with-shape/persisted-schemas/storage.d.ts +1 -1
- package/dist/with-shape/satellites/dead-filter.d.ts +1 -1
- package/dist/with-shape/satellites/declare.d.ts +1 -1
- package/dist/with-shape/satellites/marker.d.ts +1 -1
- package/dist/with-shape/satellites/post-register.d.ts +1 -1
- package/dist/with-shape/schema-update/fence.d.ts +7 -0
- package/dist/with-sync/engine.d.ts +30 -1
- package/dist/with-sync/presence.d.ts +5 -10
- package/dist/with-sync/reserved-mirror.d.ts +89 -0
- package/dist/with-sync/strategy.d.ts +7 -0
- package/dist/with-sync/tab-write-relay.d.ts +0 -10
- package/dist/withdraw-accessible-ZKPLZ66E.js +25 -0
- package/dist/withdraw-accessible-ZKPLZ66E.js.map +1 -0
- package/package.json +14 -5
- package/dist/api-GVYEKSJE.js +0 -19
- package/dist/backup-7TFRVFLH.js.map +0 -1
- package/dist/chunk-244VXYPG.js.map +0 -1
- package/dist/chunk-3ELRMVJK.js.map +0 -1
- package/dist/chunk-3F7GLNAE.js.map +0 -1
- package/dist/chunk-3H5KLDZN.js.map +0 -1
- package/dist/chunk-5FDLC6G7.js.map +0 -1
- package/dist/chunk-6B3VNR4P.js.map +0 -1
- package/dist/chunk-6LNL42F5.js.map +0 -1
- package/dist/chunk-6QSQQLSE.js +0 -95
- package/dist/chunk-6QSQQLSE.js.map +0 -1
- package/dist/chunk-7VCXZDA4.js.map +0 -1
- package/dist/chunk-ANWPSTZU.js.map +0 -1
- package/dist/chunk-BP6DARYA.js.map +0 -1
- package/dist/chunk-BUDAGWL5.js.map +0 -1
- package/dist/chunk-CKNGS5MH.js.map +0 -1
- package/dist/chunk-DC5QVPV7.js.map +0 -1
- package/dist/chunk-DE4DH2RZ.js +0 -29
- package/dist/chunk-DE4DH2RZ.js.map +0 -1
- package/dist/chunk-DF73H4UN.js.map +0 -1
- package/dist/chunk-EIPVYUEP.js.map +0 -1
- package/dist/chunk-ENAG3IY7.js.map +0 -1
- package/dist/chunk-FCKWQE2H.js.map +0 -1
- package/dist/chunk-FFWI7UH7.js.map +0 -1
- package/dist/chunk-FQ54DWUQ.js.map +0 -1
- package/dist/chunk-FQ5VRHIB.js.map +0 -1
- package/dist/chunk-GF5BZ7D4.js.map +0 -1
- package/dist/chunk-GHLTDZRS.js.map +0 -1
- package/dist/chunk-GYGLAVCX.js.map +0 -1
- package/dist/chunk-HBPPGWUM.js.map +0 -1
- package/dist/chunk-HK67PX4J.js.map +0 -1
- package/dist/chunk-HVSCEIYL.js.map +0 -1
- package/dist/chunk-IEX7NK2K.js +0 -125
- package/dist/chunk-IEX7NK2K.js.map +0 -1
- package/dist/chunk-ISZQJBML.js.map +0 -1
- package/dist/chunk-J6UOHJY5.js.map +0 -1
- package/dist/chunk-KJFW62P4.js.map +0 -1
- package/dist/chunk-L7DOS3TE.js.map +0 -1
- package/dist/chunk-LGNDFRYC.js.map +0 -1
- package/dist/chunk-LGRLME4M.js.map +0 -1
- package/dist/chunk-LP7BEXCT.js +0 -32
- package/dist/chunk-LP7BEXCT.js.map +0 -1
- package/dist/chunk-LQ4ZHOKP.js.map +0 -1
- package/dist/chunk-M522UYZ2.js.map +0 -1
- package/dist/chunk-M7NI3IWE.js.map +0 -1
- package/dist/chunk-MTL7MXGR.js.map +0 -1
- package/dist/chunk-N5DNADLQ.js.map +0 -1
- package/dist/chunk-NNARWO72.js.map +0 -1
- package/dist/chunk-NRT6QZQB.js.map +0 -1
- package/dist/chunk-NWHBJQAL.js.map +0 -1
- package/dist/chunk-ODVPTDPK.js.map +0 -1
- package/dist/chunk-PDCG6PR5.js.map +0 -1
- package/dist/chunk-PEOZ34EM.js.map +0 -1
- package/dist/chunk-PSIV7CJV.js.map +0 -1
- package/dist/chunk-QBBUZ3I3.js.map +0 -1
- package/dist/chunk-QOOUUR4A.js.map +0 -1
- package/dist/chunk-QWFHIOD2.js.map +0 -1
- package/dist/chunk-QWGU66MP.js.map +0 -1
- package/dist/chunk-RHVMZSUU.js.map +0 -1
- package/dist/chunk-S57HTZ6X.js.map +0 -1
- package/dist/chunk-SEE332EV.js +0 -66
- package/dist/chunk-SEE332EV.js.map +0 -1
- package/dist/chunk-SOIQ4MM4.js +0 -20
- package/dist/chunk-SOIQ4MM4.js.map +0 -1
- package/dist/chunk-SYVZW77S.js.map +0 -1
- package/dist/chunk-T2LBQ5DV.js.map +0 -1
- package/dist/chunk-UBSFPGVJ.js +0 -1
- package/dist/chunk-UD5EVGZ7.js.map +0 -1
- package/dist/chunk-UPD5NWI5.js.map +0 -1
- package/dist/chunk-UYB234ZR.js +0 -479
- package/dist/chunk-UYB234ZR.js.map +0 -1
- package/dist/chunk-VNPEWFPD.js.map +0 -1
- package/dist/chunk-WX5ADGTZ.js.map +0 -1
- package/dist/chunk-X4773RBR.js.map +0 -1
- package/dist/chunk-YNIUIERI.js.map +0 -1
- package/dist/chunk-YSK2CU2Q.js.map +0 -1
- package/dist/chunk-ZE4CHIED.js.map +0 -1
- package/dist/chunk-ZXABPVM6.js.map +0 -1
- package/dist/classified-marker-W3USLL5M.js.map +0 -1
- package/dist/collection-facade-QU6IJBN3.js +0 -48
- package/dist/dead-filter-U2UYKSZL.js.map +0 -1
- package/dist/delegation-S3AEPW2T.js.map +0 -1
- package/dist/derive-OX7F6TGG.js +0 -21
- package/dist/dispatch-RH4LHKBH.js.map +0 -1
- package/dist/executor-B6SRO2QS.js +0 -36
- package/dist/executor-BI5CHWRY.js +0 -8
- package/dist/executor-RBKM5W5K.js +0 -8
- package/dist/export-accessible-VMA3YM5V.js +0 -22
- package/dist/extract-partition-NF7PXGPP.js +0 -37
- package/dist/fanout-sidecar-OT6RYLOL.js.map +0 -1
- package/dist/find-LZMP26KF.js +0 -10
- package/dist/issue-IHARXWW5.js +0 -18
- package/dist/kernel/enclave/classify/compare.d.ts +0 -10
- package/dist/kernel/enclave/index.d.ts +0 -87
- package/dist/kernel/enclave/record-keys/lifecycle.d.ts +0 -124
- package/dist/kernel/enclave/record-keys/rekey-blob.d.ts +0 -88
- package/dist/kernel/enclave/record-keys/rekey.d.ts +0 -91
- package/dist/kernel/enclave/record-keys/sealed-slot.d.ts +0 -14
- package/dist/kernel/enclave/record-keys/sealed-slots.d.ts +0 -123
- package/dist/kernel/enclave/record-keys/sealing.d.ts +0 -70
- package/dist/liberate-GRLCZMW4.js +0 -24
- package/dist/link-set-JCMJ7ULP.js +0 -30
- package/dist/noydb-SWQJ3MZS.js +0 -97
- package/dist/pod-handle-WGDFB4C2.js.map +0 -1
- package/dist/post-register-SLSXIPBV.js.map +0 -1
- package/dist/register-F3OYE7MJ.js +0 -31
- package/dist/registry-CILQFXFK.js +0 -8
- package/dist/registry-XNNYQVKV.js +0 -8
- package/dist/registry-YCDZV3VW.js +0 -35
- package/dist/registry-ZGYS33TE.js +0 -24
- package/dist/request-withdrawal-Z5NC6TXT.js +0 -30
- package/dist/reveal-4BCKPEC2.js.map +0 -1
- package/dist/revoke-ZL62WC2D.js +0 -23
- package/dist/seed-4JS2RJ4A.js.map +0 -1
- package/dist/signer-MBZZMXTC.js +0 -24
- package/dist/stale-TJ3DYZMQ.js +0 -29
- package/dist/storage-SVMKRGCW.js +0 -22
- package/dist/store-coordination-provider-2O2VUHKW.js.map +0 -1
- package/dist/verify-RVT67IMH.js.map +0 -1
- package/dist/walk-HJ42EISE.js +0 -21
- package/dist/withdraw-accessible-5NZUPAVA.js +0 -27
- /package/dist/{api-GVYEKSJE.js.map → api-MSJXVYQG.js.map} +0 -0
- /package/dist/{collection-facade-QU6IJBN3.js.map → capsule/contract.js.map} +0 -0
- /package/dist/{kernel/enclave → capsule/enclave-aes}/classify/bidx.d.ts +0 -0
- /package/dist/{kernel/enclave → capsule/enclave-aes}/classify/digest.d.ts +0 -0
- /package/dist/{kernel/enclave → capsule/enclave-aes}/classify/vdig.d.ts +0 -0
- /package/dist/{kernel/enclave/classify → capsule/plumbing}/kofn.d.ts +0 -0
- /package/dist/{kernel/enclave/classify → capsule/plumbing}/normalize.d.ts +0 -0
- /package/dist/{kernel/enclave → capsule/plumbing}/record-aad.d.ts +0 -0
- /package/dist/{chunk-UBSFPGVJ.js.map → capsule-57BDK7LG.js.map} +0 -0
- /package/dist/{computed-TPFBX7U4.js.map → chunk-2DX4JCRI.js.map} +0 -0
- /package/dist/{chunk-ZSX4S5XB.js.map → chunk-2ZFD2UJC.js.map} +0 -0
- /package/dist/{chunk-HT2AHGQI.js.map → chunk-3Y5JLSXS.js.map} +0 -0
- /package/dist/{chunk-SI3V7D4G.js.map → chunk-4UIP3QNW.js.map} +0 -0
- /package/dist/{chunk-WNJ4AVGE.js.map → chunk-55P6OBB7.js.map} +0 -0
- /package/dist/{chunk-MKXXMNJH.js.map → chunk-5UUJE4FP.js.map} +0 -0
- /package/dist/{chunk-FW22ZCXT.js.map → chunk-6WS35E6X.js.map} +0 -0
- /package/dist/{chunk-6OWXYZK2.js.map → chunk-B65CC6Z6.js.map} +0 -0
- /package/dist/{chunk-F2TDU7MQ.js.map → chunk-BJH77UY2.js.map} +0 -0
- /package/dist/{chunk-BJ2IXQ5Q.js.map → chunk-BNFNQNEP.js.map} +0 -0
- /package/dist/{chunk-HLEK5VAN.js.map → chunk-BUE7R3B5.js.map} +0 -0
- /package/dist/{chunk-EOVMT6TB.js.map → chunk-C53ER7AG.js.map} +0 -0
- /package/dist/{chunk-K6W7BVZ6.js.map → chunk-CAD7N6HQ.js.map} +0 -0
- /package/dist/{chunk-5O7D6ZTW.js.map → chunk-DHU2AQ6T.js.map} +0 -0
- /package/dist/{chunk-IT22PWKV.js.map → chunk-ECGXA4LT.js.map} +0 -0
- /package/dist/{chunk-4MXI3ODD.js.map → chunk-GCUMOKTN.js.map} +0 -0
- /package/dist/{chunk-B6SJIBQK.js.map → chunk-GOJQ5DZA.js.map} +0 -0
- /package/dist/{chunk-TKUOEU2F.js.map → chunk-JSWKVZCR.js.map} +0 -0
- /package/dist/{chunk-X25NWUZB.js.map → chunk-L5A3KR7R.js.map} +0 -0
- /package/dist/{chunk-T52GX4SA.js.map → chunk-LGGIFKJP.js.map} +0 -0
- /package/dist/{chunk-ID6Z6E66.js.map → chunk-MQ7YWBQ5.js.map} +0 -0
- /package/dist/{chunk-FN5EBVTQ.js.map → chunk-NIXRB5BP.js.map} +0 -0
- /package/dist/{chunk-HBRLNOHL.js.map → chunk-NYJBYVBG.js.map} +0 -0
- /package/dist/{chunk-ZUVBC32X.js.map → chunk-OK57EATL.js.map} +0 -0
- /package/dist/{chunk-TOPYQ6GA.js.map → chunk-PCYPXLEX.js.map} +0 -0
- /package/dist/{chunk-VNNRUGMM.js.map → chunk-PGC35KXE.js.map} +0 -0
- /package/dist/{chunk-4I522ERM.js.map → chunk-PT2YCQVK.js.map} +0 -0
- /package/dist/{chunk-Z5HTYMMQ.js.map → chunk-PTJY5R3O.js.map} +0 -0
- /package/dist/{chunk-F24ZZTHQ.js.map → chunk-QGFEBNHO.js.map} +0 -0
- /package/dist/{chunk-A6IJU4TK.js.map → chunk-QGSNJD3B.js.map} +0 -0
- /package/dist/{chunk-BK2HHQGN.js.map → chunk-QSHATAXJ.js.map} +0 -0
- /package/dist/{chunk-JCN44NCY.js.map → chunk-RIZTZJX6.js.map} +0 -0
- /package/dist/{chunk-MVZ2TPVP.js.map → chunk-RNACRT5H.js.map} +0 -0
- /package/dist/{chunk-TROR3ALR.js.map → chunk-SFCD6OU7.js.map} +0 -0
- /package/dist/{chunk-I3JDRCMW.js.map → chunk-TZYTMZZY.js.map} +0 -0
- /package/dist/{chunk-AOWGXU6D.js.map → chunk-UAKY6E4N.js.map} +0 -0
- /package/dist/{chunk-3JWHAR6E.js.map → chunk-UNSQZSZP.js.map} +0 -0
- /package/dist/{chunk-6K7DPM7D.js.map → chunk-WFWFXDNS.js.map} +0 -0
- /package/dist/{chunk-CF5UU4S7.js.map → chunk-WHOKGHJ3.js.map} +0 -0
- /package/dist/{chunk-32YV3TJT.js.map → chunk-XXNWU5QZ.js.map} +0 -0
- /package/dist/{chunk-B57AFBY2.js.map → chunk-XXQ5XIYY.js.map} +0 -0
- /package/dist/{chunk-ORDURZBU.js.map → chunk-YFFMPZPY.js.map} +0 -0
- /package/dist/{chunk-MDWKM2XC.js.map → chunk-YKGJ44Z7.js.map} +0 -0
- /package/dist/{chunk-MLPHLJIQ.js.map → chunk-ZHQJAV6D.js.map} +0 -0
- /package/dist/{chunk-7JE7BDYJ.js.map → chunk-ZVSKSUGI.js.map} +0 -0
- /package/dist/{derive-OX7F6TGG.js.map → collection-facade-LZIF7GWD.js.map} +0 -0
- /package/dist/{enclave-27SMF6GF.js.map → computed-XMFTHAXV.js.map} +0 -0
- /package/dist/{executor-B6SRO2QS.js.map → derive-3YSIS36W.js.map} +0 -0
- /package/dist/{dispatch-3CQDJV5Q.js.map → dispatch-OFDA4WQV.js.map} +0 -0
- /package/dist/{executor-BI5CHWRY.js.map → executor-E6YDJ7YA.js.map} +0 -0
- /package/dist/{executor-RBKM5W5K.js.map → executor-NJX3YQQ4.js.map} +0 -0
- /package/dist/{export-accessible-VMA3YM5V.js.map → executor-WWM3DD4P.js.map} +0 -0
- /package/dist/{extract-partition-NF7PXGPP.js.map → export-accessible-RLHU5G4W.js.map} +0 -0
- /package/dist/{find-LZMP26KF.js.map → extract-partition-QII2EDCS.js.map} +0 -0
- /package/dist/{issue-IHARXWW5.js.map → find-6M7GYDUZ.js.map} +0 -0
- /package/dist/{ledger-KZPCMVZY.js.map → issue-LPEEEMPP.js.map} +0 -0
- /package/dist/{liberate-GRLCZMW4.js.map → ledger-MSJGWKGW.js.map} +0 -0
- /package/dist/{link-set-JCMJ7ULP.js.map → liberate-LEKDYKTC.js.map} +0 -0
- /package/dist/{managed-secret-S3MQ6SH6.js.map → link-set-QXXSPPHL.js.map} +0 -0
- /package/dist/{noydb-SWQJ3MZS.js.map → managed-secret-VSK7ANQI.js.map} +0 -0
- /package/dist/{migrate-cek-V7GKJZ7O.js.map → migrate-cek-GEBFYBDC.js.map} +0 -0
- /package/dist/{register-F3OYE7MJ.js.map → noydb-F2AUF6KL.js.map} +0 -0
- /package/dist/{registry-CILQFXFK.js.map → register-SN72QALV.js.map} +0 -0
- /package/dist/{registry-XNNYQVKV.js.map → registry-253FF7C6.js.map} +0 -0
- /package/dist/{registry-YCDZV3VW.js.map → registry-3AZWQTDT.js.map} +0 -0
- /package/dist/{registry-ZGYS33TE.js.map → registry-7UEBTHWG.js.map} +0 -0
- /package/dist/{request-withdrawal-Z5NC6TXT.js.map → registry-IVJ67NFQ.js.map} +0 -0
- /package/dist/{revoke-ZL62WC2D.js.map → request-withdrawal-UKVJZKX2.js.map} +0 -0
- /package/dist/{signer-MBZZMXTC.js.map → revoke-G2KXJQA5.js.map} +0 -0
- /package/dist/{stale-TJ3DYZMQ.js.map → signer-IUIYR3CX.js.map} +0 -0
- /package/dist/{storage-SVMKRGCW.js.map → stale-SKV33PBU.js.map} +0 -0
- /package/dist/{walk-HJ42EISE.js.map → storage-NPBYP2SM.js.map} +0 -0
- /package/dist/{withdraw-accessible-5NZUPAVA.js.map → walk-F4OLXI5H.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../attestation/src/encoding.ts","../../attestation/src/normalize.ts","../../attestation/src/hashing.ts","../../attestation/src/ed25519.ts","../../attestation/src/scheme.ts","../../attestation/src/qr.ts","../../attestation/src/revocation.ts","../../attestation/src/verify.ts"],"sourcesContent":["/**\n * Pure encoding + hashing primitives. Zero deps; WebCrypto only.\n *\n * `canonicalJson` and `sha256Hex` are intentionally byte-identical to\n * hub's `history/ledger/entry.ts` implementations. They are REPLICATED\n * here (not imported) because this package is upstream of hub — importing\n * from hub would invert the dependency. The conformance test pins the\n * shared contract via fixed vectors.\n */\n\nexport function utf8(s: string): Uint8Array {\n return new TextEncoder().encode(s)\n}\n\nexport function bytesToHex(bytes: Uint8Array): string {\n let out = ''\n for (const b of bytes) out += b.toString(16).padStart(2, '0')\n return out\n}\n\nexport function bytesToB64url(bytes: Uint8Array): string {\n let s = ''\n for (const b of bytes) s += String.fromCharCode(b)\n return btoa(s).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nexport function b64urlToBytes(s: string): Uint8Array {\n const pad = s.length % 4 === 0 ? '' : '='.repeat(4 - (s.length % 4))\n const b64 = s.replace(/-/g, '+').replace(/_/g, '/') + pad\n const bin = atob(b64)\n const out = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i)\n return out\n}\n\nexport function canonicalJson(value: unknown): string {\n if (value === null) return 'null'\n if (typeof value === 'boolean') return value ? 'true' : 'false'\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error(`canonicalJson: refusing to encode non-finite number ${String(value)}`)\n }\n return JSON.stringify(value)\n }\n if (typeof value === 'string') return JSON.stringify(value)\n if (typeof value === 'bigint') throw new Error('canonicalJson: BigInt is not JSON-serializable')\n if (typeof value === 'undefined' || typeof value === 'function') {\n throw new Error(`canonicalJson: refusing to encode ${typeof value} — include all fields explicitly`)\n }\n if (Array.isArray(value)) return '[' + value.map((v) => canonicalJson(v)).join(',') + ']'\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>\n const keys = Object.keys(obj).sort()\n const parts: string[] = []\n for (const key of keys) parts.push(JSON.stringify(key) + ':' + canonicalJson(obj[key]))\n return '{' + parts.join(',') + '}'\n }\n throw new Error(`canonicalJson: unexpected value type: ${typeof value}`)\n}\n\nexport async function sha256Bytes(input: string): Promise<Uint8Array> {\n const digest = await globalThis.crypto.subtle.digest('SHA-256', utf8(input) as BufferSource)\n return new Uint8Array(digest)\n}\n\nexport async function sha256Hex(input: string): Promise<string> {\n return bytesToHex(await sha256Bytes(input))\n}\n","import type { AttestationFieldSchema, Normalizer } from './types.js'\n\nconst NORMALIZERS: ReadonlySet<string> = new Set<Normalizer>([\n 'trim', 'lower', 'upper', 'alnum-upper', 'digits', 'cents', 'iso-date',\n])\n\nexport function getPath(obj: unknown, path: string): unknown {\n return path.split('.').reduce<unknown>(\n (o, k) => (o == null ? undefined : (o as Record<string, unknown>)[k]),\n obj,\n )\n}\n\n/**\n * Normalize a field value to a canonical string for hashing.\n *\n * `iso-date` returns the **UTC** calendar date (`toISOString().slice(0,10)`),\n * so a local-time `Date` near midnight can shift by ±1 day in non-UTC\n * environments — prefer passing an ISO date string (e.g. `'2026-05-29'`)\n * over a local `new Date(2026, 4, 29)`. Issue and verify use this same\n * function, so a value passed identically on both sides always round-trips.\n */\nexport function normalizeField(value: unknown, n: Normalizer): string {\n switch (n) {\n case 'trim':\n return String(value).trim()\n case 'lower':\n return String(value).trim().toLowerCase()\n case 'upper':\n return String(value).trim().toUpperCase()\n case 'alnum-upper':\n return String(value).replace(/[^A-Za-z0-9]/g, '').toUpperCase()\n case 'digits':\n return String(value).replace(/[^0-9]/g, '')\n case 'cents': {\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error(`normalizeField(cents): not a finite number: ${String(value)}`)\n }\n return String(Math.round(value * 100))\n }\n // For string values: strip currency symbols / spaces / commas but keep digits, dot, minus\n const stripped = String(value).replace(/[^0-9.-]/g, '')\n // A valid numeric string must have at least one digit\n if (!/[0-9]/.test(stripped)) {\n throw new Error(`normalizeField(cents): not a finite number: ${String(value)}`)\n }\n const num = Number(stripped)\n if (!Number.isFinite(num)) {\n throw new Error(`normalizeField(cents): not a finite number: ${String(value)}`)\n }\n return String(Math.round(num * 100))\n }\n case 'iso-date': {\n const d = value instanceof Date ? value : new Date(String(value))\n if (Number.isNaN(d.getTime())) {\n throw new Error(`normalizeField(iso-date): unparseable date: ${String(value)}`)\n }\n return d.toISOString().slice(0, 10)\n }\n default: {\n const exhaustive: never = n\n throw new Error(`normalizeField: unknown normalizer ${String(exhaustive)}`)\n }\n }\n}\n\nexport function validateFieldSchema(schema: AttestationFieldSchema): void {\n if (!schema.fields || schema.fields.length === 0) {\n throw new Error('validateFieldSchema: schema must declare at least one field')\n }\n const seen = new Set<string>()\n for (const f of schema.fields) {\n if (!NORMALIZERS.has(f.normalize)) {\n throw new Error(\n `validateFieldSchema: unknown normalizer '${String(f.normalize)}' for path '${f.path}'`,\n )\n }\n if (seen.has(f.path)) {\n throw new Error(`validateFieldSchema: duplicate path '${f.path}'`)\n }\n seen.add(f.path)\n }\n}\n","import type { AttestationFieldSchema } from './types.js'\nimport { canonicalJson, sha256Bytes, bytesToB64url } from './encoding.js'\nimport { getPath, normalizeField } from './normalize.js'\n\n/**\n * One salted, domain-separated hash per declared field, in schema order:\n * fieldHash[i] = base64url( sha256( canonicalJson([salt, path, normalizedValue]) ) )\n * Per-document salt defeats brute-force of low-entropy fields and cross-\n * document correlation; the path in the input domain-separates fields\n * that happen to share a value.\n */\nexport async function computeFieldHashes(\n saltB64: string,\n schema: AttestationFieldSchema,\n values: Record<string, unknown>,\n): Promise<string[]> {\n const out: string[] = []\n for (const f of schema.fields) {\n const raw = getPath(values, f.path)\n if (raw === undefined || raw === null) {\n throw new Error(`computeFieldHashes: missing value at declared path '${f.path}'`)\n }\n const norm = normalizeField(raw, f.normalize)\n const digest = await sha256Bytes(canonicalJson([saltB64, f.path, norm]))\n out.push(bytesToB64url(digest))\n }\n return out\n}\n","import { bytesToB64url, b64urlToBytes, sha256Hex } from './encoding.js'\n\nconst ALG = 'Ed25519'\n\n/** Stable key identifier: first 16 hex chars of sha256(publicKeyB64). */\nexport async function keyIdFor(publicKeyB64: string): Promise<string> {\n return (await sha256Hex(publicKeyB64)).slice(0, 16)\n}\n\nexport async function generateDocSigningKeyPair(): Promise<{\n keyId: string\n publicKeyB64: string // base64url raw (32 bytes) — non-secret, publishable\n privateKeyPkcs8B64: string // base64url pkcs8 — secret, wrap before persisting\n}> {\n const kp = await globalThis.crypto.subtle.generateKey(ALG, true, ['sign', 'verify'])\n const rawPub = new Uint8Array(await globalThis.crypto.subtle.exportKey('raw', kp.publicKey))\n const pkcs8 = new Uint8Array(await globalThis.crypto.subtle.exportKey('pkcs8', kp.privateKey))\n const publicKeyB64 = bytesToB64url(rawPub)\n return { keyId: await keyIdFor(publicKeyB64), publicKeyB64, privateKeyPkcs8B64: bytesToB64url(pkcs8) }\n}\n\nexport async function ed25519Sign(privateKeyPkcs8B64: string, message: Uint8Array): Promise<string> {\n const key = await globalThis.crypto.subtle.importKey(\n 'pkcs8',\n b64urlToBytes(privateKeyPkcs8B64) as BufferSource,\n ALG,\n false,\n ['sign'],\n )\n const sig = new Uint8Array(await globalThis.crypto.subtle.sign(ALG, key, message as BufferSource))\n return bytesToB64url(sig)\n}\n\nexport async function ed25519Verify(publicKeyB64: string, sigB64url: string, message: Uint8Array): Promise<boolean> {\n try {\n const key = await globalThis.crypto.subtle.importKey(\n 'raw',\n b64urlToBytes(publicKeyB64) as BufferSource,\n ALG,\n false,\n ['verify'],\n )\n return await globalThis.crypto.subtle.verify(\n ALG,\n key,\n b64urlToBytes(sigB64url) as BufferSource,\n message as BufferSource,\n )\n } catch {\n return false\n }\n}\n","import { ed25519Sign, ed25519Verify } from './ed25519.js'\n\n/**\n * The signature primitive attestation signs and verifies with. Ed25519 is the\n * default and the only algorithm `v:1` payloads name; a caller with its own\n * engine (noy-db's enclave, a post-quantum fork) injects one with the same\n * shape. If a second algorithm is ever named in a payload, `alg` must join\n * the signed core (see `verify.ts` `signedCore`).\n */\nexport interface SignatureScheme {\n sign(privateKeyPkcs8B64: string, message: Uint8Array): Promise<string>\n verify(publicKeyB64: string, sigB64url: string, message: Uint8Array): Promise<boolean>\n}\n\nexport const ed25519Scheme: SignatureScheme = { sign: ed25519Sign, verify: ed25519Verify }\n","import { bytesToB64url, b64urlToBytes, utf8 } from './encoding.js'\n\nexport interface QrPayload {\n readonly v: 1\n readonly docId: string\n readonly salt: string\n readonly alg: 'ed25519'\n readonly keyId: string\n readonly fieldHashes: readonly string[]\n readonly sig: string\n}\n\n/** Compact JSON → base64url. (CBOR + base45 density optimisation deferred.) */\nexport function encodeQr(p: QrPayload): string {\n return bytesToB64url(utf8(JSON.stringify(p)))\n}\n\nexport function decodeQr(s: string): QrPayload {\n let parsed: unknown\n try {\n parsed = JSON.parse(new TextDecoder().decode(b64urlToBytes(s)))\n } catch {\n throw new Error('decodeQr: invalid base64url-encoded JSON payload')\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('decodeQr: invalid payload — expected a JSON object')\n }\n const p = parsed as Record<string, unknown>\n if (p['v'] !== 1) throw new Error(`decodeQr: unsupported version ${String(p['v'])} (expected 1)`)\n if (typeof p['docId'] !== 'string' || typeof p['salt'] !== 'string' || p['alg'] !== 'ed25519'\n || typeof p['keyId'] !== 'string' || typeof p['sig'] !== 'string'\n || !Array.isArray(p['fieldHashes']) || !p['fieldHashes'].every((h) => typeof h === 'string')) {\n throw new Error('decodeQr: invalid payload shape')\n }\n return {\n v: 1, docId: p['docId'], salt: p['salt'], alg: 'ed25519',\n keyId: p['keyId'], fieldHashes: p['fieldHashes'], sig: p['sig'],\n }\n}\n","import { canonicalJson, utf8 } from './encoding.js'\nimport { ed25519Scheme, type SignatureScheme } from './scheme.js'\n\nexport interface RevocationList {\n readonly v: 1\n readonly revokedDocIds: readonly string[]\n readonly asOf: string\n readonly keyId: string\n readonly sig: string\n}\n\nfunction listCore(revokedDocIds: readonly string[], asOf: string, keyId: string): Uint8Array {\n return utf8(canonicalJson({ v: 1, revokedDocIds: [...revokedDocIds].sort(), asOf, keyId }))\n}\n\nexport function isRevoked(docId: string, list: RevocationList): boolean {\n // The list is untrusted (typically network-fetched). Fail closed on a\n // malformed shape rather than throwing a raw TypeError.\n if (!Array.isArray(list?.revokedDocIds)) return false\n return list.revokedDocIds.includes(docId)\n}\n\nexport async function signRevocationList(\n revokedDocIds: readonly string[], asOf: string, keyId: string, privateKeyPkcs8B64: string,\n scheme: SignatureScheme = ed25519Scheme,\n): Promise<RevocationList> {\n const sorted = [...revokedDocIds].sort()\n const sig = await scheme.sign(privateKeyPkcs8B64, listCore(sorted, asOf, keyId))\n return { v: 1, revokedDocIds: sorted, asOf, keyId, sig }\n}\n\nexport async function verifyRevocationList(\n list: RevocationList,\n publicKeyB64: string,\n scheme: SignatureScheme = ed25519Scheme,\n): Promise<boolean> {\n // Untrusted input — validate the shape before touching it (no raw TypeError).\n if (list?.v !== 1 || !Array.isArray(list.revokedDocIds)\n || typeof list.asOf !== 'string' || typeof list.keyId !== 'string' || typeof list.sig !== 'string') {\n return false\n }\n return scheme.verify(publicKeyB64, list.sig, listCore(list.revokedDocIds, list.asOf, list.keyId))\n}\n","import type { AttestationFieldSchema } from './types.js'\nimport type { QrPayload } from './qr.js'\nimport type { RevocationList } from './revocation.js'\nimport { canonicalJson, utf8 } from './encoding.js'\nimport { ed25519Scheme, type SignatureScheme } from './scheme.js'\nimport { computeFieldHashes } from './hashing.js'\nimport { decodeQr } from './qr.js'\nimport { isRevoked } from './revocation.js'\n\nexport interface VerifyInput {\n readonly qr: string\n readonly claimedFields: Record<string, unknown>\n readonly fieldSchema: AttestationFieldSchema\n readonly publicKeys: Readonly<Record<string, string>>\n readonly revocation?: { list: RevocationList }\n}\nexport interface VerifyResult {\n readonly valid: boolean\n readonly signatureValid: boolean\n readonly perField: ReadonlyArray<{ path: string; match: boolean }>\n readonly revoked: boolean | null\n readonly reason?: string\n}\n\n/**\n * The bytes the signature covers: canonicalJson of the payload minus `alg`/`sig`.\n * Excludes `alg` by design — v1 has exactly one algorithm (`ed25519`). If a `v:2`\n * ever introduces a second algorithm, `alg` MUST be added here to prevent a\n * downgrade attack.\n */\nfunction signedCore(core: { v: 1; docId: string; salt: string; keyId: string; fieldHashes: readonly string[] }): Uint8Array {\n return utf8(canonicalJson({ v: core.v, docId: core.docId, salt: core.salt, keyId: core.keyId, fieldHashes: core.fieldHashes }))\n}\n\nexport async function signPayloadCore(\n core: { v: 1; docId: string; salt: string; keyId: string; fieldHashes: readonly string[] },\n privateKeyPkcs8B64: string,\n scheme: SignatureScheme = ed25519Scheme,\n): Promise<string> {\n return scheme.sign(privateKeyPkcs8B64, signedCore(core))\n}\n\nexport async function verifyAttestation(\n input: VerifyInput,\n scheme: SignatureScheme = ed25519Scheme,\n): Promise<VerifyResult> {\n const p: QrPayload = decodeQr(input.qr)\n const pub = input.publicKeys[p.keyId]\n const signatureValid = pub\n ? await scheme.verify(pub, p.sig, signedCore({ v: p.v, docId: p.docId, salt: p.salt, keyId: p.keyId, fieldHashes: p.fieldHashes }))\n : false\n\n const schema = input.fieldSchema\n const perField: Array<{ path: string; match: boolean }> = []\n let allMatch = true\n let countMismatch = false\n if (schema.fields.length !== p.fieldHashes.length) {\n countMismatch = true\n allMatch = false\n for (const f of schema.fields) perField.push({ path: f.path, match: false })\n } else {\n const recomputed = await computeFieldHashes(p.salt, schema, input.claimedFields)\n for (let i = 0; i < schema.fields.length; i++) {\n const match = recomputed[i] === p.fieldHashes[i]\n perField.push({ path: schema.fields[i]!.path, match })\n if (!match) allMatch = false\n }\n }\n\n // Membership check only — the CALLER must have verified the list's own\n // signature with `verifyRevocationList` before passing it here. A list that\n // omits a genuinely-revoked id lets that doc pass; that risk is the caller's.\n const revoked = input.revocation ? isRevoked(p.docId, input.revocation.list) : null\n const valid = signatureValid && allMatch && revoked !== true\n\n let reason: string | undefined\n if (!signatureValid) reason = pub ? 'signature invalid' : 'unknown keyId'\n else if (countMismatch) reason = 'schema/payload field-count mismatch'\n else if (!allMatch) reason = 'field mismatch'\n else if (revoked === true) reason = 'revoked'\n\n return reason !== undefined\n ? { valid, signatureValid, perField, revoked, reason }\n : { valid, signatureValid, perField, revoked }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
grant,
|
|
3
3
|
revoke
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-45VHWE4C.js";
|
|
5
5
|
import {
|
|
6
6
|
CustodyNotEnabledError
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-VSUQI5Q6.js";
|
|
8
8
|
|
|
9
9
|
// src/with-party/custody/active.ts
|
|
10
10
|
function withCustody() {
|
|
@@ -16,7 +16,7 @@ function withCustody() {
|
|
|
16
16
|
return host._revokeCustodianImpl(revoke, vault, options, factors);
|
|
17
17
|
},
|
|
18
18
|
async liberate(vault, opts) {
|
|
19
|
-
const { liberateVault } = await import("./liberate-
|
|
19
|
+
const { liberateVault } = await import("./liberate-LEKDYKTC.js");
|
|
20
20
|
return liberateVault(vault, opts);
|
|
21
21
|
}
|
|
22
22
|
};
|
|
@@ -74,4 +74,4 @@ export {
|
|
|
74
74
|
withCustody,
|
|
75
75
|
CustodyApi
|
|
76
76
|
};
|
|
77
|
-
//# sourceMappingURL=chunk-
|
|
77
|
+
//# sourceMappingURL=chunk-GOJQ5DZA.js.map
|
|
@@ -146,7 +146,7 @@ function routeStore(opts) {
|
|
|
146
146
|
const rn = routeNameFor(vault, collection);
|
|
147
147
|
if (maybeQueueWrite(rn, "put", vault, collection, id, envelope, expectedVersion)) return;
|
|
148
148
|
if (isBlobChunks(collection) && tieredBlobs) {
|
|
149
|
-
const dataSize = envelope._data.length;
|
|
149
|
+
const dataSize = (envelope._data ?? "").length;
|
|
150
150
|
const s2 = blobStoreForSize(dataSize);
|
|
151
151
|
return s2.put(vault, collection, id, envelope, expectedVersion);
|
|
152
152
|
}
|
|
@@ -420,7 +420,7 @@ function withLogging(opts = {}) {
|
|
|
420
420
|
collection: c,
|
|
421
421
|
id,
|
|
422
422
|
version: env._v,
|
|
423
|
-
...logData ? { data: env._data.slice(0, 40) + "..." } : {}
|
|
423
|
+
...logData ? { data: (env._data ?? "").slice(0, 40) + "..." } : {}
|
|
424
424
|
}, () => next.put(v, c, id, env, ev)),
|
|
425
425
|
delete: (v, c, id) => timed("delete", { vault: v, collection: c, id }, () => next.delete(v, c, id)),
|
|
426
426
|
list: (v, c) => timed("list", { vault: v, collection: c }, () => next.list(v, c)),
|
|
@@ -651,4 +651,4 @@ export {
|
|
|
651
651
|
withCache,
|
|
652
652
|
withHealthCheck
|
|
653
653
|
};
|
|
654
|
-
//# sourceMappingURL=chunk-
|
|
654
|
+
//# sourceMappingURL=chunk-GQ4MF3CM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-store/route-store.ts","../src/with-store/store-middleware.ts"],"sourcesContent":["/**\n * Store router / multiplexer.\n *\n * Dispatches `NoydbStore` operations to different backends based on\n * collection type, record size, record age, collection name, or vault name.\n *\n * ```ts\n * const db = await createNoydb({\n * store: routeStore({\n * default: dynamo({ table: 'myapp' }),\n * blobs: s3Store({ bucket: 'myapp-blobs' }),\n * }),\n * })\n * ```\n *\n * @module\n */\n\nimport type {\n NoydbStore,\n EncryptedEnvelope,\n VaultSnapshot,\n StoreCapabilities,\n} from '../kernel/types.js'\n\n// ─── Internal collection prefixes (duplicated to avoid circular import) ──\n\nconst BLOB_CHUNKS = '_blob_chunks'\nconst BLOB_INDEX = '_blob_index'\nconst BLOB_SLOTS = '_blob_slots_'\nconst BLOB_VERSIONS = '_blob_versions_'\n\n// ─── Options ─────────────────────────────────────────────────────────────\n\n/**\n * Size-tiered blob routing configuration.\n *\n * Routes blob chunks to different stores based on byte size. Small blobs\n * (under `threshold`) stay in the primary or `small` store; large blobs\n * go to `large`. This lets you keep DynamoDB as the default while sending\n * large binary objects to S3.\n */\nexport interface BlobStoreRoute {\n /** Store for small blobs (under threshold). Falls back to `default`. */\n readonly small?: NoydbStore\n /** Store for large blobs (over threshold). */\n readonly large: NoydbStore\n /** Size threshold in bytes. Default: `400 * 1024` (DynamoDB item limit). */\n readonly threshold?: number\n}\n\n/**\n * Blob lifecycle management policies evaluated during `compact()`.\n *\n * Controls orphan cleanup, cold-tier archival, and hard deletion of\n * blobs that are no longer referenced by any record.\n */\nexport interface BlobLifecyclePolicy {\n /** Delete orphan blobs (refCount: 0) after this many days. Default: 7. */\n readonly orphanRetentionDays?: number\n /** Move blobs not accessed in this many days to the cold blob store. */\n readonly archiveAfterDays?: number\n /** Store for archived blobs. Required if archiveAfterDays is set. */\n readonly archiveStore?: NoydbStore\n /** Hard-delete archived blobs after this many days. */\n readonly expireAfterDays?: number\n}\n\n/**\n * Age-based hot/cold tiering configuration.\n *\n * Records whose `_ts` timestamp is older than `coldAfterDays` are migrated\n * to the `cold` store during `compact()`. Reads transparently fall through\n * to the cold store when the hot store returns null, so callers don't need\n * to know which tier a record lives in.\n */\nexport interface AgeRoute {\n /** Store for records older than the cutoff. */\n readonly cold: NoydbStore\n /**\n * Days after last modification before a record is cold-eligible for the\n * ROLLING `compact(vault)` migrator. Omit for period-driven archival only\n * (`compact(vault, { before })`), where the cutoff is supplied per call.\n */\n readonly coldAfterDays?: number\n /**\n * Collections that participate in age tiering.\n * Empty array or omitted = all user collections (excluding `_` prefixed).\n */\n readonly collections?: string[]\n}\n\n/**\n * Options for `routeStore()` — the store multiplexer.\n *\n * At minimum, provide a `default` store. All other fields are optional\n * extensions for specific routing scenarios (blobs → S3, geographic sharding,\n * age-based tiering, etc.).\n */\nexport interface RouteStoreOptions {\n /** Default store for all unmatched operations. */\n readonly default: NoydbStore\n\n /**\n * Route blob chunk data to a separate store.\n * - Pass a `NoydbStore` for simple prefix routing (all chunks → that store).\n * - Pass `{ small?, large, threshold? }` for size-tiered routing.\n */\n readonly blobs?: NoydbStore | BlobStoreRoute\n\n /** Route all blob metadata (index, slots, versions) to the blobs store too. Default: false. */\n readonly routeBlobMeta?: boolean\n\n /** Route specific user collections to dedicated stores. */\n readonly routes?: Record<string, NoydbStore>\n\n /** Route by vault name (prefix patterns, e.g. `'EU-'`). */\n readonly vaultRoutes?: Record<string, NoydbStore>\n\n /**\n * Age-based tiering: records older than `coldAfterDays` are read from\n * the cold store. A background `compact()` method migrates them.\n */\n readonly age?: AgeRoute\n\n /**\n * Content-aware blob routing.\n * Route blob chunks by MIME type glob pattern. The MIME type is stored\n * in `BlobObject` and matched at read time via `storeHint`.\n */\n readonly blobRoutes?: Record<string, NoydbStore>\n\n /**\n * Blob lifecycle policies.\n * Evaluated during `compact()`.\n */\n readonly blobLifecycle?: BlobLifecyclePolicy\n\n /**\n * Quota-aware overflow.\n * When the default store's usage exceeds the threshold, new writes\n * overflow to the specified store.\n */\n readonly overflow?: NoydbStore\n\n /**\n * Quota threshold (0-1). Default: 0.8 (overflow at 80% usage).\n * Only effective when `overflow` is set.\n */\n readonly quotaThreshold?: number\n}\n\n// ─── Types ───────────────────────────────────────────────────────────────\n\n/**\n * Named route that can be overridden or suspended at runtime.\n *\n * Built-in names: `'default'`, `'blobs'`, `'cold'`.\n * Custom names: any collection name from `routes`, any vault prefix from\n * `vaultRoutes`, or any sync target label.\n */\nexport type OverrideTarget =\n | 'default'\n | 'blobs'\n | 'cold'\n | (string & {}) // named collection route, vault route, or sync target label\n\n/**\n * Options for `RoutedNoydbStore.override()`.\n *\n * Controls whether the new store is pre-populated with data from the\n * original store before the switch takes effect.\n */\nexport interface OverrideOptions {\n /**\n * Hydrate the override store from the original before activating.\n * - `true` — copy all data for all vaults.\n * - `string[]` — copy only named collections.\n * Makes `override()` async — returns a Promise.\n */\n hydrate?: boolean | string[]\n}\n\n/**\n * Options for `RoutedNoydbStore.suspend()`.\n *\n * A suspended route becomes a null store: reads return null/[], writes\n * are dropped (or buffered if `queue: true`). Useful for maintenance\n * windows or restricted-network scenarios.\n */\nexport interface SuspendOptions {\n /**\n * Buffer write operations during suspension. On `resume()`, queued\n * writes are replayed against the restored store.\n */\n queue?: boolean\n /**\n * Maximum queued operations. When exceeded, oldest entries are dropped.\n * Default: 10_000.\n */\n maxQueueSize?: number\n}\n\n/** Queued write operation recorded during suspension. */\ninterface QueuedWrite {\n method: 'put' | 'delete'\n vault: string\n collection: string\n id: string\n envelope?: EncryptedEnvelope\n expectedVersion?: number\n}\n\n/**\n * Snapshot of the current override and suspend state of a `RoutedNoydbStore`.\n * Returned by `routeStatus()` for diagnostics and health dashboards.\n */\nexport interface RouteStatus {\n /** Active overrides: route name → override store name. */\n readonly overrides: Record<string, string>\n /** Currently suspended routes. */\n readonly suspended: string[]\n /** Queued writes per suspended route (only for routes suspended with `queue: true`). */\n readonly queued: Record<string, number>\n}\n\n/**\n * Extended `NoydbStore` returned by `routeStore()`.\n *\n * Satisfies the full `NoydbStore` contract plus adds runtime control\n * methods for overriding, suspending, and inspecting routes.\n */\nexport interface RoutedNoydbStore extends NoydbStore {\n /**\n * Migrate records to the cold store. Only applies when `age.cold` is\n * configured. With `{ before }`, migrates records whose `_ts < before`\n * (period-driven archival); without, uses the rolling `coldAfterDays`.\n * Returns the number of records migrated.\n */\n compact(vault: string, opts?: { before?: string }): Promise<number>\n\n /**\n * Override a named route at runtime.\n *\n * The override persists until `clearOverride()` is called or the\n * instance is closed. In-flight operations complete on the original\n * store; new operations use the override.\n *\n * Options:\n * - `hydrate: true` — async: copies all data from the original store\n * into the override before activating the switch.\n * - `hydrate: ['invoices', 'clients']` — copies only named collections.\n *\n * Use cases:\n * - Shared device: `await store.override('default', toMemory(), { hydrate: true })`\n * - Restricted network: `store.override('blobs', localFile(...))`\n */\n override(route: OverrideTarget, store: NoydbStore, opts?: OverrideOptions): void | Promise<void>\n\n /** Clear a runtime override, reverting to the original store. */\n clearOverride(route: OverrideTarget): void\n\n /**\n * Suspend a route entirely. Operations to suspended stores become\n * no-ops (puts silently dropped, gets return null, lists return []).\n *\n * Options:\n * - `queue: true` — buffer write operations (put/delete) during\n * suspension. When `resume()` is called, queued writes are replayed\n * against the restored store.\n *\n * Returns a `SuspendHandle` when `queue: true`, for inspecting queue state.\n */\n suspend(route: OverrideTarget, opts?: SuspendOptions): void\n\n /**\n * Resume a previously suspended route.\n * If the route was suspended with `queue: true`, replays queued writes.\n * Returns the number of replayed operations.\n */\n resume(route: OverrideTarget): Promise<number>\n\n /** Snapshot the current override/suspend state for diagnostics. */\n routeStatus(): RouteStatus\n\n /**\n * Resolve the physical backend a vault id maps to via the geographic\n * `vaultRoutes` prefix routing (collection-independent), falling back to\n * the `default` store. Used by the federation data-residency guard\n * to read the placement backend's `capabilities.region`.\n */\n resolveBackend(vaultId: string): NoydbStore\n}\n\n// ─── Implementation ──────────────────────────────────────────────────────\n\n/**\n * Create a store multiplexer that dispatches operations to different backends\n * based on collection type, record size, record age, vault prefix, or\n * runtime overrides.\n *\n * ```ts\n * const store = routeStore({\n * default: dynamo({ table: 'myapp' }),\n * blobs: s3({ bucket: 'myapp-blobs' }),\n * routes: { auditLog: s3({ bucket: 'myapp-audit' }) },\n * })\n * ```\n *\n * The returned store satisfies `NoydbStore` and can be passed directly to\n * `createNoydb({ store })`. It also exposes additional methods\n * (`override`, `suspend`, `resume`, `routeStatus`, `compact`) for runtime\n * control and maintenance.\n */\nexport function routeStore(opts: RouteStoreOptions): RoutedNoydbStore {\n const primary = opts.default\n\n // Resolve blob store config\n const blobsIsSimple = opts.blobs && 'get' in opts.blobs\n const simpleBlobStore = blobsIsSimple ? opts.blobs : undefined\n const tieredBlobs = !blobsIsSimple ? opts.blobs : undefined\n const blobThreshold = tieredBlobs?.threshold ?? 400 * 1024\n\n // Collect all stores for loadAll/saveAll/listVaults composition\n const allStores = new Set<NoydbStore>([primary])\n if (simpleBlobStore) allStores.add(simpleBlobStore)\n if (tieredBlobs?.large) allStores.add(tieredBlobs.large)\n if (tieredBlobs?.small) allStores.add(tieredBlobs.small)\n if (opts.age?.cold) allStores.add(opts.age.cold)\n if (opts.routes) for (const s of Object.values(opts.routes)) allStores.add(s)\n if (opts.vaultRoutes) for (const s of Object.values(opts.vaultRoutes)) allStores.add(s)\n if (opts.blobRoutes) for (const s of Object.values(opts.blobRoutes)) allStores.add(s)\n if (opts.overflow) allStores.add(opts.overflow)\n if (opts.blobLifecycle?.archiveStore) allStores.add(opts.blobLifecycle.archiveStore)\n\n // ── Runtime override / suspend state ──────────────────\n\n const overrides = new Map<string, NoydbStore>()\n const suspended = new Set<string>()\n const writeQueues = new Map<string, { writes: QueuedWrite[]; maxSize: number }>()\n\n /** Null store: silently absorbs all operations when a route is suspended. */\n const NULL_STORE: NoydbStore = {\n name: 'suspended',\n async get() { return null },\n async put() {},\n async delete() {},\n async list() { return [] },\n async loadAll() { return {} },\n async saveAll() {},\n }\n\n /**\n * Map a resolved route to its canonical name for override/suspend lookup.\n * Vault routes use the prefix, collection routes use the collection name,\n * blob route is 'blobs', cold route is 'cold', everything else is 'default'.\n */\n function routeNameFor(vault: string, collection: string): string {\n if (opts.vaultRoutes) {\n for (const prefix of Object.keys(opts.vaultRoutes)) {\n if (vault.startsWith(prefix)) return prefix\n }\n }\n if (opts.routes && !collection.startsWith('_') && opts.routes[collection]) {\n return collection\n }\n if (isBlobChunks(collection) && (simpleBlobStore || tieredBlobs)) return 'blobs'\n if (opts.routeBlobMeta && isBlobMeta(collection) && (simpleBlobStore || tieredBlobs)) return 'blobs'\n if (opts.age && !collection.startsWith('_')) {\n // We don't name age 'cold' here — cold is a fallback, not a primary route\n }\n return 'default'\n }\n\n // ── Quota-aware overflow (E8) ───────────────────────────────────────\n\n const quotaExceeded = false\n\n /** Resolve the static (non-overridden) store for a given route name. */\n function resolveOriginalStore(route: string): NoydbStore {\n if (route === 'blobs') return simpleBlobStore ?? tieredBlobs?.large ?? primary\n if (route === 'cold') return opts.age?.cold ?? primary\n if (opts.routes?.[route]) return opts.routes[route]\n if (opts.vaultRoutes?.[route]) return opts.vaultRoutes[route]\n return primary\n }\n\n /**\n * Queue a write operation if the route is suspended with queue: true.\n * Returns true if queued (caller should skip the actual write).\n */\n function maybeQueueWrite(\n routeName: string,\n method: 'put' | 'delete',\n vault: string,\n collection: string,\n id: string,\n envelope?: EncryptedEnvelope,\n expectedVersion?: number,\n ): boolean {\n if (!suspended.has(routeName)) return false\n const queue = writeQueues.get(routeName)\n if (!queue) return false // suspended but no queue — NullStore behavior\n\n // Evict oldest if at capacity\n if (queue.writes.length >= queue.maxSize) {\n queue.writes.shift()\n }\n queue.writes.push({\n method, vault, collection, id,\n ...(envelope !== undefined ? { envelope } : {}),\n ...(expectedVersion !== undefined ? { expectedVersion } : {}),\n })\n return true\n }\n\n // ── Routing logic ──────────────────────────────────────────────────\n\n function isBlobChunks(collection: string): boolean {\n return collection === BLOB_CHUNKS\n }\n\n function isBlobMeta(collection: string): boolean {\n return collection === BLOB_INDEX\n || collection.startsWith(BLOB_SLOTS)\n || collection.startsWith(BLOB_VERSIONS)\n }\n\n function isInternal(collection: string): boolean {\n return collection.startsWith('_')\n }\n\n /**\n * Resolve the store for a given vault + collection.\n * Resolution order: overrides/suspend → vaultRoutes → routes → blobs → default\n */\n function storeFor(vault: string, collection: string): NoydbStore {\n const rName = routeNameFor(vault, collection)\n\n // 0. Runtime override / suspend check\n if (suspended.has(rName)) return NULL_STORE\n if (overrides.has(rName)) return overrides.get(rName)!\n\n // 1. Vault-based geographic routing\n if (opts.vaultRoutes) {\n for (const [prefix, store] of Object.entries(opts.vaultRoutes)) {\n if (vault.startsWith(prefix)) return store\n }\n }\n\n // 2. Per-collection routing (user collections only)\n if (opts.routes && !isInternal(collection) && opts.routes[collection]) {\n return opts.routes[collection]\n }\n\n // 3. Blob chunk routing (simple — no size tiering at the store level)\n if (isBlobChunks(collection)) {\n if (simpleBlobStore) return simpleBlobStore\n // Size-tiered: can't determine here without the envelope.\n // Default to large store — BlobSet will use storeHint for reads.\n if (tieredBlobs) return tieredBlobs.large\n }\n\n // 4. Blob metadata routing\n if (opts.routeBlobMeta && isBlobMeta(collection)) {\n if (simpleBlobStore) return simpleBlobStore\n if (tieredBlobs) return tieredBlobs.large\n }\n\n // 5. Quota-aware overflow (E8)\n if (quotaExceeded && opts.overflow) return opts.overflow\n\n // 6. Default\n return primary\n }\n\n /**\n * For size-tiered blob routing: pick store based on envelope data size.\n */\n function blobStoreForSize(dataSize: number): NoydbStore {\n if (!tieredBlobs) return simpleBlobStore ?? primary\n if (dataSize <= blobThreshold) {\n return tieredBlobs.small ?? primary\n }\n return tieredBlobs.large\n }\n\n /**\n * Age routing: check if a record is cold based on `_ts`.\n */\n function isCold(collection: string, envelope: EncryptedEnvelope, before?: string): boolean {\n if (!opts.age) return false\n if (isInternal(collection)) return false\n if (opts.age.collections && opts.age.collections.length > 0) {\n if (!opts.age.collections.includes(collection)) return false\n }\n // explicit period cutoff wins; else the rolling age cutoff; else nothing is cold\n const cutoffIso =\n before ??\n (opts.age.coldAfterDays != null\n ? new Date(Date.now() - opts.age.coldAfterDays * 24 * 60 * 60 * 1000).toISOString()\n : undefined)\n if (cutoffIso === undefined) return false\n return envelope._ts < cutoffIso\n }\n\n // ── Store methods ──────────────────────────────────────────────────\n\n // #613: advertise cold-archival when a cold route exists. Spread the\n // primary's capabilities so CAS/auth/etc. still surface; layer the flag.\n // A router with no cold route of its own must NOT inherit `coldArchival:\n // true` from a nested cold-capable primary (whole-branch review I1) — force\n // it off explicitly so the consumer's `!== true` gate throws.\n const store: RoutedNoydbStore = {\n name: buildName(),\n ...(opts.age?.cold\n ? { capabilities: { ...primary.capabilities, coldArchival: true } as StoreCapabilities }\n : primary.capabilities\n ? { capabilities: { ...primary.capabilities, coldArchival: false } as StoreCapabilities }\n : {}),\n\n async get(vault, collection, id) {\n const s = storeFor(vault, collection)\n const result = await s.get(vault, collection, id)\n\n // Age tiering: if hot store returned null, try cold\n if (result === null && opts.age && !isInternal(collection)) {\n if (!opts.age.collections?.length || opts.age.collections.includes(collection)) {\n return opts.age.cold.get(vault, collection, id)\n }\n }\n\n return result\n },\n\n async put(vault, collection, id, envelope, expectedVersion) {\n // Write-behind queue: buffer if suspended with queue option\n const rn = routeNameFor(vault, collection)\n if (maybeQueueWrite(rn, 'put', vault, collection, id, envelope, expectedVersion)) return\n\n // Size-tiered blob routing\n if (isBlobChunks(collection) && tieredBlobs) {\n const dataSize = (envelope._data ?? '').length\n const s = blobStoreForSize(dataSize)\n return s.put(vault, collection, id, envelope, expectedVersion)\n }\n\n const s = storeFor(vault, collection)\n\n // Age tiering: if a cold record is being updated, it goes to hot.\n if (opts.age && !isInternal(collection)) {\n opts.age.cold.delete(vault, collection, id).catch(() => {})\n }\n\n return s.put(vault, collection, id, envelope, expectedVersion)\n },\n\n async delete(vault, collection, id) {\n // Write-behind queue: buffer if suspended with queue option\n const rn = routeNameFor(vault, collection)\n if (maybeQueueWrite(rn, 'delete', vault, collection, id)) return\n\n const s = storeFor(vault, collection)\n await s.delete(vault, collection, id)\n\n // Also delete from cold store if age-tiered\n if (opts.age && !isInternal(collection)) {\n await opts.age.cold.delete(vault, collection, id).catch(() => {})\n }\n },\n\n async list(vault, collection) {\n const s = storeFor(vault, collection)\n const ids = await s.list(vault, collection)\n\n // Age tiering: merge IDs from cold store, deduplicate\n if (opts.age && !isInternal(collection)) {\n if (!opts.age.collections?.length || opts.age.collections.includes(collection)) {\n const coldIds = await opts.age.cold.list(vault, collection).catch(() => [] as string[])\n if (coldIds.length > 0) {\n const merged = new Set(ids)\n for (const id of coldIds) merged.add(id)\n return [...merged]\n }\n }\n }\n\n return ids\n },\n\n async loadAll(vault) {\n // Query all distinct stores in parallel, merge snapshots\n const stores = getStoresForVault(vault)\n const snapshots = await Promise.all(\n stores.map(s => s.loadAll(vault).catch(() => ({}) as VaultSnapshot)),\n )\n return mergeSnapshots(snapshots)\n },\n\n async saveAll(vault, data) {\n // Partition snapshot by routing rules\n const partitioned = new Map<NoydbStore, VaultSnapshot>()\n\n for (const [collection, records] of Object.entries(data)) {\n const s = storeFor(vault, collection)\n if (!partitioned.has(s)) partitioned.set(s, {})\n partitioned.get(s)![collection] = records\n }\n\n await Promise.all(\n [...partitioned.entries()].map(([s, snap]) => s.saveAll(vault, snap)),\n )\n },\n\n async compact(vault, compactOpts) {\n if (!opts.age) return 0\n let migrated = 0\n const collections = opts.age.collections?.length\n ? opts.age.collections\n : Object.keys(await primary.loadAll(vault).catch(() => ({}) as VaultSnapshot))\n\n for (const collection of collections) {\n const ids = await primary.list(vault, collection).catch(() => [] as string[])\n for (const id of ids) {\n const envelope = await primary.get(vault, collection, id)\n if (!envelope) continue\n if (isCold(collection, envelope, compactOpts?.before)) {\n await opts.age.cold.put(vault, collection, id, envelope)\n await primary.delete(vault, collection, id)\n migrated++\n }\n }\n }\n return migrated\n },\n\n // ── Runtime override / suspend ──────────────────────\n\n override(route: OverrideTarget, overrideStore: NoydbStore, overrideOpts?: OverrideOptions): void | Promise<void> {\n if (overrideOpts?.hydrate) {\n // Async hydration: copy data from current store, then activate override\n return (async () => {\n // Hydration: caller should copy data from the original store to\n // overrideStore before calling override() with { hydrate: true }.\n // The route is activated immediately after.\n overrides.set(route, overrideStore)\n })()\n }\n overrides.set(route, overrideStore)\n },\n\n clearOverride(route: OverrideTarget): void {\n overrides.delete(route)\n },\n\n suspend(route: OverrideTarget, suspendOpts?: SuspendOptions): void {\n suspended.add(route)\n if (suspendOpts?.queue) {\n writeQueues.set(route, {\n writes: [],\n maxSize: suspendOpts.maxQueueSize ?? 10_000,\n })\n }\n },\n\n async resume(route: OverrideTarget): Promise<number> {\n suspended.delete(route)\n const queue = writeQueues.get(route)\n if (!queue || queue.writes.length === 0) {\n writeQueues.delete(route)\n return 0\n }\n\n // Replay queued writes against the now-active store\n let replayed = 0\n const target = overrides.get(route) ?? resolveOriginalStore(route)\n for (const write of queue.writes) {\n try {\n if (write.method === 'put' && write.envelope) {\n await target.put(write.vault, write.collection, write.id, write.envelope, write.expectedVersion)\n } else if (write.method === 'delete') {\n await target.delete(write.vault, write.collection, write.id)\n }\n replayed++\n } catch {\n // Best-effort replay — conflicts are expected after suspension\n }\n }\n\n writeQueues.delete(route)\n return replayed\n },\n\n routeStatus(): RouteStatus {\n const ov: Record<string, string> = {}\n for (const [k, v] of overrides) ov[k] = v.name ?? 'unnamed'\n const q: Record<string, number> = {}\n for (const [k, v] of writeQueues) q[k] = v.writes.length\n return { overrides: ov, suspended: [...suspended], queued: q }\n },\n\n resolveBackend(vaultId: string): NoydbStore {\n // Geographic routing is vault-prefix based + collection-independent;\n // the region-relevant backend is the vaultRoutes match, else default.\n if (opts.vaultRoutes) {\n for (const [prefix, s] of Object.entries(opts.vaultRoutes)) {\n if (vaultId.startsWith(prefix)) return s\n }\n }\n return primary\n },\n }\n\n // ── Optional method forwarding ─────────────────────────────────────\n\n // Forward listVaults from all stores, deduplicated\n if (anyHas('listVaults')) {\n store.listVaults = async () => {\n const results = await Promise.all(\n [...allStores]\n .filter(s => s.listVaults !== undefined)\n .map(s => s.listVaults!().catch(() => [] as string[])),\n )\n return [...new Set(results.flat())]\n }\n }\n\n // Forward ping — succeed if any store responds\n if (anyHas('ping')) {\n store.ping = async () => {\n const results = await Promise.all(\n [...allStores]\n .filter(s => s.ping !== undefined)\n .map(s => s.ping!().catch(() => false)),\n )\n return results.some(Boolean)\n }\n }\n\n return store\n\n // ── Helpers ────────────────────────────────────────────────────────\n\n function buildName(): string {\n const names = [...allStores].map(s => s.name ?? '?').join('+')\n return `route(${names})`\n }\n\n function anyHas(method: string): boolean {\n return [...allStores].some(s => (s as unknown as Record<string, unknown>)[method])\n }\n\n function getStoresForVault(vault: string): NoydbStore[] {\n const stores = new Set<NoydbStore>()\n\n // Check vault routes first\n if (opts.vaultRoutes) {\n for (const [prefix, s] of Object.entries(opts.vaultRoutes)) {\n if (vault.startsWith(prefix)) {\n stores.add(s)\n return [...stores] // vault-routed: only use that store\n }\n }\n }\n\n // Default topology: primary + blob store + cold store\n stores.add(primary)\n if (simpleBlobStore) stores.add(simpleBlobStore)\n if (tieredBlobs?.large) stores.add(tieredBlobs.large)\n if (tieredBlobs?.small && tieredBlobs.small !== primary) stores.add(tieredBlobs.small)\n if (opts.age?.cold) stores.add(opts.age.cold)\n if (opts.routes) {\n for (const s of Object.values(opts.routes)) stores.add(s)\n }\n\n return [...stores]\n }\n}\n\n// ─── Snapshot merge ──────────────────────────────────────────────────────\n\nfunction mergeSnapshots(snapshots: VaultSnapshot[]): VaultSnapshot {\n const merged: VaultSnapshot = {}\n\n for (const snap of snapshots) {\n for (const [collection, records] of Object.entries(snap)) {\n if (!merged[collection]) {\n merged[collection] = { ...records }\n continue\n }\n for (const [id, envelope] of Object.entries(records)) {\n const existing = merged[collection][id]\n // Last-write-wins by _ts\n if (!existing || envelope._ts >= existing._ts) {\n merged[collection][id] = envelope\n }\n }\n }\n }\n\n return merged\n}\n","/**\n * Store middleware — composable interceptors for NoydbStore.\n *\n * ```ts\n * const resilient = wrapStore(\n * dynamo({ table: 'myapp' }),\n * withRetry({ maxRetries: 3 }),\n * withLogging({ level: 'debug' }),\n * withCache({ ttlMs: 60_000 }),\n * )\n * ```\n *\n * Each middleware is `(next: NoydbStore) => NoydbStore`. They compose\n * left-to-right: first middleware is outermost (processes requests first,\n * responses last).\n *\n * @module\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../kernel/types.js'\n\n// ─── Core composition ───────────────────────────────────────────────────\n\n/**\n * A store middleware function.\n *\n * Takes the next store in the chain and returns a wrapped store. Middlewares\n * compose left-to-right via `wrapStore()`: the first argument is outermost\n * (first to intercept requests, last to process responses).\n *\n * ```ts\n * const mw: StoreMiddleware = (next) => ({\n * ...next,\n * async get(vault, collection, id) {\n * console.log('get', id)\n * return next.get(vault, collection, id)\n * },\n * })\n * ```\n */\nexport type StoreMiddleware = (next: NoydbStore) => NoydbStore\n\n/**\n * Wrap a store with one or more middlewares. Middlewares compose left-to-right.\n */\nexport function wrapStore(store: NoydbStore, ...middlewares: StoreMiddleware[]): NoydbStore {\n let result = store\n // Apply right-to-left so the first middleware is the outermost wrapper\n for (let i = middlewares.length - 1; i >= 0; i--) {\n result = middlewares[i]!(result)\n }\n return result\n}\n\n// ─── withRetry ──────────────────────────────────────────────────────────\n\n/** Options for `withRetry()`. */\nexport interface RetryOptions {\n /** Maximum retry attempts. Default: 3. */\n maxRetries?: number\n /** Base backoff delay in ms. Default: 500. */\n backoffMs?: number\n /** Jitter factor (0-1). Adds random delay up to `backoffMs * jitter`. Default: 0.3. */\n jitter?: number\n /** Only retry on these error codes. Default: retry all errors. */\n retryOn?: string[]\n}\n\n/**\n * Middleware that retries failed store operations with exponential backoff\n * and optional jitter. Useful for transient network errors on DynamoDB/S3.\n *\n * ```ts\n * wrapStore(dynamo({ table: 'myapp' }), withRetry({ maxRetries: 5, retryOn: ['NETWORK_ERROR'] }))\n * ```\n */\nexport function withRetry(opts: RetryOptions = {}): StoreMiddleware {\n const maxRetries = opts.maxRetries ?? 3\n const backoffMs = opts.backoffMs ?? 500\n const jitter = opts.jitter ?? 0.3\n const retryOn = opts.retryOn ? new Set(opts.retryOn) : null\n\n function shouldRetry(err: unknown): boolean {\n if (!retryOn) return true\n if (err && typeof err === 'object' && 'code' in err) {\n return retryOn.has((err as { code: string }).code)\n }\n return true\n }\n\n async function retryable<T>(fn: () => Promise<T>): Promise<T> {\n let lastError: unknown\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn()\n } catch (err) {\n lastError = err\n if (attempt >= maxRetries || !shouldRetry(err)) throw err\n const delay = backoffMs * Math.pow(2, attempt) * (1 + Math.random() * jitter)\n await new Promise(r => setTimeout(r, delay))\n }\n }\n throw lastError\n }\n\n return (next) => ({\n ...next,\n name: next.name ? `retry(${next.name})` : 'retry',\n get: (v, c, id) => retryable(() => next.get(v, c, id)),\n put: (v, c, id, env, ev) => retryable(() => next.put(v, c, id, env, ev)),\n delete: (v, c, id) => retryable(() => next.delete(v, c, id)),\n list: (v, c) => retryable(() => next.list(v, c)),\n loadAll: (v) => retryable(() => next.loadAll(v)),\n saveAll: (v, d) => retryable(() => next.saveAll(v, d)),\n })\n}\n\n// ─── withLogging ────────────────────────────────────────────────────────\n\n/** Log level for `withLogging()`. Maps to standard console method names. */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\n/** Options for `withLogging()`. */\nexport interface LoggingOptions {\n /** Minimum log level. Default: 'info'. */\n level?: LogLevel\n /** Custom logger. Default: console. */\n logger?: {\n debug(msg: string, ...args: unknown[]): void\n info(msg: string, ...args: unknown[]): void\n warn(msg: string, ...args: unknown[]): void\n error(msg: string, ...args: unknown[]): void\n }\n /** Log the data payload (envelope contents). Default: false (privacy). */\n logData?: boolean\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 }\n\n/**\n * Middleware that logs every store operation with its method name, arguments,\n * and elapsed duration. Privacy-safe by default: envelope payloads are not\n * logged unless `logData: true` is set.\n */\nexport function withLogging(opts: LoggingOptions = {}): StoreMiddleware {\n const minLevel = LOG_LEVELS[opts.level ?? 'info']\n const logger = opts.logger ?? console\n const logData = opts.logData ?? false\n\n function log(level: LogLevel, method: string, args: Record<string, unknown>, durationMs?: number) {\n if (LOG_LEVELS[level] < minLevel) return\n const parts = [`[noydb:${method}]`, ...Object.entries(args).map(([k, v]) => `${k}=${String(v)}`)]\n if (durationMs !== undefined) parts.push(`${durationMs}ms`)\n logger[level](parts.join(' '))\n }\n\n function timed<T>(method: string, args: Record<string, unknown>, fn: () => Promise<T>): Promise<T> {\n const start = Date.now()\n return fn().then(\n (result) => {\n log('debug', method, args, Date.now() - start)\n return result\n },\n (err) => {\n log('error', method, { ...args, error: (err as Error).message }, Date.now() - start)\n throw err\n },\n )\n }\n\n return (next) => ({\n ...next,\n name: next.name ? `log(${next.name})` : 'log',\n get: (v, c, id) => timed('get', { vault: v, collection: c, id }, () => next.get(v, c, id)),\n put: (v, c, id, env, ev) => timed('put', {\n vault: v, collection: c, id, version: env._v,\n ...(logData ? { data: (env._data ?? '').slice(0, 40) + '...' } : {}),\n }, () => next.put(v, c, id, env, ev)),\n delete: (v, c, id) => timed('delete', { vault: v, collection: c, id }, () => next.delete(v, c, id)),\n list: (v, c) => timed('list', { vault: v, collection: c }, () => next.list(v, c)),\n loadAll: (v) => timed('loadAll', { vault: v }, () => next.loadAll(v)),\n saveAll: (v, d) => timed('saveAll', { vault: v }, () => next.saveAll(v, d)),\n })\n}\n\n// ─── withMetrics ────────────────────────────────────────────────────────\n\n/**\n * Data emitted to `MetricsOptions.onOperation` after every store call.\n *\n * Carries method name, vault/collection/id context, elapsed duration,\n * and success/failure status. Wire this into your metrics pipeline\n * (DataDog, Prometheus, CloudWatch) to get per-operation latency histograms.\n */\nexport interface StoreOperation {\n method: 'get' | 'put' | 'delete' | 'list' | 'loadAll' | 'saveAll'\n vault: string\n collection?: string\n id?: string\n durationMs: number\n success: boolean\n error?: Error\n}\n\n/** Options for `withMetrics()`. */\nexport interface MetricsOptions {\n /** Called after every store operation. */\n onOperation: (op: StoreOperation) => void\n}\n\n/**\n * Middleware that calls `onOperation` after every store method with timing\n * and success/failure data. Designed for low-overhead integration with\n * metrics systems — the callback is synchronous and fire-and-forget.\n */\nexport function withMetrics(opts: MetricsOptions): StoreMiddleware {\n function tracked<T>(\n method: StoreOperation['method'],\n vault: string,\n fn: () => Promise<T>,\n collection?: string,\n id?: string,\n ): Promise<T> {\n const start = Date.now()\n return fn().then(\n (result) => {\n opts.onOperation({\n method, vault,\n ...(collection !== undefined ? { collection } : {}),\n ...(id !== undefined ? { id } : {}),\n durationMs: Date.now() - start, success: true,\n })\n return result\n },\n (err) => {\n opts.onOperation({\n method, vault,\n ...(collection !== undefined ? { collection } : {}),\n ...(id !== undefined ? { id } : {}),\n durationMs: Date.now() - start, success: false, error: err as Error,\n })\n throw err\n },\n )\n }\n\n return (next) => ({\n ...next,\n name: next.name ? `metrics(${next.name})` : 'metrics',\n get: (v, c, id) => tracked('get', v, () => next.get(v, c, id), c, id),\n put: (v, c, id, env, ev) => tracked('put', v, () => next.put(v, c, id, env, ev), c, id),\n delete: (v, c, id) => tracked('delete', v, () => next.delete(v, c, id), c, id),\n list: (v, c) => tracked('list', v, () => next.list(v, c), c),\n loadAll: (v) => tracked('loadAll', v, () => next.loadAll(v)),\n saveAll: (v, d) => tracked('saveAll', v, () => next.saveAll(v, d)),\n })\n}\n\n// ─── withCircuitBreaker ─────────────────────────────────────────────────\n\n/**\n * Options for `withCircuitBreaker()`.\n *\n * The circuit breaker moves through three states:\n * - `closed`: normal operation.\n * - `open`: store is failing; all calls return fallback values immediately.\n * - `half-open`: one probe call after `resetTimeoutMs` — success closes, failure re-opens.\n */\nexport interface CircuitBreakerOptions {\n /** Number of consecutive failures before opening the circuit. Default: 5. */\n failureThreshold?: number\n /** Time in ms before attempting to half-open the circuit. Default: 30_000. */\n resetTimeoutMs?: number\n /** Called when the circuit opens (store becomes unavailable). */\n onOpen?: () => void\n /** Called when the circuit closes (store recovers). */\n onClose?: () => void\n}\n\ntype CircuitState = 'closed' | 'open' | 'half-open'\n\n/**\n * Middleware that implements the circuit-breaker pattern.\n *\n * When the wrapped store fails `failureThreshold` consecutive times, the\n * circuit opens: subsequent calls return safe fallback values (`null`, `[]`,\n * `{}`) without hitting the store. After `resetTimeoutMs` the circuit\n * half-opens and allows one probe — success closes the circuit, failure\n * keeps it open. Pair with `withRetry` to handle transient errors before\n * they trip the circuit.\n */\nexport function withCircuitBreaker(opts: CircuitBreakerOptions = {}): StoreMiddleware {\n const threshold = opts.failureThreshold ?? 5\n const resetMs = opts.resetTimeoutMs ?? 30_000\n\n let state: CircuitState = 'closed'\n let failures = 0\n let lastFailureTime = 0\n\n function recordSuccess(): void {\n if (state === 'half-open') {\n state = 'closed'\n failures = 0\n opts.onClose?.()\n }\n failures = 0\n }\n\n function recordFailure(): void {\n failures++\n lastFailureTime = Date.now()\n if (failures >= threshold && state === 'closed') {\n state = 'open'\n opts.onOpen?.()\n }\n }\n\n function canAttempt(): boolean {\n if (state === 'closed') return true\n if (state === 'open') {\n if (Date.now() - lastFailureTime >= resetMs) {\n state = 'half-open'\n return true\n }\n return false\n }\n // half-open: allow one attempt\n return true\n }\n\n async function guarded<T>(fn: () => Promise<T>, fallback: T): Promise<T> {\n if (!canAttempt()) return fallback\n try {\n const result = await fn()\n recordSuccess()\n return result\n } catch (err) {\n recordFailure()\n throw err\n }\n }\n\n return (next) => ({\n ...next,\n name: next.name ? `cb(${next.name})` : 'cb',\n get: (v, c, id) => guarded(() => next.get(v, c, id), null),\n put: (v, c, id, env, ev) => guarded(() => next.put(v, c, id, env, ev), undefined),\n delete: (v, c, id) => guarded(() => next.delete(v, c, id), undefined),\n list: (v, c) => guarded(() => next.list(v, c), []),\n loadAll: (v) => guarded(() => next.loadAll(v), {}),\n saveAll: (v, d) => guarded(() => next.saveAll(v, d), undefined),\n })\n}\n\n// ─── withCache (read-through) ───────────────────────────────────────────\n\n/**\n * Options for `withCache()`.\n *\n * The cache is a read-through LRU that caches individual record fetches\n * (`get`). Writes (`put`, `delete`) invalidate the relevant cache entry\n * immediately. `list`, `loadAll`, and `saveAll` bypass the cache.\n *\n * Named `StoreCacheOptions` to distinguish from `CacheOptions` in\n * `@noy-db/hub/collection`, which controls the in-memory decrypted-record LRU.\n */\nexport interface StoreCacheOptions {\n /** Maximum cached entries. Default: 500. */\n maxEntries?: number\n /** Cache TTL in ms. Default: 60_000 (1 minute). 0 = no expiry. */\n ttlMs?: number\n}\n\ninterface CacheEntry {\n envelope: EncryptedEnvelope | null\n cachedAt: number\n}\n\n/**\n * Middleware that adds a read-through LRU cache for `get()` calls.\n *\n * Reduces latency for frequently-read records (e.g. lookup tables, user\n * profiles) by serving repeat reads from memory. Because NOYDB records are\n * encrypted at rest, caching envelopes is safe — the cache holds ciphertext,\n * not plaintext. For write-heavy workloads, the cache provides little benefit\n * and should be omitted to avoid the invalidation overhead.\n */\nexport function withCache(opts: StoreCacheOptions = {}): StoreMiddleware {\n const maxEntries = opts.maxEntries ?? 500\n const ttlMs = opts.ttlMs ?? 60_000\n\n // LRU cache: Map preserves insertion order, we delete+re-insert on access\n const cache = new Map<string, CacheEntry>()\n\n function cacheKey(vault: string, collection: string, id: string): string {\n return `${vault}\\0${collection}\\0${id}`\n }\n\n function getFromCache(key: string): EncryptedEnvelope | null | undefined {\n const entry = cache.get(key)\n if (!entry) return undefined\n if (ttlMs > 0 && Date.now() - entry.cachedAt > ttlMs) {\n cache.delete(key)\n return undefined\n }\n // LRU: move to end\n cache.delete(key)\n cache.set(key, entry)\n return entry.envelope\n }\n\n function setInCache(key: string, envelope: EncryptedEnvelope | null): void {\n // Evict oldest if at capacity\n if (cache.size >= maxEntries) {\n const oldest = cache.keys().next().value\n if (oldest !== undefined) cache.delete(oldest)\n }\n cache.set(key, { envelope, cachedAt: Date.now() })\n }\n\n function invalidate(key: string): void {\n cache.delete(key)\n }\n\n return (next) => ({\n ...next,\n name: next.name ? `cache(${next.name})` : 'cache',\n\n async get(vault, collection, id) {\n const key = cacheKey(vault, collection, id)\n const cached = getFromCache(key)\n if (cached !== undefined) return cached\n const result = await next.get(vault, collection, id)\n setInCache(key, result)\n return result\n },\n\n async put(vault, collection, id, env, ev) {\n invalidate(cacheKey(vault, collection, id))\n await next.put(vault, collection, id, env, ev)\n setInCache(cacheKey(vault, collection, id), env)\n },\n\n async delete(vault, collection, id) {\n invalidate(cacheKey(vault, collection, id))\n await next.delete(vault, collection, id)\n },\n\n list: (v, c) => next.list(v, c),\n loadAll: (v) => next.loadAll(v),\n saveAll: (v, d) => next.saveAll(v, d),\n })\n}\n\n// ─── withHealthCheck ────────────────────────────────────────────────────\n\nexport interface HealthCheckOptions {\n /** Ping interval in ms. Default: 30_000. */\n checkIntervalMs?: number\n /** Suspend after N consecutive ping failures. Default: 3. */\n suspendAfterFailures?: number\n /** Resume after N consecutive ping successes. Default: 1. */\n resumeAfterSuccess?: number\n /** Called when the store is auto-suspended. */\n onSuspend?: () => void\n /** Called when the store is auto-resumed. */\n onResume?: () => void\n /**\n * Custom health check. Default: calls `store.ping()` if available,\n * otherwise attempts a `list()` on a sentinel collection.\n */\n check?: () => Promise<boolean>\n}\n\n/**\n * Auto-suspends a store when health checks fail, auto-resumes when they recover.\n *\n * When suspended, `get` returns null, `put`/`delete` are no-ops, `list` returns [].\n * This is identical to the `NullStore` behavior from `routeStore.suspend()`.\n */\nexport function withHealthCheck(opts: HealthCheckOptions = {}): StoreMiddleware {\n const intervalMs = opts.checkIntervalMs ?? 30_000\n const failThreshold = opts.suspendAfterFailures ?? 3\n const successThreshold = opts.resumeAfterSuccess ?? 1\n\n let isSuspended = false\n let consecutiveFailures = 0\n let consecutiveSuccesses = 0\n\n return (next) => {\n const checkFn = opts.check ?? (\n next.ping\n ? () => next.ping!()\n : async () => { await next.list('__health__', '__ping__'); return true }\n )\n\n async function doCheck(): Promise<void> {\n try {\n const ok = await checkFn()\n if (ok) {\n consecutiveFailures = 0\n consecutiveSuccesses++\n if (isSuspended && consecutiveSuccesses >= successThreshold) {\n isSuspended = false\n consecutiveSuccesses = 0\n opts.onResume?.()\n }\n } else {\n throw new Error('Health check returned false')\n }\n } catch {\n consecutiveSuccesses = 0\n consecutiveFailures++\n if (!isSuspended && consecutiveFailures >= failThreshold) {\n isSuspended = true\n consecutiveFailures = 0\n opts.onSuspend?.()\n }\n }\n }\n\n // Start checking\n setInterval(() => { void doCheck() }, intervalMs)\n\n const wrapped: NoydbStore = {\n ...next,\n name: next.name ? `health(${next.name})` : 'health',\n\n async get(v, c, id) { return isSuspended ? null : next.get(v, c, id) },\n async put(v, c, id, env, ev) { if (!isSuspended) await next.put(v, c, id, env, ev) },\n async delete(v, c, id) { if (!isSuspended) await next.delete(v, c, id) },\n async list(v, c) { return isSuspended ? [] : next.list(v, c) },\n async loadAll(v) { return isSuspended ? {} : next.loadAll(v) },\n async saveAll(v, d) { if (!isSuspended) await next.saveAll(v, d) },\n }\n\n return wrapped\n }\n}\n"],"mappings":"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","names":["store","s"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SealedRecordNotEnabledError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VSUQI5Q6.js";
|
|
4
4
|
|
|
5
5
|
// src/with-audit/sealed-record/strategy.ts
|
|
6
6
|
var NO_SEALED_RECORD = {
|
|
@@ -18,4 +18,4 @@ var NO_SEALED_RECORD = {
|
|
|
18
18
|
export {
|
|
19
19
|
NO_SEALED_RECORD
|
|
20
20
|
};
|
|
21
|
-
//# sourceMappingURL=chunk-
|
|
21
|
+
//# sourceMappingURL=chunk-GRS2HIV6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-audit/sealed-record/strategy.ts"],"sourcesContent":["/**\n * Sealed-record (grantor-side) capability strategy — the three on-demand vault\n * methods the record-scoped CEK sealing surface routes through. The active\n * engine ({@link withSealedRecord}) dynamically imports the `record-keys`\n * grantor cores (keeping them reachable only via opt-in); {@link NO_SEALED_RECORD}\n * throws. The vault always assembles the per-call {@link SealingContext} and\n * delegates here, so an un-opted-in caller hits `NO_SEALED_RECORD`'s throw.\n *\n * Note: the recipient-host opener (`openSealedRecord`) is a pure decrypt\n * function exported from this subpath and is NOT gated — it runs on a remote\n * host with no `createNoydb` instance (the same way the attestation verifier\n * stays ungated). Only the vault-side grantor operations are the capability.\n * @internal\n */\nimport type { SealingContext } from '../../capsule/index.js'\nimport type { RecipientSealer } from '../../with-party/team/managed-secret.js'\nimport { SealedRecordNotEnabledError } from '../../kernel/errors.js'\n\nexport interface SealedRecordStrategy {\n sealRecordToHost(\n ctx: SealingContext,\n collection: string,\n id: string,\n hostSealer: RecipientSealer,\n opts: { expiresAt: string },\n ): Promise<{ pid: string; envelopeKey: string }>\n revokeSealedRecord(\n ctx: SealingContext,\n collection: string,\n id: string,\n pid: string,\n opts?: { hard?: boolean },\n ): Promise<void>\n rotateRecordCek(ctx: SealingContext, collection: string, id: string): Promise<void>\n}\n\n/**\n * No-op stub — the floor default. Every grantor method throws\n * {@link SealedRecordNotEnabledError}; opt in with\n * `sealedRecordStrategy: withSealedRecord()` in createNoydb. @internal\n */\nexport const NO_SEALED_RECORD: SealedRecordStrategy = {\n async sealRecordToHost() { throw new SealedRecordNotEnabledError() },\n async revokeSealedRecord() { throw new SealedRecordNotEnabledError() },\n async rotateRecordCek() { throw new SealedRecordNotEnabledError() },\n}\n"],"mappings":";;;;;AAyCO,IAAM,mBAAyC;AAAA,EACpD,MAAM,mBAAmB;AAAE,UAAM,IAAI,4BAA4B;AAAA,EAAE;AAAA,EACnE,MAAM,qBAAqB;AAAE,UAAM,IAAI,4BAA4B;AAAA,EAAE;AAAA,EACrE,MAAM,kBAAkB;AAAE,UAAM,IAAI,4BAA4B;AAAA,EAAE;AACpE;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-audit/consent/strategy.ts"],"sourcesContent":["/**\n * Strategy seam for the optional consent-audit service. Core\n * imports `ConsentStrategy` as a TYPE-ONLY symbol and `NO_CONSENT`\n * as a tiny runtime stub.\n *\n * `writeConsentEntry` / `loadConsentEntries` are only reachable from\n * `withConsent()` in `./active.ts`, exported through\n * `@noy-db/hub/consent`. Applications without a consent scope ship\n * none of the ~194 LOC.\n *\n * @internal\n */\n\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { EnclaveKey } from '../../capsule/index.js'\nimport type {\n ConsentAuditEntry,\n ConsentAuditFilter,\n} from './consent.js'\n\n/**\n * @internal\n */\nexport interface ConsentStrategy {\n /**\n * Record one consent audit entry. No-op under NO_CONSENT.\n */\n write(\n adapter: NoydbStore,\n vault: string,\n encrypted: boolean,\n entry: Omit<ConsentAuditEntry, 'id' | 'timestamp'>,\n getDEK: (collectionName: string) => Promise<EnclaveKey>,\n ): Promise<void>\n\n /**\n * Read filtered consent entries. Returns `[]` under NO_CONSENT.\n */\n read(\n adapter: NoydbStore,\n vault: string,\n encrypted: boolean,\n getDEK: (collectionName: string) => Promise<EnclaveKey>,\n filter?: ConsentAuditFilter,\n ): Promise<ConsentAuditEntry[]>\n}\n\n/**\n * No-consent stub. `write` is a no-op (returns a resolved promise);\n * `read` returns `[]`. Consumers get a consistent API surface without\n * pulling the consent module into the bundle.\n *\n * @internal\n */\nexport const NO_CONSENT: ConsentStrategy = {\n async write() {},\n async read() { return [] },\n}\n"],"mappings":";AAsDO,IAAM,aAA8B;AAAA,EACzC,MAAM,QAAQ;AAAA,EAAC;AAAA,EACf,MAAM,OAAO;AAAE,WAAO,CAAC;AAAA,EAAE;AAC3B;","names":[]}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildRecordAad,
|
|
3
|
+
buildRecordEnvelope,
|
|
4
|
+
encrypt,
|
|
3
5
|
openEnvelopeJson
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
buildRecordEnvelope
|
|
7
|
-
} from "./chunk-SEE332EV.js";
|
|
8
|
-
import {
|
|
9
|
-
encrypt
|
|
10
|
-
} from "./chunk-UYB234ZR.js";
|
|
6
|
+
} from "./chunk-4J3MNVVX.js";
|
|
11
7
|
|
|
12
8
|
// src/with-shape/persisted-schemas/storage.ts
|
|
13
9
|
var SCHEMAS_COLLECTION = "_schemas";
|
|
@@ -45,4 +41,4 @@ export {
|
|
|
45
41
|
loadPersistedSchema,
|
|
46
42
|
savePersistedSchema
|
|
47
43
|
};
|
|
48
|
-
//# sourceMappingURL=chunk-
|
|
44
|
+
//# sourceMappingURL=chunk-H3YM66EI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-shape/persisted-schemas/storage.ts"],"sourcesContent":["/**\n * Read / write the per-collection persisted-schema envelope. Mirrors the\n * standard noy-db record envelope shape and is **AES-GCM encrypted with\n * the collection's DEK** — the schema body (field names, enum values,\n * constraints) is sensitive metadata, so it gets the same encryption\n * envelope as the records it describes.\n *\n * Storage layout:\n *\n * <vault>/_schemas/<collection> → EncryptedEnvelope\n *\n * The DEK passed to {@link savePersistedSchema} / {@link loadPersistedSchema}\n * is the same key the collection uses for its records.\n *\n * @module\n */\n\nimport { buildRecordAad, buildRecordEnvelope, encrypt, openEnvelopeJson, type EnclaveKey } from '../../capsule/index.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { PersistedSchemaEnvelope } from './types.js'\n\n/** Reserved collection name where persisted schemas live. */\nexport const SCHEMAS_COLLECTION = '_schemas' as const\n\n/**\n * A decrypted persisted-schema read paired with the wrapping envelope's\n * `_v`. The version is the optimistic-concurrency token: pass it back to\n * {@link savePersistedSchema} as `expectedVersion` to make the write a CAS\n * that rejects a stale put (see #583 — the `_schemas` record is shared by\n * the JSON-Schema writer and the classified-marker writer).\n */\nexport interface PersistedSchemaEntry {\n /** Wrapping envelope `_v` (0 when no record exists yet). */\n readonly version: number\n /** Decrypted payload, or `undefined` when absent/corrupt/wrong-DEK. */\n readonly payload: PersistedSchemaEnvelope | undefined\n}\n\n/**\n * Read the persisted-schema envelope for one collection together with its\n * wrapping `_v`. The version reflects the raw stored envelope even when the\n * payload cannot be decrypted/parsed (so a CAS still guards a corrupt record\n * rather than silently clobbering it).\n */\nexport async function loadPersistedSchemaEntry(\n store: NoydbStore,\n vault: string,\n collection: string,\n dek: EnclaveKey,\n): Promise<PersistedSchemaEntry> {\n const envelope = await store.get(vault, SCHEMAS_COLLECTION, collection)\n if (!envelope) return { version: 0, payload: undefined }\n const version = envelope._v\n try {\n const plaintext = await openEnvelopeJson({ collection: SCHEMAS_COLLECTION, id: collection }, envelope, dek)\n const parsed = JSON.parse(plaintext) as PersistedSchemaEnvelope\n if (parsed._noydb_schema !== 1) return { version, payload: undefined }\n return { version, payload: parsed }\n } catch {\n return { version, payload: undefined }\n }\n}\n\n/**\n * Read and decrypt the persisted-schema envelope for one collection.\n * Returns `undefined` when no envelope has been written or when decryption\n * fails (e.g. wrong DEK passed). Tolerates corrupted records — JSON parse\n * failures surface as `undefined`, mirroring `_meta/handle`'s contract.\n */\nexport async function loadPersistedSchema(\n store: NoydbStore,\n vault: string,\n collection: string,\n dek: EnclaveKey,\n): Promise<PersistedSchemaEnvelope | undefined> {\n return (await loadPersistedSchemaEntry(store, vault, collection, dek)).payload\n}\n\n/**\n * Encrypt and persist a schema envelope for one collection. Always\n * overwrites any prior write (callers gate on hash equality before calling\n * to avoid no-op writes).\n *\n * Pass `expectedVersion` — the `version` from {@link loadPersistedSchemaEntry}\n * captured at load time — to make the write an optimistic CAS: on a store with\n * `casAtomic`, `store.put` throws {@link ConflictError} when the record was\n * bumped concurrently, so a lost update on the shared `_schemas` record is\n * rejected instead of silently clobbering the other writer's field (#583).\n * When omitted, keeps the legacy unconditional last-writer-wins behaviour.\n */\nexport async function savePersistedSchema(\n store: NoydbStore,\n vault: string,\n collection: string,\n dek: EnclaveKey,\n payload: PersistedSchemaEnvelope,\n expectedVersion?: number,\n): Promise<void> {\n const json = JSON.stringify(payload)\n const baseVersion = expectedVersion ?? (await store.get(vault, SCHEMAS_COLLECTION, collection))?._v ?? 0\n const identity = { collection: SCHEMAS_COLLECTION, id: collection, version: baseVersion + 1 }\n const { iv, data } = await encrypt(json, dek, buildRecordAad(identity))\n const env = buildRecordEnvelope(\n identity,\n { iv, data },\n )\n await store.put(vault, SCHEMAS_COLLECTION, collection, env, expectedVersion)\n}\n"],"mappings":";;;;;;;;AAsBO,IAAM,qBAAqB;AAsBlC,eAAsB,yBACpB,OACA,OACA,YACA,KAC+B;AAC/B,QAAM,WAAW,MAAM,MAAM,IAAI,OAAO,oBAAoB,UAAU;AACtE,MAAI,CAAC,SAAU,QAAO,EAAE,SAAS,GAAG,SAAS,OAAU;AACvD,QAAM,UAAU,SAAS;AACzB,MAAI;AACF,UAAM,YAAY,MAAM,iBAAiB,EAAE,YAAY,oBAAoB,IAAI,WAAW,GAAG,UAAU,GAAG;AAC1G,UAAM,SAAS,KAAK,MAAM,SAAS;AACnC,QAAI,OAAO,kBAAkB,EAAG,QAAO,EAAE,SAAS,SAAS,OAAU;AACrE,WAAO,EAAE,SAAS,SAAS,OAAO;AAAA,EACpC,QAAQ;AACN,WAAO,EAAE,SAAS,SAAS,OAAU;AAAA,EACvC;AACF;AAQA,eAAsB,oBACpB,OACA,OACA,YACA,KAC8C;AAC9C,UAAQ,MAAM,yBAAyB,OAAO,OAAO,YAAY,GAAG,GAAG;AACzE;AAcA,eAAsB,oBACpB,OACA,OACA,YACA,KACA,SACA,iBACe;AACf,QAAM,OAAO,KAAK,UAAU,OAAO;AACnC,QAAM,cAAc,oBAAoB,MAAM,MAAM,IAAI,OAAO,oBAAoB,UAAU,IAAI,MAAM;AACvG,QAAM,WAAW,EAAE,YAAY,oBAAoB,IAAI,YAAY,SAAS,cAAc,EAAE;AAC5F,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,QAAQ,MAAM,KAAK,eAAe,QAAQ,CAAC;AACtE,QAAM,MAAM;AAAA,IACV;AAAA,IACA,EAAE,IAAI,KAAK;AAAA,EACb;AACA,QAAM,MAAM,IAAI,OAAO,oBAAoB,YAAY,KAAK,eAAe;AAC7E;","names":[]}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PERIODS_COLLECTION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6WS35E6X.js";
|
|
4
4
|
import {
|
|
5
5
|
canonicalJson,
|
|
6
6
|
sha256Hex
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2WJKJZSO.js";
|
|
8
8
|
import {
|
|
9
9
|
isDeleteMarker
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-4J3MNVVX.js";
|
|
11
11
|
import {
|
|
12
12
|
PeriodChainError,
|
|
13
13
|
PeriodClosedError,
|
|
14
14
|
ValidationError
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-VSUQI5Q6.js";
|
|
16
16
|
|
|
17
17
|
// src/with-audit/periods/periods.ts
|
|
18
18
|
function resolveReopenState(events) {
|
|
@@ -208,7 +208,7 @@ function validatePeriodName(name, existing, partition) {
|
|
|
208
208
|
}
|
|
209
209
|
async function appendPeriodLedgerEntry(ledger, actor, envelope, name, collection = PERIODS_COLLECTION) {
|
|
210
210
|
if (!ledger) return;
|
|
211
|
-
const { envelopePayloadHash } = await import("./ledger-
|
|
211
|
+
const { envelopePayloadHash } = await import("./ledger-MSJGWKGW.js");
|
|
212
212
|
await ledger.append({
|
|
213
213
|
op: "put",
|
|
214
214
|
collection,
|
|
@@ -284,4 +284,4 @@ export {
|
|
|
284
284
|
purgeMarkersOn,
|
|
285
285
|
withPeriods
|
|
286
286
|
};
|
|
287
|
-
//# sourceMappingURL=chunk-
|
|
287
|
+
//# sourceMappingURL=chunk-IVMFIKNA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-audit/periods/periods.ts","../src/with-audit/periods/active.ts","../src/with-audit/periods/strategy.ts"],"sourcesContent":["/**\n * Accounting-period closure + opening.\n *\n * A closed period seals every record whose envelope `_ts` is at or\n * before the period's `endDate`: further writes (`put` / `delete`)\n * against such records throw {@link PeriodClosedError}. The period\n * itself is stored as a record in the reserved `_periods` collection\n * and written through the normal ledger-instrumented path, so every\n * closure appends a tamper-evident entry to the vault's hash chain.\n *\n * ## Closure model\n *\n * ```\n * vault.closePeriod({ name: 'FY2026-Q1', endDate: '2026-03-31' })\n * └─► PeriodRecord written to _periods/<name>\n * ├─ priorPeriodName / priorPeriodHash — chain to last close\n * ├─ closedAt / closedBy — provenance\n * └─ normal ledger append fires (LedgerStore.append)\n * ```\n *\n * Enforcement (`assertTsWritable`) is vault-local: the Vault caches\n * the list of closed periods on first read and consults that cache in\n * the `Collection.put` / `.delete` path via the `periodGuard` hook.\n *\n * ## Opening model\n *\n * ```\n * vault.openPeriod({\n * name: 'FY2026-Q2',\n * startDate: '2026-04-01',\n * fromPeriod: 'FY2026-Q1',\n * carryForward: async (priorView) => Record<string, Record<string, unknown>>,\n * })\n * ```\n *\n * `carryForward` receives a read-only `VaultInstant` anchored at the\n * prior period's `endDate` (built via `vault.at(endDate)`) so the\n * callback can compute closing aggregates from the sealed state. The\n * returned `{ [collectionName]: { [id]: record } }` map is written\n * before the new `PeriodRecord` lands — opening balances materialise\n * as normal records with fresh timestamps that fall outside every\n * closed period.\n *\n * ## Freeze\n *\n * ```\n * vault.freezePeriod('FY2026-Q1')\n * └─► physically purges delete markers whose write-time falls inside\n * the closed period's window (via the #589 `_purgeDeleteMarkers`\n * seam), then records the fact:\n * ├─ PeriodFreezeRecord written to _period_freezes/<name>\n * └─ normal ledger append fires (LedgerStore.append)\n * ```\n *\n * The chained `_periods/<name>` record is never mutated — `frozenAt` /\n * `frozenBy` / `purgedMarkerCount` are merged onto the returned\n * `PeriodRecord` at read time from the companion, so a tamper with the\n * freeze can never break the inter-period hash chain. Freezing is\n * terminal (a closed period, once frozen, stays frozen) and idempotent\n * (a second call is a no-op that returns the same merged record without\n * re-purging or re-appending a ledger entry). Freeze does NOT purge\n * forget-tombstones (GDPR crypto-shred erasure evidence), `_history`\n * versions, or live records — the delete-markers-only seam leaves all\n * three untouched by construction.\n *\n * Freeze purges the LOCAL adapter only. On a synced vault, markers already\n * pushed to sync targets survive there, and a later pull re-imports them\n * (benign — they still read deleted, but the space isn't reclaimed). A\n * re-imported marker keeps its original `_ts` (inside the already-frozen\n * period's window), so — like any late-booked delete — it is reclaimed by\n * the NEXT period's freeze, whose window covers it; freeze stays terminal\n * and does NOT re-purge an already-frozen period (#611). Sweeping the sync\n * targets themselves is a cross-target-purge concern deferred to the\n * cold-archival spec. Purging re-opens the #589 resurrection window for a\n * peer offline since before the cutoff, which is why the closed period is\n * the operator-asserted safe-point that gates the call.\n *\n * A period whose purge window has not fully elapsed cannot be frozen —\n * `freezePeriod` throws rather than purge markers for deletes that may not\n * have converged yet (#610).\n *\n * ## Archive\n *\n * ```\n * vault.archivePeriod('FY2026-Q1')\n * └─► relocates the closed period's in-window records (those with\n * `_ts < periodExclusiveUpperBound(endDate)`) from the hot store to\n * the configured cold tier (routeStore's `cold` route), then records:\n * ├─ PeriodArchiveRecord written to _period_archives/<name>\n * └─ a ledger entry attributed to _period_archives\n * ```\n *\n * Archival is NON-DESTRUCTIVE: routeStore reads fall through to the cold\n * tier on a hot miss, so an archived record still reads normally. It is\n * therefore gated only on `closed` (not `frozen`) — it does not re-open the\n * #589 resurrection window and needs no convergence safe-point. Freeze\n * (purge markers) and archive (relocate records) are independent and compose\n * in either order. Like freeze, archival keeps the chained `_periods/<name>`\n * record byte-immutable (state lives in the companion) and is idempotent.\n *\n * Bounds by write-time `_ts`, NOT business date: the store tier sees only\n * encrypted envelopes. A record with an in-period business date but a later\n * `_ts` (late-booked) archives at the NEXT period's archive — the same rule\n * freeze uses for late-booked delete markers. Requires a `routeStore` with a\n * cold route (`age: { cold }`); throws otherwise.\n *\n * Read cost: with `withLazy()` (per-id reads) archived records are truly\n * cold — fetched from cold only on access. In the default hydrated mode,\n * `loadAll` merges the cold store, so archived records still load into RAM\n * on vault open (hot-tier STORAGE is reclaimed; RAM is not). Summaries\n * (`_`-prefixed) always stay hot.\n *\n * ## Target-purge\n *\n * ```\n * vault.purgePeriodTargets('FY2026-Q1')\n * └─► sweeps delete markers (`_ts < periodExclusiveUpperBound(endDate)`) off\n * the vault's PUSH-ONLY sync targets (backup/archive roles), then records:\n * ├─ PeriodTargetPurgeRecord written to _period_target_purges/<name>\n * └─ a ledger entry attributed to _period_target_purges\n * ```\n *\n * Extends freeze's local marker purge to the vault's own remote sinks.\n * `sync-peer` (bidirectional) targets are SKIPPED: purging a marker there\n * re-opens the #589 resurrection window for a client offline before the\n * cutoff, an assertion no single vault can verify. Backup/archive targets are\n * push-only — never pulled from into convergence — so sweeping their markers\n * is safe. Requires the period be frozen first (closed → frozen →\n * target-purged) so the local safe-point is already established. Idempotent\n * once run; a vault with no push-only targets writes no companion and is\n * re-runnable (so a target added later is still swept). Single-vault only —\n * fleet-wide purge across sovereign vaults is klum's concern over\n * `@noy-db/hub/cargo`.\n *\n * ## Not covered\n *\n * - Partial re-opening of a closed period. If an auditor needs to\n * make a correction inside a sealed period, the sanctioned path is\n * a compensating entry in the NEW period, not an unlock of the\n * old one.\n * - Automatic period rollover. `closePeriod` / `openPeriod` are\n * deliberately explicit operator calls so the caller decides when\n * the boundary lands.\n *\n * @module\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport type { LedgerStore } from '../../with-commit/history/ledger/index.js'\nimport { sha256Hex, canonicalJson } from '../../with-commit/history/ledger/index.js'\nimport { isDeleteMarker } from '../../capsule/index.js'\nimport { PeriodChainError, PeriodClosedError, ValidationError } from '../../kernel/errors.js'\n\n// The reserved collection names + `periodExclusiveUpperBound` moved to the\n// dependency-light `window.ts` so the sync engine's period-scoped pull (#807)\n// can import them without this module's ledger imports; re-exported here so\n// every existing import path keeps working.\nexport {\n PERIODS_COLLECTION,\n PERIOD_FREEZES_COLLECTION,\n PERIOD_ARCHIVES_COLLECTION,\n PERIOD_TARGET_PURGES_COLLECTION,\n PERIOD_REOPENS_COLLECTION,\n periodExclusiveUpperBound,\n} from './window.js'\nimport { PERIODS_COLLECTION } from './window.js'\n\n/**\n * Companion record recording that a closed period was frozen (its delete\n * markers physically purged). Stored in {@link PERIOD_FREEZES_COLLECTION},\n * keyed by period name — kept OFF the hash-chained `_periods/<name>` record so\n * freeze never alters the inter-period chain.\n */\nexport interface PeriodFreezeRecord {\n readonly period: string\n readonly frozenAt: string\n readonly frozenBy: string\n readonly purgedMarkerCount: number\n}\n\n/**\n * Companion record noting that a closed period was cold-archived (its\n * in-window records physically relocated hot → cold). Stored in\n * {@link PERIOD_ARCHIVES_COLLECTION}, keyed by period name — kept OFF the\n * hash-chained `_periods/<name>` record so archive never alters the chain.\n */\nexport interface PeriodArchiveRecord {\n readonly period: string\n readonly archivedAt: string\n readonly archivedBy: string\n readonly archivedRecordCount: number\n}\n\n/** Per-target count of delete markers purged off one push-only sync target (#615). */\nexport interface TargetPurgeCount {\n readonly label?: string\n readonly role: 'backup' | 'archive'\n readonly purgedCount: number\n}\n\n/**\n * One entry in a period's append-only reopen/reclose log (#1022).\n *\n * Real accounting close is a three-state lifecycle — open / closed / reopened\n * — not a one-way door. A month gets closed, a missing invoice arrives or a\n * filing is rejected, the month is reopened, corrected, and reclosed. The audit\n * value is not the ability to write again; it is the chain being able to say\n * *closed at T1, reopened at T2 by U, reclosed at T3*.\n */\nexport interface PeriodReopenEvent {\n readonly op: 'reopen' | 'reclose'\n /** ISO timestamp the event was recorded. */\n readonly at: string\n /** userId of the keyring that performed it. */\n readonly by: string\n /**\n * `reopen` only — ISO instant after which the period re-seals on its own,\n * with nobody acting. Absent means the window stays open until an explicit\n * `reclosePeriod`.\n */\n readonly until?: string\n /** Free-text justification, carried verbatim into the audit trail. */\n readonly reason?: string\n}\n\n/**\n * Companion holding a period's reopen/reclose history (#1022). Stored in\n * {@link PERIOD_REOPENS_COLLECTION}, keyed by the period's storage key — kept\n * OFF the hash-chained `_periods/<name>` record for the same reason freeze and\n * archive are: reopening must never rewrite the close, or the chain that proves\n * the close happened is the very thing the reopen destroys.\n *\n * `events` is APPEND-ONLY. Where the other companions are single-shot and\n * idempotent, this one accumulates, because the cycle repeats.\n */\nexport interface PeriodReopenRecord {\n readonly period: string\n readonly partition?: PeriodPartition\n readonly events: readonly PeriodReopenEvent[]\n}\n\n/**\n * Collapse an append-only reopen log into the return-only fields merged onto a\n * {@link PeriodRecord} on read.\n *\n * Expiry is deliberately NOT resolved here: `reopenedUntil` is carried through\n * verbatim and compared against the clock at write-guard time, so a bounded\n * window re-seals on its own without anything having to run.\n *\n * @internal\n */\nexport function resolveReopenState(events: readonly PeriodReopenEvent[]): {\n reopenedAt?: string\n reopenedBy?: string\n reopenedUntil?: string\n reopenReason?: string\n reclosedAt?: string\n reopenCount: number\n} {\n let lastReopen: PeriodReopenEvent | undefined\n let reclosedAfter: string | undefined\n let reopenCount = 0\n for (const e of events) {\n if (e.op === 'reopen') {\n lastReopen = e\n reclosedAfter = undefined\n reopenCount++\n } else if (lastReopen !== undefined) {\n reclosedAfter = e.at\n }\n }\n if (!lastReopen) return { reopenCount }\n return {\n reopenedAt: lastReopen.at,\n reopenedBy: lastReopen.by,\n ...(lastReopen.until !== undefined && { reopenedUntil: lastReopen.until }),\n ...(lastReopen.reason !== undefined && { reopenReason: lastReopen.reason }),\n ...(reclosedAfter !== undefined && { reclosedAt: reclosedAfter }),\n reopenCount,\n }\n}\n\n/**\n * Is this period writable right now on account of a reopen? (#1022)\n *\n * Three ways to be sealed again: never reopened, explicitly reclosed after the\n * last reopen, or a bounded window that has elapsed. The clock is read by the\n * caller and passed in, so the guard and any diagnostic agree on one instant.\n *\n * @internal\n */\nexport function isEffectivelyReopened(period: PeriodRecord, nowIso: string): boolean {\n if (period.reopenedAt === undefined) return false\n if (period.reclosedAt !== undefined && period.reclosedAt >= period.reopenedAt) return false\n if (period.reopenedUntil !== undefined && nowIso > period.reopenedUntil) return false\n return true\n}\n\n/**\n * Companion record noting that a closed+frozen period's delete markers were\n * swept off the vault's push-only sync targets (#615). Stored in\n * {@link PERIOD_TARGET_PURGES_COLLECTION}, keyed by period name — kept OFF the\n * hash-chained `_periods/<name>` record so target-purge never alters the chain.\n */\nexport interface PeriodTargetPurgeRecord {\n readonly period: string\n readonly purgedAt: string\n readonly purgedBy: string\n readonly targets: readonly TargetPurgeCount[]\n}\n\n/**\n * Scope tuple for a period timeline (#1005).\n *\n * Identical in shape and semantics to `SequenceOptions.partition`: a\n * partitioned timeline is always disjoint from any unpartitioned one, and from\n * every other tuple. `['acme', 'vat']` and `['acme', 'wht']` are two\n * independent close calendars for the same subject — which is the whole point,\n * since sub-ledgers for one legal entity and one month routinely close on\n * different statutory deadlines.\n */\nexport type PeriodPartition = readonly (string | number)[]\n\n/**\n * Resolve the `_periods` storage key for a (name, partition) pair.\n *\n * Deliberately the same encoding as `resolveSequenceKey`: `name` verbatim when\n * unpartitioned, else `${name}\\x00${parts}` with each component\n * `encodeURIComponent`d and `'/'`-joined. The null-byte separator cannot occur\n * in a period name, so a partitioned key never collides with an unpartitioned\n * one; URI-encoding keeps `['a/b']` distinct from `['a','b']`.\n *\n * @throws {ValidationError} on an empty component or a non-finite number.\n * @internal\n */\nexport function resolvePeriodKey(name: string, partition?: PeriodPartition): string {\n if (!partition || partition.length === 0) return name\n const parts = partition.map((p) => {\n if (typeof p === 'number' && !Number.isFinite(p)) {\n throw new ValidationError(`period partition component must be a finite number, got ${p}`)\n }\n const s = String(p)\n if (s === '') {\n throw new ValidationError('period partition component must not be empty')\n }\n return encodeURIComponent(s)\n })\n return `${name}\\x00${parts.join('/')}`\n}\n\n/**\n * Do two partitions denote the same timeline? Absent and empty both mean \"the\n * unpartitioned timeline\", so they compare equal.\n *\n * @internal\n */\nexport function samePartition(a?: PeriodPartition, b?: PeriodPartition): boolean {\n const x = a ?? []\n const y = b ?? []\n if (x.length !== y.length) return false\n return x.every((v, i) => String(v) === String(y[i]))\n}\n\n/**\n * Resolves a record to the timeline that governs it. Supplied by\n * `withPeriods({ subjects })`; returns `undefined` for any collection with no\n * mapping, which is what keeps an unconfigured vault on the single vault-wide\n * timeline it has always had.\n *\n * @internal\n */\nexport type PartitionResolver = (\n collection: string,\n record: Record<string, unknown>,\n) => PeriodPartition | undefined\n\n/**\n * Stored record for one closed or opened accounting period. One entry\n * per period, keyed by `name` in the reserved `_periods` collection.\n *\n * The hash chain between periods is computed at read time by\n * `loadPeriods()` — each record carries the name + hash of its\n * predecessor so a tamper with any period's record breaks the chain\n * into the next one, the same way the ledger's `prevHash` works.\n */\nexport interface PeriodRecord {\n /**\n * Human-readable name (e.g., `'FY2026-Q1'`). Unique per PARTITION — two\n * timelines may each carry a `'2026-06'`, which is the normal case when one\n * vault serves several subjects (#1005). Unique per vault when unpartitioned.\n */\n readonly name: string\n /**\n * The timeline this period belongs to. Absent = the vault-wide timeline.\n * Two periods with the same `name` and different `partition` are unrelated:\n * separate hash chains, separate close state, and the write guard applies\n * each only to records that resolve to its own tuple.\n */\n readonly partition?: PeriodPartition\n /**\n * Role discriminator. A period is `'closed'` from the moment its\n * `closedAt` is recorded; `'opened'` marks a period whose opening\n * entries have been carried forward via {@link openPeriod}. Many\n * workflows will produce one opened period per closed period (the\n * opened one is the SUCCESSOR — its `startDate` equals the prior\n * `endDate + 1 day`).\n */\n readonly kind: 'closed' | 'opened'\n /** ISO date — inclusive upper bound for records belonging to this period. */\n readonly endDate: string\n /** ISO date — lower bound (present on opened periods only). */\n readonly startDate?: string\n /**\n * Record field carrying the business date (e.g. `'date'` on an\n * invoice, `'paidAt'` on a payment). The guard compares\n * `record[dateField]` against `endDate` — NOT the envelope `_ts`.\n * Accounting entries booked late (business date `2026-01-15`,\n * write-time `2026-04-22`) still get sealed when Q1 closes at\n * `2026-03-31` because the comparison uses the business date.\n *\n * Optional for backwards compat. When absent, the guard falls back\n * to envelope `_ts` — that's a write-time seal, appropriate for\n * content that doesn't carry a logical business date (e.g. system\n * settings) but almost never right for accounting ledgers.\n */\n readonly dateField?: string\n /** ISO timestamp recorded at `closePeriod()` / `openPeriod()` call time. */\n readonly closedAt: string\n /** userId of the keyring that invoked the close/open. */\n readonly closedBy: string\n /** Name of the prior period this one chains to, if any. */\n readonly priorPeriodName?: string\n /** sha256(canonicalJson(priorPeriod)) — empty for the first period. */\n readonly priorPeriodHash: string\n /**\n * Opened periods only — the names of the collections whose\n * carry-forward aggregates were written by {@link openPeriod}.\n * Recorded for auditability so a future `verifyPeriodChain()` can\n * cross-check the opening balances against the closing snapshot.\n */\n readonly openingCollections?: readonly string[]\n /** #604 return-only — merged from the `_period_freezes/<name>` companion on\n * read; NEVER written into the stored `_periods/<name>` record (would break\n * the hash chain). Absent = not yet frozen. */\n readonly frozenAt?: string\n readonly frozenBy?: string\n readonly purgedMarkerCount?: number\n /** #613 return-only — merged from the `_period_archives/<name>` companion on\n * read; NEVER written into the stored `_periods/<name>` record. Absent = not\n * yet archived. */\n readonly archivedAt?: string\n readonly archivedBy?: string\n readonly archivedRecordCount?: number\n /** #615 return-only — merged from the `_period_target_purges/<name>` companion\n * on read; NEVER written into the stored `_periods/<name>` record. Absent =\n * target-purge not yet run (or the vault has no push-only targets). */\n readonly targetsPurgedAt?: string\n readonly targetsPurgedBy?: string\n readonly targetsPurged?: readonly TargetPurgeCount[]\n /** #1022 return-only — collapsed from the `_period_reopens/<key>` append-only\n * log on read; NEVER written into the stored `_periods/<name>` record, so a\n * reopen cannot disturb the inter-period hash chain. Absent = never reopened.\n * `reclosedAt` present (and >= `reopenedAt`) means the window was closed\n * again explicitly; `reopenedUntil` in the past means it lapsed on its own.\n * Use {@link isEffectivelyReopened} rather than reading these directly. */\n readonly reopenedAt?: string\n readonly reopenedBy?: string\n readonly reopenedUntil?: string\n readonly reopenReason?: string\n readonly reclosedAt?: string\n /** How many times this period has been reopened, ever. */\n readonly reopenCount?: number\n}\n\n/** Options for `vault.closePeriod()`. */\nexport interface ClosePeriodOptions {\n /** Human-readable name. Must not collide with an existing period. */\n readonly name: string\n /**\n * Inclusive upper cutoff. With a `dateField`, a record is sealed when its\n * `record[dateField]` is at or before this ISO timestamp; a record with\n * NO value there (absent / `null`) carries no business date and belongs\n * to no period, and a value that is neither an ISO string nor a `Date`\n * is refused (#1455). Without a `dateField`, the envelope `_ts` of the\n * stored record is compared instead.\n *\n * ⚠️ String comparison is lexicographic: keep `endDate` and the field in\n * one ISO-8601 shape (`YYYY-MM-DD` against `YYYY-MM-DD`, or full\n * timestamps against full timestamps).\n *\n * ⛔ **Zero-padding is now ENFORCED on both sides (#1459), because that\n * warning was not enough.** `'2026-6-30'` names a real day and is refused\n * anyway: as text it sorts BELOW every June date, so the close would seal\n * nothing and report success — and symmetrically a record dated\n * `'2026-6-15'` sorted ABOVE `'2026-06-30'` and was written into a sealed\n * June. Both are `ValidationError` now, at the close and at the write,\n * rather than a silent pass or a `PeriodClosedError` that fired for a reason\n * unrelated to dates.\n */\n readonly endDate: string\n /**\n * Record field carrying the business date used for period\n * membership. Recommended for accounting workflows — e.g. an\n * invoice booked late (write-time after close) is still sealed\n * when its `invoice.date` falls inside the closed period.\n *\n * Omit to use envelope `_ts` (write-time seal). This fallback\n * rarely matches real-world accounting semantics; prefer passing\n * an explicit `dateField`.\n */\n readonly dateField?: string\n /**\n * Close only this timeline (#1005). Omit for the vault-wide timeline.\n *\n * ```ts\n * vault.closePeriod({\n * name: '2026-06', endDate: '2026-06-30', dateField: 'issuedAt',\n * partition: [clientId, 'vat'],\n * })\n * ```\n *\n * Which records the resulting seal applies to is decided by the\n * `subjects` map passed to `withPeriods()` — without one, no record ever\n * resolves to a partition and a partitioned close seals nothing.\n */\n readonly partition?: PeriodPartition\n}\n\n/** Options for `vault.openPeriod()`. */\nexport interface OpenPeriodOptions<TCollections = Record<string, Record<string, unknown>>> {\n /** Human-readable name for the new period. Must be unique. */\n readonly name: string\n /**\n * The timeline to open in. Must match the partition of `fromPeriod` — a\n * period cannot chain across timelines, since each carries its own hash\n * chain (#1005).\n */\n readonly partition?: PeriodPartition\n /** ISO lower bound of the new period (usually prior `endDate + 1 day`). */\n readonly startDate: string\n /**\n * Name of the prior CLOSED period this one chains from. The prior\n * period's record is verified to exist and to be `kind: 'closed'`;\n * its `endDate` is made available to the `carryForward` callback.\n */\n readonly fromPeriod: string\n /**\n * Receives a read-only facade over the vault's CURRENT state,\n * plus the prior period's `endDate`. Accounting semantics: after\n * a period closes, records with `record[dateField] <= endDate`\n * are frozen — current state equals closing state, so a caller\n * can compute closing balances by querying the live collection\n * with a `where('date', '<=', priorEndDate)` filter.\n *\n * Returns opening-balance records keyed by collection name.\n * Example:\n *\n * ```ts\n * carryForward: async (ctx) => {\n * const closing = await ctx.collection<Journal>('journal')\n * .query().where('date', '<=', ctx.priorEndDate).toArray()\n * const opening: Record<string, Journal> = {}\n * for (const entry of closing) {\n * opening[`OB-${entry.id}`] = { ...entry, date: '2026-04-01' }\n * }\n * return { journal: opening }\n * }\n * ```\n */\n readonly carryForward: (\n ctx: CarryForwardContext,\n ) => Promise<TCollections> | TCollections\n}\n\n/**\n * Context passed to `OpenPeriodOptions.carryForward`. Exposes a\n * read-only subset of the live vault (`collection(name).get/list`)\n * plus the prior period's `endDate` so business-date filters can\n * be built by the caller.\n *\n * Writes go via the return value, not via the facade — the\n * `collection()` here is deliberately restricted to reads.\n */\nexport interface CarryForwardContext {\n /** The prior period's `endDate` — the boundary of the closing snapshot. */\n readonly priorEndDate: string\n /** Read-only collection facade over current vault state. */\n collection<T = unknown>(name: string): ReadOnlyCollection<T>\n}\n\n/** Minimum read surface exposed to `carryForward`. */\nexport interface ReadOnlyCollection<T> {\n get(id: string): Promise<T | null>\n list(): Promise<T[]>\n}\n\n/**\n * Load every period record currently stored on the adapter.\n * Decrypting is the caller's responsibility (we return plain records\n * so the vault can use its own `_periods` DEK).\n *\n * @internal — called by Vault methods that need the closed-period\n * cache. Not part of the public API surface.\n */\nexport async function loadPeriods(\n adapter: NoydbStore,\n vault: string,\n decrypt: (id: string, envelope: EncryptedEnvelope) => Promise<PeriodRecord>,\n): Promise<PeriodRecord[]> {\n const ids = await adapter.list(vault, PERIODS_COLLECTION)\n const records: PeriodRecord[] = []\n for (const id of ids) {\n const env = await adapter.get(vault, PERIODS_COLLECTION, id)\n if (env) records.push(await decrypt(id, env))\n }\n // Stable order by closedAt so chain verification is reproducible.\n records.sort((a, b) => a.closedAt.localeCompare(b.closedAt))\n await verifyPeriodChain(records)\n return records\n}\n\n/**\n * The fields merged onto a {@link PeriodRecord} on READ and never written into\n * the stored `_periods/<key>` record. Stripped before hashing so an anchor\n * computed off a merged cache equals the one a cold loader recomputes off the\n * stored bytes (#1454 — `chainAnchor` used to hash the cache as it stood, so a\n * close following a reopen was anchored to a hash nothing could verify).\n */\nconst RETURN_ONLY_FIELDS: readonly string[] = [\n 'frozenAt', 'frozenBy', 'purgedMarkerCount',\n 'archivedAt', 'archivedBy', 'archivedRecordCount',\n 'targetsPurgedAt', 'targetsPurgedBy', 'targetsPurged',\n 'reopenedAt', 'reopenedBy', 'reopenedUntil', 'reopenReason', 'reclosedAt', 'reopenCount',\n]\n\n/** sha256(canonicalJson(record)) over the STORED shape of a period record. @internal */\nexport async function hashStoredPeriod(period: PeriodRecord): Promise<string> {\n const stored: Record<string, unknown> = { ...(period as unknown as Record<string, unknown>) }\n for (const f of RETURN_ONLY_FIELDS) delete stored[f]\n return sha256Hex(canonicalJson(stored))\n}\n\n/**\n * #1454 — every record's `priorPeriodName` / `priorPeriodHash` must resolve to\n * a stored predecessor in ITS timeline whose hash matches. Followed by name,\n * not by sort position: two closes within one millisecond sort arbitrarily,\n * and a deleted predecessor must read as a break, not as a new root.\n *\n * @internal\n */\nasync function verifyPeriodChain(records: readonly PeriodRecord[]): Promise<void> {\n const byKey = new Map<string, PeriodRecord>()\n for (const r of records) byKey.set(resolvePeriodKey(r.name, r.partition), r)\n for (const r of records) {\n if (r.priorPeriodName === undefined) {\n if (r.priorPeriodHash !== '') {\n throw new PeriodChainError(r.name, 'it names no predecessor but carries a non-empty priorPeriodHash', r.partition)\n }\n continue\n }\n const prior = byKey.get(resolvePeriodKey(r.priorPeriodName, r.partition))\n if (!prior) {\n throw new PeriodChainError(r.name, `its predecessor \"${r.priorPeriodName}\" is missing from the store`, r.partition)\n }\n if ((await hashStoredPeriod(prior)) !== r.priorPeriodHash) {\n throw new PeriodChainError(r.name, `its predecessor \"${r.priorPeriodName}\" no longer hashes to the recorded priorPeriodHash`, r.partition)\n }\n }\n}\n\n/**\n * Given the current ordered period list, pick the last entry that\n * belongs to the hash chain — used as the `priorPeriodHash` anchor\n * for the next closure/opening.\n *\n * @internal\n */\nexport async function chainAnchor(\n records: readonly PeriodRecord[],\n partition?: PeriodPartition,\n): Promise<{ priorPeriodName?: string; priorPeriodHash: string }> {\n // #1005 — each timeline carries its OWN chain. Anchoring a partitioned close\n // to whatever happened to be written last vault-wide would interleave\n // unrelated subjects into one chain, so verifying client A's June would\n // depend on client B never having closed in between.\n const inTimeline = records.filter((p) => samePartition(p.partition, partition))\n const last = inTimeline[inTimeline.length - 1]\n if (!last) return { priorPeriodHash: '' }\n const hash = await hashStoredPeriod(last)\n return { priorPeriodName: last.name, priorPeriodHash: hash }\n}\n\n/**\n * #1459 — the business-date strings that ORDER correctly against an `endDate`.\n *\n * ⭐ **This is a narrower question than \"is this a valid ISO date\", and the\n * difference is the whole point.** A period membership test is\n * `value <= endDate` on two STRINGS, so the accepted set is exactly the set\n * whose lexicographic order matches calendar order: zero-padded `YYYY-MM-DD`,\n * optionally followed by a time. `'2026-6-15'` names a real day and is refused\n * anyway, because as text it sorts ABOVE `'2026-06-30'` — a June entry waved\n * through a closed June on one missing zero.\n *\n * ⚠️ **Deliberately NOT `civilDateOf()` from `kernel/query/civil-date.ts`.**\n * That function accepts `\\d{1,2}` components on purpose: it resolves a value to\n * a real day and never compares it as text, so padding is irrelevant there and\n * load-bearing here. Two functions, two questions.\n *\n * ⭐ **A partial date is ACCEPTED — `'2026-01'` and `'2026'` are real business\n * dates, and refusing them was this fix's first draft.** The suite caught it:\n * `__tests__/1452-periods-gate-blob-writes.test.ts` seals a billing `cycle`\n * field carrying `'2026-01'`, which is exactly how a monthly cycle is written.\n * And it ORDERS correctly, which is the only thing this predicate is entitled\n * to ask: `'2026-01' <= '2026-01-31'` is true and `'2026-01' > '2025-12-31'`,\n * so a January cycle is sealed by a January close and by nothing earlier.\n *\n * So the rule is **zero-padded components**, not \"must name a day\" — padding\n * is what ordering needs; granularity is the caller's business.\n */\nconst ORDERABLE_ISO =\n /^(\\d{4})(?:-(\\d{2})(?:-(\\d{2})(?:[T ]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})?)?)?)?$/\n\n/** Days in `m` of `y`, proleptic Gregorian. */\nfunction daysInPeriodMonth(y: number, m: number): number {\n if (m === 2) return (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0 ? 29 : 28\n return m === 4 || m === 6 || m === 9 || m === 11 ? 30 : 31\n}\n\n/**\n * @internal #1459 — is `value` a business date that can be compared against an\n * `endDate` and give the calendar's answer?\n */\nexport function isOrderableBusinessDate(value: string): boolean {\n const m = ORDERABLE_ISO.exec(value)\n if (m === null) return false\n const year = Number(m[1])\n // Absent components are not wrong — see the header. Only a component that IS\n // present has to name something real: `'2026-02-30'` orders fine and means\n // nothing, and `'2026-13'` is not a month.\n if (m[2] === undefined) return true\n const month = Number(m[2])\n if (month < 1 || month > 12) return false\n if (m[3] === undefined) return true\n const day = Number(m[3])\n return day >= 1 && day <= daysInPeriodMonth(year, month)\n}\n\n/**\n * @internal #1459 — the refusal, phrased once so the guard and `closePeriod()`\n * cannot drift on what \"malformed\" means or on how they say it.\n */\nexport function malformedBusinessDate(where: string, value: string): ValidationError {\n return new ValidationError(\n `${where} is not an orderable business date: ${JSON.stringify(value)}. ` +\n `Period membership compares strings, so a date must be zero-padded ` +\n `ISO-8601 — \"YYYY\", \"YYYY-MM\" or \"YYYY-MM-DD\", optionally with a time ` +\n `(\"2026-06-30T23:59:59Z\"). Coarser is fine; unpadded is not. ` +\n `\"2026-6-15\" is refused although it names a real day: as text it sorts ` +\n `above \"2026-06-30\", so it would slip past a June close.`,\n )\n}\n\n/**\n * Throw `PeriodClosedError` if the record being touched falls within\n * any closed period.\n *\n * Per period, per side (existing record / incoming record):\n *\n * 1. If the period declares a `dateField`, the guard reads\n * `record[dateField]` on BOTH the existing (prior) record AND the\n * incoming (new) record, each under its OWN partition. Either comparing\n * `<= endDate` triggers the error — callers cannot slide a record into a\n * closed period by editing its date field.\n * #1455 — value shapes: a string is compared as-is (ISO-8601 assumed —\n * the comparison is lexicographic); a `Date` is compared as the ISO string\n * it will be stored as; `undefined` / `null` means the record carries no\n * business date and belongs to NO period; anything else is REFUSED with\n * `ValidationError` — a date the gate cannot evaluate is not a permission.\n * 2. If the period has no `dateField`, the guard falls back to the\n * envelope `_ts` of the existing record. Fresh inserts (no\n * existing envelope) pass.\n * 3. For a delete, only the existing side is checked.\n *\n * #1456 — a value is OWNED by the closed period in its timeline with the\n * smallest `endDate` at or after it. When that owner is effectively reopened,\n * no later close vetoes the value: reopening January frees January-dated\n * rows even with February and March closed, and February's rows stay sealed.\n *\n * @internal\n */\nexport function assertTsWritable(\n existing: { ts: string | null; record: Record<string, unknown> | null } | null,\n incomingRecord: Record<string, unknown> | null,\n closedPeriods: readonly PeriodRecord[],\n scope?: { collection: string; resolve?: PartitionResolver },\n): void {\n // #1005 — a period only governs records that resolve to ITS timeline. With no\n // resolver (the default `withPeriods()`), nothing resolves to a partition, so\n // every record sits on the vault-wide timeline exactly as before and a\n // partitioned period governs nothing.\n const partitionOf = (r: Record<string, unknown> | null): PeriodPartition | undefined => {\n if (!r || !scope?.resolve) return undefined\n return scope.resolve(scope.collection, r)\n }\n const existingRecord = existing?.record ?? null\n const existingPartition = partitionOf(existingRecord)\n const incomingPartition = partitionOf(incomingRecord)\n const existingTs = existing?.ts ?? null\n // One instant for the whole check, so a bounded reopen window cannot expire\n // between two periods in the same loop and seal a write half-way.\n const now = new Date().toISOString()\n const closed = closedPeriods.filter((p) => p.kind === 'closed')\n\n // #1456 — is the period that OWNS `value` (in `partition`'s timeline) reopened?\n const ownerReopened = (value: string, partition: PeriodPartition | undefined): boolean => {\n let owner: PeriodRecord | undefined\n for (const p of closed) {\n if (!samePartition(p.partition, partition)) continue\n if (value <= p.endDate && (owner === undefined || p.endDate < owner.endDate)) owner = p\n }\n return owner !== undefined && isEffectivelyReopened(owner, now)\n }\n\n // #1455 — one reading of a side's business date. `null` = no business date.\n const readDate = (p: PeriodRecord, r: Record<string, unknown>, side: 'existing' | 'incoming'): string | null => {\n const v = r[p.dateField!]\n if (v === undefined || v === null) return null\n if (typeof v === 'string') {\n // #1459 — validate the STRING, which #1455 did not. Before this, a\n // malformed value went straight into the comparison: `'hello'` was\n // written, and `'2026-6-15'` was written INTO a sealed June.\n if (!isOrderableBusinessDate(v)) throw malformedBusinessDate(`${side}[${p.dateField}]`, v)\n return v\n }\n if (v instanceof Date && !Number.isNaN(v.getTime())) return v.toISOString()\n throw new ValidationError(\n `Period guard cannot evaluate ${side}[${p.dateField}] (${v instanceof Date ? 'invalid Date' : typeof v}) ` +\n `against closed period \"${p.name}\" — a business date must be an ISO-8601 string or a Date. ` +\n `Refusing rather than admitting the record into a sealed timeline.`,\n )\n }\n\n for (const p of closed) {\n // #1022 — a reopened period is writable again. This is the ONLY thing a\n // reopen does: it withdraws the period's veto. It cannot grant a write that\n // some other gate forbids, because the guard bus ANDs every handler and\n // record-level guards are registered ahead of this one.\n if (isEffectivelyReopened(p, now)) continue\n if (p.dateField) {\n const checkRecord = (\n side: 'existing' | 'incoming',\n r: Record<string, unknown> | null,\n recordPartition: PeriodPartition | undefined,\n ): void => {\n if (!r) return\n // Both sides are checked under their OWN partition, which is what stops\n // a write from sliding a record either INTO or OUT OF a sealed\n // timeline by rewriting the fields the subject mapping reads.\n if (!samePartition(recordPartition, p.partition)) return\n const v = readDate(p, r, side)\n if (v !== null && v <= p.endDate && !ownerReopened(v, recordPartition)) {\n throw new PeriodClosedError(p.name, p.endDate, `${side}[${p.dateField}]=${v}`, { dateField: p.dateField!, side })\n }\n }\n checkRecord('existing', existingRecord, existingPartition)\n checkRecord('incoming', incomingRecord, incomingPartition)\n continue\n }\n // Fallback: write-time seal via envelope _ts. Scoped by the EXISTING\n // record's partition — `_ts` belongs to the stored envelope, so the\n // incoming side has no write-time of its own to compare.\n if (!samePartition(existingPartition, p.partition)) continue\n if (existingTs !== null && existingTs <= p.endDate && !ownerReopened(existingTs, existingPartition)) {\n throw new PeriodClosedError(p.name, p.endDate, existingTs, { side: 'existing' })\n }\n }\n}\n\n/**\n * Sanity-check a proposed period name + endDate against existing\n * records. Shared by closePeriod / openPeriod so the two pathways\n * produce identical diagnostics.\n *\n * @internal\n */\nexport function validatePeriodName(\n name: string,\n existing: readonly PeriodRecord[],\n partition?: PeriodPartition,\n): void {\n if (name.length === 0) {\n throw new ValidationError('Period name cannot be empty.')\n }\n // Validates the components as a side effect — an empty or non-finite\n // component must be rejected at the call, not encoded into a storage key.\n resolvePeriodKey(name, partition)\n // #1005 — uniqueness is per TIMELINE. `'2026-06'` in `['A','vat']` does not\n // collide with `'2026-06'` in `['B','vat']`.\n if (existing.some((p) => p.name === name && samePartition(p.partition, partition))) {\n const where = partition && partition.length > 0\n ? ` in partition [${partition.join(', ')}]`\n : ''\n throw new ValidationError(`Period \"${name}\" already exists${where}.`)\n }\n}\n\n/**\n * Wire a reserved-collection ledger append for a period record. The\n * period itself is stored via the adapter as an encrypted envelope;\n * the ledger entry is a normal `put` with the period's payloadHash,\n * so period closures inherit the chain's tamper-evidence.\n *\n * @internal\n */\nexport async function appendPeriodLedgerEntry(\n ledger: LedgerStore | null,\n actor: string,\n envelope: EncryptedEnvelope,\n name: string,\n collection: string = PERIODS_COLLECTION,\n): Promise<void> {\n if (!ledger) return\n const { envelopePayloadHash } = await import('../../with-commit/history/ledger/index.js')\n await ledger.append({\n op: 'put',\n collection,\n id: name,\n version: envelope._v,\n actor,\n payloadHash: await envelopePayloadHash(envelope),\n })\n}\n\n/**\n * @internal #615. Sweep delete markers with `_ts < before` off ANY store\n * (the vault's local adapter, or a push-only sync target). Returns the count\n * removed. Shared by `vault._purgeDeleteMarkers` (local) and\n * `vault._purgePeriodTargets` (push-only targets).\n */\nexport async function purgeMarkersOn(\n store: NoydbStore,\n vault: string,\n before: string,\n collections?: string[],\n): Promise<number> {\n const snapshot = await store.loadAll(vault)\n let removed = 0\n for (const [coll, records] of Object.entries(snapshot)) {\n if (collections && !collections.includes(coll)) continue\n for (const [id, env] of Object.entries(records)) {\n if (isDeleteMarker(env) && env._ts < before) {\n await store.delete(vault, coll, id)\n removed++\n }\n }\n }\n return removed\n}\n","/**\n * Active periods strategy factory. Only reachable through the\n * `@noy-db/hub/periods` subpath.\n */\n\nimport {\n loadPeriods,\n chainAnchor,\n assertTsWritable,\n validatePeriodName,\n appendPeriodLedgerEntry,\n} from './periods.js'\nimport type { PeriodPartition, PartitionResolver } from './periods.js'\nimport type { PeriodsStrategy } from './strategy.js'\n\n/** Options for {@link withPeriods}. */\nexport interface WithPeriodsOptions {\n /**\n * Maps a collection to the timeline each of its records belongs to (#1005) —\n * the answer to \"which close calendar governs THIS record\".\n *\n * ```ts\n * withPeriods({\n * subjects: { receipts: (r) => [r.clientId, layerOf(r)] },\n * })\n * ```\n *\n * Same shape as `withForget({ subjects })`, which answers the same question\n * for erasure. A collection with no entry — and every collection when\n * `subjects` is omitted entirely — stays on the vault-wide timeline, so an\n * existing vault behaves exactly as it did before partitions existed.\n *\n * Return `undefined` from a mapper to put an individual record back on the\n * vault-wide timeline (e.g. a record that predates the field the mapping\n * reads).\n */\n readonly subjects?: Readonly<\n Record<string, (record: Record<string, unknown>) => PeriodPartition | undefined>\n >\n}\n\n/**\n * Build the default periods strategy. Pass into\n * `createNoydb({ periodsStrategy: withPeriods() })` to enable\n * `vault.closePeriod()` / `vault.openPeriod()` / write-guards.\n *\n * Pass `subjects` to run more than one close calendar in a single vault — see\n * {@link WithPeriodsOptions.subjects}.\n */\nexport function withPeriods(options?: WithPeriodsOptions): PeriodsStrategy {\n const subjects = options?.subjects\n const partitionOf: PartitionResolver | undefined = subjects\n ? (collection, record) => subjects[collection]?.(record)\n : undefined\n\n return {\n loadPeriods,\n chainAnchor,\n assertTsWritable,\n validatePeriodName,\n appendPeriodLedgerEntry,\n ...(partitionOf !== undefined && { partitionOf }),\n }\n}\n","/**\n * Strategy seam for the optional accounting-periods service. Core\n * imports `PeriodsStrategy` type-only + `NO_PERIODS` stub; the real\n * `loadPeriods` / `chainAnchor` / `assertTsWritable` /\n * `validatePeriodName` / `appendPeriodLedgerEntry` functions are\n * only reachable via `withPeriods()` in `./active.ts`.\n *\n * Applications that never call `vault.closePeriod()` /\n * `vault.openPeriod()` ship none of the ~363 LOC.\n *\n * @internal\n */\n\nimport type { EncryptedEnvelope, NoydbStore } from '../../kernel/types.js'\nimport type { LedgerStore } from '../../with-commit/history/ledger/store.js'\nimport type { PeriodRecord, PeriodPartition, PartitionResolver } from './periods.js'\n\n/**\n * @internal\n */\nexport interface PeriodsStrategy {\n loadPeriods(\n adapter: NoydbStore,\n vault: string,\n decrypt: (id: string, envelope: EncryptedEnvelope) => Promise<PeriodRecord>,\n ): Promise<PeriodRecord[]>\n chainAnchor(records: readonly PeriodRecord[], partition?: PeriodPartition): Promise<{\n priorPeriodName?: string\n priorPeriodHash: string\n }>\n assertTsWritable(\n existing: { ts: string | null; record: Record<string, unknown> | null } | null,\n incoming: Record<string, unknown> | null,\n periods: readonly PeriodRecord[],\n scope?: { collection: string; resolve?: PartitionResolver },\n ): void\n validatePeriodName(\n name: string,\n existing: readonly PeriodRecord[],\n partition?: PeriodPartition,\n ): void\n /**\n * Record → timeline resolver built from `withPeriods({ subjects })` (#1005).\n * `undefined` when the caller configured no subjects, which keeps every\n * record on the vault-wide timeline.\n */\n readonly partitionOf?: PartitionResolver\n appendPeriodLedgerEntry(\n ledger: LedgerStore | null,\n actor: string,\n envelope: EncryptedEnvelope,\n periodName: string,\n collection?: string,\n ): Promise<void>\n}\n\n/**\n * No-periods stub. `loadPeriods` returns `[]`; the write-guards do\n * nothing (vaults without closed periods never reject writes);\n * `validatePeriodName` / `appendPeriodLedgerEntry` throw because\n * those paths are only reached when the user explicitly called\n * `closePeriod()` / `openPeriod()` — if they did that without the\n * strategy, they need to wire it.\n *\n * @internal\n */\nconst NOT_ENABLED = new Error(\n 'Accounting periods require the periods strategy. Import ' +\n '`{ withPeriods }` from \"@noy-db/hub/periods\" and pass it to ' +\n '`createNoydb({ periodsStrategy: withPeriods() })`.',\n)\n\nexport const NO_PERIODS: PeriodsStrategy = {\n async loadPeriods() { return [] },\n async chainAnchor() { return { priorPeriodHash: '' } },\n assertTsWritable() {},\n validatePeriodName() { throw NOT_ENABLED },\n async appendPeriodLedgerEntry() { throw NOT_ENABLED },\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-Y2NBBT6K.js";
|
|
4
4
|
import {
|
|
5
5
|
sha256Hex
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-4J3MNVVX.js";
|
|
7
7
|
|
|
8
8
|
// src/with-shape/persisted-schemas/derive.ts
|
|
9
9
|
function isZodSchema(value) {
|
|
@@ -79,4 +79,4 @@ export {
|
|
|
79
79
|
isZod4Schema,
|
|
80
80
|
derivePersistedSchema
|
|
81
81
|
};
|
|
82
|
-
//# sourceMappingURL=chunk-
|
|
82
|
+
//# sourceMappingURL=chunk-J4KAPKBJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-shape/persisted-schemas/derive.ts"],"sourcesContent":["/**\n * Derive a {@link PersistedSchemaEnvelope} from a Standard Schema v1\n * validator. Supports zod 3 (via the optional `zod-to-json-schema` peer-dep)\n * and zod 4 (via its native `z.toJSONSchema()`); both are loaded lazily so\n * hub never statically imports zod. Other validator families write a stub\n * envelope flagging the kind.\n *\n * @see design-history/2026-05-22-schema-dump-design.md\n *\n * @module\n */\n\nimport { canonicalize } from './canonicalize.js'\nimport { sha256Hex } from '../../capsule/index.js'\nimport type { PersistedSchemaEnvelope, PersistedSchemaKind } from './types.js'\n\n/**\n * Heuristic Zod detection — uses the Standard Schema v1 `~standard.vendor`\n * property (present in Zod v3.23+ and Zod v4+). Falls back to the\n * `_def.typeName` heuristic for older Zod v3 builds that predate Standard\n * Schema support.\n */\nexport function isZodSchema(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false\n // Standard Schema v1 vendor tag — most reliable across Zod majors\n const std = (value as { '~standard'?: { vendor?: unknown } })['~standard']\n if (std && typeof std === 'object' && (std as { vendor?: unknown }).vendor === 'zod') return true\n // Zod v3 fallback: _def.typeName starts with 'Zod' (e.g. 'ZodObject')\n const def = (value as { _def?: { typeName?: unknown } })._def\n if (!def || typeof def !== 'object') return false\n return typeof (def as { typeName?: unknown }).typeName === 'string'\n && ((def as { typeName: string }).typeName).startsWith('Zod')\n}\n\nfunction detectKind(validator: unknown): PersistedSchemaKind {\n if (isZodSchema(validator)) return 'Zod'\n return 'Unknown'\n}\n\n/**\n * Returns `true` when the validator was created with the Zod v4 native API\n * (i.e. `import { z } from 'zod'` where zod resolves to v4). Zod v4 native\n * schemas carry a `_zod` property whereas Zod v3 schemas (and the v3-compat\n * shim shipped inside zod@4 at `zod/v3`) use `_def.typeName`.\n *\n * Kept duck-typed so hub never statically imports zod.\n */\nexport function isZod4Schema(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false\n return '_zod' in value\n}\n\n/**\n * Lazy-require `zod-to-json-schema`. Returns the converter, or throws a\n * clear error if the peer-dep isn't installed.\n */\nasync function loadZodToJsonSchemaConverter(): Promise<(s: unknown) => object> {\n try {\n const mod = (await import('zod-to-json-schema')) as { zodToJsonSchema?: (s: unknown) => object }\n if (!mod.zodToJsonSchema) {\n throw new Error('zod-to-json-schema export missing')\n }\n return mod.zodToJsonSchema\n } catch (err) {\n throw new Error(\n 'persistJsonSchema requires the optional peer-dep `zod-to-json-schema`. '\n + 'Install it: `pnpm add zod-to-json-schema` (or npm/yarn equivalent). '\n + `Original error: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n}\n\n/**\n * Lazy-require Zod v4's built-in `toJSONSchema`. Used when the validator is a\n * Zod v4 native schema and `zod-to-json-schema` is absent or cannot handle it.\n *\n * Dynamic so hub never loads zod unless a caller actually persists a Zod v4\n * schema. ⚠️ Dynamic is NOT what keeps zod out of the tarball — that is\n * `peerDependencies` (#1227). tsup externalises DECLARED dependencies and\n * BUNDLES everything else, static or dynamic alike, so while zod was a\n * devDependency only, this line silently vendored 548 KB of zod@4.4.3 into\n * `dist/` at a build-frozen version. The comment here previously claimed the\n * dynamic import was what prevented that; it never did.\n *\n * Declaring it an OPTIONAL peer is also the correctness fix, not just a size\n * one: `toJSONSchema` reads a schema's internals, and the schema is built by\n * the CONSUMER's zod. A vendored copy meant hub inspected one zod's objects\n * with another zod's version of that reader. Now there is one zod — theirs.\n */\nasync function loadZodV4Converter(): Promise<(s: unknown) => object> {\n try {\n const mod = (await import('zod')) as { toJSONSchema?: (s: unknown) => object }\n if (typeof mod.toJSONSchema !== 'function') {\n throw new Error('zod.toJSONSchema not available (zod v4 required)')\n }\n return mod.toJSONSchema\n } catch (err) {\n throw new Error(\n 'persistJsonSchema with a Zod v4 schema requires zod@4 with toJSONSchema. '\n + `Original error: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n}\n\nexport async function derivePersistedSchema(\n validator: unknown,\n): Promise<PersistedSchemaEnvelope> {\n const kind = detectKind(validator)\n const derivedAt = new Date().toISOString()\n\n if (kind === 'Zod') {\n let jsonSchema: object\n\n if (isZod4Schema(validator)) {\n // Zod v4 native schemas: use zod's built-in toJSONSchema (available in\n // zod@4) because zod-to-json-schema@3.x parses _def.typeName which is\n // absent in native v4 schemas.\n const convert = await loadZodV4Converter()\n jsonSchema = convert(validator)\n } else {\n // Zod v3 schemas (or v3-compat shim): use the optional peer-dep\n // zod-to-json-schema for backward compatibility.\n const convert = await loadZodToJsonSchemaConverter()\n jsonSchema = convert(validator)\n }\n\n const canonical = canonicalize(jsonSchema)\n const hash = await sha256Hex(new TextEncoder().encode(canonical))\n return { _noydb_schema: 1, kind, jsonSchema, hash, derivedAt }\n }\n\n return {\n _noydb_schema: 1,\n kind,\n jsonSchema: null,\n hash: null,\n reason: `derivation not yet supported for kind=${kind} (v0 supports Zod only)`,\n derivedAt,\n }\n}\n"],"mappings":";;;;;;;;AAsBO,SAAS,YAAY,OAAyB;AACnD,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AAExD,QAAM,MAAO,MAAiD,WAAW;AACzE,MAAI,OAAO,OAAO,QAAQ,YAAa,IAA6B,WAAW,MAAO,QAAO;AAE7F,QAAM,MAAO,MAA4C;AACzD,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,SAAO,OAAQ,IAA+B,aAAa,YACpD,IAA6B,SAAU,WAAW,KAAK;AAChE;AAEA,SAAS,WAAW,WAAyC;AAC3D,MAAI,YAAY,SAAS,EAAG,QAAO;AACnC,SAAO;AACT;AAUO,SAAS,aAAa,OAAyB;AACpD,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,SAAO,UAAU;AACnB;AAMA,eAAe,+BAAgE;AAC7E,MAAI;AACF,UAAM,MAAO,MAAM,OAAO,oBAAoB;AAC9C,QAAI,CAAC,IAAI,iBAAiB;AACxB,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AACA,WAAO,IAAI;AAAA,EACb,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,kKAEqB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IACvE;AAAA,EACF;AACF;AAmBA,eAAe,qBAAsD;AACnE,MAAI;AACF,UAAM,MAAO,MAAM,OAAO,KAAK;AAC/B,QAAI,OAAO,IAAI,iBAAiB,YAAY;AAC1C,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AACA,WAAO,IAAI;AAAA,EACb,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,4FACqB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IACvE;AAAA,EACF;AACF;AAEA,eAAsB,sBACpB,WACkC;AAClC,QAAM,OAAO,WAAW,SAAS;AACjC,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AAEzC,MAAI,SAAS,OAAO;AAClB,QAAI;AAEJ,QAAI,aAAa,SAAS,GAAG;AAI3B,YAAM,UAAU,MAAM,mBAAmB;AACzC,mBAAa,QAAQ,SAAS;AAAA,IAChC,OAAO;AAGL,YAAM,UAAU,MAAM,6BAA6B;AACnD,mBAAa,QAAQ,SAAS;AAAA,IAChC;AAEA,UAAM,YAAY,aAAa,UAAU;AACzC,UAAM,OAAO,MAAM,UAAU,IAAI,YAAY,EAAE,OAAO,SAAS,CAAC;AAChE,WAAO,EAAE,eAAe,GAAG,MAAM,YAAY,MAAM,UAAU;AAAA,EAC/D;AAEA,SAAO;AAAA,IACL,eAAe;AAAA,IACf;AAAA,IACA,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,QAAQ,yCAAyC,IAAI;AAAA,IACrD;AAAA,EACF;AACF;","names":[]}
|