@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":["../src/with-commit/history/ledger/patch.ts","../src/with-commit/history/ledger/store.ts"],"sourcesContent":["/**\n * RFC 6902 JSON Patch — compute + apply.\n *\n * This module is the \"delta history\" primitive: instead of\n * snapshotting the full record on every put (the behavior),\n * `Collection.put` computes a JSON Patch from the previous version to\n * the new version and stores only the patch in the ledger. To\n * reconstruct version N, we walk from the genesis snapshot forward\n * applying patches. Storage scales with **edit size**, not record\n * size — a 10 KB record edited 1000 times costs ~10 KB of deltas\n * instead of ~10 MB of snapshots.\n *\n * ## Why hand-roll instead of using a library?\n *\n * RFC 6902 has good libraries (`fast-json-patch`, `rfc6902`) but every\n * single one of them adds a runtime dependency to `@noy-db/core`. The\n * \"zero runtime dependencies\" promise is one of the core's load-bearing\n * features, and the patch surface we actually need is small enough\n * (~150 LoC) that vendoring is the right call.\n *\n * What we implement:\n * - `add` — insert a value at a path\n * - `remove` — delete the value at a path\n * - `replace` — overwrite the value at a path\n *\n * What we deliberately skip (out of scope for the ledger use):\n * - `move` and `copy` — optimizations; the diff algorithm doesn't\n * emit them, so the apply path doesn't need them\n * - `test` — used for transactional patches; we already have\n * optimistic concurrency via `_v` at the envelope layer\n * - Sophisticated array diffing (LCS, edit distance) — we treat\n * arrays as atomic values and emit a single `replace` op when\n * they differ. The accounting domain has small arrays where this\n * is fine; if we ever need patch-level array diffing we can add\n * it without changing the storage format.\n *\n * ## Path encoding (RFC 6902 §3)\n *\n * Paths look like `/foo/bar/0`. Each path segment is either an object\n * key or a numeric array index. Two characters need escaping inside\n * keys: `~` becomes `~0` and `/` becomes `~1`. We implement both.\n *\n * Empty path (`\"\"`) refers to the root document. Only `replace` makes\n * sense at the root, and our diff function emits it as a top-level\n * `replace` when `prev` and `next` differ in shape (object vs array,\n * primitive vs object, etc.).\n */\n\n/** A single JSON Patch operation. Subset of RFC 6902 — see file docstring. */\nexport type JsonPatchOp =\n | { readonly op: 'add'; readonly path: string; readonly value: unknown }\n | { readonly op: 'remove'; readonly path: string }\n | { readonly op: 'replace'; readonly path: string; readonly value: unknown }\n\n/** A complete JSON Patch document — an array of operations. */\nexport type JsonPatch = readonly JsonPatchOp[]\n\n// ─── Compute (diff) ──────────────────────────────────────────────────\n\n/**\n * Compute a JSON Patch that, when applied to `prev`, produces `next`.\n *\n * The algorithm is a straightforward recursive object walk:\n *\n * 1. If both inputs are plain objects (and not arrays/null):\n * - For each key in `prev`, recurse if `next` has it, else emit `remove`\n * - For each key in `next` not in `prev`, emit `add`\n * 2. If both inputs are arrays AND structurally equal, no-op.\n * Otherwise emit a single `replace` for the whole array.\n * 3. If both inputs are deeply equal primitives, no-op.\n * 4. Otherwise emit a `replace` at the current path.\n *\n * We do not minimize patches across move-like rearrangements — every\n * generated patch is straightforward enough to apply by hand if you\n * had to debug it.\n */\nexport function computePatch(prev: unknown, next: unknown): JsonPatch {\n const ops: JsonPatchOp[] = []\n diff(prev, next, '', ops)\n return ops\n}\n\nfunction diff(\n prev: unknown,\n next: unknown,\n path: string,\n out: JsonPatchOp[],\n): void {\n // Both null / both undefined → no-op (we don't differentiate them\n // in JSON terms; canonicalJson would reject undefined anyway).\n if (prev === next) return\n\n // One side null, the other not → straight replace.\n if (prev === null || next === null) {\n out.push({ op: 'replace', path, value: next })\n return\n }\n\n const prevIsArray = Array.isArray(prev)\n const nextIsArray = Array.isArray(next)\n const prevIsObject = typeof prev === 'object' && !prevIsArray\n const nextIsObject = typeof next === 'object' && !nextIsArray\n\n // Type changed (e.g., object → primitive, array → object). Replace.\n if (prevIsArray !== nextIsArray || prevIsObject !== nextIsObject) {\n out.push({ op: 'replace', path, value: next })\n return\n }\n\n // Both arrays. We don't do clever LCS-based diffing — emit a single\n // replace for the whole array if they differ. See file docstring for\n // the rationale.\n if (prevIsArray && nextIsArray) {\n if (!arrayDeepEqual(prev as unknown[], next as unknown[])) {\n out.push({ op: 'replace', path, value: next })\n }\n return\n }\n\n // Both plain objects. Recurse key by key.\n if (prevIsObject && nextIsObject) {\n const prevObj = prev as Record<string, unknown>\n const nextObj = next as Record<string, unknown>\n const prevKeys = Object.keys(prevObj)\n const nextKeys = Object.keys(nextObj)\n\n // Handle removes and overlapping recursions in one pass over prev.\n for (const key of prevKeys) {\n const childPath = path + '/' + escapePathSegment(key)\n if (!(key in nextObj)) {\n out.push({ op: 'remove', path: childPath })\n } else {\n diff(prevObj[key], nextObj[key], childPath, out)\n }\n }\n // Handle adds.\n for (const key of nextKeys) {\n if (!(key in prevObj)) {\n out.push({\n op: 'add',\n path: path + '/' + escapePathSegment(key),\n value: nextObj[key],\n })\n }\n }\n return\n }\n\n // Two primitives that aren't strictly equal — replace.\n out.push({ op: 'replace', path, value: next })\n}\n\nfunction arrayDeepEqual(a: unknown[], b: unknown[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false\n }\n return true\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (a === null || b === null) return false\n if (typeof a !== typeof b) return false\n if (typeof a !== 'object') return false\n const aArray = Array.isArray(a)\n const bArray = Array.isArray(b)\n if (aArray !== bArray) return false\n if (aArray && bArray) return arrayDeepEqual(a, b as unknown[])\n const aObj = a as Record<string, unknown>\n const bObj = b as Record<string, unknown>\n const aKeys = Object.keys(aObj)\n const bKeys = Object.keys(bObj)\n if (aKeys.length !== bKeys.length) return false\n for (const key of aKeys) {\n if (!(key in bObj)) return false\n if (!deepEqual(aObj[key], bObj[key])) return false\n }\n return true\n}\n\n// ─── Apply ──────────────────────────────────────────────────────────\n\n/**\n * Apply a JSON Patch to a base document and return the result.\n *\n * The base document is **not mutated** — every op clones the parent\n * container before writing to it, so the caller's reference to `base`\n * stays untouched. This costs an extra allocation per op but makes\n * the apply pipeline reorderable and safe to interrupt.\n *\n * Throws on:\n * - Removing a path that doesn't exist\n * - Adding to a path whose parent doesn't exist\n * - A path component that doesn't match the document shape (e.g.,\n * trying to step into a primitive)\n *\n * Throwing is the right behavior for the ledger use case: a failed\n * apply means the chain is corrupted, which should be loud rather\n * than silently producing a wrong reconstruction.\n */\nexport function applyPatch<T = unknown>(base: T, patch: JsonPatch): T {\n let result: unknown = clone(base)\n for (const op of patch) {\n result = applyOp(result, op)\n }\n return result as T\n}\n\nfunction applyOp(doc: unknown, op: JsonPatchOp): unknown {\n // Empty path → operation targets the root. Only `replace` and `add`\n // make sense at the root, but we handle `remove` for completeness\n // (root removal returns null).\n if (op.path === '') {\n if (op.op === 'remove') return null\n return clone(op.value)\n }\n\n const segments = parsePath(op.path)\n return walkAndApply(doc, segments, op)\n}\n\nfunction walkAndApply(\n doc: unknown,\n segments: string[],\n op: JsonPatchOp,\n): unknown {\n if (segments.length === 0) {\n // Should never happen — empty path is handled in applyOp().\n throw new Error('walkAndApply: empty segments (internal error)')\n }\n\n const [head, ...rest] = segments\n if (head === undefined) throw new Error('walkAndApply: undefined segment')\n\n if (rest.length === 0) {\n return applyAtTerminal(doc, head, op)\n }\n\n // Recurse into the child container, then rebuild the parent with\n // the modified child.\n if (Array.isArray(doc)) {\n const idx = parseArrayIndex(head, doc.length)\n const child = doc[idx]\n const newChild = walkAndApply(child, rest, op)\n const next = doc.slice()\n next[idx] = newChild\n return next\n }\n if (doc !== null && typeof doc === 'object') {\n const obj = doc as Record<string, unknown>\n if (!(head in obj)) {\n throw new Error(`applyPatch: path segment \"${head}\" not found in object`)\n }\n const newChild = walkAndApply(obj[head], rest, op)\n return { ...obj, [head]: newChild }\n }\n throw new Error(\n `applyPatch: cannot step into ${typeof doc} at segment \"${head}\"`,\n )\n}\n\nfunction applyAtTerminal(\n doc: unknown,\n segment: string,\n op: JsonPatchOp,\n): unknown {\n if (Array.isArray(doc)) {\n const idx =\n segment === '-' ? doc.length : parseArrayIndex(segment, doc.length + 1)\n const next = doc.slice()\n if (op.op === 'remove') {\n next.splice(idx, 1)\n return next\n }\n if (op.op === 'add') {\n next.splice(idx, 0, clone(op.value))\n return next\n }\n if (op.op === 'replace') {\n if (idx >= doc.length) {\n throw new Error(\n `applyPatch: replace at out-of-bounds array index ${idx}`,\n )\n }\n next[idx] = clone(op.value)\n return next\n }\n }\n if (doc !== null && typeof doc === 'object') {\n const obj = doc as Record<string, unknown>\n if (op.op === 'remove') {\n if (!(segment in obj)) {\n throw new Error(\n `applyPatch: remove on missing key \"${segment}\"`,\n )\n }\n const next = { ...obj }\n delete next[segment]\n return next\n }\n if (op.op === 'add') {\n // RFC 6902: `add` on an existing key replaces it.\n return { ...obj, [segment]: clone(op.value) }\n }\n if (op.op === 'replace') {\n if (!(segment in obj)) {\n throw new Error(\n `applyPatch: replace on missing key \"${segment}\"`,\n )\n }\n return { ...obj, [segment]: clone(op.value) }\n }\n }\n throw new Error(\n `applyPatch: cannot apply ${op.op} at terminal segment \"${segment}\"`,\n )\n}\n\n// ─── Path encoding (RFC 6902 §3) ─────────────────────────────────────\n\n/**\n * Escape a single path segment per RFC 6902 §3:\n * `~` → `~0`\n * `/` → `~1`\n *\n * Order matters: `~` must be escaped first, otherwise the `~1` we\n * just emitted would be re-escaped to `~01`.\n */\nfunction escapePathSegment(segment: string): string {\n return segment.replace(/~/g, '~0').replace(/\\//g, '~1')\n}\n\nfunction unescapePathSegment(segment: string): string {\n return segment.replace(/~1/g, '/').replace(/~0/g, '~')\n}\n\nfunction parsePath(path: string): string[] {\n if (!path.startsWith('/')) {\n throw new Error(`applyPatch: path must start with '/', got \"${path}\"`)\n }\n return path\n .slice(1)\n .split('/')\n .map(unescapePathSegment)\n}\n\nfunction parseArrayIndex(segment: string, max: number): number {\n if (!/^\\d+$/.test(segment)) {\n throw new Error(\n `applyPatch: array index must be a non-negative integer, got \"${segment}\"`,\n )\n }\n const idx = Number.parseInt(segment, 10)\n if (idx < 0 || idx > max) {\n throw new Error(\n `applyPatch: array index ${idx} out of range [0, ${max}]`,\n )\n }\n return idx\n}\n\n// ─── Cheap structural clone ─────────────────────────────────────────\n\n/**\n * Plain-JSON clone via JSON.parse(JSON.stringify(value)).\n *\n * Faster than `structuredClone` for our use because (a) we know our\n * inputs are JSON-compatible (no Dates, Maps, or BigInts — anything\n * else gets rejected by canonicalJson upstream), and (b) `structuredClone`\n * has overhead for handling arbitrary structured data we don't need.\n *\n * For tiny ledger entries (< 1 KB), the JSON round-trip is in the\n * single-digit microsecond range.\n */\nfunction clone<T>(value: T): T {\n if (value === null || value === undefined) return value\n if (typeof value !== 'object') return value\n return JSON.parse(JSON.stringify(value)) as T\n}\n","/**\n * `LedgerStore` — read/write access to a compartment's hash-chained\n * audit log.\n *\n * The store is a thin wrapper around the adapter's `_ledger/` internal\n * collection. Every append:\n *\n * 1. Loads the current head (or treats an empty ledger as head = -1)\n * 2. Computes `prevHash` = sha256(canonicalJson(head))\n * 3. Builds the new entry with `index = head.index + 1`\n * 4. Encrypts the entry with the compartment's ledger DEK\n * 5. Writes the encrypted envelope to `_ledger/<paddedIndex>`\n *\n * `verify()` walks the chain from genesis forward and returns\n * `{ ok: true, head }` on success or `{ ok: false, divergedAt }` on the\n * first broken link.\n *\n * ## Thread / concurrency model\n *\n * For we assume a **single writer per vault**. Two\n * concurrent `append()` calls would race on the \"read head, write\n * head+1\" cycle and could produce a broken chain. The sync engine\n * is the primary concurrent-writer scenario, and it uses\n * optimistic-concurrency via `expectedVersion` on the adapter — but\n * the ledger path has no such guard today. Multi-writer hardening is a\n * follow-up.\n *\n * Single-writer usage IS safe, including across process restarts:\n * `head()` reads the adapter fresh each call, so a crash between the\n * adapter.put of a data record and the ledger append just means the\n * ledger is missing an entry for that record. `verify()` still\n * succeeds; a future `verifyIntegrity()` helper can cross-check the\n * ledger against the data collections to catch the gap.\n *\n * ## Why hide the ledger from `vault.collection()`?\n *\n * The `_ledger` name starts with `_`, matching the existing prefix\n * convention for internal collections (`_keyring`, `_sync`,\n * `_history`). The Vault's public `collection()` method already\n * returns entries for any name, but `loadAll()` filters out\n * underscore-prefixed collections so backups and exports don't leak\n * ledger metadata. We keep the ledger accessible ONLY via\n * `vault.ledger()` to enforce the hash-chain invariants — direct\n * puts via `collection('_ledger')` would bypass the `append()` logic.\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../../kernel/types.js'\nimport { buildRecordAad, buildRecordEnvelope, encrypt, openEnvelopeJson, type EnclaveKey } from '../../../capsule/index.js'\nimport type { ConflictError} from '../../../kernel/errors.js';\nimport { isConflictError, LedgerContentionError } from '../../../kernel/errors.js'\nimport {\n canonicalJson,\n hashEntry,\n paddedIndex,\n sha256Hex,\n type LedgerEntry,\n} from './entry.js'\nimport type { JsonPatch } from './patch.js'\nimport { applyPatch } from './patch.js'\nimport { LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION } from './constants.js'\nimport { envelopePayloadHash } from './hash.js'\n\n/**\n * Maximum optimistic-CAS retries on the ledger head. Each failed\n * attempt invalidates the head cache, re-reads, and retries with a\n * fresh next-index. After N failures we surface\n * `LedgerContentionError` so the caller can decide whether to retry,\n * queue, or alert.\n */\nconst MAX_APPEND_ATTEMPTS = 8\n\n// — re-export the constants + helper so any existing\n// `import { LEDGER_COLLECTION } from '...store.js'` paths keep\n// working. Internal core paths (vault.ts) import from the leaf\n// modules directly to avoid pulling this file's class into the\n// floor bundle.\nexport { LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION, envelopePayloadHash }\n\n/**\n * Input shape for `LedgerStore.append()`. The caller supplies the\n * operation metadata; the store fills in `index` and `prevHash`.\n */\nexport interface AppendInput {\n op: LedgerEntry['op']\n collection: string\n id: string\n version: number\n actor: string\n payloadHash: string\n /**\n * Optional JSON Patch representing the delta from the previous\n * version to the new version. Present only for `put` operations\n * that had a previous version; omitted for genesis puts and for\n * deletes. When present, `LedgerStore.append` persists the patch\n * in `_ledger_deltas/<paddedIndex>` and records its sha256 hash\n * as the entry's `deltaHash` field.\n */\n delta?: JsonPatch\n /**\n * Present only for `op === 'amendment'` — structured audit\n * payload for multi-record repair operations performed via\n * `withTransactions(...)`. Carried through verbatim to the\n * resulting ledger entry.\n */\n amendment?: LedgerEntry['amendment']\n /**\n * Optional human-readable tag describing why this mutation happened.\n * Threaded from `collection.put(_, _, { reason })`.\n * Carried verbatim onto the resulting ledger entry's `reason` field;\n * omitted from canonical JSON when undefined.\n */\n reason?: string\n}\n\n/**\n * Result of `LedgerStore.verify()`. On success, `head` is the hash of\n * the last entry — the same value that should be published to any\n * external anchoring service (blockchain, OpenTimestamps, etc.). On\n * failure, `divergedAt` is the 0-based index of the first entry whose\n * recorded `prevHash` does not match the recomputed hash of its\n * predecessor. Entries at `divergedAt` and later are untrustworthy;\n * entries before that index are still valid.\n */\nexport type VerifyResult =\n | { readonly ok: true; readonly head: string; readonly length: number }\n | {\n readonly ok: false\n readonly divergedAt: number\n readonly expected: string\n readonly actual: string\n }\n\n/**\n * A LedgerStore is bound to a single vault. Callers obtain one\n * via `vault.ledger()` — there is no public constructor to keep\n * the hash-chain invariants in one place.\n *\n * The class holds no mutable state beyond its dependencies (adapter,\n * vault name, DEK resolver, actor id). Every method reads the\n * adapter fresh so multiple instances against the same vault\n * see each other's writes immediately (at the cost of re-parsing the\n * ledger on every head() / verify() call; acceptable at scale).\n */\nexport class LedgerStore {\n private readonly adapter: NoydbStore\n private readonly vault: string\n private readonly encrypted: boolean\n private readonly getDEK: (collectionName: string) => Promise<EnclaveKey>\n private readonly actor: string\n\n /**\n * In-memory cache of the chain head — the most recently appended\n * entry along with its precomputed hash. Without this, every\n * `append()` would re-load every prior entry to recompute the\n * prevHash, making N puts O(N²) — a 1K-record stress test goes from\n * < 100ms to a multi-second timeout.\n *\n * The cache is populated on first read (`append`, `head`, `verify`)\n * and updated in-place on every successful `append`. Single-writer\n * usage (the assumption) keeps it consistent. A second\n * LedgerStore instance writing to the same vault would not\n * see the first instance's appends in its cached state — that's the\n * concurrency caveat documented at the class level.\n *\n * Sentinel `undefined` means \"not yet loaded\"; an explicit `null`\n * value means \"loaded and confirmed empty\" — distinguishing these\n * matters because an empty ledger is a valid state (genesis prevHash\n * is the empty string), and we don't want to re-scan the adapter\n * just because the chain is freshly initialized.\n */\n private headCache: { entry: LedgerEntry; hash: string } | null | undefined = undefined\n\n constructor(opts: {\n adapter: NoydbStore\n vault: string\n encrypted: boolean\n getDEK: (collectionName: string) => Promise<EnclaveKey>\n actor: string\n }) {\n this.adapter = opts.adapter\n this.vault = opts.vault\n this.encrypted = opts.encrypted\n this.getDEK = opts.getDEK\n this.actor = opts.actor\n }\n\n /**\n * Lazily load (or return cached) the current chain head. The cache\n * sentinel is `undefined` until first access; after the first call,\n * the cache holds either a `{ entry, hash }` for non-empty ledgers\n * or `null` for empty ones.\n */\n private async getCachedHead(): Promise<{ entry: LedgerEntry; hash: string } | null> {\n if (this.headCache !== undefined) return this.headCache\n const entries = await this.loadAllEntries()\n const last = entries[entries.length - 1]\n if (!last) {\n this.headCache = null\n return null\n }\n this.headCache = { entry: last, hash: await hashEntry(last) }\n return this.headCache\n }\n\n /**\n * Append a new entry to the ledger. Returns the full entry that was\n * written (with its assigned index and computed prevHash) so the\n * caller can use the hash for downstream purposes (e.g., embedding\n * in a verifiable backup).\n *\n * This is the **only** way to add entries. Direct adapter writes to\n * `_ledger/` would bypass the chain math and would be caught by the\n * next `verify()` call as a divergence.\n *\n * ## Multi-writer correctness\n *\n * Append is implemented as an optimistic-CAS retry loop. On every\n * attempt:\n *\n * 1. Read fresh head (cache invalidated on retry).\n * 2. Compute `nextIndex = head.index + 1`, `prevHash = hash(head)`.\n * 3. Encrypt delta payload IN MEMORY (no adapter write yet) so we\n * can compute `deltaHash` before claiming the chain slot.\n * 4. Build + encrypt the entry envelope.\n * 5. `adapter.put(_ledger, paddedIndex, envelope, expectedVersion: 0)`\n * — the `expectedVersion: 0` asserts \"this slot must not exist.\"\n * Stores with `casAtomic: true` honor the CAS check; under\n * contention the second writer's put throws `ConflictError`.\n * 6. On `ConflictError`: invalidate the head cache, sleep with\n * bounded backoff + jitter, retry. After `MAX_APPEND_ATTEMPTS`\n * retries throw {@link LedgerContentionError}.\n * 7. On success: write the delta envelope (if any) at the same\n * index. Update the head cache.\n *\n * Entry-first ordering matters: writing the delta first under\n * contention would orphan delta records at indices the writer never\n * actually claimed. The deltaHash is computed off the encrypted\n * envelope's `_data` field, which doesn't require the envelope to\n * be persisted.\n *\n * Stores with `casAtomic: false` (file, s3, r2 by default) silently\n * accept the `expectedVersion: 0` argument and proceed without a\n * CAS check. Concurrent appends against those stores remain\n * best-effort — pair them with an advisory lock or with sync\n * single-writer discipline.\n */\n async append(input: AppendInput): Promise<LedgerEntry> {\n let lastConflict: ConflictError | undefined\n for (let attempt = 0; attempt < MAX_APPEND_ATTEMPTS; attempt++) {\n // Force a fresh head read on every retry. The first attempt may\n // hit the cache; subsequent attempts must re-scan the adapter\n // because the prior conflict means our cached state is stale.\n if (attempt > 0) {\n this.headCache = undefined\n }\n try {\n return await this.appendOnce(input)\n } catch (err) {\n if (isConflictError(err)) {\n lastConflict = err\n if (attempt < MAX_APPEND_ATTEMPTS - 1) {\n await sleepBackoff(attempt)\n }\n continue\n }\n throw err\n }\n }\n void lastConflict\n throw new LedgerContentionError(MAX_APPEND_ATTEMPTS)\n }\n\n /**\n * One attempt at the append cycle. Throws `ConflictError` when the\n * CAS check on the entry put fails — `append()` catches that and\n * retries. Any other error propagates to the caller.\n */\n private async appendOnce(input: AppendInput): Promise<LedgerEntry> {\n const cached = await this.getCachedHead()\n const lastEntry = cached?.entry\n const prevHash = cached?.hash ?? ''\n const nextIndex = lastEntry ? lastEntry.index + 1 : 0\n\n // Encrypt the delta in memory so we can compute deltaHash WITHOUT\n // claiming the deltas slot yet — entry-put is the chain claim.\n let deltaEnvelope: EncryptedEnvelope | undefined\n let deltaHash: string | undefined\n if (input.delta !== undefined) {\n deltaEnvelope = await this.encryptDelta(input.delta, nextIndex)\n deltaHash = await sha256Hex(deltaEnvelope._data ?? '')\n }\n\n // Build the entry. Conditionally include `deltaHash` so\n // canonicalJson (which rejects undefined) never sees it when\n // there's no delta.\n const entryBase = {\n index: nextIndex,\n prevHash,\n op: input.op,\n collection: input.collection,\n id: input.id,\n version: input.version,\n ts: new Date().toISOString(),\n actor: input.actor === '' ? this.actor : input.actor,\n payloadHash: input.payloadHash,\n } as const\n const entry: LedgerEntry = {\n ...entryBase,\n ...(deltaHash !== undefined ? { deltaHash } : {}),\n ...(input.amendment !== undefined ? { amendment: input.amendment } : {}),\n ...(input.reason !== undefined ? { reason: input.reason } : {}),\n }\n\n const envelope = await this.encryptEntry(entry)\n // expectedVersion: 0 ≡ \"the slot must not yet exist.\" Honored by\n // casAtomic stores; silently passed through by non-CAS stores.\n await this.adapter.put(\n this.vault,\n LEDGER_COLLECTION,\n paddedIndex(entry.index),\n envelope,\n 0,\n )\n\n // Chain slot claimed. Now write the delta record (if any).\n if (deltaEnvelope) {\n await this.adapter.put(\n this.vault,\n LEDGER_DELTAS_COLLECTION,\n paddedIndex(entry.index),\n deltaEnvelope,\n 0,\n )\n }\n\n // Update the head cache so the next append() doesn't re-scan the\n // adapter.\n this.headCache = { entry, hash: await hashEntry(entry) }\n return entry\n }\n\n /**\n * Load a delta payload by its entry index. Returns `null` if the\n * entry at that index doesn't reference a delta (genesis puts and\n * deletes leave the slot empty) or if the delta row is missing\n * (possible after a `pruneHistory` fold).\n *\n * The caller is responsible for deciding what to do with a missing\n * delta — `ledger.reconstruct()` uses it as a \"stop walking\n * backward\" signal and falls back to the on-disk current value.\n */\n async loadDelta(index: number): Promise<JsonPatch | null> {\n const envelope = await this.adapter.get(\n this.vault,\n LEDGER_DELTAS_COLLECTION,\n paddedIndex(index),\n )\n if (!envelope) return null\n if (!this.encrypted) {\n return JSON.parse(envelope._data ?? '') as JsonPatch\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const json = await openEnvelopeJson({ collection: LEDGER_DELTAS_COLLECTION, id: paddedIndex(index) }, envelope, dek)\n return JSON.parse(json) as JsonPatch\n }\n\n /**\n * Purge a record's tier-0-era plaintext deltas (#729). Deletes each\n * `_ledger_deltas/<paddedIndex>` row whose entry matches\n * `(collection, id)` and carries a delta, then returns the count\n * deleted.\n *\n * Chain-safe by construction: `verify()` recomputes the tamper-chain\n * from each `_ledger` entry's own canonical fields — including the\n * stored `deltaHash` field, which lives on the entry, not on the\n * `_ledger_deltas` row — and never re-reads `_ledger_deltas`.\n * Deleting a delta row therefore cannot break `verify()`.\n * `reconstruct()` already treats a missing delta (`loadDelta` →\n * `null`) as a pruned stop, so a purge just makes the pre-purge\n * plaintext unreachable rather than corrupting anything.\n *\n * No `_ledger` entry is touched and nothing is re-encrypted — this\n * is irreversible (the deleted plaintext cannot be recovered) but\n * leaves the entry metadata (that the record was mutated, at which\n * version/timestamp/actor) fully intact for audit purposes.\n */\n async purgeRecordDeltas(collection: string, id: string): Promise<number> {\n const entries = await this.loadAllEntries()\n let purged = 0\n for (const entry of entries) {\n if (entry.collection !== collection || entry.id !== id) continue\n if (entry.deltaHash === undefined) continue\n const key = paddedIndex(entry.index)\n // adapter.delete() is a void no-op on an already-missing key, so\n // check for the row's presence first — the count must reflect\n // rows actually removed (idempotent repeat purges return 0), not\n // the number of delta-bearing entries (which never changes).\n const existing = await this.adapter.get(this.vault, LEDGER_DELTAS_COLLECTION, key)\n if (!existing) continue\n await this.adapter.delete(this.vault, LEDGER_DELTAS_COLLECTION, key)\n purged++\n }\n return purged\n }\n\n /**\n * Encrypt a JSON Patch into an envelope for storage. Mirrors encryptEntry.\n *\n * `index` is the ledger index this delta will be stored under — the caller\n * knows it before the entry exists, and it is what makes the envelope's\n * identity bindable (#1051).\n */\n private async encryptDelta(patch: JsonPatch, index: number): Promise<EncryptedEnvelope> {\n const identity = { collection: LEDGER_DELTAS_COLLECTION, id: paddedIndex(index), by: this.actor, version: 1 }\n const json = JSON.stringify(patch)\n if (!this.encrypted) {\n return buildRecordEnvelope(identity, { iv: '', data: json})\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const { iv, data } = await encrypt(json, dek, buildRecordAad(identity))\n return buildRecordEnvelope(identity, { iv, data})\n }\n\n /**\n * Read all entries in ascending-index order. Used internally by\n * `append()`, `head()`, `verify()`, and `entries()`. Decryption is\n * serial because the entries are tiny and the overhead of a Promise\n * pool would dominate at realistic chain lengths (< 100K entries).\n */\n async loadAllEntries(): Promise<LedgerEntry[]> {\n const keys = await this.adapter.list(this.vault, LEDGER_COLLECTION)\n // Sort lexicographically, which matches numeric order because\n // keys are zero-padded to 10 digits.\n keys.sort()\n const entries: LedgerEntry[] = []\n for (const key of keys) {\n const envelope = await this.adapter.get(\n this.vault,\n LEDGER_COLLECTION,\n key,\n )\n if (!envelope) continue\n entries.push(await this.decryptEntry(key, envelope))\n }\n return entries\n }\n\n /**\n * Return the current head of the ledger: the last entry, its hash,\n * and the total chain length. `null` on an empty ledger so callers\n * can distinguish \"no history yet\" from \"empty history\".\n */\n async head(): Promise<\n | { readonly entry: LedgerEntry; readonly hash: string; readonly length: number }\n | null\n > {\n const cached = await this.getCachedHead()\n if (!cached) return null\n // `length` is `entry.index + 1` because indices are zero-based and\n // contiguous. We don't need to re-scan the adapter to compute it.\n return {\n entry: cached.entry,\n hash: cached.hash,\n length: cached.entry.index + 1,\n }\n }\n\n /**\n * Return entries in the requested half-open range `[from, to)`.\n * Defaults: `from = 0`, `to = length`. The indices are clipped to\n * the valid range; no error is thrown for out-of-range queries.\n */\n async entries(opts: { from?: number; to?: number } = {}): Promise<LedgerEntry[]> {\n const all = await this.loadAllEntries()\n const from = Math.max(0, opts.from ?? 0)\n const to = Math.min(all.length, opts.to ?? all.length)\n return all.slice(from, to)\n }\n\n /**\n * Reconstruct a record's state at a given historical version by\n * walking the ledger's delta chain backward from the current state.\n *\n * ## Algorithm\n *\n * Ledger deltas are stored in **reverse** form — each entry's\n * patch describes how to undo that put, transforming the new\n * record back into the previous one. `reconstruct` exploits this\n * by:\n *\n * 1. Finding every ledger entry for `(collection, id)` in the\n * chain, sorted by index ascending.\n * 2. Starting from `current` (the present value of the record,\n * as held by the caller — typically fetched via\n * `Collection.get()`).\n * 3. Walking entries in **descending** index order and applying\n * each entry's reverse patch, stopping when we reach the\n * entry whose version equals `atVersion`.\n *\n * The result is the record as it existed immediately AFTER the\n * put at `atVersion`. To get the state at the genesis put\n * (version 1), the walk runs all the way back through every put\n * after the first.\n *\n * ## Caveats\n *\n * - **Delete entries** break the walk: once we see a delete, the\n * record didn't exist before that point, so there's nothing to\n * reconstruct. We return `null` in that case.\n * - **Missing deltas** (e.g., after `pruneHistory` folds old\n * entries into a base snapshot) also stop the walk. does\n * not ship pruneHistory, so today this only happens if an entry\n * was deleted out-of-band.\n * - The caller MUST pass the correct current value. Passing a\n * mutated object would corrupt the reconstruction — the patch\n * chain is only valid against the exact state that was in\n * effect when the most recent put happened.\n *\n * For, `reconstruct` is the only way to read a historical\n * version via deltas. The legacy `_history` collection still\n * holds full snapshots and `Collection.getVersion()` still reads\n * from there — the two paths coexist until pruneHistory lands in\n * a follow-up and delta becomes the default.\n */\n async reconstruct<T>(\n collection: string,\n id: string,\n current: T,\n atVersion: number,\n ): Promise<T | null> {\n const all = await this.loadAllEntries()\n // Filter to entries for this (collection, id), in ascending index.\n const matching = all.filter(\n (e) => e.collection === collection && e.id === id,\n )\n if (matching.length === 0) {\n // No ledger history at all; the current state IS version 1\n // (or there's nothing), so the only valid atVersion is the\n // current record's version. We can't verify that here, so\n // return current if atVersion is plausible, null otherwise.\n return null\n }\n\n // Walk entries in descending index order, applying each reverse\n // delta until we reach the target version.\n let state: T | null = current\n for (let i = matching.length - 1; i >= 0; i--) {\n const entry = matching[i]\n if (!entry) continue\n\n // Defensive: skip every non-put/non-delete op variant. The\n // outer filter on `e.collection === collection && e.id === id`\n // already excludes `amendment` entries (their collection/id are\n // empty strings), but a top-of-loop guard keeps the walker\n // robust if a future op variant slips through the filter.\n if (entry.op !== 'put' && entry.op !== 'delete') continue\n\n // Match check FIRST — before applying this entry's reverse\n // patch. `state` at this point is the record state immediately\n // after this entry's put (or before this entry's delete), so\n // if the caller asked for this exact version, we're done.\n if (entry.version === atVersion && entry.op !== 'delete') {\n return state\n }\n\n if (entry.op === 'delete') {\n // A delete erases the live state. If the caller asks for a\n // version older than the delete we should continue walking\n // (state becomes null and the next put resets it). But we\n // can't reconstruct that pre-delete state from the current\n // in-memory `state` — the delete has no reverse patch. So\n // anything past this point is unreachable; return null.\n return null\n }\n\n if (entry.deltaHash === undefined) {\n // Genesis put — the earliest state for this lifecycle. We\n // can't walk further back. If the caller asked for exactly\n // this version, return the current state (we already failed\n // the match check above because a fresh genesis after a\n // delete can have version === atVersion). Otherwise the\n // target is unreachable from here.\n if (entry.version === atVersion) return state\n return null\n }\n\n const patch = await this.loadDelta(entry.index)\n if (!patch) {\n // Delta row is missing (probably pruned). Stop walking.\n return null\n }\n\n if (state === null) {\n // We're trying to walk back across a delete range and there's\n // nothing to apply a reverse patch to. Bail.\n return null\n }\n\n state = applyPatch(state, patch)\n }\n\n // Ran off the end of the walk without matching. The target\n // version doesn't exist in this record's chain.\n return null\n }\n\n /**\n * Walk the chain from genesis forward and verify every link.\n *\n * Returns `{ ok: true, head, length }` if every entry's `prevHash`\n * matches the recomputed hash of its predecessor (and the genesis\n * entry's `prevHash` is the empty string).\n *\n * Returns `{ ok: false, divergedAt, expected, actual }` on the first\n * mismatch. `divergedAt` is the 0-based index of the BROKEN entry\n * — entries before that index still verify cleanly; entries at and\n * after `divergedAt` are untrustworthy.\n *\n * This method detects:\n * - Mutated entry content (fields changed)\n * - Reordered entries (if any adjacent pair swaps, the prevHash\n * of the second no longer matches)\n * - Inserted entries (the inserted entry's prevHash likely fails,\n * and the following entry's prevHash definitely fails)\n * - Deleted entries (the entry after the deletion sees a wrong\n * prevHash)\n *\n * It does NOT detect:\n * - Tampering with the DATA collections that bypassed the ledger\n * entirely (e.g., an attacker who modifies records without\n * appending matching ledger entries — this is why we also\n * plan a `verifyIntegrity()` helper in a follow-up)\n * - Truncation of the chain at the tail (dropping the last N\n * entries leaves a shorter but still consistent chain). External\n * anchoring of `head.hash` to a trusted service is the defense\n * against this.\n */\n async verify(): Promise<VerifyResult> {\n const entries = await this.loadAllEntries()\n let expectedPrevHash = ''\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i]\n if (!entry) continue\n if (entry.prevHash !== expectedPrevHash) {\n return {\n ok: false,\n divergedAt: i,\n expected: expectedPrevHash,\n actual: entry.prevHash,\n }\n }\n if (entry.index !== i) {\n // An entry whose stored index doesn't match its position in\n // the sorted list means someone rewrote the adapter keys.\n // Treat as divergence.\n return {\n ok: false,\n divergedAt: i,\n expected: `index=${i}`,\n actual: `index=${entry.index}`,\n }\n }\n expectedPrevHash = await hashEntry(entry)\n }\n return {\n ok: true,\n head: expectedPrevHash,\n length: entries.length,\n }\n }\n\n // ─── Encryption plumbing ─────────────────────────────────────────\n\n /**\n * Serialize + encrypt a ledger entry into an EncryptedEnvelope. The\n * envelope's `_v` field is set to `entry.index + 1` so the usual\n * optimistic-concurrency machinery has a reasonable version number\n * to compare against (the ledger is append-only, so concurrent\n * writes should always bump the index).\n */\n private async encryptEntry(entry: LedgerEntry): Promise<EncryptedEnvelope> {\n const identity = { collection: LEDGER_COLLECTION, id: paddedIndex(entry.index), by: entry.actor, version: entry.index + 1 }\n const body = { ts: entry.ts }\n const json = canonicalJson(entry)\n if (!this.encrypted) {\n return buildRecordEnvelope(identity, { ...body, iv: '', data: json })\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const { iv, data } = await encrypt(json, dek, buildRecordAad(identity))\n return buildRecordEnvelope(identity, { ...body, iv, data })\n }\n\n /** Decrypt an envelope into a LedgerEntry. Throws on bad key / tamper. */\n private async decryptEntry(key: string, envelope: EncryptedEnvelope): Promise<LedgerEntry> {\n if (!this.encrypted) {\n return JSON.parse(envelope._data ?? '') as LedgerEntry\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const json = await openEnvelopeJson({ collection: LEDGER_COLLECTION, id: key }, envelope, dek)\n return JSON.parse(json) as LedgerEntry\n }\n}\n\n// `envelopePayloadHash` lives in `./hash.ts` so it can be\n// imported by core code without dragging this file's `LedgerStore`\n// class into the floor bundle. The re-export at the top of this\n// file keeps the `import { envelopePayloadHash } from '.../store.js'`\n// path working.\n\n/**\n * Exponential backoff with jitter for the append CAS retry loop.\n * Attempt 0 → ~5–10 ms, attempt 7 → ~640–1280 ms. Jitter avoids the\n * thundering-herd problem when multiple writers collide repeatedly.\n */\nfunction sleepBackoff(attempt: number): Promise<void> {\n const base = 5 * Math.pow(2, attempt)\n const jitter = Math.random() * base\n return new Promise((resolve) => setTimeout(resolve, base + jitter))\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
validateProjectionLegs
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-D4V5JJAK.js";
|
|
4
4
|
import {
|
|
5
5
|
describeGroupKey
|
|
6
6
|
} from "./chunk-XBKH4FOC.js";
|
|
7
7
|
import {
|
|
8
8
|
MaterializedViewConfigError,
|
|
9
9
|
ValidationError
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-VSUQI5Q6.js";
|
|
11
11
|
|
|
12
12
|
// src/with-formula/materialized-views/with-materialized-view.ts
|
|
13
13
|
function withMaterializedView(spec) {
|
|
@@ -208,4 +208,4 @@ function withMaterializedView(spec) {
|
|
|
208
208
|
export {
|
|
209
209
|
withMaterializedView
|
|
210
210
|
};
|
|
211
|
-
//# sourceMappingURL=chunk-
|
|
211
|
+
//# sourceMappingURL=chunk-L5A3KR7R.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildLiveReduction,
|
|
3
3
|
compareReduced
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WHOKGHJ3.js";
|
|
5
5
|
import {
|
|
6
6
|
canonicalGroupKey
|
|
7
7
|
} from "./chunk-XZHEOSQ2.js";
|
|
@@ -9,14 +9,14 @@ import {
|
|
|
9
9
|
bindDistinctReducers,
|
|
10
10
|
moneySum,
|
|
11
11
|
sum
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-BJH77UY2.js";
|
|
13
13
|
import {
|
|
14
14
|
isDateTruncKey,
|
|
15
15
|
projectDateTruncKeys
|
|
16
16
|
} from "./chunk-XBKH4FOC.js";
|
|
17
17
|
import {
|
|
18
18
|
readPath
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-BUE7R3B5.js";
|
|
20
20
|
|
|
21
21
|
// src/with-lookup/reduce/window.ts
|
|
22
22
|
function isWindowFn(v) {
|
|
@@ -212,4 +212,4 @@ export {
|
|
|
212
212
|
WindowedQuery,
|
|
213
213
|
WindowSelection
|
|
214
214
|
};
|
|
215
|
-
//# sourceMappingURL=chunk-
|
|
215
|
+
//# sourceMappingURL=chunk-LGGIFKJP.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
formatScaledInt,
|
|
5
5
|
parseToScaledInt,
|
|
6
6
|
rescaleScaledInt
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-OJU67C23.js";
|
|
8
8
|
|
|
9
9
|
// src/via/money/arith.ts
|
|
10
10
|
function parseAmount(label, amount, scale, rounding) {
|
|
@@ -120,4 +120,4 @@ export {
|
|
|
120
120
|
isMoneyString,
|
|
121
121
|
moneyNumber
|
|
122
122
|
};
|
|
123
|
-
//# sourceMappingURL=chunk-
|
|
123
|
+
//# sourceMappingURL=chunk-MQ7YWBQ5.js.map
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bindDistinctReducers,
|
|
3
3
|
reducerBuilder
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BJH77UY2.js";
|
|
5
5
|
import {
|
|
6
6
|
ViaPipeline
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YFFMPZPY.js";
|
|
8
8
|
import {
|
|
9
9
|
distinctKeyOf
|
|
10
10
|
} from "./chunk-SUZKW6UR.js";
|
|
11
11
|
import {
|
|
12
12
|
MaintenanceMethods
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-BNFNQNEP.js";
|
|
14
14
|
import {
|
|
15
15
|
groupKeyName,
|
|
16
16
|
isDateTruncKey,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from "./chunk-XBKH4FOC.js";
|
|
19
19
|
import {
|
|
20
20
|
ScanBuilder
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-QGFEBNHO.js";
|
|
22
22
|
import {
|
|
23
23
|
installMethods
|
|
24
24
|
} from "./chunk-F6NCB7KT.js";
|
|
@@ -29,14 +29,14 @@ import {
|
|
|
29
29
|
executeClausePipeline,
|
|
30
30
|
filterRecords,
|
|
31
31
|
fnViewDecoder
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-ZVSKSUGI.js";
|
|
33
33
|
import {
|
|
34
34
|
readPath
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-BUE7R3B5.js";
|
|
36
36
|
import {
|
|
37
37
|
FieldNotQueryableError,
|
|
38
38
|
GroupCardinalityError
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-VSUQI5Q6.js";
|
|
40
40
|
|
|
41
41
|
// src/kernel/query/relate/join-reduce.ts
|
|
42
42
|
function liftToAlias(alias, path, inner) {
|
|
@@ -295,6 +295,12 @@ var ReduceMethods = class {
|
|
|
295
295
|
}
|
|
296
296
|
const derived = keys.filter(isDateTruncKey);
|
|
297
297
|
const fields = keys.map(groupKeyName);
|
|
298
|
+
const groupVia = this.source.via;
|
|
299
|
+
if (groupVia) {
|
|
300
|
+
for (const f of fields) {
|
|
301
|
+
if (groupVia.postureFor(f)?.queryable === "none") throw new FieldNotQueryableError(f);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
298
304
|
const aliases = this.aliasNames();
|
|
299
305
|
const keyIsAliased = this.plan.joins.length > 0 && fields.some((f) => this.addressesAlias(f, aliases));
|
|
300
306
|
const source = this.source;
|
|
@@ -521,4 +527,4 @@ function installReduce() {
|
|
|
521
527
|
export {
|
|
522
528
|
installReduce
|
|
523
529
|
};
|
|
524
|
-
//# sourceMappingURL=chunk-
|
|
530
|
+
//# sourceMappingURL=chunk-MRJDLXI3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/kernel/query/relate/join-reduce.ts","../src/kernel/query/reduce/methods.ts","../src/kernel/query/reduce/scan-methods.ts","../src/kernel/query/reduce/install.ts"],"sourcesContent":["/**\n * Reducing over a JOIN ALIAS (#1338).\n *\n * `aggregate()` and `groupBy()` used to refuse any field rooted at a join\n * alias. The refusal's own doc named the real blocker, and it was never \"the\n * legs do not run\" — running them is the easy half. It was that the Via\n * pipeline is LEFT-SCOPED: `Query.aggregate()` calls\n * `this.source.via.wrapReducers(spec)`, which resolves every reducer's field\n * against the LEFT collection's map. For `sum('client.balance')` that map has\n * no entry, so two things silently did not happen — money's exact-BigInt\n * rewrite was skipped (a generic sum over stored scaled-integer strings, which\n * `readNumber` coerces to 0), and `refuseUnqueryableReducers` never fired, so\n * the `queryable: 'none'` gate that would refuse a blob field never applied.\n *\n * This module supplies the missing half as a Via BINDING rather than as a\n * special case threaded through the reduce strategy. A binding composes: the\n * pipeline handed to `GroupedQuery`/`aggregate()` is the left one plus this,\n * so every existing call site keeps its exact behaviour and the alias case\n * arrives through the same `wrapReducers` door as everything else.\n *\n * Two properties make that safe:\n *\n * - **It delegates, it does not reimplement.** An aliased reducer is handed\n * to the RIGHT collection's OWN pipeline under its BARE field name — which\n * is how a pipeline is keyed (that is the whole shape of #1335) — so the\n * right side's rewrite AND its posture gate apply together, from one call.\n * - **`covers()` returns false**, so the binding is inert for `postureFor`,\n * `present`, `decodeResults` and every other phase. It exists for exactly\n * one hook.\n */\nimport type { NoydbVia } from '../../via/index.js'\nimport { ViaPipeline } from '../../via/pipeline.js'\n\n/**\n * The reducer shape this file needs, declared STRUCTURALLY rather than\n * imported from `with-lookup/reduce/`.\n *\n * Not a style choice: `check-architecture.mjs`'s port-layering guard refuses a\n * NEW static kernel→`with-*` import (its allowlist is a frozen per-file, per-\n * specifier baseline, and adding a row would be the thing the freeze exists to\n * stop). `NoydbVia.wrapReducers` is declared `(spec: unknown) => unknown`\n * precisely so a binding never has to name the service's types, and these\n * three members are all this file touches — every reducer that reaches it was\n * built by the reduce service and keeps every other member intact through the\n * spread below.\n */\ninterface AliasReducer {\n readonly field?: string\n init(): unknown\n step(state: unknown, record: unknown): unknown\n remove?(state: unknown, record: unknown): unknown\n finalize(state: unknown): unknown\n}\ntype AliasSpec = Readonly<Record<string, AliasReducer>>\n\n/**\n * Re-aim a reducer at the sub-record under `alias`.\n *\n * The right side's pipeline rewrote the reducer for the BARE field name, so\n * its `step`/`remove` read `credit` off whatever record they are handed. On a\n * joined row that value lives at `row.client.credit`, so the row is unwrapped\n * one level on the way in rather than the field path being rewritten — the\n * rewritten reducer closes over its field name internally, so re-pointing the\n * `.field` property afterwards would change the label and not the read.\n *\n * An unmatched left row carries `null` under the alias; every reducer's read\n * already treats a nullish record as \"no value here\", so it contributes\n * nothing — the same thing a missing left-side field does.\n */\nfunction liftToAlias(alias: string, path: string, inner: AliasReducer): AliasReducer {\n const under = (record: unknown): unknown =>\n record === null || typeof record !== 'object' ? undefined : (record as Record<string, unknown>)[alias]\n return {\n ...inner,\n // The dotted path, so anything reading `.field` for a label or a\n // dependency still sees the field the CALLER named.\n field: path,\n init: () => inner.init(),\n step: (state, record) => inner.step(state, under(record)),\n ...(inner.remove ? { remove: (state: unknown, record: unknown) => inner.remove!(state, under(record)) } : {}),\n finalize: (state) => inner.finalize(state),\n }\n}\n\n/**\n * The binding: rewrite every reducer whose field is rooted at a join alias\n * through that alias's own collection pipeline, and leave everything else\n * exactly as it arrived.\n *\n * A reducer the right side does NOT rewrite (a plain `avg('client.score')`)\n * is returned untouched and keeps reading the dotted path off the joined row,\n * which is already correct — only a REWRITTEN reducer needs lifting, because\n * only a rewritten one has swapped to the bare name.\n */\nfunction joinAliasBinding(aliasVia: ReadonlyMap<string, ViaPipeline>): NoydbVia {\n return {\n brand: 'join-alias',\n posture: { encryptedAtRest: 'envelope', queryable: 'full', exportable: true, forgettable: true },\n // Claims no field: this binding must never shadow a left-side posture.\n covers: () => false,\n wrapReducers: (spec) => {\n let changed = false\n const out: Record<string, AliasReducer> = {}\n for (const [key, reducer] of Object.entries(spec as AliasSpec)) {\n const field = reducer.field\n const dot = field === undefined ? -1 : field.indexOf('.')\n const via = dot > 0 ? aliasVia.get(field!.slice(0, dot)) : undefined\n if (!via) { out[key] = reducer; continue }\n const bare = field!.slice(dot + 1)\n // One call: the right side's rewrite and its `queryable: 'none'`\n // refusal, both keyed by the bare name it actually declares.\n const wrapped = via.wrapReducers({ [key]: { ...reducer, field: bare } } as AliasSpec)[key]!\n if (wrapped === reducer || wrapped.field !== bare) { out[key] = reducer; continue }\n changed = true\n out[key] = liftToAlias(field!.slice(0, dot), field!, wrapped)\n }\n return changed ? out : spec\n },\n }\n}\n\n/**\n * The pipeline `aggregate()` / `groupBy()` should reduce THIS plan with: the\n * source's own, plus the alias binding when the plan can produce an alias.\n *\n * Returns the left pipeline unchanged when there is no alias to serve, so a\n * query with no joins — every query written before #1338 — is handed exactly\n * the object it was handed before.\n */\nexport function reduceViaFor(\n leftVia: ViaPipeline | undefined,\n aliasVia: ReadonlyMap<string, ViaPipeline> | undefined,\n): ViaPipeline | undefined {\n if (aliasVia === undefined || aliasVia.size === 0) return leftVia\n return ViaPipeline.build([...(leftVia?.bindings ?? []), joinAliasBinding(aliasVia)], leftVia?.taint)\n}\n\n/**\n * The other half, and the reason this one is not merely a missing feature:\n * a binding that REFUSES an aliased reducer instead of rewriting it.\n *\n * It is installed on the path where the legs do NOT run —\n * `groupBy('status').aggregate({ x: sum('client.credit') })`, a group key over\n * a left field with a reducer over an alias. `Query.aggregate()`'s guard never\n * saw that spec (the grouped terminal is `GroupedQuery.aggregate`, one object\n * along the chain), so it reduced `undefined` for every row and returned a\n * confident `0`. Group by the alias — then the legs run and the reducer is\n * rewritten by the branch above — or drop the grouping.\n */\nfunction refuseAliasBinding(aliases: ReadonlySet<string>): NoydbVia {\n return {\n brand: 'join-alias-refuse',\n posture: { encryptedAtRest: 'envelope', queryable: 'full', exportable: true, forgettable: true },\n covers: () => false,\n wrapReducers: (spec) => {\n for (const reducer of Object.values(spec as AliasSpec)) {\n const field = reducer.field\n if (field === undefined) continue\n const head = field.split('.')[0]!\n if (!aliases.has(head)) continue\n throw new Error(\n `Query.groupBy().aggregate(): reducer field \"${field}\" addresses the join alias ` +\n `\"${head}\", but the GROUP KEY does not — so the join legs are not applied for ` +\n `this grouping and the reducer would fold undefined into every bucket. ` +\n `Group by a field on \"${head}\" (then the legs run), or aggregate without ` +\n `grouping, where an aliased reducer is applied directly.`,\n )\n }\n return spec\n },\n }\n}\n\n/**\n * The pipeline for a grouped plan whose GROUP KEY stays on the left: the\n * source's own, plus the refusal above so an aliased reducer is a loud error\n * rather than a confident zero. No aliases in the plan → unchanged.\n */\nexport function refuseAliasReduceVia(\n leftVia: ViaPipeline | undefined,\n aliases: ReadonlySet<string>,\n): ViaPipeline | undefined {\n if (aliases.size === 0) return leftVia\n return ViaPipeline.build([...(leftVia?.bindings ?? []), refuseAliasBinding(aliases)], leftVia?.taint)\n}\n","/**\n * #1458 — **Reduce**: a fold over a result set.\n *\n * `aggregate` · `groupBy` · `window` · `distinct`\n *\n * Bodies moved out of `kernel/query/builder.ts` unchanged; see\n * `../relate/methods.ts` for why `this.plan` still resolves.\n *\n * ⚠️ This group depends on TWO others, and both edges are real rather than\n * tidyable:\n * - **Live**, for `incrementalMaintainer` — a live reduction maintains its\n * state through Live's maintainer (see `../internal/maintenance.ts`);\n * - **Relate**, for `reduceViaFor` / `refuseAliasReduceVia` — a reducer over\n * a JOIN ALIAS has to resolve the right side's Via pipeline, and refusing\n * it correctly is #1338's whole subject.\n * Neither is a layering accident: both are what \"reduce over a joined, live\n * result\" means.\n */\nimport type { DeclaredPredicate, InternalSource, QueryPlan } from '../builder.js'\n// @internal Find helpers. A reduce terminal narrows its record set through the\n// SAME executor the projection terminals use — sharing the functions is what\n// keeps \"which records does this terminal see\" one definition (#1347).\nimport { buildDictLabelResolver, candidateRecords, executeClausePipeline, filterRecords, fnViewDecoder } from '../builder.js'\nimport { readPath } from '../predicate.js'\nimport type { SourceChange } from '../live/incremental.js'\nimport type { QueryField } from '../../types.js'\nimport type { ViaPipeline } from '../../via/pipeline.js'\nimport type { JoinContext, JoinLeg } from '../relate/join.js'\nimport { reduceViaFor, refuseAliasReduceVia } from '../relate/join-reduce.js'\nimport type { DateTruncKey, GroupKey } from './date-trunc.js'\nimport { groupKeyName, isDateTruncKey, projectDateTruncKeys } from './date-trunc.js'\nimport { distinctKeyOf } from '../distinct-key.js'\nimport type { LiveMaintainer, GroupMaintenanceSource } from '../live/incremental.js'\nimport type { ReduceSpec, ReduceResult, ReductionUpstream, Reduction } from '../../../with-lookup/reduce/reduction.js'\nimport type { ReducerBuilder } from '../../../with-lookup/reduce/reducers.js'\nimport { bindDistinctReducers, reducerBuilder } from '../../../with-lookup/reduce/reducers.js'\nimport type { GroupedQuery, GroupedQueryN } from '../../../with-lookup/reduce/groupby.js'\nimport type { ReduceStrategy, WindowSpec, WindowedQuery } from '../../../with-lookup/reduce/strategy.js'\nimport { FieldNotQueryableError } from '../../errors.js'\n\nfunction assertNoJoinAliasField(\n fields: readonly string[],\n joins: readonly JoinLeg[],\n terminal: string,\n): void {\n if (joins.length === 0) return\n const aliases = new Set(joins.map(leg => leg.as))\n for (const field of fields) {\n const head = field.split('.')[0]!\n if (!aliases.has(head)) continue\n throw new Error(\n `Query.${terminal}(): field \"${field}\" addresses the join alias \"${head}\", but ` +\n `${terminal}() does not apply join legs — it would silently group every row ` +\n `under undefined. Joined aggregation is not supported. Either aggregate over ` +\n `the left collection's own fields, or use .crossJoin(\"${head}\", { as: … }), ` +\n `whose expansion IS visible to ${terminal}().`,\n )\n }\n}\n\n/** @internal — the mixin whose prototype `./index.ts` copies onto `Query`. */\n /* eslint-disable @typescript-eslint/no-unused-vars -- #1458: the parameter\n list must match `Query`'s exactly (T, S, Q, M) because `declare module`\n merges this mixin's `Pick` into it, and TypeScript requires every\n declaration of an interface to carry identical type parameters. A group\n whose methods happen to use only `T` still declares all four. */\nexport class ReduceMethods<\n T,\n S extends keyof T = never,\n Q extends keyof T & string = never,\n M extends keyof T & string = never,\n /* eslint-enable @typescript-eslint/no-unused-vars */\n> {\n declare protected readonly source: InternalSource\n declare protected readonly plan: QueryPlan\n declare protected readonly joinContext: JoinContext | undefined\n declare protected readonly reduceStrategy: ReduceStrategy\n declare protected readonly predicates: ReadonlyMap<string, DeclaredPredicate> | undefined\n declare protected matchedRecords: (terminal: string, forceJoins?: boolean) => readonly unknown[]\n declare protected decodeVia: (records: readonly unknown[], locale?: string) => unknown[]\n declare protected aliasVia: () => ReadonlyMap<string, ViaPipeline> | undefined\n declare protected incrementalMaintainer: (mode: 'rows' | 'records') => LiveMaintainer | undefined\n declare protected groupMaintenance: (derived: readonly DateTruncKey[]) => GroupMaintenanceSource | undefined\n declare toArray: (opts?: { locale?: string }) => T[]\n\n /**\n * The right-side change streams a joined query has to merge (#1338), deduped\n * by target the way `live()` does — a chain joining the same collection\n * twice must not double-fire. Extracted so a joined REDUCTION and a joined\n * live query cannot disagree on what \"the sources of this result\" means.\n */\n private rightSideUpstreams(): ReductionUpstream[] {\n const ctx = this.joinContext\n if (!ctx) return []\n const out: ReductionUpstream[] = []\n const seen = new Set<string>()\n for (const leg of this.plan.joins) {\n if (seen.has(leg.target)) continue\n seen.add(leg.target)\n const right = ctx.resolveSource(leg.target)\n if (!right?.subscribe) continue\n const subscribe = right.subscribe.bind(right)\n out.push({ subscribe: (cb: () => void) => subscribe(cb) })\n }\n return out\n }\n\n /**\n * Every alias this plan can produce, pipeline or not (#1338).\n *\n * `aliasVia()` above holds only the aliases whose collection compiles a Via\n * pipeline; refusal has to know about ALL of them, or an aliased reducer\n * over a plain right-side collection stays silent.\n */\n private aliasNames(): ReadonlySet<string> {\n const out = new Set<string>()\n for (const clause of this.plan.clauses) {\n if (clause.type !== 'crossJoin') continue\n out.add(clause.as)\n if (clause.leftAs !== undefined) out.add(clause.leftAs)\n }\n for (const leg of this.plan.joins) out.add(leg.as)\n return out\n }\n\n /** Is `field` rooted at an alias this plan produces? */\n\n private addressesAlias(field: string, aliases: ReadonlySet<string>): boolean {\n const dot = field.indexOf('.')\n return dot > 0 && aliases.has(field.slice(0, dot))\n }\n\n /**\n * The distinct values of `field` across the match set (#1347) — the\n * declarative form of `new Set(q.toArray().map(r => r[field]))`, which is\n * what consumers write today and which is wrong in two ways this is not.\n *\n * **Distinctness is decided on the canonical index key, not on the value\n * you get back.** For a Via-covered field that is the difference between a\n * right answer and a plausible one: money stores a scaled integer, and a\n * row written before the field was declared can spell it non-canonically\n * (`'0100'` for `'100'`). Deduping the stored strings reports two values\n * where there is one; deduping the FORMATTED strings makes the answer\n * depend on a locale this layer does not have (`'10.00'` vs `'10,00'`).\n * See `distinct-key.ts`. The values RETURNED are the decoded ones, so they\n * match what `toArray()` hands back for the same field.\n *\n * **Nullish values are excluded** — `null`/`undefined`/missing is the\n * absence of a value, not a distinct one. That is also the only choice\n * under which the index-backed and scanned paths can agree, since the hash\n * index does not hold nullish keys; see `distinctKeyOf`.\n *\n * Same match set as `count()`: where/filter apply, orderBy/limit/offset do\n * not. Values come back in first-encountered order.\n *\n * ⭐ INDEX-BACKED when the field carries a hash index AND the plan narrows\n * nothing — the buckets ARE the distinct key set, so the answer costs\n * O(distinct values) instead of O(records) and never decrypts a snapshot.\n * The guard is deliberately all-or-nothing: with a where clause the whole\n * collection's buckets are not the answer, and intersecting a candidate id\n * set with every bucket would cost more than the scan it replaced.\n *\n * ⛔ THE RETURN TYPE IS `(T & Record<F, unknown>)[F][]`, NOT\n * `F extends keyof T ? T[F][] : unknown[]` — and it must stay that way. The\n * conditional form reads better and type-checks at every call site, but a\n * DEFERRED conditional over `T` makes TypeScript give up relating\n * `Query<T>` to `Query<unknown>`, which makes `Collection<T>` invariant in\n * `T`, which breaks `vault.ts`'s `Map<string, Collection<unknown>>` cache\n * with four errors that name neither this method nor this file. The\n * intersection form resolves to the same thing (`T[F]` for a literal key,\n * `unknown` for a `string`-typed one) and stays covariant.\n */\n distinct<F extends QueryField<T, S>>(field: F): (T & Record<F, unknown>)[F][] {\n const name = field as string\n assertNoJoinAliasField([name], this.plan.joins, 'distinct')\n const via = this.source.via\n // Same posture gate `.where()` / `.orderBy()` apply: a `queryable: 'none'`\n // field (a blob handle) must not have its value set enumerated either.\n if (via?.postureFor(name)?.queryable === 'none') throw new FieldNotQueryableError(name)\n const decode = via?.hasResultDecode ? (r: unknown) => via.decodeResults(r) : (r: unknown) => r\n\n const indexes =\n this.plan.clauses.length === 0 && this.plan.joins.length === 0\n ? this.source.getIndexes?.()\n : undefined\n if (indexes && this.source.lookupById) {\n const lookupById = (id: string): unknown => this.source.lookupById?.(id)\n const reps = indexes.bucketRepresentatives(name)\n if (reps) {\n const fromIndex: unknown[] = []\n for (const id of reps) {\n const record = lookupById(id)\n if (record === undefined) continue\n fromIndex.push(readPath(decode(record), name))\n }\n return fromIndex as (T & Record<F, unknown>)[F][]\n }\n }\n\n const seen = new Set<string>()\n const out: unknown[] = []\n for (const record of this.matchedRecords('distinct')) {\n const key = distinctKeyOf(name, readPath(record, name), via)\n if (key === undefined || seen.has(key)) continue\n seen.add(key)\n out.push(readPath(decode(record), name))\n }\n return out as (T & Record<F, unknown>)[F][]\n }\n\n /**\n * Reduce the matching records through a named set of reducers.\n * the aggregation terminal.\n *\n * ```ts\n * const { total, n, avgAmount } = invoices.query()\n * .where('status', '==', 'open')\n * .aggregate({\n * total: sum('amount'),\n * n: count(),\n * avgAmount: avg('amount'),\n * })\n * .run()\n * ```\n *\n * Returns an `Reduction<R>` wrapper with two terminals:\n * - `.run(): R` — synchronous one-shot reduction\n * - `.live(): LiveReduction<R>` — reactive primitive that\n * re-runs the reduction whenever the source notifies of a\n * change. Always call `live.stop()` when finished.\n *\n * The reducer spec is bound here once and reused by both\n * terminals — this is why `.aggregate()` returns a wrapper instead\n * of being a direct terminal. Consumers who only need the static\n * value read `.run()`; consumers wiring a reactive UI read\n * `.live()`.\n *\n * Joins are NOT applied by default — the same logic as `.count()`. A leg is\n * projection-only, so running one just to throw the alias away would be\n * wasteful. ⭐ **The exception is a reducer that ADDRESSES an alias**\n * (#1338): then the joined relation IS what is being reduced, so the legs\n * run and the reducer is rewritten by the right collection's own Via\n * pipeline — money stays exact, and the right side's `queryable: 'none'`\n * posture refuses a field it would refuse locally. A live joined\n * aggregation also merges every right-side change stream, so it does not\n * quietly stop updating when the right side moves.\n *\n * Every reducer factory accepts an optional `{ seed }` parameter\n * that is plumbed through the protocol but unused by the\n * executor — that's constraint #2. When partition-aware\n * aggregation lands, the seed will carry running state across\n * partition boundaries without an API break.\n *\n * KNOWN GAP (sealed fields, bare-spec form): `where`/`orderBy`/`groupBy`\n * refuse a `sensitive` field at compile time, but a reducer over a sensitive\n * field in the BARE-SPEC form (e.g. `aggregate({ x: sum('ssn') })`) is NOT\n * refused — the reducer factories (`sum`/`min`/`max`/…) are standalone\n * `(field: string)` functions with no collection-type context. This form is\n * preserved as-is for backward compatibility.\n *\n * The BUILDER form closes this gap: `aggregate(b => ({ x: b.sum('field') }))`\n * types the builder's field parameter as `QueryField<T, S>`, refusing any\n * `sensitive` field at compile time. Use the builder form for new code that\n * aggregates over a collection with sensitive fields.\n *\n */\n aggregate<Spec extends ReduceSpec>(spec: Spec): Reduction<ReduceResult<Spec>>\n aggregate<Spec extends ReduceSpec>(build: (b: ReducerBuilder<T, S, M>) => Spec): Reduction<ReduceResult<Spec>>\n aggregate<Spec extends ReduceSpec>(\n specOrBuild: Spec | ((b: ReducerBuilder<T, S, M>) => Spec),\n ): Reduction<ReduceResult<Spec>> {\n let spec = typeof specOrBuild === 'function'\n ? (specOrBuild as (b: ReducerBuilder<T, S, M>) => Spec)((reducerBuilder as unknown) as ReducerBuilder<T, S, M>)\n : specOrBuild\n // #1338 — a reducer over a joined alias used to be refused here. It now\n // routes: the legs run (so the reducer sees the joined relation) and the\n // spec is wrapped by a pipeline that delegates each aliased field to the\n // RIGHT collection's own — which carries money's exact-BigInt rewrite and\n // the `queryable: 'none'` posture gate together. Reducers without a field\n // (e.g. `count()`) address nothing and are unaffected.\n const aliases = this.aliasNames()\n const aliasedReducer =\n this.plan.joins.length > 0 &&\n Object.values(spec).some(r => {\n const f = (r as { field?: unknown }).field\n return typeof f === 'string' && this.addressesAlias(f, aliases)\n })\n // Rewrite sum/min/max over Via-covered fields (e.g. money) into exact\n // BigInt reducers before the strategy runs (covers static run() and\n // live/MV paths).\n spec = (aliasedReducer ? reduceViaFor(this.source.via, this.aliasVia()) : this.source.via)?.wrapReducers(spec) ?? spec\n // #1347 — `countDistinct` needs the SAME canonicalizer the hash index\n // buckets by, and only the source knows it. Bound here, beside the\n // money rewrite, so both spec-wrapping seams stay in one place.\n spec = bindDistinctReducers(spec, this.source.via)\n // Closure over the current query. Produces the record set that\n // the aggregation reduces — same pipeline as `count()`, skipping\n // limit/offset because aggregation is over the full match set,\n // not a paginated slice. (A paginated aggregation would be a\n // different operation; see docs for rationale.)\n const source = this.source\n const clauses = this.plan.clauses\n const joinCtx = this.joinContext\n const hasCrossJoins = clauses.some(c => c.type === 'crossJoin')\n const fullScan = aliasedReducer\n // The reduced relation IS the joined one — same pipeline `count()` uses,\n // with the legs forced on (#1338).\n ? (): readonly unknown[] => this.matchedRecords('aggregate', true)\n : (): readonly unknown[] => {\n if (hasCrossJoins) {\n if (!joinCtx) throw new Error('Query.aggregate(): crossJoin requires a join context')\n return executeClausePipeline(source, clauses, joinCtx)\n }\n const { candidates, remainingClauses } = candidateRecords(source, clauses)\n return remainingClauses.length === 0\n ? candidates\n : filterRecords(candidates, remainingClauses, fnViewDecoder(source))\n }\n\n // #1341 — when the plan admits it, the match set is MAINTAINED across\n // change events instead of re-scanned, so a live reduction folds over an\n // incrementally-patched array. The reducers themselves still run a full\n // fold: the array they see is byte-for-byte the one a re-scan would\n // produce (same records, same candidate order), so an incremental result\n // is not merely close to a re-run's — it is the identical computation.\n const maintainer = this.incrementalMaintainer('records')\n const executeRecords = maintainer ? (): readonly unknown[] => maintainer.rows() : fullScan\n\n // Upstream for live mode. The left source always subscribes; a joined\n // aggregation ALSO merges every right-side change stream (#1338) — the\n // refusal's own doc named a live joined aggregate that silently stops\n // updating as half the work, and it is this half.\n const upstreams: ReductionUpstream[] = []\n if (aliasedReducer) upstreams.push(...this.rightSideUpstreams())\n if (source.subscribe) {\n const subscribe = source.subscribe.bind(source)\n upstreams.push(\n maintainer\n ? {\n // The maintainer folds the delta in BEFORE the reduction reads,\n // which is why it is wrapped here rather than subscribed\n // separately — emitter callback order would otherwise decide\n // whether the reduction saw the new record.\n subscribe: (cb: () => void) => {\n maintainer.attach()\n const unsubscribe = subscribe(change => {\n // A predicate that throws must not escape into the emitter —\n // the maintainer drops its state and the reduction's own\n // re-run raises the same error where it can be caught.\n try {\n maintainer.apply(change)\n } catch {\n maintainer.invalidate()\n }\n cb()\n })\n // Detach on teardown: the Reduction survives its LiveReduction\n // (`.run()` reuses the same closure), and a maintainer with no\n // subscription feeding it would go quietly out of date.\n return () => {\n maintainer.detach()\n unsubscribe()\n }\n },\n }\n : { subscribe: (cb: () => void) => subscribe(cb) },\n )\n }\n\n // #1414 — the gate travels INTO the reduction rather than wrapping it.\n // `Reduction.run()` is the synchronous terminal, and `await …aggregate(\n // spec).run()` is what an async guard's Σ-over-siblings invariant writes;\n // gating the builder call instead would break that awaited form.\n return this.reduceStrategy.aggregate<Spec>(executeRecords, spec, upstreams, this.source.hydration)\n }\n\n /**\n * Partition matching records into buckets keyed by a field, then\n * terminate with `.aggregate(spec)` to compute per-bucket\n * reducers..\n *\n * ```ts\n * const byClient = invoices.query()\n * .where('status', '==', 'open')\n * .groupBy('clientId')\n * .aggregate({ total: sum('amount'), n: count() })\n * .run()\n * // → [ { clientId: 'c1', total: 5250, n: 3 }, … ]\n * ```\n *\n * Result rows carry the group key value under the grouping field\n * name plus every reducer output from the spec. Buckets are\n * emitted in first-seen order — consumers who want a specific\n * ordering should `.sort()` downstream.\n *\n * **Cardinality caps:** a one-shot warning fires at 10_000\n * distinct groups; `GroupCardinalityError` throws at 100_000.\n * Grouping on a high-uniqueness field like `id` or `createdAt` is\n * almost always a query mistake — the error message names the\n * field and observed cardinality and suggests narrowing with\n * `.where()` first.\n *\n * **Null / undefined keys:** records with a missing or explicitly\n * `null` group field get their own buckets. `Map`-based\n * partitioning distinguishes `undefined` from `null`, so the two\n * cases do NOT merge. Consumers who want them merged should\n * coalesce upstream with `.filter()`.\n *\n * **Joins are not applied unless the GROUP KEY addresses an alias** (#1338).\n * For a left-side key the old rationale still holds — a leg is\n * projection-only, so running one inside a grouping pipeline would be\n * wasteful and could trigger `DanglingReferenceError` in strict mode for a\n * call whose intent is purely to bucket-and-reduce. For an aliased key\n * (`groupBy('client.region')`, the accounting-report shape) the legs run and\n * the buckets are the joined relation's: the key is stamped under its DOTTED\n * path (`row['client.region']`), and a left row whose right side is absent\n * reads `undefined` and lands in the undefined bucket — the same bucket a\n * missing left-side key gets, and still distinct from an explicit `null`.\n *\n * ⚠️ The decision is taken from the KEY, because the reducer spec arrives a\n * call later. A reducer over an alias under a LEFT key is therefore REFUSED\n * rather than folded as `undefined` — group by the alias, or aggregate\n * without grouping.\n *\n * **Filter clauses (`.filter(fn)`):** grouped queries still\n * support filter clauses in the underlying plan — they run in\n * the same candidate/filter pipeline that `.aggregate()` uses.\n * The performance caveat is the same: filter clauses cost O(N)\n * per record and can't be index-accelerated.\n */\n // The `field` param is `QueryField<T, S>` so a sealed (`sensitive`) field is\n // refused — grouping BY a sensitive field leaks its value distribution as\n // group-key labels. With `S = never` this is exactly `string` (zero churn).\n\n groupBy<F extends QueryField<T, S>>(field: F): GroupedQuery<T, F, S, M>\n groupBy<F extends readonly [QueryField<T, S>, QueryField<T, S>, ...QueryField<T, S>[]]>(\n ...fields: F\n ): GroupedQueryN<T, F, S, M>\n // Derived calendar keys (#1350). Listed last so the two field-name overloads\n // above still win for plain string arguments — a `DateTruncKey` is an object\n // and matches neither, so nothing existing re-resolves onto these.\n groupBy(key: DateTruncKey): GroupedQuery<T, string, S, M>\n // The string members stay `QueryField<T, S>`, so the sealed-field refusal\n // still applies to every plain key in a mixed grouping.\n groupBy(\n ...keys: readonly [\n QueryField<T, S> | DateTruncKey,\n QueryField<T, S> | DateTruncKey,\n ...(QueryField<T, S> | DateTruncKey)[],\n ]\n ): GroupedQueryN<T, readonly string[], S, M>\n groupBy(...keys: readonly GroupKey[]): GroupedQuery<T, string, S, M> | GroupedQueryN<T, readonly string[], S, M> {\n if (keys.length === 0) {\n throw new Error('.groupBy() requires at least one field')\n }\n // A derived key is bucketed into an ordinary row field before the grouping\n // pipeline runs, so everything downstream — cardinality caps, null/undefined\n // bucket semantics, live re-grouping — keeps working unchanged.\n const derived = keys.filter(isDateTruncKey)\n const fields: readonly string[] = keys.map(groupKeyName)\n // #1338 — a group key addressing an alias used to be refused. It now\n // decides the shape of the whole grouped pipeline: the legs run, and the\n // reducers are wrapped through the right side's pipeline.\n //\n // ⚠️ The decision is made HERE, from the group KEY alone, because the\n // reducer spec does not exist yet — it arrives one call later, at\n // `.aggregate()`. That is exactly why the left-key case installs a\n // REFUSING binding rather than nothing: a reducer over an alias under a\n // left-side key would otherwise fold undefined into every bucket, and the\n // grouped terminal is not the one `Query.aggregate()`'s guard sees.\n const aliases = this.aliasNames()\n const keyIsAliased = this.plan.joins.length > 0 && fields.some(f => this.addressesAlias(f, aliases))\n // Same record-producing closure as .aggregate() — grouped and\n // non-grouped aggregations execute over the same candidate set.\n // We inline the closure here instead of sharing a helper so the\n // builder stays allocation-friendly for the hot path.\n const source = this.source\n const clauses = this.plan.clauses\n const joinCtx = this.joinContext\n const hasCrossJoins = clauses.some(c => c.type === 'crossJoin')\n const executeRecords = keyIsAliased\n ? (): readonly unknown[] => this.matchedRecords('groupBy', true)\n : (): readonly unknown[] => {\n if (hasCrossJoins) {\n if (!joinCtx) throw new Error('Query.groupBy(): crossJoin requires a join context')\n return executeClausePipeline(source, clauses, joinCtx)\n }\n const { candidates, remainingClauses } = candidateRecords(source, clauses)\n return remainingClauses.length === 0\n ? candidates\n : filterRecords(candidates, remainingClauses, fnViewDecoder(source))\n }\n const executeGroupRecords = derived.length === 0\n ? executeRecords\n : (): readonly unknown[] => projectDateTruncKeys(executeRecords(), derived)\n\n // #1341 (grouped half) — the upstream passes the DELTA through, not just a\n // \"something changed\" ping, so `GroupedReduction.live()` can patch the one\n // or two buckets a change touches instead of re-grouping everything. A cb\n // that ignores the argument (every pre-#1341 caller) is unaffected.\n const upstreams: ReductionUpstream[] = []\n if (source.subscribe) {\n const subscribe = source.subscribe.bind(source)\n upstreams.push({ subscribe: (cb: (change?: SourceChange) => void) => subscribe(cb) })\n }\n if (keyIsAliased) upstreams.push(...this.rightSideUpstreams())\n // Withheld for a plan the whitelist refuses, and for an aliased key (which\n // implies joins, which the whitelist refuses anyway) — `.live()` then\n // re-runs the whole grouping, exactly as it did before.\n const maintenance = keyIsAliased ? undefined : this.groupMaintenance(derived)\n\n // The pipeline the reducers are wrapped with: the right side's rewrite\n // when the legs run, the refusal when they do not (#1338).\n const reduceVia = keyIsAliased\n ? reduceViaFor(this.source.via, this.aliasVia())\n : refuseAliasReduceVia(this.source.via, aliases)\n\n // Dict-label resolution is single-field only — the <field>Label\n // projection has no meaningful shape for composite keys. A derived\n // calendar key is never a dictKey, so it skips the lookup entirely.\n if (fields.length === 1) {\n const field = fields[0]!\n const dictLabelResolver = derived.length > 0\n ? undefined\n : buildDictLabelResolver(this.joinContext, field)\n return this.reduceStrategy.groupBy<T, string, S, M>(\n executeGroupRecords,\n field,\n upstreams,\n dictLabelResolver,\n reduceVia,\n maintenance,\n )\n }\n return this.reduceStrategy.groupByN<T, readonly string[], S, M>(\n executeGroupRecords,\n fields,\n upstreams,\n reduceVia,\n maintenance,\n )\n }\n\n /**\n * SQL window functions over the query's result rows (#1349) — terminate with\n * `.select(spec)`.\n *\n * ```ts\n * invoices.query()\n * .where('status', '==', 'open')\n * .window({ partitionBy: 'clientId', orderBy: 'date' })\n * .select({ balance: runningSum('amount'), prev: lag('amount', 1), n: rowNumber() })\n * .run()\n * ```\n *\n * ⭐ Distinguished from `.groupBy()` by ARITY, not by capability: grouping\n * emits one row per bucket and drops the source rows; a window keeps EVERY\n * row and attaches per-row values computed over its partition.\n *\n * **The record set is `toArray()`'s** — the full pipeline, including joins,\n * `orderBy`, `offset`/`limit` and the Via decode. That differs deliberately\n * from `.groupBy()`, which reads the candidate/filter pipeline only: a\n * window returns ROWS, so the query's own ordering and paging are exactly\n * what decides which rows there are and how they are presented. `orderBy`\n * inside the window decides only how a partition is WALKED.\n *\n * **Frame:** `rows unbounded preceding → current row`, and only that in v1.\n *\n * `partitionBy` / `orderBy` accept a `dateTrunc()` key (#1350) alongside\n * plain field names; the derived bucket is used for partitioning and is not\n * stamped on the output row. A sealed (`sensitive`) field is refused at\n * compile time, same as `.groupBy()`.\n *\n * ⛔ **Not an `explain()` node.** `explain()` is a terminal on `Query`;\n * `.window()` leaves `Query` for `WindowedQuery`, so there is nothing past\n * here to add a node to — the same reason #1336's post-group ops are absent.\n */\n window(spec: WindowSpec<QueryField<T, S>>): WindowedQuery<T> {\n const upstreams: ReductionUpstream[] = []\n if (this.source.subscribe) {\n const subscribe = this.source.subscribe.bind(this.source)\n upstreams.push({ subscribe: (cb: () => void) => subscribe(cb) })\n }\n return this.reduceStrategy.window<T>(\n () => this.toArray() as readonly unknown[],\n spec as WindowSpec,\n upstreams,\n this.source.via,\n )\n }\n}\n\n/** The public Reduce surface — merged into `Query` by `./index.ts`. */\nexport type ReduceSurface<\n T,\n S extends keyof T = never,\n Q extends keyof T & string = never,\n M extends keyof T & string = never,\n> = Pick<ReduceMethods<T, S, Q, M>, 'aggregate' | 'groupBy' | 'window' | 'distinct'>\n\n","/**\n * #1458 — the **Reduce** half of `ScanBuilder`.\n *\n * `aggregate` · `groupBy` · the grouped-scan handle they return.\n *\n * See `../relate/scan-methods.ts` for why `ScanBuilder` is split at all.\n *\n * ⭐ **The streaming memory property travels with the code.**\n * `scan().aggregate()` is O(reducers), not O(records) — it steps every reducer\n * per record and never collects the stream — and `groupBy().aggregate()` is\n * O(groups). That is a code-level invariant visible in the bodies below, so\n * moving them wholesale is what preserves it; re-expressing them over the\n * eager reducers would have quietly turned an unbounded scan into an array.\n */\nimport type { MultiReduceResult, ScanPageProvider } from '../scan-builder.js'\nimport type { QueryField } from '../../types.js'\nimport type { Clause } from '../predicate.js'\nimport { readPath } from '../predicate.js'\nimport type { ViaPipeline } from '../../via/pipeline.js'\nimport type { JoinContext, JoinLeg } from '../relate/join.js'\nimport type { ReducerBuilder } from '../../../with-lookup/reduce/reducers.js'\nimport { bindDistinctReducers, reducerBuilder } from '../../../with-lookup/reduce/reducers.js'\nimport type { ReduceSpec, ReduceResult } from '../../../with-lookup/reduce/reduction.js'\nimport type { DateTruncKey } from './date-trunc.js'\nimport { groupKeyName, isDateTruncKey, projectDateTruncKeys } from './date-trunc.js'\nimport { GroupCardinalityError } from '../../errors.js'\n\n/** @internal — the mixin whose prototype `./index.ts` copies onto `ScanBuilder`. */\nexport class ScanReduceMethods<T, S extends keyof T = never, M extends keyof T & string = never> {\n declare protected readonly pageProvider: ScanPageProvider<T>\n declare protected readonly pageSize: number\n declare protected readonly clauses: readonly Clause[]\n declare protected readonly joins: readonly JoinLeg[]\n declare protected readonly joinContext: JoinContext | undefined\n declare protected readonly via: ViaPipeline | undefined\n declare protected decodeVia: (record: T) => T\n declare protected recordMatches: (record: T, clauses?: readonly Clause[]) => boolean\n declare [Symbol.asyncIterator]: () => AsyncIterator<T>\n\n async aggregate<const Specs extends readonly ReduceSpec[]>(specs: Specs): Promise<MultiReduceResult<Specs>>\n async aggregate<Spec extends ReduceSpec>(spec: Spec): Promise<ReduceResult<Spec>>\n async aggregate<Spec extends ReduceSpec>(build: (b: ReducerBuilder<T, S, M>) => Spec): Promise<ReduceResult<Spec>>\n async aggregate<Spec extends ReduceSpec>(\n specOrBuild: Spec | readonly ReduceSpec[] | ((b: ReducerBuilder<T, S, M>) => Spec),\n ): Promise<ReduceResult<Spec> | unknown[]> {\n // #1340 — the multi-spec form. N independent specs, ONE pass: the states\n // for every spec are stepped from the same yielded record, so the page\n // provider is walked exactly once no matter how many specs are passed.\n // (Calling `.aggregate(specA)` then `.aggregate(specB)` costs two full\n // scans — the immutable-builder docs above say so, and this is the fix.)\n if (Array.isArray(specOrBuild)) return this.aggregateMany(specOrBuild as readonly ReduceSpec[])\n // Opt-in builder form `aggregate(b => spec)`: `b`'s field args are\n // `QueryField<T, S>`, refusing sensitive fields (the standalone-spec form\n // stays unrefused for back-compat), and `sum`/`min`/`max` over a declared\n // `moneyFields` (`M`) member return a `MoneyString`. Mirrors `Query.aggregate`.\n const spec: Spec = typeof specOrBuild === 'function'\n ? (specOrBuild as (b: ReducerBuilder<T, S, M>) => Spec)(reducerBuilder as unknown as ReducerBuilder<T, S, M>)\n // `Array.isArray` above does not narrow a READONLY array out of the\n // union, so the array arm is already returned and this cast is the\n // remainder — a plain spec.\n : (specOrBuild as Spec)\n this.via?.refuseUnqueryableReducers(spec)\n const keys = Object.keys(spec)\n // Per-reducer state. Exactly |keys| entries, never grows with\n // the record count — that's the O(reducers) memory guarantee.\n const state: Record<string, unknown> = {}\n for (const key of keys) {\n state[key] = spec[key]!.init()\n }\n\n // Record-by-record streaming step. `for await (… of this)`\n // invokes the Symbol.asyncIterator above, which honors the\n // clause list, so filtered-out records never reach the step\n // loop — they're dropped at the iterator boundary.\n for await (const record of this) {\n for (const key of keys) {\n state[key] = spec[key]!.step(state[key], record)\n }\n }\n\n const result: Record<string, unknown> = {}\n for (const key of keys) {\n result[key] = spec[key]!.finalize(state[key])\n }\n return result as ReduceResult<Spec>\n }\n\n /**\n * The multi-spec `.aggregate([specA, specB, …])` executor (#1340).\n *\n * One iteration of the scan, `Σ|spec|` reducer states — memory stays\n * O(reducers) exactly as the single-spec terminal's does, with the sum taken\n * across specs instead of within one. Every spec's state is stepped from the\n * SAME yielded record, so the page provider is read once per page.\n *\n * Semantics per spec are identical to the single-spec form, deliberately:\n * the posture gate is `refuseUnqueryableReducers` (metadata only), NOT the\n * full `wrapReducers` — so `aggregate([a, b])` returns exactly what\n * `aggregate(a)` and `aggregate(b)` return, and the array form is a pure\n * cost optimisation with no behavioural delta to reason about. (The grouped\n * scan path DOES wrap — it has to agree with the eager grouped path, which\n * wraps. See `scan-groupby.ts`.)\n */\n private async aggregateMany(specs: readonly ReduceSpec[]): Promise<unknown[]> {\n for (const spec of specs) this.via?.refuseUnqueryableReducers(spec)\n const keysPerSpec = specs.map((spec) => Object.keys(spec))\n const states: Record<string, unknown>[] = specs.map((spec, i) => {\n const state: Record<string, unknown> = {}\n for (const key of keysPerSpec[i]!) state[key] = spec[key]!.init()\n return state\n })\n\n for await (const record of this) {\n for (let i = 0; i < specs.length; i++) {\n const spec = specs[i]!\n const state = states[i]!\n for (const key of keysPerSpec[i]!) state[key] = spec[key]!.step(state[key], record)\n }\n }\n\n return specs.map((spec, i) => {\n const result: Record<string, unknown> = {}\n for (const key of keysPerSpec[i]!) result[key] = spec[key]!.finalize(states[i]![key])\n return result\n })\n }\n\n /**\n * Group the scan stream and reduce each group — `#1340`.\n *\n * ```ts\n * const byClient = await invoices.scan()\n * .where('status', '==', 'open')\n * .groupBy('clientId', { maxGroups: 5_000 })\n * .aggregate({ total: sum('amount'), n: count() })\n * ```\n *\n * ⚠️ **This is the one scan terminal whose memory is NOT O(pageSize).** It\n * holds one reducer state per group, which is why the budget is explicit:\n * `maxGroups` defaults to the eager path's 100_000-group ceiling and is\n * REFUSED — never truncated — the moment a scan would exceed it. Price the\n * budget before raising it: an exact `median`/`percentile`, a `mode` or a\n * `countDistinct` holds O(values) per group, so an unbounded scan wants\n * `{ approx: true }` on the quantiles. Full rationale in `scan-groupby.ts`.\n *\n * The key may be a field name or a `dateTrunc()` derived calendar key (the\n * monthly-rollup shape); grouping BY a `sensitive` field is refused at\n * compile time, same as `Query.groupBy()`.\n */\n groupBy<F extends QueryField<T, S>>(field: F, opts?: { maxGroups?: number }): ScanGroupedScan<T, F, S, M>\n groupBy(key: DateTruncKey, opts?: { maxGroups?: number }): ScanGroupedScan<T, string, S, M>\n groupBy(key: QueryField<T, S> | DateTruncKey, opts?: { maxGroups?: number }): ScanGroupedScan<T, string, S, M> {\n return new ScanGroupedScan<T, string, S, M>(\n this,\n key as string | DateTruncKey,\n opts?.maxGroups ?? SCAN_GROUPBY_DEFAULT_MAX_GROUPS,\n this.via,\n )\n }\n}\n\n/** The public Reduce surface of `ScanBuilder` — merged by `./index.ts`. */\nexport type ScanReduceSurface<\n T,\n S extends keyof T = never,\n M extends keyof T & string = never,\n> = Pick<ScanReduceMethods<T, S, M>, 'aggregate' | 'groupBy'>\n\n/**\n * Default group ceiling — the same constant the eager `.groupBy()` enforces.\n * Duplicated as a literal rather than imported from\n * `with-lookup/reduce/groupby.ts` on purpose: that module carries the eager\n * `groupAndReduce` + `GroupedReduction` classes, and the always-on kernel must\n * not pull them in to read one number. The pairing is asserted by a test.\n */\nexport const SCAN_GROUPBY_DEFAULT_MAX_GROUPS = 100_000\n\n/**\n * Single-field spelling of `canonicalGroupKey` (`with-lookup/reduce/canonical-key.ts`).\n *\n * ⛔ Inlined, not imported, and not by preference: `check-architecture`'s\n * `port-layering` rule grandfathers this file for exactly two\n * `with-lookup/reduce/*` specifiers (`reducers.js`, `reduction.js`), and\n * `canonical-key.js` is not one of them — a new one may not be added\n * silently. The eager helper sorts its field list and serialises\n * each value — with exactly one field, that reduces to this line, including\n * the part that matters: `undefined` gets a sentinel so a MISSING key and an\n * explicit `null` land in different buckets, as they do under\n * `Query.groupBy()`. The agreement is held by test, not by comment\n * (`query-scan-groupby.test.ts` — null/undefined bucketing, and full equality\n * with the eager path).\n */\nfunction scanGroupKey(field: string, value: unknown): string {\n return `${field}=${value === undefined ? 'undefined' : JSON.stringify(value)}`\n}\n\n/** Group-key result-row shape: the key under its own name, plus the reducers. */\n\nexport type ScanGroupedRow<F extends string, R> = { [K in F]: unknown } & R\n\n/**\n * Chainable wrapper returned by `ScanBuilder.groupBy()`. The only operation on\n * it is `.aggregate()` — same minimal shape as `GroupedQuery`.\n */\nexport class ScanGroupedScan<\n T,\n F extends string,\n S extends keyof T = never,\n M extends keyof T & string = never,\n> {\n constructor(\n private readonly stream: AsyncIterable<T>,\n private readonly key: string | DateTruncKey,\n private readonly maxGroups: number,\n private readonly via: ViaPipeline | undefined,\n ) {\n if (!Number.isInteger(maxGroups) || maxGroups < 1) {\n throw new Error(\n `scan().groupBy(): { maxGroups: ${String(maxGroups)} } must be a positive integer — ` +\n `it is the declared ceiling on how many reducer states the grouped scan may hold.`,\n )\n }\n }\n\n /**\n * Fold the scan into one row per group. Resolves to `R[]`, ordered by each\n * group's FIRST-SEEN position in the stream (`Map` insertion order), which\n * is the same ordering rule the eager path documents.\n *\n * ```ts\n * const byMonth = await invoices.scan()\n * .where('status', '==', 'paid')\n * .groupBy(dateTrunc('closedAt', 'month', { as: 'month', timeZone: 'UTC' }), { maxGroups: 240 })\n * .aggregate({ total: sum('amount'), n: count() })\n * ```\n */\n async aggregate<Spec extends ReduceSpec>(spec: Spec): Promise<ScanGroupedRow<F, ReduceResult<Spec>>[]>\n async aggregate<Spec extends ReduceSpec>(\n build: (b: ReducerBuilder<T, S, M>) => Spec,\n ): Promise<ScanGroupedRow<F, ReduceResult<Spec>>[]>\n async aggregate<Spec extends ReduceSpec>(\n specOrBuild: Spec | ((b: ReducerBuilder<T, S, M>) => Spec),\n ): Promise<ScanGroupedRow<F, ReduceResult<Spec>>[]> {\n const raw: Spec =\n typeof specOrBuild === 'function'\n ? (specOrBuild as (b: ReducerBuilder<T, S, M>) => Spec)(reducerBuilder as unknown as ReducerBuilder<T, S, M>)\n : specOrBuild\n // Same reducer rewriting as the eager grouped path, and for the same\n // reason: a money `sum` must accumulate per-currency BigInt totals, and a\n // `countDistinct` must dedup on the canonical index key — otherwise a\n // grouped scan and a grouped query disagree on identical data.\n // `wrapReducers` runs the `queryable: 'none'` posture refusal itself.\n const spec: ReduceSpec = bindDistinctReducers(this.via ? this.via.wrapReducers(raw) : raw, this.via)\n const keys = Object.keys(spec)\n\n const field = groupKeyName(this.key)\n const derived: readonly DateTruncKey[] = isDateTruncKey(this.key) ? [this.key] : []\n const groups = new Map<string, { keyValue: unknown; state: Record<string, unknown> }>()\n\n for await (const record of this.stream) {\n // A derived calendar key is stamped onto a shallow copy before bucketing\n // — the reducers then see an ordinary row carrying an ordinary field,\n // exactly as they do under `Query.groupBy(dateTrunc(...))`.\n const row = derived.length === 0 ? record : projectDateTruncKeys([record], derived)[0]!\n const keyValue = readPath(row, field)\n const dedupKey = scanGroupKey(field, keyValue)\n let group = groups.get(dedupKey)\n if (group === undefined) {\n if (groups.size >= this.maxGroups) {\n // Loud and early: the state for this group is never allocated, so\n // the refusal fires at the budget, not after blowing through it.\n throw new GroupCardinalityError(field, groups.size + 1, this.maxGroups, 'scan')\n }\n const state: Record<string, unknown> = {}\n for (const k of keys) state[k] = spec[k]!.init()\n group = { keyValue, state }\n groups.set(dedupKey, group)\n }\n for (const k of keys) group.state[k] = spec[k]!.step(group.state[k], row)\n }\n\n const out: ScanGroupedRow<F, ReduceResult<Spec>>[] = []\n for (const group of groups.values()) {\n // Group key first, then the reducer outputs — same row shape as the\n // eager path, which tests assert by key order.\n const outRow: Record<string, unknown> = { [field]: group.keyValue }\n for (const k of keys) outRow[k] = spec[k]!.finalize(group.state[k])\n out.push(outRow as ScanGroupedRow<F, ReduceResult<Spec>>)\n }\n return out\n }\n}\n","/**\n * #1458 — the Reduce install. See `../relate/install.ts` for why this is a\n * function called from the entry rather than a module-level side effect.\n */\nimport { Query } from '../builder.js'\nimport { ScanBuilder } from '../scan-builder.js'\nimport { installMethods } from '../internal/core.js'\nimport { MaintenanceMethods } from '../internal/maintenance.js'\nimport { ReduceMethods } from './methods.js'\nimport { ScanReduceMethods } from './scan-methods.js'\n\nlet done = false\n\n/** Idempotent: the root barrel and the subpath entry both call it. */\nexport function installReduce(): void {\n if (done) return\n done = true\n installMethods(Query.prototype, MaintenanceMethods)\n installMethods(Query.prototype, ReduceMethods)\n installMethods(ScanBuilder.prototype, ScanReduceMethods)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEA,SAAS,YAAY,OAAe,MAAc,OAAmC;AACnF,QAAM,QAAQ,CAAC,WACb,WAAW,QAAQ,OAAO,WAAW,WAAW,SAAa,OAAmC,KAAK;AACvG,SAAO;AAAA,IACL,GAAG;AAAA;AAAA;AAAA,IAGH,OAAO;AAAA,IACP,MAAM,MAAM,MAAM,KAAK;AAAA,IACvB,MAAM,CAAC,OAAO,WAAW,MAAM,KAAK,OAAO,MAAM,MAAM,CAAC;AAAA,IACxD,GAAI,MAAM,SAAS,EAAE,QAAQ,CAAC,OAAgB,WAAoB,MAAM,OAAQ,OAAO,MAAM,MAAM,CAAC,EAAE,IAAI,CAAC;AAAA,IAC3G,UAAU,CAAC,UAAU,MAAM,SAAS,KAAK;AAAA,EAC3C;AACF;AAYA,SAAS,iBAAiB,UAAsD;AAC9E,SAAO;AAAA,IACL,OAAO;AAAA,IACP,SAAS,EAAE,iBAAiB,YAAY,WAAW,QAAQ,YAAY,MAAM,aAAa,KAAK;AAAA;AAAA,IAE/F,QAAQ,MAAM;AAAA,IACd,cAAc,CAAC,SAAS;AACtB,UAAI,UAAU;AACd,YAAM,MAAoC,CAAC;AAC3C,iBAAW,CAAC,KAAK,OAAO,KAAK,OAAO,QAAQ,IAAiB,GAAG;AAC9D,cAAM,QAAQ,QAAQ;AACtB,cAAM,MAAM,UAAU,SAAY,KAAK,MAAM,QAAQ,GAAG;AACxD,cAAM,MAAM,MAAM,IAAI,SAAS,IAAI,MAAO,MAAM,GAAG,GAAG,CAAC,IAAI;AAC3D,YAAI,CAAC,KAAK;AAAE,cAAI,GAAG,IAAI;AAAS;AAAA,QAAS;AACzC,cAAM,OAAO,MAAO,MAAM,MAAM,CAAC;AAGjC,cAAM,UAAU,IAAI,aAAa,EAAE,CAAC,GAAG,GAAG,EAAE,GAAG,SAAS,OAAO,KAAK,EAAE,CAAc,EAAE,GAAG;AACzF,YAAI,YAAY,WAAW,QAAQ,UAAU,MAAM;AAAE,cAAI,GAAG,IAAI;AAAS;AAAA,QAAS;AAClF,kBAAU;AACV,YAAI,GAAG,IAAI,YAAY,MAAO,MAAM,GAAG,GAAG,GAAG,OAAQ,OAAO;AAAA,MAC9D;AACA,aAAO,UAAU,MAAM;AAAA,IACzB;AAAA,EACF;AACF;AAUO,SAAS,aACd,SACA,UACyB;AACzB,MAAI,aAAa,UAAa,SAAS,SAAS,EAAG,QAAO;AAC1D,SAAO,YAAY,MAAM,CAAC,GAAI,SAAS,YAAY,CAAC,GAAI,iBAAiB,QAAQ,CAAC,GAAG,SAAS,KAAK;AACrG;AAcA,SAAS,mBAAmB,SAAwC;AAClE,SAAO;AAAA,IACL,OAAO;AAAA,IACP,SAAS,EAAE,iBAAiB,YAAY,WAAW,QAAQ,YAAY,MAAM,aAAa,KAAK;AAAA,IAC/F,QAAQ,MAAM;AAAA,IACd,cAAc,CAAC,SAAS;AACtB,iBAAW,WAAW,OAAO,OAAO,IAAiB,GAAG;AACtD,cAAM,QAAQ,QAAQ;AACtB,YAAI,UAAU,OAAW;AACzB,cAAM,OAAO,MAAM,MAAM,GAAG,EAAE,CAAC;AAC/B,YAAI,CAAC,QAAQ,IAAI,IAAI,EAAG;AACxB,cAAM,IAAI;AAAA,UACR,+CAA+C,KAAK,+BAC9C,IAAI,wKAEgB,IAAI;AAAA,QAEhC;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAOO,SAAS,qBACd,SACA,SACyB;AACzB,MAAI,QAAQ,SAAS,EAAG,QAAO;AAC/B,SAAO,YAAY,MAAM,CAAC,GAAI,SAAS,YAAY,CAAC,GAAI,mBAAmB,OAAO,CAAC,GAAG,SAAS,KAAK;AACtG;;;AChJA,SAAS,uBACP,QACA,OACA,UACM;AACN,MAAI,MAAM,WAAW,EAAG;AACxB,QAAM,UAAU,IAAI,IAAI,MAAM,IAAI,SAAO,IAAI,EAAE,CAAC;AAChD,aAAW,SAAS,QAAQ;AAC1B,UAAM,OAAO,MAAM,MAAM,GAAG,EAAE,CAAC;AAC/B,QAAI,CAAC,QAAQ,IAAI,IAAI,EAAG;AACxB,UAAM,IAAI;AAAA,MACR,SAAS,QAAQ,cAAc,KAAK,+BAA+B,IAAI,UAClE,QAAQ,yMAE6C,IAAI,qDAC3B,QAAQ;AAAA,IAC7C;AAAA,EACF;AACF;AAQO,IAAM,gBAAN,MAML;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBQ,qBAA0C;AAChD,UAAM,MAAM,KAAK;AACjB,QAAI,CAAC,IAAK,QAAO,CAAC;AAClB,UAAM,MAA2B,CAAC;AAClC,UAAM,OAAO,oBAAI,IAAY;AAC7B,eAAW,OAAO,KAAK,KAAK,OAAO;AACjC,UAAI,KAAK,IAAI,IAAI,MAAM,EAAG;AAC1B,WAAK,IAAI,IAAI,MAAM;AACnB,YAAM,QAAQ,IAAI,cAAc,IAAI,MAAM;AAC1C,UAAI,CAAC,OAAO,UAAW;AACvB,YAAM,YAAY,MAAM,UAAU,KAAK,KAAK;AAC5C,UAAI,KAAK,EAAE,WAAW,CAAC,OAAmB,UAAU,EAAE,EAAE,CAAC;AAAA,IAC3D;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,aAAkC;AACxC,UAAM,MAAM,oBAAI,IAAY;AAC5B,eAAW,UAAU,KAAK,KAAK,SAAS;AACtC,UAAI,OAAO,SAAS,YAAa;AACjC,UAAI,IAAI,OAAO,EAAE;AACjB,UAAI,OAAO,WAAW,OAAW,KAAI,IAAI,OAAO,MAAM;AAAA,IACxD;AACA,eAAW,OAAO,KAAK,KAAK,MAAO,KAAI,IAAI,IAAI,EAAE;AACjD,WAAO;AAAA,EACT;AAAA;AAAA,EAIQ,eAAe,OAAe,SAAuC;AAC3E,UAAM,MAAM,MAAM,QAAQ,GAAG;AAC7B,WAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,MAAM,GAAG,GAAG,CAAC;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0CA,SAAqC,OAAyC;AAC5E,UAAM,OAAO;AACb,2BAAuB,CAAC,IAAI,GAAG,KAAK,KAAK,OAAO,UAAU;AAC1D,UAAM,MAAM,KAAK,OAAO;AAGxB,QAAI,KAAK,WAAW,IAAI,GAAG,cAAc,OAAQ,OAAM,IAAI,uBAAuB,IAAI;AACtF,UAAM,SAAS,KAAK,kBAAkB,CAAC,MAAe,IAAI,cAAc,CAAC,IAAI,CAAC,MAAe;AAE7F,UAAM,UACJ,KAAK,KAAK,QAAQ,WAAW,KAAK,KAAK,KAAK,MAAM,WAAW,IACzD,KAAK,OAAO,aAAa,IACzB;AACN,QAAI,WAAW,KAAK,OAAO,YAAY;AACrC,YAAM,aAAa,CAAC,OAAwB,KAAK,OAAO,aAAa,EAAE;AACvE,YAAM,OAAO,QAAQ,sBAAsB,IAAI;AAC/C,UAAI,MAAM;AACR,cAAM,YAAuB,CAAC;AAC9B,mBAAW,MAAM,MAAM;AACrB,gBAAM,SAAS,WAAW,EAAE;AAC5B,cAAI,WAAW,OAAW;AAC1B,oBAAU,KAAK,SAAS,OAAO,MAAM,GAAG,IAAI,CAAC;AAAA,QAC/C;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,OAAO,oBAAI,IAAY;AAC7B,UAAM,MAAiB,CAAC;AACxB,eAAW,UAAU,KAAK,eAAe,UAAU,GAAG;AACpD,YAAM,MAAM,cAAc,MAAM,SAAS,QAAQ,IAAI,GAAG,GAAG;AAC3D,UAAI,QAAQ,UAAa,KAAK,IAAI,GAAG,EAAG;AACxC,WAAK,IAAI,GAAG;AACZ,UAAI,KAAK,SAAS,OAAO,MAAM,GAAG,IAAI,CAAC;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAAA,EA4DA,UACE,aAC+B;AAC/B,QAAI,OAAO,OAAO,gBAAgB,aAC7B,YAAsD,cAAqD,IAC5G;AAOJ,UAAM,UAAU,KAAK,WAAW;AAChC,UAAM,iBACJ,KAAK,KAAK,MAAM,SAAS,KACzB,OAAO,OAAO,IAAI,EAAE,KAAK,OAAK;AAC5B,YAAM,IAAK,EAA0B;AACrC,aAAO,OAAO,MAAM,YAAY,KAAK,eAAe,GAAG,OAAO;AAAA,IAChE,CAAC;AAIH,YAAQ,iBAAiB,aAAa,KAAK,OAAO,KAAK,KAAK,SAAS,CAAC,IAAI,KAAK,OAAO,MAAM,aAAa,IAAI,KAAK;AAIlH,WAAO,qBAAqB,MAAM,KAAK,OAAO,GAAG;AAMjD,UAAM,SAAS,KAAK;AACpB,UAAM,UAAU,KAAK,KAAK;AAC1B,UAAM,UAAU,KAAK;AACrB,UAAM,gBAAgB,QAAQ,KAAK,OAAK,EAAE,SAAS,WAAW;AAC9D,UAAM,WAAW,iBAGb,MAA0B,KAAK,eAAe,aAAa,IAAI,IAC/D,MAA0B;AAC1B,UAAI,eAAe;AACjB,YAAI,CAAC,QAAS,OAAM,IAAI,MAAM,sDAAsD;AACpF,eAAO,sBAAsB,QAAQ,SAAS,OAAO;AAAA,MACvD;AACA,YAAM,EAAE,YAAY,iBAAiB,IAAI,iBAAiB,QAAQ,OAAO;AACzE,aAAO,iBAAiB,WAAW,IAC/B,aACA,cAAc,YAAY,kBAAkB,cAAc,MAAM,CAAC;AAAA,IACvE;AAQF,UAAM,aAAa,KAAK,sBAAsB,SAAS;AACvD,UAAM,iBAAiB,aAAa,MAA0B,WAAW,KAAK,IAAI;AAMlF,UAAM,YAAiC,CAAC;AACxC,QAAI,eAAgB,WAAU,KAAK,GAAG,KAAK,mBAAmB,CAAC;AAC/D,QAAI,OAAO,WAAW;AACpB,YAAM,YAAY,OAAO,UAAU,KAAK,MAAM;AAC9C,gBAAU;AAAA,QACR,aACI;AAAA;AAAA;AAAA;AAAA;AAAA,UAKE,WAAW,CAAC,OAAmB;AAC7B,uBAAW,OAAO;AAClB,kBAAM,cAAc,UAAU,YAAU;AAItC,kBAAI;AACF,2BAAW,MAAM,MAAM;AAAA,cACzB,QAAQ;AACN,2BAAW,WAAW;AAAA,cACxB;AACA,iBAAG;AAAA,YACL,CAAC;AAID,mBAAO,MAAM;AACX,yBAAW,OAAO;AAClB,0BAAY;AAAA,YACd;AAAA,UACF;AAAA,QACF,IACA,EAAE,WAAW,CAAC,OAAmB,UAAU,EAAE,EAAE;AAAA,MACrD;AAAA,IACF;AAMA,WAAO,KAAK,eAAe,UAAgB,gBAAgB,MAAM,WAAW,KAAK,OAAO,SAAS;AAAA,EACnG;AAAA,EA6EA,WAAW,MAAsG;AAC/G,QAAI,KAAK,WAAW,GAAG;AACrB,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AAIA,UAAM,UAAU,KAAK,OAAO,cAAc;AAC1C,UAAM,SAA4B,KAAK,IAAI,YAAY;AAWvD,UAAM,UAAU,KAAK,WAAW;AAChC,UAAM,eAAe,KAAK,KAAK,MAAM,SAAS,KAAK,OAAO,KAAK,OAAK,KAAK,eAAe,GAAG,OAAO,CAAC;AAKnG,UAAM,SAAS,KAAK;AACpB,UAAM,UAAU,KAAK,KAAK;AAC1B,UAAM,UAAU,KAAK;AACrB,UAAM,gBAAgB,QAAQ,KAAK,OAAK,EAAE,SAAS,WAAW;AAC9D,UAAM,iBAAiB,eACnB,MAA0B,KAAK,eAAe,WAAW,IAAI,IAC7D,MAA0B;AAC1B,UAAI,eAAe;AACjB,YAAI,CAAC,QAAS,OAAM,IAAI,MAAM,oDAAoD;AAClF,eAAO,sBAAsB,QAAQ,SAAS,OAAO;AAAA,MACvD;AACA,YAAM,EAAE,YAAY,iBAAiB,IAAI,iBAAiB,QAAQ,OAAO;AACzE,aAAO,iBAAiB,WAAW,IAC/B,aACA,cAAc,YAAY,kBAAkB,cAAc,MAAM,CAAC;AAAA,IACvE;AACF,UAAM,sBAAsB,QAAQ,WAAW,IAC3C,iBACA,MAA0B,qBAAqB,eAAe,GAAG,OAAO;AAM5E,UAAM,YAAiC,CAAC;AACxC,QAAI,OAAO,WAAW;AACpB,YAAM,YAAY,OAAO,UAAU,KAAK,MAAM;AAC9C,gBAAU,KAAK,EAAE,WAAW,CAAC,OAAwC,UAAU,EAAE,EAAE,CAAC;AAAA,IACtF;AACA,QAAI,aAAc,WAAU,KAAK,GAAG,KAAK,mBAAmB,CAAC;AAI7D,UAAM,cAAc,eAAe,SAAY,KAAK,iBAAiB,OAAO;AAI5E,UAAM,YAAY,eACd,aAAa,KAAK,OAAO,KAAK,KAAK,SAAS,CAAC,IAC7C,qBAAqB,KAAK,OAAO,KAAK,OAAO;AAKjD,QAAI,OAAO,WAAW,GAAG;AACvB,YAAM,QAAQ,OAAO,CAAC;AACtB,YAAM,oBAAoB,QAAQ,SAAS,IACvC,SACA,uBAAuB,KAAK,aAAa,KAAK;AAClD,aAAO,KAAK,eAAe;AAAA,QACzB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,WAAO,KAAK,eAAe;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoCA,OAAO,MAAsD;AAC3D,UAAM,YAAiC,CAAC;AACxC,QAAI,KAAK,OAAO,WAAW;AACzB,YAAM,YAAY,KAAK,OAAO,UAAU,KAAK,KAAK,MAAM;AACxD,gBAAU,KAAK,EAAE,WAAW,CAAC,OAAmB,UAAU,EAAE,EAAE,CAAC;AAAA,IACjE;AACA,WAAO,KAAK,eAAe;AAAA,MACzB,MAAM,KAAK,QAAQ;AAAA,MACnB;AAAA,MACA;AAAA,MACA,KAAK,OAAO;AAAA,IACd;AAAA,EACF;AACF;;;ACljBO,IAAM,oBAAN,MAA0F;AAAA,EAc/F,MAAM,UACJ,aACyC;AAMzC,QAAI,MAAM,QAAQ,WAAW,EAAG,QAAO,KAAK,cAAc,WAAoC;AAK9F,UAAM,OAAa,OAAO,gBAAgB,aACrC,YAAqD,cAAoD,IAIzG;AACL,SAAK,KAAK,0BAA0B,IAAI;AACxC,UAAM,OAAO,OAAO,KAAK,IAAI;AAG7B,UAAM,QAAiC,CAAC;AACxC,eAAW,OAAO,MAAM;AACtB,YAAM,GAAG,IAAI,KAAK,GAAG,EAAG,KAAK;AAAA,IAC/B;AAMA,qBAAiB,UAAU,MAAM;AAC/B,iBAAW,OAAO,MAAM;AACtB,cAAM,GAAG,IAAI,KAAK,GAAG,EAAG,KAAK,MAAM,GAAG,GAAG,MAAM;AAAA,MACjD;AAAA,IACF;AAEA,UAAM,SAAkC,CAAC;AACzC,eAAW,OAAO,MAAM;AACtB,aAAO,GAAG,IAAI,KAAK,GAAG,EAAG,SAAS,MAAM,GAAG,CAAC;AAAA,IAC9C;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAc,cAAc,OAAkD;AAC5E,eAAW,QAAQ,MAAO,MAAK,KAAK,0BAA0B,IAAI;AAClE,UAAM,cAAc,MAAM,IAAI,CAAC,SAAS,OAAO,KAAK,IAAI,CAAC;AACzD,UAAM,SAAoC,MAAM,IAAI,CAAC,MAAM,MAAM;AAC/D,YAAM,QAAiC,CAAC;AACxC,iBAAW,OAAO,YAAY,CAAC,EAAI,OAAM,GAAG,IAAI,KAAK,GAAG,EAAG,KAAK;AAChE,aAAO;AAAA,IACT,CAAC;AAED,qBAAiB,UAAU,MAAM;AAC/B,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,OAAO,MAAM,CAAC;AACpB,cAAM,QAAQ,OAAO,CAAC;AACtB,mBAAW,OAAO,YAAY,CAAC,EAAI,OAAM,GAAG,IAAI,KAAK,GAAG,EAAG,KAAK,MAAM,GAAG,GAAG,MAAM;AAAA,MACpF;AAAA,IACF;AAEA,WAAO,MAAM,IAAI,CAAC,MAAM,MAAM;AAC5B,YAAM,SAAkC,CAAC;AACzC,iBAAW,OAAO,YAAY,CAAC,EAAI,QAAO,GAAG,IAAI,KAAK,GAAG,EAAG,SAAS,OAAO,CAAC,EAAG,GAAG,CAAC;AACpF,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EA0BA,QAAQ,KAAsC,MAAiE;AAC7G,WAAO,IAAI;AAAA,MACT;AAAA,MACA;AAAA,MACA,MAAM,aAAa;AAAA,MACnB,KAAK;AAAA,IACP;AAAA,EACF;AACF;AAgBO,IAAM,kCAAkC;AAiB/C,SAAS,aAAa,OAAe,OAAwB;AAC3D,SAAO,GAAG,KAAK,IAAI,UAAU,SAAY,cAAc,KAAK,UAAU,KAAK,CAAC;AAC9E;AAUO,IAAM,kBAAN,MAKL;AAAA,EACA,YACmB,QACA,KACA,WACA,KACjB;AAJiB;AACA;AACA;AACA;AAEjB,QAAI,CAAC,OAAO,UAAU,SAAS,KAAK,YAAY,GAAG;AACjD,YAAM,IAAI;AAAA,QACR,kCAAkC,OAAO,SAAS,CAAC;AAAA,MAErD;AAAA,IACF;AAAA,EACF;AAAA,EAXmB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EA0BnB,MAAM,UACJ,aACkD;AAClD,UAAM,MACJ,OAAO,gBAAgB,aAClB,YAAqD,cAAoD,IAC1G;AAMN,UAAM,OAAmB,qBAAqB,KAAK,MAAM,KAAK,IAAI,aAAa,GAAG,IAAI,KAAK,KAAK,GAAG;AACnG,UAAM,OAAO,OAAO,KAAK,IAAI;AAE7B,UAAM,QAAQ,aAAa,KAAK,GAAG;AACnC,UAAM,UAAmC,eAAe,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;AAClF,UAAM,SAAS,oBAAI,IAAmE;AAEtF,qBAAiB,UAAU,KAAK,QAAQ;AAItC,YAAM,MAAM,QAAQ,WAAW,IAAI,SAAS,qBAAqB,CAAC,MAAM,GAAG,OAAO,EAAE,CAAC;AACrF,YAAM,WAAW,SAAS,KAAK,KAAK;AACpC,YAAM,WAAW,aAAa,OAAO,QAAQ;AAC7C,UAAI,QAAQ,OAAO,IAAI,QAAQ;AAC/B,UAAI,UAAU,QAAW;AACvB,YAAI,OAAO,QAAQ,KAAK,WAAW;AAGjC,gBAAM,IAAI,sBAAsB,OAAO,OAAO,OAAO,GAAG,KAAK,WAAW,MAAM;AAAA,QAChF;AACA,cAAM,QAAiC,CAAC;AACxC,mBAAW,KAAK,KAAM,OAAM,CAAC,IAAI,KAAK,CAAC,EAAG,KAAK;AAC/C,gBAAQ,EAAE,UAAU,MAAM;AAC1B,eAAO,IAAI,UAAU,KAAK;AAAA,MAC5B;AACA,iBAAW,KAAK,KAAM,OAAM,MAAM,CAAC,IAAI,KAAK,CAAC,EAAG,KAAK,MAAM,MAAM,CAAC,GAAG,GAAG;AAAA,IAC1E;AAEA,UAAM,MAA+C,CAAC;AACtD,eAAW,SAAS,OAAO,OAAO,GAAG;AAGnC,YAAM,SAAkC,EAAE,CAAC,KAAK,GAAG,MAAM,SAAS;AAClE,iBAAW,KAAK,KAAM,QAAO,CAAC,IAAI,KAAK,CAAC,EAAG,SAAS,MAAM,MAAM,CAAC,CAAC;AAClE,UAAI,KAAK,MAA+C;AAAA,IAC1D;AACA,WAAO;AAAA,EACT;AACF;;;ACxRA,IAAI,OAAO;AAGJ,SAAS,gBAAsB;AACpC,MAAI,KAAM;AACV,SAAO;AACP,iBAAe,MAAM,WAAW,kBAAkB;AAClD,iBAAe,MAAM,WAAW,aAAa;AAC7C,iBAAe,YAAY,WAAW,iBAAiB;AACzD;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/kernel/query/relate/join-reduce.ts","../src/kernel/query/reduce/methods.ts","../src/kernel/query/reduce/scan-methods.ts","../src/kernel/query/reduce/install.ts"],"sourcesContent":["/**\n * Reducing over a JOIN ALIAS (#1338).\n *\n * `aggregate()` and `groupBy()` used to refuse any field rooted at a join\n * alias. The refusal's own doc named the real blocker, and it was never \"the\n * legs do not run\" — running them is the easy half. It was that the Via\n * pipeline is LEFT-SCOPED: `Query.aggregate()` calls\n * `this.source.via.wrapReducers(spec)`, which resolves every reducer's field\n * against the LEFT collection's map. For `sum('client.balance')` that map has\n * no entry, so two things silently did not happen — money's exact-BigInt\n * rewrite was skipped (a generic sum over stored scaled-integer strings, which\n * `readNumber` coerces to 0), and `refuseUnqueryableReducers` never fired, so\n * the `queryable: 'none'` gate that would refuse a blob field never applied.\n *\n * This module supplies the missing half as a Via BINDING rather than as a\n * special case threaded through the reduce strategy. A binding composes: the\n * pipeline handed to `GroupedQuery`/`aggregate()` is the left one plus this,\n * so every existing call site keeps its exact behaviour and the alias case\n * arrives through the same `wrapReducers` door as everything else.\n *\n * Two properties make that safe:\n *\n * - **It delegates, it does not reimplement.** An aliased reducer is handed\n * to the RIGHT collection's OWN pipeline under its BARE field name — which\n * is how a pipeline is keyed (that is the whole shape of #1335) — so the\n * right side's rewrite AND its posture gate apply together, from one call.\n * - **`covers()` returns false**, so the binding is inert for `postureFor`,\n * `present`, `decodeResults` and every other phase. It exists for exactly\n * one hook.\n */\nimport type { NoydbVia } from '../../via/index.js'\nimport { ViaPipeline } from '../../via/pipeline.js'\n\n/**\n * The reducer shape this file needs, declared STRUCTURALLY rather than\n * imported from `with-lookup/reduce/`.\n *\n * Not a style choice: `check-architecture.mjs`'s port-layering guard refuses a\n * NEW static kernel→`with-*` import (its allowlist is a frozen per-file, per-\n * specifier baseline, and adding a row would be the thing the freeze exists to\n * stop). `NoydbVia.wrapReducers` is declared `(spec: unknown) => unknown`\n * precisely so a binding never has to name the service's types, and these\n * three members are all this file touches — every reducer that reaches it was\n * built by the reduce service and keeps every other member intact through the\n * spread below.\n */\ninterface AliasReducer {\n readonly field?: string\n init(): unknown\n step(state: unknown, record: unknown): unknown\n remove?(state: unknown, record: unknown): unknown\n finalize(state: unknown): unknown\n}\ntype AliasSpec = Readonly<Record<string, AliasReducer>>\n\n/**\n * Re-aim a reducer at the sub-record under `alias`.\n *\n * The right side's pipeline rewrote the reducer for the BARE field name, so\n * its `step`/`remove` read `credit` off whatever record they are handed. On a\n * joined row that value lives at `row.client.credit`, so the row is unwrapped\n * one level on the way in rather than the field path being rewritten — the\n * rewritten reducer closes over its field name internally, so re-pointing the\n * `.field` property afterwards would change the label and not the read.\n *\n * An unmatched left row carries `null` under the alias; every reducer's read\n * already treats a nullish record as \"no value here\", so it contributes\n * nothing — the same thing a missing left-side field does.\n */\nfunction liftToAlias(alias: string, path: string, inner: AliasReducer): AliasReducer {\n const under = (record: unknown): unknown =>\n record === null || typeof record !== 'object' ? undefined : (record as Record<string, unknown>)[alias]\n return {\n ...inner,\n // The dotted path, so anything reading `.field` for a label or a\n // dependency still sees the field the CALLER named.\n field: path,\n init: () => inner.init(),\n step: (state, record) => inner.step(state, under(record)),\n ...(inner.remove ? { remove: (state: unknown, record: unknown) => inner.remove!(state, under(record)) } : {}),\n finalize: (state) => inner.finalize(state),\n }\n}\n\n/**\n * The binding: rewrite every reducer whose field is rooted at a join alias\n * through that alias's own collection pipeline, and leave everything else\n * exactly as it arrived.\n *\n * A reducer the right side does NOT rewrite (a plain `avg('client.score')`)\n * is returned untouched and keeps reading the dotted path off the joined row,\n * which is already correct — only a REWRITTEN reducer needs lifting, because\n * only a rewritten one has swapped to the bare name.\n */\nfunction joinAliasBinding(aliasVia: ReadonlyMap<string, ViaPipeline>): NoydbVia {\n return {\n brand: 'join-alias',\n posture: { encryptedAtRest: 'envelope', queryable: 'full', exportable: true, forgettable: true },\n // Claims no field: this binding must never shadow a left-side posture.\n covers: () => false,\n wrapReducers: (spec) => {\n let changed = false\n const out: Record<string, AliasReducer> = {}\n for (const [key, reducer] of Object.entries(spec as AliasSpec)) {\n const field = reducer.field\n const dot = field === undefined ? -1 : field.indexOf('.')\n const via = dot > 0 ? aliasVia.get(field!.slice(0, dot)) : undefined\n if (!via) { out[key] = reducer; continue }\n const bare = field!.slice(dot + 1)\n // One call: the right side's rewrite and its `queryable: 'none'`\n // refusal, both keyed by the bare name it actually declares.\n const wrapped = via.wrapReducers({ [key]: { ...reducer, field: bare } } as AliasSpec)[key]!\n if (wrapped === reducer || wrapped.field !== bare) { out[key] = reducer; continue }\n changed = true\n out[key] = liftToAlias(field!.slice(0, dot), field!, wrapped)\n }\n return changed ? out : spec\n },\n }\n}\n\n/**\n * The pipeline `aggregate()` / `groupBy()` should reduce THIS plan with: the\n * source's own, plus the alias binding when the plan can produce an alias.\n *\n * Returns the left pipeline unchanged when there is no alias to serve, so a\n * query with no joins — every query written before #1338 — is handed exactly\n * the object it was handed before.\n */\nexport function reduceViaFor(\n leftVia: ViaPipeline | undefined,\n aliasVia: ReadonlyMap<string, ViaPipeline> | undefined,\n): ViaPipeline | undefined {\n if (aliasVia === undefined || aliasVia.size === 0) return leftVia\n return ViaPipeline.build([...(leftVia?.bindings ?? []), joinAliasBinding(aliasVia)], leftVia?.taint)\n}\n\n/**\n * The other half, and the reason this one is not merely a missing feature:\n * a binding that REFUSES an aliased reducer instead of rewriting it.\n *\n * It is installed on the path where the legs do NOT run —\n * `groupBy('status').aggregate({ x: sum('client.credit') })`, a group key over\n * a left field with a reducer over an alias. `Query.aggregate()`'s guard never\n * saw that spec (the grouped terminal is `GroupedQuery.aggregate`, one object\n * along the chain), so it reduced `undefined` for every row and returned a\n * confident `0`. Group by the alias — then the legs run and the reducer is\n * rewritten by the branch above — or drop the grouping.\n */\nfunction refuseAliasBinding(aliases: ReadonlySet<string>): NoydbVia {\n return {\n brand: 'join-alias-refuse',\n posture: { encryptedAtRest: 'envelope', queryable: 'full', exportable: true, forgettable: true },\n covers: () => false,\n wrapReducers: (spec) => {\n for (const reducer of Object.values(spec as AliasSpec)) {\n const field = reducer.field\n if (field === undefined) continue\n const head = field.split('.')[0]!\n if (!aliases.has(head)) continue\n throw new Error(\n `Query.groupBy().aggregate(): reducer field \"${field}\" addresses the join alias ` +\n `\"${head}\", but the GROUP KEY does not — so the join legs are not applied for ` +\n `this grouping and the reducer would fold undefined into every bucket. ` +\n `Group by a field on \"${head}\" (then the legs run), or aggregate without ` +\n `grouping, where an aliased reducer is applied directly.`,\n )\n }\n return spec\n },\n }\n}\n\n/**\n * The pipeline for a grouped plan whose GROUP KEY stays on the left: the\n * source's own, plus the refusal above so an aliased reducer is a loud error\n * rather than a confident zero. No aliases in the plan → unchanged.\n */\nexport function refuseAliasReduceVia(\n leftVia: ViaPipeline | undefined,\n aliases: ReadonlySet<string>,\n): ViaPipeline | undefined {\n if (aliases.size === 0) return leftVia\n return ViaPipeline.build([...(leftVia?.bindings ?? []), refuseAliasBinding(aliases)], leftVia?.taint)\n}\n","/**\n * #1458 — **Reduce**: a fold over a result set.\n *\n * `aggregate` · `groupBy` · `window` · `distinct`\n *\n * Bodies moved out of `kernel/query/builder.ts` unchanged; see\n * `../relate/methods.ts` for why `this.plan` still resolves.\n *\n * ⚠️ This group depends on TWO others, and both edges are real rather than\n * tidyable:\n * - **Live**, for `incrementalMaintainer` — a live reduction maintains its\n * state through Live's maintainer (see `../internal/maintenance.ts`);\n * - **Relate**, for `reduceViaFor` / `refuseAliasReduceVia` — a reducer over\n * a JOIN ALIAS has to resolve the right side's Via pipeline, and refusing\n * it correctly is #1338's whole subject.\n * Neither is a layering accident: both are what \"reduce over a joined, live\n * result\" means.\n */\nimport type { DeclaredPredicate, InternalSource, QueryPlan } from '../builder.js'\n// @internal Find helpers. A reduce terminal narrows its record set through the\n// SAME executor the projection terminals use — sharing the functions is what\n// keeps \"which records does this terminal see\" one definition (#1347).\nimport { buildDictLabelResolver, candidateRecords, executeClausePipeline, filterRecords, fnViewDecoder } from '../builder.js'\nimport { readPath } from '../predicate.js'\nimport type { SourceChange } from '../live/incremental.js'\nimport type { QueryField } from '../../types.js'\nimport type { ViaPipeline } from '../../via/pipeline.js'\nimport type { JoinContext, JoinLeg } from '../relate/join.js'\nimport { reduceViaFor, refuseAliasReduceVia } from '../relate/join-reduce.js'\nimport type { DateTruncKey, GroupKey } from './date-trunc.js'\nimport { groupKeyName, isDateTruncKey, projectDateTruncKeys } from './date-trunc.js'\nimport { distinctKeyOf } from '../distinct-key.js'\nimport type { LiveMaintainer, GroupMaintenanceSource } from '../live/incremental.js'\nimport type { ReduceSpec, ReduceResult, ReductionUpstream, Reduction } from '../../../with-lookup/reduce/reduction.js'\nimport type { ReducerBuilder } from '../../../with-lookup/reduce/reducers.js'\nimport { bindDistinctReducers, reducerBuilder } from '../../../with-lookup/reduce/reducers.js'\nimport type { GroupedQuery, GroupedQueryN } from '../../../with-lookup/reduce/groupby.js'\nimport type { ReduceStrategy, WindowSpec, WindowedQuery } from '../../../with-lookup/reduce/strategy.js'\nimport { FieldNotQueryableError } from '../../errors.js'\n\nfunction assertNoJoinAliasField(\n fields: readonly string[],\n joins: readonly JoinLeg[],\n terminal: string,\n): void {\n if (joins.length === 0) return\n const aliases = new Set(joins.map(leg => leg.as))\n for (const field of fields) {\n const head = field.split('.')[0]!\n if (!aliases.has(head)) continue\n throw new Error(\n `Query.${terminal}(): field \"${field}\" addresses the join alias \"${head}\", but ` +\n `${terminal}() does not apply join legs — it would silently group every row ` +\n `under undefined. Joined aggregation is not supported. Either aggregate over ` +\n `the left collection's own fields, or use .crossJoin(\"${head}\", { as: … }), ` +\n `whose expansion IS visible to ${terminal}().`,\n )\n }\n}\n\n/** @internal — the mixin whose prototype `./index.ts` copies onto `Query`. */\n /* eslint-disable @typescript-eslint/no-unused-vars -- #1458: the parameter\n list must match `Query`'s exactly (T, S, Q, M) because `declare module`\n merges this mixin's `Pick` into it, and TypeScript requires every\n declaration of an interface to carry identical type parameters. A group\n whose methods happen to use only `T` still declares all four. */\nexport class ReduceMethods<\n T,\n S extends keyof T = never,\n Q extends keyof T & string = never,\n M extends keyof T & string = never,\n /* eslint-enable @typescript-eslint/no-unused-vars */\n> {\n declare protected readonly source: InternalSource\n declare protected readonly plan: QueryPlan\n declare protected readonly joinContext: JoinContext | undefined\n declare protected readonly reduceStrategy: ReduceStrategy\n declare protected readonly predicates: ReadonlyMap<string, DeclaredPredicate> | undefined\n declare protected matchedRecords: (terminal: string, forceJoins?: boolean) => readonly unknown[]\n declare protected decodeVia: (records: readonly unknown[], locale?: string) => unknown[]\n declare protected aliasVia: () => ReadonlyMap<string, ViaPipeline> | undefined\n declare protected incrementalMaintainer: (mode: 'rows' | 'records') => LiveMaintainer | undefined\n declare protected groupMaintenance: (derived: readonly DateTruncKey[]) => GroupMaintenanceSource | undefined\n declare toArray: (opts?: { locale?: string }) => T[]\n\n /**\n * The right-side change streams a joined query has to merge (#1338), deduped\n * by target the way `live()` does — a chain joining the same collection\n * twice must not double-fire. Extracted so a joined REDUCTION and a joined\n * live query cannot disagree on what \"the sources of this result\" means.\n */\n private rightSideUpstreams(): ReductionUpstream[] {\n const ctx = this.joinContext\n if (!ctx) return []\n const out: ReductionUpstream[] = []\n const seen = new Set<string>()\n for (const leg of this.plan.joins) {\n if (seen.has(leg.target)) continue\n seen.add(leg.target)\n const right = ctx.resolveSource(leg.target)\n if (!right?.subscribe) continue\n const subscribe = right.subscribe.bind(right)\n out.push({ subscribe: (cb: () => void) => subscribe(cb) })\n }\n return out\n }\n\n /**\n * Every alias this plan can produce, pipeline or not (#1338).\n *\n * `aliasVia()` above holds only the aliases whose collection compiles a Via\n * pipeline; refusal has to know about ALL of them, or an aliased reducer\n * over a plain right-side collection stays silent.\n */\n private aliasNames(): ReadonlySet<string> {\n const out = new Set<string>()\n for (const clause of this.plan.clauses) {\n if (clause.type !== 'crossJoin') continue\n out.add(clause.as)\n if (clause.leftAs !== undefined) out.add(clause.leftAs)\n }\n for (const leg of this.plan.joins) out.add(leg.as)\n return out\n }\n\n /** Is `field` rooted at an alias this plan produces? */\n\n private addressesAlias(field: string, aliases: ReadonlySet<string>): boolean {\n const dot = field.indexOf('.')\n return dot > 0 && aliases.has(field.slice(0, dot))\n }\n\n /**\n * The distinct values of `field` across the match set (#1347) — the\n * declarative form of `new Set(q.toArray().map(r => r[field]))`, which is\n * what consumers write today and which is wrong in two ways this is not.\n *\n * **Distinctness is decided on the canonical index key, not on the value\n * you get back.** For a Via-covered field that is the difference between a\n * right answer and a plausible one: money stores a scaled integer, and a\n * row written before the field was declared can spell it non-canonically\n * (`'0100'` for `'100'`). Deduping the stored strings reports two values\n * where there is one; deduping the FORMATTED strings makes the answer\n * depend on a locale this layer does not have (`'10.00'` vs `'10,00'`).\n * See `distinct-key.ts`. The values RETURNED are the decoded ones, so they\n * match what `toArray()` hands back for the same field.\n *\n * **Nullish values are excluded** — `null`/`undefined`/missing is the\n * absence of a value, not a distinct one. That is also the only choice\n * under which the index-backed and scanned paths can agree, since the hash\n * index does not hold nullish keys; see `distinctKeyOf`.\n *\n * Same match set as `count()`: where/filter apply, orderBy/limit/offset do\n * not. Values come back in first-encountered order.\n *\n * ⭐ INDEX-BACKED when the field carries a hash index AND the plan narrows\n * nothing — the buckets ARE the distinct key set, so the answer costs\n * O(distinct values) instead of O(records) and never decrypts a snapshot.\n * The guard is deliberately all-or-nothing: with a where clause the whole\n * collection's buckets are not the answer, and intersecting a candidate id\n * set with every bucket would cost more than the scan it replaced.\n *\n * ⛔ THE RETURN TYPE IS `(T & Record<F, unknown>)[F][]`, NOT\n * `F extends keyof T ? T[F][] : unknown[]` — and it must stay that way. The\n * conditional form reads better and type-checks at every call site, but a\n * DEFERRED conditional over `T` makes TypeScript give up relating\n * `Query<T>` to `Query<unknown>`, which makes `Collection<T>` invariant in\n * `T`, which breaks `vault.ts`'s `Map<string, Collection<unknown>>` cache\n * with four errors that name neither this method nor this file. The\n * intersection form resolves to the same thing (`T[F]` for a literal key,\n * `unknown` for a `string`-typed one) and stays covariant.\n */\n distinct<F extends QueryField<T, S>>(field: F): (T & Record<F, unknown>)[F][] {\n const name = field as string\n assertNoJoinAliasField([name], this.plan.joins, 'distinct')\n const via = this.source.via\n // Same posture gate `.where()` / `.orderBy()` apply: a `queryable: 'none'`\n // field (a blob handle) must not have its value set enumerated either.\n if (via?.postureFor(name)?.queryable === 'none') throw new FieldNotQueryableError(name)\n const decode = via?.hasResultDecode ? (r: unknown) => via.decodeResults(r) : (r: unknown) => r\n\n const indexes =\n this.plan.clauses.length === 0 && this.plan.joins.length === 0\n ? this.source.getIndexes?.()\n : undefined\n if (indexes && this.source.lookupById) {\n const lookupById = (id: string): unknown => this.source.lookupById?.(id)\n const reps = indexes.bucketRepresentatives(name)\n if (reps) {\n const fromIndex: unknown[] = []\n for (const id of reps) {\n const record = lookupById(id)\n if (record === undefined) continue\n fromIndex.push(readPath(decode(record), name))\n }\n return fromIndex as (T & Record<F, unknown>)[F][]\n }\n }\n\n const seen = new Set<string>()\n const out: unknown[] = []\n for (const record of this.matchedRecords('distinct')) {\n const key = distinctKeyOf(name, readPath(record, name), via)\n if (key === undefined || seen.has(key)) continue\n seen.add(key)\n out.push(readPath(decode(record), name))\n }\n return out as (T & Record<F, unknown>)[F][]\n }\n\n /**\n * Reduce the matching records through a named set of reducers.\n * the aggregation terminal.\n *\n * ```ts\n * const { total, n, avgAmount } = invoices.query()\n * .where('status', '==', 'open')\n * .aggregate({\n * total: sum('amount'),\n * n: count(),\n * avgAmount: avg('amount'),\n * })\n * .run()\n * ```\n *\n * Returns an `Reduction<R>` wrapper with two terminals:\n * - `.run(): R` — synchronous one-shot reduction\n * - `.live(): LiveReduction<R>` — reactive primitive that\n * re-runs the reduction whenever the source notifies of a\n * change. Always call `live.stop()` when finished.\n *\n * The reducer spec is bound here once and reused by both\n * terminals — this is why `.aggregate()` returns a wrapper instead\n * of being a direct terminal. Consumers who only need the static\n * value read `.run()`; consumers wiring a reactive UI read\n * `.live()`.\n *\n * Joins are NOT applied by default — the same logic as `.count()`. A leg is\n * projection-only, so running one just to throw the alias away would be\n * wasteful. ⭐ **The exception is a reducer that ADDRESSES an alias**\n * (#1338): then the joined relation IS what is being reduced, so the legs\n * run and the reducer is rewritten by the right collection's own Via\n * pipeline — money stays exact, and the right side's `queryable: 'none'`\n * posture refuses a field it would refuse locally. A live joined\n * aggregation also merges every right-side change stream, so it does not\n * quietly stop updating when the right side moves.\n *\n * Every reducer factory accepts an optional `{ seed }` parameter\n * that is plumbed through the protocol but unused by the\n * executor — that's constraint #2. When partition-aware\n * aggregation lands, the seed will carry running state across\n * partition boundaries without an API break.\n *\n * KNOWN GAP (sealed fields, bare-spec form): `where`/`orderBy`/`groupBy`\n * refuse a `sensitive` field at compile time, but a reducer over a sensitive\n * field in the BARE-SPEC form (e.g. `aggregate({ x: sum('ssn') })`) is NOT\n * refused — the reducer factories (`sum`/`min`/`max`/…) are standalone\n * `(field: string)` functions with no collection-type context. This form is\n * preserved as-is for backward compatibility.\n *\n * The BUILDER form closes this gap: `aggregate(b => ({ x: b.sum('field') }))`\n * types the builder's field parameter as `QueryField<T, S>`, refusing any\n * `sensitive` field at compile time. Use the builder form for new code that\n * aggregates over a collection with sensitive fields.\n *\n */\n aggregate<Spec extends ReduceSpec>(spec: Spec): Reduction<ReduceResult<Spec>>\n aggregate<Spec extends ReduceSpec>(build: (b: ReducerBuilder<T, S, M>) => Spec): Reduction<ReduceResult<Spec>>\n aggregate<Spec extends ReduceSpec>(\n specOrBuild: Spec | ((b: ReducerBuilder<T, S, M>) => Spec),\n ): Reduction<ReduceResult<Spec>> {\n let spec = typeof specOrBuild === 'function'\n ? (specOrBuild as (b: ReducerBuilder<T, S, M>) => Spec)((reducerBuilder as unknown) as ReducerBuilder<T, S, M>)\n : specOrBuild\n // #1338 — a reducer over a joined alias used to be refused here. It now\n // routes: the legs run (so the reducer sees the joined relation) and the\n // spec is wrapped by a pipeline that delegates each aliased field to the\n // RIGHT collection's own — which carries money's exact-BigInt rewrite and\n // the `queryable: 'none'` posture gate together. Reducers without a field\n // (e.g. `count()`) address nothing and are unaffected.\n const aliases = this.aliasNames()\n const aliasedReducer =\n this.plan.joins.length > 0 &&\n Object.values(spec).some(r => {\n const f = (r as { field?: unknown }).field\n return typeof f === 'string' && this.addressesAlias(f, aliases)\n })\n // Rewrite sum/min/max over Via-covered fields (e.g. money) into exact\n // BigInt reducers before the strategy runs (covers static run() and\n // live/MV paths).\n spec = (aliasedReducer ? reduceViaFor(this.source.via, this.aliasVia()) : this.source.via)?.wrapReducers(spec) ?? spec\n // #1347 — `countDistinct` needs the SAME canonicalizer the hash index\n // buckets by, and only the source knows it. Bound here, beside the\n // money rewrite, so both spec-wrapping seams stay in one place.\n spec = bindDistinctReducers(spec, this.source.via)\n // Closure over the current query. Produces the record set that\n // the aggregation reduces — same pipeline as `count()`, skipping\n // limit/offset because aggregation is over the full match set,\n // not a paginated slice. (A paginated aggregation would be a\n // different operation; see docs for rationale.)\n const source = this.source\n const clauses = this.plan.clauses\n const joinCtx = this.joinContext\n const hasCrossJoins = clauses.some(c => c.type === 'crossJoin')\n const fullScan = aliasedReducer\n // The reduced relation IS the joined one — same pipeline `count()` uses,\n // with the legs forced on (#1338).\n ? (): readonly unknown[] => this.matchedRecords('aggregate', true)\n : (): readonly unknown[] => {\n if (hasCrossJoins) {\n if (!joinCtx) throw new Error('Query.aggregate(): crossJoin requires a join context')\n return executeClausePipeline(source, clauses, joinCtx)\n }\n const { candidates, remainingClauses } = candidateRecords(source, clauses)\n return remainingClauses.length === 0\n ? candidates\n : filterRecords(candidates, remainingClauses, fnViewDecoder(source))\n }\n\n // #1341 — when the plan admits it, the match set is MAINTAINED across\n // change events instead of re-scanned, so a live reduction folds over an\n // incrementally-patched array. The reducers themselves still run a full\n // fold: the array they see is byte-for-byte the one a re-scan would\n // produce (same records, same candidate order), so an incremental result\n // is not merely close to a re-run's — it is the identical computation.\n const maintainer = this.incrementalMaintainer('records')\n const executeRecords = maintainer ? (): readonly unknown[] => maintainer.rows() : fullScan\n\n // Upstream for live mode. The left source always subscribes; a joined\n // aggregation ALSO merges every right-side change stream (#1338) — the\n // refusal's own doc named a live joined aggregate that silently stops\n // updating as half the work, and it is this half.\n const upstreams: ReductionUpstream[] = []\n if (aliasedReducer) upstreams.push(...this.rightSideUpstreams())\n if (source.subscribe) {\n const subscribe = source.subscribe.bind(source)\n upstreams.push(\n maintainer\n ? {\n // The maintainer folds the delta in BEFORE the reduction reads,\n // which is why it is wrapped here rather than subscribed\n // separately — emitter callback order would otherwise decide\n // whether the reduction saw the new record.\n subscribe: (cb: () => void) => {\n maintainer.attach()\n const unsubscribe = subscribe(change => {\n // A predicate that throws must not escape into the emitter —\n // the maintainer drops its state and the reduction's own\n // re-run raises the same error where it can be caught.\n try {\n maintainer.apply(change)\n } catch {\n maintainer.invalidate()\n }\n cb()\n })\n // Detach on teardown: the Reduction survives its LiveReduction\n // (`.run()` reuses the same closure), and a maintainer with no\n // subscription feeding it would go quietly out of date.\n return () => {\n maintainer.detach()\n unsubscribe()\n }\n },\n }\n : { subscribe: (cb: () => void) => subscribe(cb) },\n )\n }\n\n // #1414 — the gate travels INTO the reduction rather than wrapping it.\n // `Reduction.run()` is the synchronous terminal, and `await …aggregate(\n // spec).run()` is what an async guard's Σ-over-siblings invariant writes;\n // gating the builder call instead would break that awaited form.\n return this.reduceStrategy.aggregate<Spec>(executeRecords, spec, upstreams, this.source.hydration)\n }\n\n /**\n * Partition matching records into buckets keyed by a field, then\n * terminate with `.aggregate(spec)` to compute per-bucket\n * reducers..\n *\n * ```ts\n * const byClient = invoices.query()\n * .where('status', '==', 'open')\n * .groupBy('clientId')\n * .aggregate({ total: sum('amount'), n: count() })\n * .run()\n * // → [ { clientId: 'c1', total: 5250, n: 3 }, … ]\n * ```\n *\n * Result rows carry the group key value under the grouping field\n * name plus every reducer output from the spec. Buckets are\n * emitted in first-seen order — consumers who want a specific\n * ordering should `.sort()` downstream.\n *\n * **Cardinality caps:** a one-shot warning fires at 10_000\n * distinct groups; `GroupCardinalityError` throws at 100_000.\n * Grouping on a high-uniqueness field like `id` or `createdAt` is\n * almost always a query mistake — the error message names the\n * field and observed cardinality and suggests narrowing with\n * `.where()` first.\n *\n * **Null / undefined keys:** records with a missing or explicitly\n * `null` group field get their own buckets. `Map`-based\n * partitioning distinguishes `undefined` from `null`, so the two\n * cases do NOT merge. Consumers who want them merged should\n * coalesce upstream with `.filter()`.\n *\n * **Joins are not applied unless the GROUP KEY addresses an alias** (#1338).\n * For a left-side key the old rationale still holds — a leg is\n * projection-only, so running one inside a grouping pipeline would be\n * wasteful and could trigger `DanglingReferenceError` in strict mode for a\n * call whose intent is purely to bucket-and-reduce. For an aliased key\n * (`groupBy('client.region')`, the accounting-report shape) the legs run and\n * the buckets are the joined relation's: the key is stamped under its DOTTED\n * path (`row['client.region']`), and a left row whose right side is absent\n * reads `undefined` and lands in the undefined bucket — the same bucket a\n * missing left-side key gets, and still distinct from an explicit `null`.\n *\n * ⚠️ The decision is taken from the KEY, because the reducer spec arrives a\n * call later. A reducer over an alias under a LEFT key is therefore REFUSED\n * rather than folded as `undefined` — group by the alias, or aggregate\n * without grouping.\n *\n * **Filter clauses (`.filter(fn)`):** grouped queries still\n * support filter clauses in the underlying plan — they run in\n * the same candidate/filter pipeline that `.aggregate()` uses.\n * The performance caveat is the same: filter clauses cost O(N)\n * per record and can't be index-accelerated.\n */\n // The `field` param is `QueryField<T, S>` so a sealed (`sensitive`) field is\n // refused — grouping BY a sensitive field leaks its value distribution as\n // group-key labels. With `S = never` this is exactly `string` (zero churn).\n\n groupBy<F extends QueryField<T, S>>(field: F): GroupedQuery<T, F, S, M>\n groupBy<F extends readonly [QueryField<T, S>, QueryField<T, S>, ...QueryField<T, S>[]]>(\n ...fields: F\n ): GroupedQueryN<T, F, S, M>\n // Derived calendar keys (#1350). Listed last so the two field-name overloads\n // above still win for plain string arguments — a `DateTruncKey` is an object\n // and matches neither, so nothing existing re-resolves onto these.\n groupBy(key: DateTruncKey): GroupedQuery<T, string, S, M>\n // The string members stay `QueryField<T, S>`, so the sealed-field refusal\n // still applies to every plain key in a mixed grouping.\n groupBy(\n ...keys: readonly [\n QueryField<T, S> | DateTruncKey,\n QueryField<T, S> | DateTruncKey,\n ...(QueryField<T, S> | DateTruncKey)[],\n ]\n ): GroupedQueryN<T, readonly string[], S, M>\n groupBy(...keys: readonly GroupKey[]): GroupedQuery<T, string, S, M> | GroupedQueryN<T, readonly string[], S, M> {\n if (keys.length === 0) {\n throw new Error('.groupBy() requires at least one field')\n }\n // A derived key is bucketed into an ordinary row field before the grouping\n // pipeline runs, so everything downstream — cardinality caps, null/undefined\n // bucket semantics, live re-grouping — keeps working unchanged.\n const derived = keys.filter(isDateTruncKey)\n const fields: readonly string[] = keys.map(groupKeyName)\n // #29 — the same posture gate `.where()` / `.orderBy()` / `.distinct()`\n // apply. A `queryable: 'none'` field (a virtual computed field, a blob\n // handle) is absent from the stored record the reducer walks, so grouping\n // on it keys every row on `undefined` and folds the whole collection into\n // one well-formed bucket with no error anywhere. Refusing here is what a\n // caller can catch; a silently-undefined key is not detectable downstream.\n //\n // ⚠️ Gated on the RESOLVED names, so a `dateTrunc` key over a virtual\n // field is refused too — `groupKeyName` returns the underlying field.\n const groupVia = this.source.via\n if (groupVia) {\n for (const f of fields) {\n if (groupVia.postureFor(f)?.queryable === 'none') throw new FieldNotQueryableError(f)\n }\n }\n // #1338 — a group key addressing an alias used to be refused. It now\n // decides the shape of the whole grouped pipeline: the legs run, and the\n // reducers are wrapped through the right side's pipeline.\n //\n // ⚠️ The decision is made HERE, from the group KEY alone, because the\n // reducer spec does not exist yet — it arrives one call later, at\n // `.aggregate()`. That is exactly why the left-key case installs a\n // REFUSING binding rather than nothing: a reducer over an alias under a\n // left-side key would otherwise fold undefined into every bucket, and the\n // grouped terminal is not the one `Query.aggregate()`'s guard sees.\n const aliases = this.aliasNames()\n const keyIsAliased = this.plan.joins.length > 0 && fields.some(f => this.addressesAlias(f, aliases))\n // Same record-producing closure as .aggregate() — grouped and\n // non-grouped aggregations execute over the same candidate set.\n // We inline the closure here instead of sharing a helper so the\n // builder stays allocation-friendly for the hot path.\n const source = this.source\n const clauses = this.plan.clauses\n const joinCtx = this.joinContext\n const hasCrossJoins = clauses.some(c => c.type === 'crossJoin')\n const executeRecords = keyIsAliased\n ? (): readonly unknown[] => this.matchedRecords('groupBy', true)\n : (): readonly unknown[] => {\n if (hasCrossJoins) {\n if (!joinCtx) throw new Error('Query.groupBy(): crossJoin requires a join context')\n return executeClausePipeline(source, clauses, joinCtx)\n }\n const { candidates, remainingClauses } = candidateRecords(source, clauses)\n return remainingClauses.length === 0\n ? candidates\n : filterRecords(candidates, remainingClauses, fnViewDecoder(source))\n }\n const executeGroupRecords = derived.length === 0\n ? executeRecords\n : (): readonly unknown[] => projectDateTruncKeys(executeRecords(), derived)\n\n // #1341 (grouped half) — the upstream passes the DELTA through, not just a\n // \"something changed\" ping, so `GroupedReduction.live()` can patch the one\n // or two buckets a change touches instead of re-grouping everything. A cb\n // that ignores the argument (every pre-#1341 caller) is unaffected.\n const upstreams: ReductionUpstream[] = []\n if (source.subscribe) {\n const subscribe = source.subscribe.bind(source)\n upstreams.push({ subscribe: (cb: (change?: SourceChange) => void) => subscribe(cb) })\n }\n if (keyIsAliased) upstreams.push(...this.rightSideUpstreams())\n // Withheld for a plan the whitelist refuses, and for an aliased key (which\n // implies joins, which the whitelist refuses anyway) — `.live()` then\n // re-runs the whole grouping, exactly as it did before.\n const maintenance = keyIsAliased ? undefined : this.groupMaintenance(derived)\n\n // The pipeline the reducers are wrapped with: the right side's rewrite\n // when the legs run, the refusal when they do not (#1338).\n const reduceVia = keyIsAliased\n ? reduceViaFor(this.source.via, this.aliasVia())\n : refuseAliasReduceVia(this.source.via, aliases)\n\n // Dict-label resolution is single-field only — the <field>Label\n // projection has no meaningful shape for composite keys. A derived\n // calendar key is never a dictKey, so it skips the lookup entirely.\n if (fields.length === 1) {\n const field = fields[0]!\n const dictLabelResolver = derived.length > 0\n ? undefined\n : buildDictLabelResolver(this.joinContext, field)\n return this.reduceStrategy.groupBy<T, string, S, M>(\n executeGroupRecords,\n field,\n upstreams,\n dictLabelResolver,\n reduceVia,\n maintenance,\n )\n }\n return this.reduceStrategy.groupByN<T, readonly string[], S, M>(\n executeGroupRecords,\n fields,\n upstreams,\n reduceVia,\n maintenance,\n )\n }\n\n /**\n * SQL window functions over the query's result rows (#1349) — terminate with\n * `.select(spec)`.\n *\n * ```ts\n * invoices.query()\n * .where('status', '==', 'open')\n * .window({ partitionBy: 'clientId', orderBy: 'date' })\n * .select({ balance: runningSum('amount'), prev: lag('amount', 1), n: rowNumber() })\n * .run()\n * ```\n *\n * ⭐ Distinguished from `.groupBy()` by ARITY, not by capability: grouping\n * emits one row per bucket and drops the source rows; a window keeps EVERY\n * row and attaches per-row values computed over its partition.\n *\n * **The record set is `toArray()`'s** — the full pipeline, including joins,\n * `orderBy`, `offset`/`limit` and the Via decode. That differs deliberately\n * from `.groupBy()`, which reads the candidate/filter pipeline only: a\n * window returns ROWS, so the query's own ordering and paging are exactly\n * what decides which rows there are and how they are presented. `orderBy`\n * inside the window decides only how a partition is WALKED.\n *\n * **Frame:** `rows unbounded preceding → current row`, and only that in v1.\n *\n * `partitionBy` / `orderBy` accept a `dateTrunc()` key (#1350) alongside\n * plain field names; the derived bucket is used for partitioning and is not\n * stamped on the output row. A sealed (`sensitive`) field is refused at\n * compile time, same as `.groupBy()`.\n *\n * ⛔ **Not an `explain()` node.** `explain()` is a terminal on `Query`;\n * `.window()` leaves `Query` for `WindowedQuery`, so there is nothing past\n * here to add a node to — the same reason #1336's post-group ops are absent.\n */\n window(spec: WindowSpec<QueryField<T, S>>): WindowedQuery<T> {\n const upstreams: ReductionUpstream[] = []\n if (this.source.subscribe) {\n const subscribe = this.source.subscribe.bind(this.source)\n upstreams.push({ subscribe: (cb: () => void) => subscribe(cb) })\n }\n return this.reduceStrategy.window<T>(\n () => this.toArray() as readonly unknown[],\n spec as WindowSpec,\n upstreams,\n this.source.via,\n )\n }\n}\n\n/** The public Reduce surface — merged into `Query` by `./index.ts`. */\nexport type ReduceSurface<\n T,\n S extends keyof T = never,\n Q extends keyof T & string = never,\n M extends keyof T & string = never,\n> = Pick<ReduceMethods<T, S, Q, M>, 'aggregate' | 'groupBy' | 'window' | 'distinct'>\n\n","/**\n * #1458 — the **Reduce** half of `ScanBuilder`.\n *\n * `aggregate` · `groupBy` · the grouped-scan handle they return.\n *\n * See `../relate/scan-methods.ts` for why `ScanBuilder` is split at all.\n *\n * ⭐ **The streaming memory property travels with the code.**\n * `scan().aggregate()` is O(reducers), not O(records) — it steps every reducer\n * per record and never collects the stream — and `groupBy().aggregate()` is\n * O(groups). That is a code-level invariant visible in the bodies below, so\n * moving them wholesale is what preserves it; re-expressing them over the\n * eager reducers would have quietly turned an unbounded scan into an array.\n */\nimport type { MultiReduceResult, ScanPageProvider } from '../scan-builder.js'\nimport type { QueryField } from '../../types.js'\nimport type { Clause } from '../predicate.js'\nimport { readPath } from '../predicate.js'\nimport type { ViaPipeline } from '../../via/pipeline.js'\nimport type { JoinContext, JoinLeg } from '../relate/join.js'\nimport type { ReducerBuilder } from '../../../with-lookup/reduce/reducers.js'\nimport { bindDistinctReducers, reducerBuilder } from '../../../with-lookup/reduce/reducers.js'\nimport type { ReduceSpec, ReduceResult } from '../../../with-lookup/reduce/reduction.js'\nimport type { DateTruncKey } from './date-trunc.js'\nimport { groupKeyName, isDateTruncKey, projectDateTruncKeys } from './date-trunc.js'\nimport { GroupCardinalityError } from '../../errors.js'\n\n/** @internal — the mixin whose prototype `./index.ts` copies onto `ScanBuilder`. */\nexport class ScanReduceMethods<T, S extends keyof T = never, M extends keyof T & string = never> {\n declare protected readonly pageProvider: ScanPageProvider<T>\n declare protected readonly pageSize: number\n declare protected readonly clauses: readonly Clause[]\n declare protected readonly joins: readonly JoinLeg[]\n declare protected readonly joinContext: JoinContext | undefined\n declare protected readonly via: ViaPipeline | undefined\n declare protected decodeVia: (record: T) => T\n declare protected recordMatches: (record: T, clauses?: readonly Clause[]) => boolean\n declare [Symbol.asyncIterator]: () => AsyncIterator<T>\n\n async aggregate<const Specs extends readonly ReduceSpec[]>(specs: Specs): Promise<MultiReduceResult<Specs>>\n async aggregate<Spec extends ReduceSpec>(spec: Spec): Promise<ReduceResult<Spec>>\n async aggregate<Spec extends ReduceSpec>(build: (b: ReducerBuilder<T, S, M>) => Spec): Promise<ReduceResult<Spec>>\n async aggregate<Spec extends ReduceSpec>(\n specOrBuild: Spec | readonly ReduceSpec[] | ((b: ReducerBuilder<T, S, M>) => Spec),\n ): Promise<ReduceResult<Spec> | unknown[]> {\n // #1340 — the multi-spec form. N independent specs, ONE pass: the states\n // for every spec are stepped from the same yielded record, so the page\n // provider is walked exactly once no matter how many specs are passed.\n // (Calling `.aggregate(specA)` then `.aggregate(specB)` costs two full\n // scans — the immutable-builder docs above say so, and this is the fix.)\n if (Array.isArray(specOrBuild)) return this.aggregateMany(specOrBuild as readonly ReduceSpec[])\n // Opt-in builder form `aggregate(b => spec)`: `b`'s field args are\n // `QueryField<T, S>`, refusing sensitive fields (the standalone-spec form\n // stays unrefused for back-compat), and `sum`/`min`/`max` over a declared\n // `moneyFields` (`M`) member return a `MoneyString`. Mirrors `Query.aggregate`.\n const spec: Spec = typeof specOrBuild === 'function'\n ? (specOrBuild as (b: ReducerBuilder<T, S, M>) => Spec)(reducerBuilder as unknown as ReducerBuilder<T, S, M>)\n // `Array.isArray` above does not narrow a READONLY array out of the\n // union, so the array arm is already returned and this cast is the\n // remainder — a plain spec.\n : (specOrBuild as Spec)\n this.via?.refuseUnqueryableReducers(spec)\n const keys = Object.keys(spec)\n // Per-reducer state. Exactly |keys| entries, never grows with\n // the record count — that's the O(reducers) memory guarantee.\n const state: Record<string, unknown> = {}\n for (const key of keys) {\n state[key] = spec[key]!.init()\n }\n\n // Record-by-record streaming step. `for await (… of this)`\n // invokes the Symbol.asyncIterator above, which honors the\n // clause list, so filtered-out records never reach the step\n // loop — they're dropped at the iterator boundary.\n for await (const record of this) {\n for (const key of keys) {\n state[key] = spec[key]!.step(state[key], record)\n }\n }\n\n const result: Record<string, unknown> = {}\n for (const key of keys) {\n result[key] = spec[key]!.finalize(state[key])\n }\n return result as ReduceResult<Spec>\n }\n\n /**\n * The multi-spec `.aggregate([specA, specB, …])` executor (#1340).\n *\n * One iteration of the scan, `Σ|spec|` reducer states — memory stays\n * O(reducers) exactly as the single-spec terminal's does, with the sum taken\n * across specs instead of within one. Every spec's state is stepped from the\n * SAME yielded record, so the page provider is read once per page.\n *\n * Semantics per spec are identical to the single-spec form, deliberately:\n * the posture gate is `refuseUnqueryableReducers` (metadata only), NOT the\n * full `wrapReducers` — so `aggregate([a, b])` returns exactly what\n * `aggregate(a)` and `aggregate(b)` return, and the array form is a pure\n * cost optimisation with no behavioural delta to reason about. (The grouped\n * scan path DOES wrap — it has to agree with the eager grouped path, which\n * wraps. See `scan-groupby.ts`.)\n */\n private async aggregateMany(specs: readonly ReduceSpec[]): Promise<unknown[]> {\n for (const spec of specs) this.via?.refuseUnqueryableReducers(spec)\n const keysPerSpec = specs.map((spec) => Object.keys(spec))\n const states: Record<string, unknown>[] = specs.map((spec, i) => {\n const state: Record<string, unknown> = {}\n for (const key of keysPerSpec[i]!) state[key] = spec[key]!.init()\n return state\n })\n\n for await (const record of this) {\n for (let i = 0; i < specs.length; i++) {\n const spec = specs[i]!\n const state = states[i]!\n for (const key of keysPerSpec[i]!) state[key] = spec[key]!.step(state[key], record)\n }\n }\n\n return specs.map((spec, i) => {\n const result: Record<string, unknown> = {}\n for (const key of keysPerSpec[i]!) result[key] = spec[key]!.finalize(states[i]![key])\n return result\n })\n }\n\n /**\n * Group the scan stream and reduce each group — `#1340`.\n *\n * ```ts\n * const byClient = await invoices.scan()\n * .where('status', '==', 'open')\n * .groupBy('clientId', { maxGroups: 5_000 })\n * .aggregate({ total: sum('amount'), n: count() })\n * ```\n *\n * ⚠️ **This is the one scan terminal whose memory is NOT O(pageSize).** It\n * holds one reducer state per group, which is why the budget is explicit:\n * `maxGroups` defaults to the eager path's 100_000-group ceiling and is\n * REFUSED — never truncated — the moment a scan would exceed it. Price the\n * budget before raising it: an exact `median`/`percentile`, a `mode` or a\n * `countDistinct` holds O(values) per group, so an unbounded scan wants\n * `{ approx: true }` on the quantiles. Full rationale in `scan-groupby.ts`.\n *\n * The key may be a field name or a `dateTrunc()` derived calendar key (the\n * monthly-rollup shape); grouping BY a `sensitive` field is refused at\n * compile time, same as `Query.groupBy()`.\n */\n groupBy<F extends QueryField<T, S>>(field: F, opts?: { maxGroups?: number }): ScanGroupedScan<T, F, S, M>\n groupBy(key: DateTruncKey, opts?: { maxGroups?: number }): ScanGroupedScan<T, string, S, M>\n groupBy(key: QueryField<T, S> | DateTruncKey, opts?: { maxGroups?: number }): ScanGroupedScan<T, string, S, M> {\n return new ScanGroupedScan<T, string, S, M>(\n this,\n key as string | DateTruncKey,\n opts?.maxGroups ?? SCAN_GROUPBY_DEFAULT_MAX_GROUPS,\n this.via,\n )\n }\n}\n\n/** The public Reduce surface of `ScanBuilder` — merged by `./index.ts`. */\nexport type ScanReduceSurface<\n T,\n S extends keyof T = never,\n M extends keyof T & string = never,\n> = Pick<ScanReduceMethods<T, S, M>, 'aggregate' | 'groupBy'>\n\n/**\n * Default group ceiling — the same constant the eager `.groupBy()` enforces.\n * Duplicated as a literal rather than imported from\n * `with-lookup/reduce/groupby.ts` on purpose: that module carries the eager\n * `groupAndReduce` + `GroupedReduction` classes, and the always-on kernel must\n * not pull them in to read one number. The pairing is asserted by a test.\n */\nexport const SCAN_GROUPBY_DEFAULT_MAX_GROUPS = 100_000\n\n/**\n * Single-field spelling of `canonicalGroupKey` (`with-lookup/reduce/canonical-key.ts`).\n *\n * ⛔ Inlined, not imported, and not by preference: `check-architecture`'s\n * `port-layering` rule grandfathers this file for exactly two\n * `with-lookup/reduce/*` specifiers (`reducers.js`, `reduction.js`), and\n * `canonical-key.js` is not one of them — a new one may not be added\n * silently. The eager helper sorts its field list and serialises\n * each value — with exactly one field, that reduces to this line, including\n * the part that matters: `undefined` gets a sentinel so a MISSING key and an\n * explicit `null` land in different buckets, as they do under\n * `Query.groupBy()`. The agreement is held by test, not by comment\n * (`query-scan-groupby.test.ts` — null/undefined bucketing, and full equality\n * with the eager path).\n */\nfunction scanGroupKey(field: string, value: unknown): string {\n return `${field}=${value === undefined ? 'undefined' : JSON.stringify(value)}`\n}\n\n/** Group-key result-row shape: the key under its own name, plus the reducers. */\n\nexport type ScanGroupedRow<F extends string, R> = { [K in F]: unknown } & R\n\n/**\n * Chainable wrapper returned by `ScanBuilder.groupBy()`. The only operation on\n * it is `.aggregate()` — same minimal shape as `GroupedQuery`.\n */\nexport class ScanGroupedScan<\n T,\n F extends string,\n S extends keyof T = never,\n M extends keyof T & string = never,\n> {\n constructor(\n private readonly stream: AsyncIterable<T>,\n private readonly key: string | DateTruncKey,\n private readonly maxGroups: number,\n private readonly via: ViaPipeline | undefined,\n ) {\n if (!Number.isInteger(maxGroups) || maxGroups < 1) {\n throw new Error(\n `scan().groupBy(): { maxGroups: ${String(maxGroups)} } must be a positive integer — ` +\n `it is the declared ceiling on how many reducer states the grouped scan may hold.`,\n )\n }\n }\n\n /**\n * Fold the scan into one row per group. Resolves to `R[]`, ordered by each\n * group's FIRST-SEEN position in the stream (`Map` insertion order), which\n * is the same ordering rule the eager path documents.\n *\n * ```ts\n * const byMonth = await invoices.scan()\n * .where('status', '==', 'paid')\n * .groupBy(dateTrunc('closedAt', 'month', { as: 'month', timeZone: 'UTC' }), { maxGroups: 240 })\n * .aggregate({ total: sum('amount'), n: count() })\n * ```\n */\n async aggregate<Spec extends ReduceSpec>(spec: Spec): Promise<ScanGroupedRow<F, ReduceResult<Spec>>[]>\n async aggregate<Spec extends ReduceSpec>(\n build: (b: ReducerBuilder<T, S, M>) => Spec,\n ): Promise<ScanGroupedRow<F, ReduceResult<Spec>>[]>\n async aggregate<Spec extends ReduceSpec>(\n specOrBuild: Spec | ((b: ReducerBuilder<T, S, M>) => Spec),\n ): Promise<ScanGroupedRow<F, ReduceResult<Spec>>[]> {\n const raw: Spec =\n typeof specOrBuild === 'function'\n ? (specOrBuild as (b: ReducerBuilder<T, S, M>) => Spec)(reducerBuilder as unknown as ReducerBuilder<T, S, M>)\n : specOrBuild\n // Same reducer rewriting as the eager grouped path, and for the same\n // reason: a money `sum` must accumulate per-currency BigInt totals, and a\n // `countDistinct` must dedup on the canonical index key — otherwise a\n // grouped scan and a grouped query disagree on identical data.\n // `wrapReducers` runs the `queryable: 'none'` posture refusal itself.\n const spec: ReduceSpec = bindDistinctReducers(this.via ? this.via.wrapReducers(raw) : raw, this.via)\n const keys = Object.keys(spec)\n\n const field = groupKeyName(this.key)\n const derived: readonly DateTruncKey[] = isDateTruncKey(this.key) ? [this.key] : []\n const groups = new Map<string, { keyValue: unknown; state: Record<string, unknown> }>()\n\n for await (const record of this.stream) {\n // A derived calendar key is stamped onto a shallow copy before bucketing\n // — the reducers then see an ordinary row carrying an ordinary field,\n // exactly as they do under `Query.groupBy(dateTrunc(...))`.\n const row = derived.length === 0 ? record : projectDateTruncKeys([record], derived)[0]!\n const keyValue = readPath(row, field)\n const dedupKey = scanGroupKey(field, keyValue)\n let group = groups.get(dedupKey)\n if (group === undefined) {\n if (groups.size >= this.maxGroups) {\n // Loud and early: the state for this group is never allocated, so\n // the refusal fires at the budget, not after blowing through it.\n throw new GroupCardinalityError(field, groups.size + 1, this.maxGroups, 'scan')\n }\n const state: Record<string, unknown> = {}\n for (const k of keys) state[k] = spec[k]!.init()\n group = { keyValue, state }\n groups.set(dedupKey, group)\n }\n for (const k of keys) group.state[k] = spec[k]!.step(group.state[k], row)\n }\n\n const out: ScanGroupedRow<F, ReduceResult<Spec>>[] = []\n for (const group of groups.values()) {\n // Group key first, then the reducer outputs — same row shape as the\n // eager path, which tests assert by key order.\n const outRow: Record<string, unknown> = { [field]: group.keyValue }\n for (const k of keys) outRow[k] = spec[k]!.finalize(group.state[k])\n out.push(outRow as ScanGroupedRow<F, ReduceResult<Spec>>)\n }\n return out\n }\n}\n","/**\n * #1458 — the Reduce install. See `../relate/install.ts` for why this is a\n * function called from the entry rather than a module-level side effect.\n */\nimport { Query } from '../builder.js'\nimport { ScanBuilder } from '../scan-builder.js'\nimport { installMethods } from '../internal/core.js'\nimport { MaintenanceMethods } from '../internal/maintenance.js'\nimport { ReduceMethods } from './methods.js'\nimport { ScanReduceMethods } from './scan-methods.js'\n\nlet done = false\n\n/** Idempotent: the root barrel and the subpath entry both call it. */\nexport function installReduce(): void {\n if (done) return\n done = true\n installMethods(Query.prototype, MaintenanceMethods)\n installMethods(Query.prototype, ReduceMethods)\n installMethods(ScanBuilder.prototype, ScanReduceMethods)\n}\n"],"mappings":"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","names":[]}
|