@noy-db/hub 0.7.1-pre.0 → 0.8.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 +147 -14
- package/LICENSE +202 -21
- package/NOTICE +1 -0
- package/README.md +5 -5
- package/codemods/0.8.0-pre.json +61 -0
- package/dist/api-OS747DS4.js +18 -0
- package/dist/as/index.js +11 -12
- package/dist/as/index.js.map +1 -1
- package/dist/at/index.js +9 -10
- package/dist/attestation/index.js +37 -26
- package/dist/attestation/index.js.map +1 -1
- package/dist/{backup-XKCXVKV3.js → backup-YE2VQ26S.js} +15 -16
- package/dist/backup-YE2VQ26S.js.map +1 -0
- package/dist/blobs/index.js +13 -14
- package/dist/blobs/index.js.map +1 -1
- package/dist/broker/index.js +12 -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/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 +335 -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 +80 -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-VOLX3QRQ.js +209 -0
- package/dist/cargo/index.js +39 -36
- package/dist/{chunk-Q2MPFCQ7.js → chunk-23D6M6VM.js} +5 -5
- package/dist/chunk-23D6M6VM.js.map +1 -0
- package/dist/{chunk-R7BX2NB3.js → chunk-244QBK7Z.js} +4 -4
- package/dist/chunk-244QBK7Z.js.map +1 -0
- package/dist/chunk-2AFX33YA.js +936 -0
- package/dist/chunk-2AFX33YA.js.map +1 -0
- package/dist/{chunk-MVMXZYXT.js → chunk-2GHDLTPE.js} +2 -2
- package/dist/{chunk-GYGLAVCX.js → chunk-2MG3SW7Q.js} +6 -2
- package/dist/chunk-2MG3SW7Q.js.map +1 -0
- package/dist/{chunk-XLMEZIAJ.js → chunk-2ODVB6LZ.js} +2 -2
- package/dist/{chunk-HJUWN7JN.js → chunk-2PVXF5CS.js} +33 -8
- package/dist/chunk-2PVXF5CS.js.map +1 -0
- package/dist/{chunk-YJRT5UQM.js → chunk-2RH777HH.js} +21 -20
- package/dist/{chunk-YJRT5UQM.js.map → chunk-2RH777HH.js.map} +1 -1
- package/dist/{chunk-7O3Y4PPD.js → chunk-37EPTBBR.js} +153 -16
- package/dist/chunk-37EPTBBR.js.map +1 -0
- package/dist/{chunk-2ENE6445.js → chunk-3A5DUZ56.js} +102 -16
- package/dist/chunk-3A5DUZ56.js.map +1 -0
- package/dist/chunk-3EWS67C7.js +461 -0
- package/dist/chunk-3EWS67C7.js.map +1 -0
- package/dist/{chunk-SGZAIJCV.js → chunk-3L2NBKDR.js} +4 -8
- package/dist/chunk-3L2NBKDR.js.map +1 -0
- package/dist/{chunk-YMUFFJCO.js → chunk-3TDUWVWB.js} +5 -2
- package/dist/chunk-3TDUWVWB.js.map +1 -0
- package/dist/{chunk-4GVM6QZA.js → chunk-3XJQBBZD.js} +26 -28
- package/dist/chunk-3XJQBBZD.js.map +1 -0
- package/dist/{chunk-4ZS56W5S.js → chunk-42ALPZ2B.js} +672 -664
- package/dist/chunk-42ALPZ2B.js.map +1 -0
- package/dist/chunk-4CIQDWRB.js +524 -0
- package/dist/chunk-4CIQDWRB.js.map +1 -0
- package/dist/{chunk-RMS3NEA3.js → chunk-4JPMZTJY.js} +2 -2
- package/dist/{chunk-P3MG6QCN.js → chunk-4T3SE27M.js} +5 -9
- package/dist/chunk-4T3SE27M.js.map +1 -0
- package/dist/chunk-4V3Q35WG.js +204 -0
- package/dist/chunk-4V3Q35WG.js.map +1 -0
- package/dist/{chunk-NQKIUNW5.js → chunk-52MGURXO.js} +2 -2
- package/dist/{chunk-EQEYOJBM.js → chunk-5A4345TG.js} +4 -4
- package/dist/chunk-5A4345TG.js.map +1 -0
- package/dist/{chunk-334WSVOW.js → chunk-5FE75J4S.js} +6 -6
- package/dist/chunk-5FE75J4S.js.map +1 -0
- package/dist/chunk-5KC7QYEC.js +565 -0
- package/dist/chunk-5KC7QYEC.js.map +1 -0
- package/dist/{chunk-RSGLUIO6.js → chunk-5UMK7GRI.js} +13 -12
- package/dist/chunk-5UMK7GRI.js.map +1 -0
- package/dist/chunk-5WWDGFOS.js +275 -0
- package/dist/chunk-5WWDGFOS.js.map +1 -0
- package/dist/{chunk-QPQUVOZ2.js → chunk-6QVKPHBF.js} +3 -5
- package/dist/chunk-6QVKPHBF.js.map +1 -0
- package/dist/{chunk-ACAQ2DSG.js → chunk-747G6CT3.js} +2 -2
- package/dist/chunk-747G6CT3.js.map +1 -0
- package/dist/{chunk-QTQUH4DS.js → chunk-7R2Q7CPL.js} +16 -16
- package/dist/chunk-7R2Q7CPL.js.map +1 -0
- package/dist/{chunk-UGTBXF5U.js → chunk-7XMHMVTS.js} +3 -3
- package/dist/{chunk-YJX7EOJD.js → chunk-A4XMIZT3.js} +6 -6
- package/dist/{chunk-YJX7EOJD.js.map → chunk-A4XMIZT3.js.map} +1 -1
- package/dist/{chunk-A66I4JOA.js → chunk-AEQGFRU4.js} +4 -4
- package/dist/chunk-AEQGFRU4.js.map +1 -0
- package/dist/chunk-AL6ML4LA.js +1558 -0
- package/dist/chunk-AL6ML4LA.js.map +1 -0
- package/dist/{chunk-BZ3534YM.js → chunk-AUKKUPVB.js} +2 -2
- package/dist/{chunk-CUZLNGD5.js → chunk-B2YY6WDD.js} +1 -1
- package/dist/chunk-B2YY6WDD.js.map +1 -0
- package/dist/chunk-BKT776R5.js +1 -0
- package/dist/chunk-BNWA3WG5.js +22 -0
- package/dist/chunk-BNWA3WG5.js.map +1 -0
- package/dist/{chunk-VXANTWBJ.js → chunk-BPWYQ25R.js} +92 -6
- package/dist/{chunk-VXANTWBJ.js.map → chunk-BPWYQ25R.js.map} +1 -1
- package/dist/{chunk-6LKOJBV6.js → chunk-BVCUTXE3.js} +2 -2
- package/dist/chunk-CY2CYVWZ.js +41 -0
- package/dist/chunk-CY2CYVWZ.js.map +1 -0
- package/dist/{chunk-M4YPHYEH.js → chunk-DHHGL42E.js} +70 -25
- package/dist/chunk-DHHGL42E.js.map +1 -0
- package/dist/{chunk-VTFVLCUH.js → chunk-DIZLQVAA.js} +2 -2
- package/dist/{chunk-G73UVHCA.js → chunk-DLD5LGDS.js} +11 -12
- package/dist/chunk-DLD5LGDS.js.map +1 -0
- package/dist/{chunk-WQBG4SZS.js → chunk-DQESF6RP.js} +60 -49
- package/dist/chunk-DQESF6RP.js.map +1 -0
- package/dist/{chunk-LWBCUYZW.js → chunk-E2N2T5ZO.js} +2 -2
- package/dist/{chunk-3PGS7TYR.js → chunk-EGVJ7TM4.js} +29 -32
- package/dist/chunk-EGVJ7TM4.js.map +1 -0
- package/dist/{chunk-JVIBCBC7.js → chunk-EJLV62NI.js} +2 -2
- package/dist/{chunk-KKEESNPE.js → chunk-EYG2CY2X.js} +126 -19
- package/dist/chunk-EYG2CY2X.js.map +1 -0
- package/dist/{enclave-55PAM4YO.js → chunk-EZGV6F47.js} +83 -56
- package/dist/chunk-EZGV6F47.js.map +1 -0
- package/dist/{chunk-CHQCZZWC.js → chunk-EZOAKKTB.js} +36 -27
- package/dist/chunk-EZOAKKTB.js.map +1 -0
- package/dist/chunk-F6NCB7KT.js +13 -0
- package/dist/chunk-F6NCB7KT.js.map +1 -0
- package/dist/chunk-FFJFE4T7.js +100 -0
- package/dist/chunk-FFJFE4T7.js.map +1 -0
- package/dist/{chunk-JMGDHKQF.js → chunk-FLPCETP6.js} +2 -2
- package/dist/chunk-FOEDQATO.js +53 -0
- package/dist/chunk-FOEDQATO.js.map +1 -0
- package/dist/{chunk-DGOZ4UZD.js → chunk-FVH5QYY2.js} +2 -2
- package/dist/{chunk-HIWCKBEY.js → chunk-G2MVBDFP.js} +54 -28
- package/dist/chunk-G2MVBDFP.js.map +1 -0
- package/dist/chunk-GMKGGY7V.js +234 -0
- package/dist/chunk-GMKGGY7V.js.map +1 -0
- package/dist/{chunk-EIPVYUEP.js → chunk-GQ4MF3CM.js} +3 -3
- package/dist/chunk-GQ4MF3CM.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-5VCRVKOZ.js → chunk-GUZET4SG.js} +12 -4
- package/dist/chunk-GUZET4SG.js.map +1 -0
- package/dist/{chunk-JBDFMYKQ.js → chunk-GZT3NQRA.js} +2 -2
- package/dist/{chunk-K23VKTN4.js → chunk-H23XHRTT.js} +19 -34
- package/dist/chunk-H23XHRTT.js.map +1 -0
- package/dist/{chunk-TFR7DSOV.js → chunk-H427H63K.js} +2 -2
- package/dist/{chunk-DFMWUOU5.js → chunk-H7JBY4P7.js} +23 -32
- package/dist/chunk-H7JBY4P7.js.map +1 -0
- package/dist/{chunk-SKGMN35F.js → chunk-HS554XAF.js} +3 -3
- package/dist/chunk-HS554XAF.js.map +1 -0
- package/dist/{chunk-HK67PX4J.js → chunk-HW7CMKUG.js} +1 -1
- package/dist/chunk-HW7CMKUG.js.map +1 -0
- package/dist/{chunk-3BVH633C.js → chunk-IMQKWR46.js} +5 -7
- package/dist/chunk-IMQKWR46.js.map +1 -0
- package/dist/{chunk-R42NSZI6.js → chunk-IUBMZPHM.js} +7 -7
- package/dist/chunk-IUBMZPHM.js.map +1 -0
- package/dist/{chunk-CUQPWYRX.js → chunk-IUT3GWP3.js} +75 -386
- package/dist/chunk-IUT3GWP3.js.map +1 -0
- package/dist/{chunk-RF6D3Y5E.js → chunk-JDEYF3PJ.js} +4 -8
- package/dist/{chunk-RF6D3Y5E.js.map → chunk-JDEYF3PJ.js.map} +1 -1
- package/dist/{chunk-PPTHY5QO.js → chunk-JIBUPZHA.js} +99 -8
- package/dist/chunk-JIBUPZHA.js.map +1 -0
- package/dist/{chunk-SMH6SRX7.js → chunk-JOSPH3E5.js} +2 -2
- package/dist/chunk-JQYKPKG6.js +193 -0
- package/dist/chunk-JQYKPKG6.js.map +1 -0
- package/dist/{chunk-7APJ6CO6.js → chunk-JSMTXZR3.js} +45 -13
- package/dist/chunk-JSMTXZR3.js.map +1 -0
- package/dist/chunk-JWWYRR4Q.js +12 -0
- package/dist/chunk-JWWYRR4Q.js.map +1 -0
- package/dist/{chunk-BVN6KXL2.js → chunk-KZXDLBY6.js} +14 -9
- package/dist/chunk-KZXDLBY6.js.map +1 -0
- package/dist/chunk-LGNDFRYC.js +23 -0
- package/dist/chunk-LGNDFRYC.js.map +1 -0
- package/dist/{chunk-XKWGNJ26.js → chunk-LIOVX5TJ.js} +7 -11
- package/dist/chunk-LIOVX5TJ.js.map +1 -0
- package/dist/{chunk-AYX7AINO.js → chunk-LNSVGRHO.js} +5 -7
- package/dist/chunk-LNSVGRHO.js.map +1 -0
- package/dist/chunk-M5B2W6YY.js +293 -0
- package/dist/chunk-M5B2W6YY.js.map +1 -0
- package/dist/chunk-MBMTND6D.js +456 -0
- package/dist/chunk-MBMTND6D.js.map +1 -0
- package/dist/{chunk-3BZZJJMQ.js → chunk-MILI22XQ.js} +3 -3
- package/dist/{chunk-A2HKRLPV.js → chunk-MXKI4ABF.js} +18 -59
- package/dist/chunk-MXKI4ABF.js.map +1 -0
- package/dist/{chunk-ZYIYHJ5R.js → chunk-MXOQ5JCG.js} +869 -238
- package/dist/chunk-MXOQ5JCG.js.map +1 -0
- package/dist/{chunk-V7ZRRCRJ.js → chunk-N7EVQD5S.js} +9 -13
- package/dist/chunk-N7EVQD5S.js.map +1 -0
- package/dist/chunk-N7UDP5VU.js +757 -0
- package/dist/chunk-N7UDP5VU.js.map +1 -0
- package/dist/chunk-NBQ3KK6X.js +270 -0
- package/dist/chunk-NBQ3KK6X.js.map +1 -0
- package/dist/{chunk-ZORTV6NH.js → chunk-NCO25JZO.js} +88 -4
- package/dist/chunk-NCO25JZO.js.map +1 -0
- package/dist/chunk-NH2UOV3B.js +18 -0
- package/dist/chunk-NH2UOV3B.js.map +1 -0
- package/dist/chunk-NYMLINRP.js +28 -0
- package/dist/chunk-NYMLINRP.js.map +1 -0
- package/dist/{chunk-YYP7WSEB.js → chunk-O2IMQSNA.js} +2 -2
- package/dist/chunk-O46CHQT7.js +215 -0
- package/dist/chunk-O46CHQT7.js.map +1 -0
- package/dist/chunk-OIEO4WDL.js +27 -0
- package/dist/chunk-OIEO4WDL.js.map +1 -0
- package/dist/{chunk-TRGTKX5E.js → chunk-OKSTSITU.js} +27 -4
- package/dist/chunk-OKSTSITU.js.map +1 -0
- package/dist/chunk-OYW3I5M4.js +31 -0
- package/dist/chunk-OYW3I5M4.js.map +1 -0
- package/dist/{chunk-Q7HZX3IF.js → chunk-PD64727P.js} +2 -2
- package/dist/chunk-PD64727P.js.map +1 -0
- package/dist/{chunk-FET5XCAT.js → chunk-PP3K67AJ.js} +34 -6
- package/dist/chunk-PP3K67AJ.js.map +1 -0
- package/dist/chunk-Q2XRFWOI.js +40 -0
- package/dist/chunk-Q2XRFWOI.js.map +1 -0
- package/dist/{chunk-QIZPZMUI.js → chunk-Q4UHJ5SN.js} +2 -2
- package/dist/{chunk-FAHMLERK.js → chunk-QBLT6POL.js} +2 -2
- package/dist/chunk-QMWY2FDS.js +157 -0
- package/dist/chunk-QMWY2FDS.js.map +1 -0
- package/dist/{chunk-LH5A3ZPG.js → chunk-RB4DOPGZ.js} +150 -22
- package/dist/chunk-RB4DOPGZ.js.map +1 -0
- package/dist/chunk-RHVMZSUU.js +7 -0
- package/dist/chunk-RHVMZSUU.js.map +1 -0
- package/dist/{chunk-BUE2D7PX.js → chunk-SMV2HRDU.js} +2 -2
- package/dist/chunk-SUZKW6UR.js +25 -0
- package/dist/chunk-SUZKW6UR.js.map +1 -0
- package/dist/{chunk-7MG7SJT7.js → chunk-SV2PM2OY.js} +1 -1
- package/dist/chunk-SV2PM2OY.js.map +1 -0
- package/dist/{chunk-UXVWEX5L.js → chunk-SYNY3VB5.js} +6 -2
- package/dist/chunk-SYNY3VB5.js.map +1 -0
- package/dist/{chunk-4GFX6MPA.js → chunk-TEHHROZL.js} +6 -8
- package/dist/chunk-TEHHROZL.js.map +1 -0
- package/dist/chunk-TKVUE4JF.js +1289 -0
- package/dist/chunk-TKVUE4JF.js.map +1 -0
- package/dist/{chunk-SFAWCTEM.js → chunk-TZZBXPZK.js} +2 -2
- package/dist/{chunk-F3W6YMDC.js → chunk-U2FZNEWJ.js} +4 -4
- package/dist/chunk-U2FZNEWJ.js.map +1 -0
- package/dist/{chunk-BLN54W3L.js → chunk-UA372JHH.js} +56 -3
- package/dist/chunk-UA372JHH.js.map +1 -0
- package/dist/{chunk-6YXCPL4T.js → chunk-UEFOQH74.js} +2 -2
- package/dist/chunk-UEFOQH74.js.map +1 -0
- package/dist/{chunk-2N5ML6HT.js → chunk-UIYESRD6.js} +9 -11
- package/dist/chunk-UIYESRD6.js.map +1 -0
- package/dist/{chunk-UHHPLO3I.js → chunk-V3RWOOSO.js} +4 -4
- package/dist/{chunk-KTFRCC5C.js → chunk-V7ADQF5O.js} +82 -8
- package/dist/chunk-V7ADQF5O.js.map +1 -0
- package/dist/{chunk-453F654R.js → chunk-VAORSCSU.js} +3 -3
- package/dist/chunk-VAORSCSU.js.map +1 -0
- package/dist/{chunk-GPNTB4Y7.js → chunk-VFHCKG5M.js} +6 -10
- package/dist/chunk-VFHCKG5M.js.map +1 -0
- package/dist/{chunk-2577DKXB.js → chunk-VHBKYN2J.js} +3 -3
- package/dist/chunk-VHBKYN2J.js.map +1 -0
- package/dist/chunk-VHM7HCKB.js +220 -0
- package/dist/chunk-VHM7HCKB.js.map +1 -0
- package/dist/{chunk-ZHKOPKE3.js → chunk-VKI2EB3N.js} +3 -3
- package/dist/chunk-VKI2EB3N.js.map +1 -0
- package/dist/{chunk-UDHWDSRL.js → chunk-VMV5SBE4.js} +16 -17
- package/dist/chunk-VMV5SBE4.js.map +1 -0
- package/dist/{chunk-SPYWXKJN.js → chunk-VQDZYB2B.js} +3 -3
- package/dist/chunk-VQDZYB2B.js.map +1 -0
- package/dist/{chunk-Q2PU57HC.js → chunk-VVOLDVIR.js} +2 -2
- package/dist/{chunk-3VF3WQ4F.js → chunk-WPOYWFSE.js} +7 -7
- package/dist/{chunk-3VF3WQ4F.js.map → chunk-WPOYWFSE.js.map} +1 -1
- package/dist/{chunk-OTQZ7ZWU.js → chunk-WXE4W2U6.js} +7 -11
- package/dist/chunk-WXE4W2U6.js.map +1 -0
- package/dist/chunk-XBKH4FOC.js +195 -0
- package/dist/chunk-XBKH4FOC.js.map +1 -0
- package/dist/{chunk-TUFM4UZP.js → chunk-XQRFSKYM.js} +2 -2
- package/dist/{chunk-3V4KWFX7.js → chunk-XSPFDUDG.js} +58 -53
- package/dist/chunk-XSPFDUDG.js.map +1 -0
- package/dist/{chunk-GY4WYNNG.js → chunk-Y2RKVFKR.js} +2 -2
- package/dist/{chunk-UP4BECPY.js → chunk-Y3LPTBG7.js} +89 -27
- package/dist/chunk-Y3LPTBG7.js.map +1 -0
- package/dist/{chunk-4OZNXTO4.js → chunk-YRXFFWG4.js} +2 -2
- package/dist/classified/index.js +5 -5
- package/dist/{classified-marker-BWOFVYAC.js → classified-marker-SIYKUU5L.js} +3 -3
- package/dist/classified-marker-SIYKUU5L.js.map +1 -0
- package/dist/collection-facade-CS7CA2XO.js +47 -0
- package/dist/{computed-VPOKQK4N.js → computed-FAD7SED4.js} +3 -3
- package/dist/consent/index.js +10 -11
- package/dist/consent/index.js.map +1 -1
- package/dist/cover/index.js +10 -11
- package/dist/coverage/index.js +306 -0
- package/dist/coverage/index.js.map +1 -0
- package/dist/custody/index.js +12 -13
- package/dist/{dead-filter-PKQDLOT4.js → dead-filter-4F3LWYKH.js} +11 -3
- package/dist/dead-filter-4F3LWYKH.js.map +1 -0
- package/dist/debug/index.js +11 -2
- package/dist/debug/index.js.map +1 -1
- package/dist/{delegation-DS5NRILW.js → delegation-NANZDG4N.js} +13 -16
- package/dist/delegation-NANZDG4N.js.map +1 -0
- package/dist/derivations/index.js +25 -23
- package/dist/derive-XLOJVXPU.js +20 -0
- package/dist/directory/index.js +10 -11
- package/dist/{dispatch-IEPAVQUM.js → dispatch-H4HV32AM.js} +5 -5
- package/dist/{dispatch-Q6MWLOJY.js → dispatch-JDZNGEB3.js} +20 -21
- package/dist/dispatch-JDZNGEB3.js.map +1 -0
- package/dist/executor-FMPVN6DT.js +35 -0
- package/dist/executor-LBBBRTN5.js +8 -0
- package/dist/executor-OSRZST2U.js +8 -0
- package/dist/export-accessible-RAOJAAU5.js +22 -0
- package/dist/extract-partition-JD5CUSTG.js +37 -0
- package/dist/{fanout-sidecar-S3QL53YX.js → fanout-sidecar-L7NHPIOC.js} +13 -15
- package/dist/fanout-sidecar-L7NHPIOC.js.map +1 -0
- package/dist/find-JRN4RNQ5.js +10 -0
- package/dist/forget/index.js +9 -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 +36 -14
- package/dist/index.js +970 -163
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +13 -5
- package/dist/indexing/index.js.map +1 -1
- package/dist/introspection/index.js +11 -12
- package/dist/issue-YIVBZUTH.js +19 -0
- package/dist/ivf-flat-UZ6DQDLS.js +314 -0
- package/dist/ivf-flat-UZ6DQDLS.js.map +1 -0
- package/dist/{json-schema-V3M5MUCM.js → json-schema-3SHX72CC.js} +2 -1
- package/dist/json-schema-3SHX72CC.js.map +1 -0
- package/dist/kernel/attestation-types.d.ts +62 -0
- package/dist/kernel/collection-config.d.ts +5 -68
- package/dist/kernel/collection.d.ts +30 -9
- package/dist/kernel/cutover-transform.d.ts +1 -1
- package/dist/kernel/errors.d.ts +257 -38
- package/dist/kernel/generation-map.d.ts +48 -0
- package/dist/kernel/merge-authority.d.ts +1 -1
- package/dist/kernel/noydb.d.ts +6 -1
- package/dist/kernel/paths.d.ts +29 -5
- package/dist/kernel/prepared-write.d.ts +1 -1
- package/dist/kernel/present-cache.d.ts +58 -0
- package/dist/kernel/query/all.d.ts +7 -0
- package/dist/kernel/query/builder.d.ts +601 -270
- package/dist/kernel/query/civil-date.d.ts +64 -0
- package/dist/kernel/query/cursor.d.ts +40 -0
- package/dist/kernel/query/distinct-key.d.ts +98 -0
- package/dist/kernel/query/hydration.d.ts +76 -0
- package/dist/kernel/query/index.d.ts +31 -15
- package/dist/kernel/query/internal/core.d.ts +30 -0
- package/dist/kernel/query/internal/hooks.d.ts +22 -0
- package/dist/kernel/query/internal/maintenance.d.ts +43 -0
- package/dist/kernel/query/live/incremental.d.ts +314 -0
- package/dist/kernel/query/live/index.d.ts +22 -0
- package/dist/kernel/query/live/install.d.ts +2 -0
- package/dist/kernel/query/{live.d.ts → live/live.d.ts} +39 -9
- package/dist/kernel/query/live/methods.d.ts +107 -0
- package/dist/kernel/query/predicate.d.ts +119 -1
- package/dist/kernel/query/reduce/date-trunc.d.ts +75 -0
- package/dist/kernel/query/reduce/index.d.ts +36 -0
- package/dist/kernel/query/reduce/install.d.ts +2 -0
- package/dist/kernel/query/reduce/methods.d.ts +259 -0
- package/dist/kernel/query/reduce/scan-methods.d.ts +121 -0
- package/dist/kernel/query/relate/explain.d.ts +199 -0
- package/dist/kernel/query/relate/index.d.ts +48 -0
- package/dist/kernel/query/relate/install.d.ts +2 -0
- package/dist/kernel/query/relate/join-on.d.ts +126 -0
- package/dist/kernel/query/relate/join-reduce.d.ts +16 -0
- package/dist/kernel/query/relate/join.d.ts +452 -0
- package/dist/kernel/query/relate/methods.d.ts +426 -0
- package/dist/kernel/query/relate/partition.d.ts +87 -0
- package/dist/kernel/query/relate/scan-methods.d.ts +163 -0
- package/dist/kernel/query/relate/traverse.d.ts +109 -0
- package/dist/kernel/query/scan-builder.d.ts +79 -176
- package/dist/kernel/query/sort-key.d.ts +52 -0
- package/dist/kernel/secret-mode.d.ts +1 -1
- package/dist/kernel/tx-write-gate.d.ts +77 -0
- package/dist/kernel/types.d.ts +113 -8
- package/dist/kernel/validation.d.ts +27 -10
- package/dist/kernel/vault.d.ts +11 -6
- package/dist/kernel/via/compose.d.ts +3 -0
- package/dist/kernel/via/index.d.ts +56 -0
- package/dist/kernel/via/pipeline.d.ts +48 -0
- package/dist/kernel/write-queue.d.ts +17 -0
- package/dist/{ledger-EDXFFZQL.js → ledger-5ZMGE44K.js} +11 -12
- package/dist/liberate-WZSZM2W6.js +24 -0
- package/dist/link-set-MFXC24VK.js +29 -0
- package/dist/{managed-secret-NCWEMPUQ.js → managed-secret-MFTFT22X.js} +10 -11
- package/dist/materialized-views/index.js +30 -24
- package/dist/{migrate-cek-5ENH64AA.js → migrate-cek-DZRFFOHW.js} +2 -2
- package/dist/money/index.js +5 -5
- package/dist/noydb-ITIGNKUF.js +96 -0
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.js +12 -13
- package/dist/pod/index.js +74 -67
- package/dist/{pod-handle-ECVCDIXW.js → pod-handle-A3LET7NO.js} +10 -11
- package/dist/pod-handle-A3LET7NO.js.map +1 -0
- package/dist/policy/index.js +10 -11
- package/dist/port/on/index.d.ts +1 -1
- package/dist/port/to/index.d.ts +1 -1
- package/dist/port/with/blob-strategy.d.ts +10 -1
- package/dist/port/with/classified-strategy.d.ts +1 -1
- package/dist/port/with/collection-options.d.ts +6 -1
- package/dist/port/with/collection-registries.d.ts +1 -1
- package/dist/port/with/coverage-strategy.d.ts +83 -0
- package/dist/port/with/lookup-strategy.d.ts +2 -2
- package/dist/port/with/service-bus.d.ts +46 -0
- package/dist/port/with/strategies.d.ts +2 -0
- package/dist/portability/index.js +8 -8
- package/dist/{post-register-3IPCSG6Q.js → post-register-7ND27CF2.js} +6 -6
- package/dist/post-register-7ND27CF2.js.map +1 -0
- package/dist/query/all/index.js +31 -0
- package/dist/query/index.js +20 -32
- package/dist/query/live/index.js +21 -0
- package/dist/query/live/index.js.map +1 -0
- package/dist/query/reduce/index.js +33 -0
- package/dist/query/reduce/index.js.map +1 -0
- package/dist/query/relate/index.js +56 -0
- package/dist/query/relate/index.js.map +1 -0
- package/dist/reduce/index.js +72 -18
- package/dist/reduce/index.js.map +1 -1
- package/dist/register-DT5HZ2LZ.js +30 -0
- package/dist/registry-BHRZGZHF.js +8 -0
- package/dist/registry-ET6ZFFWA.js +34 -0
- package/dist/registry-HXFGLGTG.js +8 -0
- package/dist/registry-THCPDEOD.js +23 -0
- package/dist/request-withdrawal-AV2MJJKY.js +30 -0
- package/dist/{reveal-QZDHSGD4.js → reveal-B2TLP4JQ.js} +10 -9
- package/dist/reveal-B2TLP4JQ.js.map +1 -0
- package/dist/revoke-6DWIEOCV.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 +11 -5
- package/dist/{seed-X5CC22CQ.js → seed-XNSE4U3S.js} +15 -18
- package/dist/seed-XNSE4U3S.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-MHGW3OIL.js +23 -0
- package/dist/snapshots/index.js +11 -11
- package/dist/stale-6GCFMOOR.js +28 -0
- package/dist/storage-BYN5LO6Z.js +21 -0
- package/dist/storage-BYN5LO6Z.js.map +1 -0
- package/dist/store/index.js +1 -1
- package/dist/{store-coordination-provider-4WZAIRHC.js → store-coordination-provider-C4BPT5XF.js} +11 -12
- package/dist/store-coordination-provider-C4BPT5XF.js.map +1 -0
- package/dist/sync/index.js +10 -11
- package/dist/sync/index.js.map +1 -1
- package/dist/team/index.js +17 -16
- package/dist/tiers/index.js +13 -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-6MDTIUEV.js → verify-UKALW7C2.js} +14 -12
- package/dist/verify-UKALW7C2.js.map +1 -0
- package/dist/via/blob/index.d.ts +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/geo/binding.d.ts +31 -0
- package/dist/via/geo/descriptor.d.ts +54 -0
- package/dist/via/geo/geohash.d.ts +95 -0
- package/dist/via/geo/index.d.ts +13 -0
- package/dist/via/geo/where.d.ts +50 -0
- package/dist/via/i18n/core.d.ts +1 -1
- package/dist/via/lookup/handle.d.ts +4 -4
- package/dist/via/lookup/registry.d.ts +1 -1
- package/dist/via/lookup/snapshot.d.ts +3 -3
- package/dist/walk-4VPK7AS4.js +20 -0
- package/dist/walk-4VPK7AS4.js.map +1 -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 +2 -2
- package/dist/with-audit/attestation/types.d.ts +10 -0
- package/dist/with-audit/attestation/vault-facade.d.ts +2 -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/coverage/accounting.d.ts +112 -0
- package/dist/with-audit/coverage/active.d.ts +29 -0
- package/dist/with-audit/coverage/index.d.ts +61 -0
- package/dist/with-audit/coverage/sketch.d.ts +75 -0
- package/dist/with-audit/forget/subject-index.d.ts +1 -1
- package/dist/with-audit/periods/periods.d.ts +55 -7
- package/dist/with-audit/periods/reserved-collections.d.ts +3 -0
- package/dist/with-audit/periods/vault-facade.d.ts +18 -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 +53 -2
- package/dist/with-audit/tiers/strategy.d.ts +8 -0
- package/dist/with-cargo/adopt-partition.d.ts +1 -1
- package/dist/with-cargo/extract-partition.d.ts +1 -1
- package/dist/with-cargo/floor.d.ts +3 -3
- 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 -1
- package/dist/with-commit/sequence/index.d.ts +1 -1
- package/dist/with-commit/tx/invariants.d.ts +1 -1
- package/dist/with-commit/tx/transaction.d.ts +13 -3
- 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 +1 -1
- package/dist/with-commit/vault-head/verify.d.ts +1 -1
- 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/materialized-views/dependency-analyzer.d.ts +4 -3
- package/dist/with-formula/materialized-views/emit-memo.d.ts +40 -0
- package/dist/with-formula/materialized-views/executor.d.ts +8 -0
- package/dist/with-formula/materialized-views/index.d.ts +1 -1
- package/dist/with-formula/materialized-views/registry.d.ts +15 -0
- package/dist/with-formula/materialized-views/types.d.ts +106 -14
- package/dist/with-formula/materialized-views/window-stage.d.ts +11 -0
- package/dist/with-formula/overlay-views/types.d.ts +1 -1
- package/dist/with-formula/overlay-views/virtual-collection.d.ts +13 -4
- package/dist/with-lookup/embeddings/chunks.d.ts +79 -0
- package/dist/with-lookup/embeddings/descriptor.d.ts +21 -0
- package/dist/with-lookup/embeddings/index.d.ts +3 -0
- package/dist/with-lookup/embeddings/ivf-flat.d.ts +93 -0
- package/dist/with-lookup/embeddings/vector-index.d.ts +73 -0
- package/dist/with-lookup/embeddings/vector-set.d.ts +94 -1
- package/dist/with-lookup/embeddings/with-vector-index.d.ts +67 -0
- package/dist/with-lookup/indexing/collection-facade.d.ts +86 -3
- package/dist/with-lookup/indexing/compound-indexes.d.ts +129 -0
- package/dist/with-lookup/indexing/eager-indexes.d.ts +170 -1
- package/dist/with-lookup/indexing/index-snapshot.d.ts +62 -0
- package/dist/with-lookup/indexing/lazy-builder.d.ts +33 -0
- package/dist/with-lookup/indexing/persisted-field-indexes.d.ts +104 -0
- package/dist/with-lookup/indexing/persisted-indexes.d.ts +12 -0
- package/dist/with-lookup/indexing/sorted-indexes.d.ts +117 -0
- package/dist/with-lookup/indexing/unique-constraints.d.ts +106 -23
- package/dist/with-lookup/reduce/active.d.ts +2 -24
- package/dist/with-lookup/reduce/group-core.d.ts +43 -0
- package/dist/with-lookup/reduce/groupby.d.ts +195 -36
- package/dist/with-lookup/reduce/incremental-group.d.ts +153 -0
- package/dist/with-lookup/reduce/index.d.ts +26 -5
- package/dist/with-lookup/reduce/reducers.d.ts +255 -1
- package/dist/with-lookup/reduce/reduction.d.ts +44 -13
- package/dist/with-lookup/reduce/strategy.d.ts +43 -3
- package/dist/with-lookup/reduce/window.d.ts +262 -0
- package/dist/with-lookup/search/collection-facade.d.ts +45 -9
- package/dist/with-lookup/search/index-store.d.ts +5 -3
- package/dist/with-lookup/search/index.d.ts +4 -2
- package/dist/with-lookup/search/inverted-index.d.ts +38 -2
- package/dist/with-lookup/search/persisted-index-store.d.ts +5 -2
- package/dist/with-lookup/search/phrase.d.ts +28 -0
- package/dist/with-lookup/search/retrieve-types.d.ts +57 -1
- package/dist/with-lookup/search/serialize.d.ts +29 -3
- package/dist/with-lookup/search/strategy.d.ts +2 -6
- package/dist/with-party/broker/index.d.ts +3 -3
- package/dist/with-party/directory/cover/index.d.ts +1 -1
- package/dist/with-party/directory/cover/storage.d.ts +1 -1
- package/dist/with-party/directory/cover/types.d.ts +1 -1
- package/dist/with-party/directory/index.d.ts +1 -1
- package/dist/with-party/directory/types.d.ts +1 -1
- package/dist/with-party/directory/user-envelope/api.d.ts +1 -1
- package/dist/with-party/directory/user-envelope/storage.d.ts +1 -1
- package/dist/with-party/policy/engine.d.ts +1 -1
- package/dist/with-party/policy/index.d.ts +1 -1
- package/dist/with-party/policy/presets.d.ts +1 -1
- package/dist/with-party/team/authenticators.d.ts +91 -1
- package/dist/with-party/team/delegation.d.ts +1 -1
- package/dist/with-party/team/echo-secret.d.ts +2 -2
- package/dist/with-party/team/index.d.ts +4 -3
- package/dist/with-party/team/keyring.d.ts +2 -2
- package/dist/with-party/team/magic-link-grant.d.ts +1 -1
- package/dist/with-party/team/managed-secret.d.ts +1 -1
- package/dist/with-party/team/noydb-facade.d.ts +12 -3
- package/dist/with-party/team/recovery.d.ts +1 -1
- package/dist/with-party/team/roster-tag.d.ts +1 -1
- package/dist/with-party/team/rotate-recover.d.ts +1 -1
- package/dist/with-party/team/wrapped-deks.d.ts +1 -1
- package/dist/with-pod/format.d.ts +1 -1
- package/dist/with-pod/index.d.ts +1 -1
- package/dist/with-pod/pod.d.ts +1 -1
- package/dist/with-shape/blobs/blob-compaction.d.ts +17 -1
- package/dist/with-shape/blobs/blob-intent.d.ts +1 -1
- package/dist/with-shape/blobs/blob-set.d.ts +23 -4
- package/dist/with-shape/blobs/legacy-sweep.d.ts +35 -0
- package/dist/with-shape/introspection/describe.d.ts +7 -1
- package/dist/with-shape/introspection/field-meta.d.ts +18 -0
- package/dist/with-shape/introspection/types.d.ts +57 -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/links/vault-facade.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-store/partitioned/index.d.ts +337 -0
- package/dist/with-sync/change-broadcast.d.ts +153 -0
- package/dist/with-sync/presence.d.ts +5 -10
- package/dist/withdraw-accessible-HL7GACUE.js +27 -0
- package/dist/withdraw-accessible-HL7GACUE.js.map +1 -0
- package/package.json +48 -12
- package/dist/api-WYW4YEQG.js +0 -19
- package/dist/backup-XKCXVKV3.js.map +0 -1
- package/dist/chunk-2577DKXB.js.map +0 -1
- package/dist/chunk-2ENE6445.js.map +0 -1
- package/dist/chunk-2N5ML6HT.js.map +0 -1
- package/dist/chunk-334WSVOW.js.map +0 -1
- package/dist/chunk-3BVH633C.js.map +0 -1
- package/dist/chunk-3PGS7TYR.js.map +0 -1
- package/dist/chunk-3V4KWFX7.js.map +0 -1
- package/dist/chunk-453F654R.js.map +0 -1
- package/dist/chunk-4GFX6MPA.js.map +0 -1
- package/dist/chunk-4GVM6QZA.js.map +0 -1
- package/dist/chunk-4ZS56W5S.js.map +0 -1
- package/dist/chunk-5VCRVKOZ.js.map +0 -1
- package/dist/chunk-6YXCPL4T.js.map +0 -1
- package/dist/chunk-7APJ6CO6.js.map +0 -1
- package/dist/chunk-7MG2LHO5.js +0 -1616
- package/dist/chunk-7MG2LHO5.js.map +0 -1
- package/dist/chunk-7MG7SJT7.js.map +0 -1
- package/dist/chunk-7O3Y4PPD.js.map +0 -1
- package/dist/chunk-A2HKRLPV.js.map +0 -1
- package/dist/chunk-A66I4JOA.js.map +0 -1
- package/dist/chunk-ACAQ2DSG.js.map +0 -1
- package/dist/chunk-ATL3ZVXT.js +0 -151
- package/dist/chunk-ATL3ZVXT.js.map +0 -1
- package/dist/chunk-AYX7AINO.js.map +0 -1
- package/dist/chunk-BLN54W3L.js.map +0 -1
- package/dist/chunk-BRLKZUBT.js +0 -95
- package/dist/chunk-BRLKZUBT.js.map +0 -1
- package/dist/chunk-BTDR2PXY.js +0 -29
- package/dist/chunk-BTDR2PXY.js.map +0 -1
- package/dist/chunk-BVN6KXL2.js.map +0 -1
- package/dist/chunk-CHQCZZWC.js.map +0 -1
- package/dist/chunk-CUQPWYRX.js.map +0 -1
- package/dist/chunk-CUZLNGD5.js.map +0 -1
- package/dist/chunk-DFMWUOU5.js.map +0 -1
- package/dist/chunk-EIPVYUEP.js.map +0 -1
- package/dist/chunk-EQEYOJBM.js.map +0 -1
- package/dist/chunk-F3W6YMDC.js.map +0 -1
- package/dist/chunk-FET5XCAT.js.map +0 -1
- package/dist/chunk-G73UVHCA.js.map +0 -1
- package/dist/chunk-GPNTB4Y7.js.map +0 -1
- package/dist/chunk-GYGLAVCX.js.map +0 -1
- package/dist/chunk-HIWCKBEY.js.map +0 -1
- package/dist/chunk-HJUWN7JN.js.map +0 -1
- package/dist/chunk-HK67PX4J.js.map +0 -1
- package/dist/chunk-HPVZCW43.js +0 -479
- package/dist/chunk-HPVZCW43.js.map +0 -1
- package/dist/chunk-I3ISCYT7.js +0 -159
- package/dist/chunk-I3ISCYT7.js.map +0 -1
- package/dist/chunk-K23VKTN4.js.map +0 -1
- package/dist/chunk-KKEESNPE.js.map +0 -1
- package/dist/chunk-KTFRCC5C.js.map +0 -1
- package/dist/chunk-LH5A3ZPG.js.map +0 -1
- package/dist/chunk-LP7BEXCT.js +0 -32
- package/dist/chunk-LP7BEXCT.js.map +0 -1
- package/dist/chunk-M4YPHYEH.js.map +0 -1
- package/dist/chunk-NSITFOCJ.js +0 -99
- package/dist/chunk-NSITFOCJ.js.map +0 -1
- package/dist/chunk-OTQZ7ZWU.js.map +0 -1
- package/dist/chunk-P3MG6QCN.js.map +0 -1
- package/dist/chunk-PEOZ34EM.js.map +0 -1
- package/dist/chunk-PPTHY5QO.js.map +0 -1
- package/dist/chunk-Q2MPFCQ7.js.map +0 -1
- package/dist/chunk-Q7HZX3IF.js.map +0 -1
- package/dist/chunk-QPQUVOZ2.js.map +0 -1
- package/dist/chunk-QTQUH4DS.js.map +0 -1
- package/dist/chunk-R42NSZI6.js.map +0 -1
- package/dist/chunk-R7BX2NB3.js.map +0 -1
- package/dist/chunk-RSGLUIO6.js.map +0 -1
- package/dist/chunk-RTBJ5IOO.js +0 -87
- package/dist/chunk-RTBJ5IOO.js.map +0 -1
- package/dist/chunk-SGZAIJCV.js.map +0 -1
- package/dist/chunk-SKGMN35F.js.map +0 -1
- package/dist/chunk-SPYWXKJN.js.map +0 -1
- package/dist/chunk-SZJ3DLZA.js +0 -66
- package/dist/chunk-SZJ3DLZA.js.map +0 -1
- package/dist/chunk-TRGTKX5E.js.map +0 -1
- package/dist/chunk-UDHWDSRL.js.map +0 -1
- package/dist/chunk-UP4BECPY.js.map +0 -1
- package/dist/chunk-UXVWEX5L.js.map +0 -1
- package/dist/chunk-V7ZRRCRJ.js.map +0 -1
- package/dist/chunk-WQBG4SZS.js.map +0 -1
- package/dist/chunk-XKWGNJ26.js.map +0 -1
- package/dist/chunk-YMUFFJCO.js.map +0 -1
- package/dist/chunk-ZHKOPKE3.js.map +0 -1
- package/dist/chunk-ZN4FD4PE.js +0 -364
- package/dist/chunk-ZN4FD4PE.js.map +0 -1
- package/dist/chunk-ZORTV6NH.js.map +0 -1
- package/dist/chunk-ZYIYHJ5R.js.map +0 -1
- package/dist/classified-marker-BWOFVYAC.js.map +0 -1
- package/dist/collection-facade-JSJYIKS4.js +0 -45
- package/dist/dead-filter-PKQDLOT4.js.map +0 -1
- package/dist/delegation-DS5NRILW.js.map +0 -1
- package/dist/derive-E642AOUB.js +0 -21
- package/dist/dispatch-Q6MWLOJY.js.map +0 -1
- package/dist/executor-5NIMLSKZ.js +0 -8
- package/dist/executor-7UQ3BZ4F.js +0 -29
- package/dist/executor-AQVXV5GZ.js +0 -8
- package/dist/export-accessible-ORMKLA3E.js +0 -22
- package/dist/extract-partition-NKYO3WNG.js +0 -37
- package/dist/fanout-sidecar-S3QL53YX.js.map +0 -1
- package/dist/find-NOGJ25VX.js +0 -10
- package/dist/issue-3DTQ7VJJ.js +0 -18
- package/dist/json-schema-V3M5MUCM.js.map +0 -1
- 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/kernel/query/join.d.ts +0 -232
- package/dist/liberate-V35QUHJP.js +0 -24
- package/dist/link-set-2TTM3IZC.js +0 -30
- package/dist/noydb-62MX4QUG.js +0 -90
- package/dist/pod-handle-ECVCDIXW.js.map +0 -1
- package/dist/post-register-3IPCSG6Q.js.map +0 -1
- package/dist/register-HHLXEQDN.js +0 -31
- package/dist/registry-23WF3TGH.js +0 -8
- package/dist/registry-4AYVUNXK.js +0 -8
- package/dist/registry-JS3YFQQM.js +0 -18
- package/dist/registry-XX2Z7B46.js +0 -32
- package/dist/request-withdrawal-CHPATE4G.js +0 -30
- package/dist/reveal-QZDHSGD4.js.map +0 -1
- package/dist/revoke-7M2VW4ON.js +0 -23
- package/dist/seed-X5CC22CQ.js.map +0 -1
- package/dist/signer-SJQJVSM6.js +0 -24
- package/dist/stale-BZS6H2RY.js +0 -29
- package/dist/storage-RW4QBS23.js +0 -22
- package/dist/store-coordination-provider-4WZAIRHC.js.map +0 -1
- package/dist/verify-6MDTIUEV.js.map +0 -1
- package/dist/walk-ZURCH6RZ.js +0 -21
- package/dist/withdraw-accessible-PXR4IXDX.js +0 -27
- /package/dist/{api-WYW4YEQG.js.map → api-OS747DS4.js.map} +0 -0
- /package/dist/{computed-VPOKQK4N.js.map → capsule/contract.js.map} +0 -0
- /package/dist/{kernel/enclave → capsule/enclave-aes}/broker/proof.d.ts +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/{collection-facade-JSJYIKS4.js.map → capsule-VOLX3QRQ.js.map} +0 -0
- /package/dist/{chunk-MVMXZYXT.js.map → chunk-2GHDLTPE.js.map} +0 -0
- /package/dist/{chunk-XLMEZIAJ.js.map → chunk-2ODVB6LZ.js.map} +0 -0
- /package/dist/{chunk-RMS3NEA3.js.map → chunk-4JPMZTJY.js.map} +0 -0
- /package/dist/{chunk-NQKIUNW5.js.map → chunk-52MGURXO.js.map} +0 -0
- /package/dist/{chunk-UGTBXF5U.js.map → chunk-7XMHMVTS.js.map} +0 -0
- /package/dist/{chunk-BZ3534YM.js.map → chunk-AUKKUPVB.js.map} +0 -0
- /package/dist/{derive-E642AOUB.js.map → chunk-BKT776R5.js.map} +0 -0
- /package/dist/{chunk-6LKOJBV6.js.map → chunk-BVCUTXE3.js.map} +0 -0
- /package/dist/{chunk-VTFVLCUH.js.map → chunk-DIZLQVAA.js.map} +0 -0
- /package/dist/{chunk-LWBCUYZW.js.map → chunk-E2N2T5ZO.js.map} +0 -0
- /package/dist/{chunk-JVIBCBC7.js.map → chunk-EJLV62NI.js.map} +0 -0
- /package/dist/{chunk-JMGDHKQF.js.map → chunk-FLPCETP6.js.map} +0 -0
- /package/dist/{chunk-DGOZ4UZD.js.map → chunk-FVH5QYY2.js.map} +0 -0
- /package/dist/{chunk-JBDFMYKQ.js.map → chunk-GZT3NQRA.js.map} +0 -0
- /package/dist/{chunk-TFR7DSOV.js.map → chunk-H427H63K.js.map} +0 -0
- /package/dist/{chunk-SMH6SRX7.js.map → chunk-JOSPH3E5.js.map} +0 -0
- /package/dist/{chunk-3BZZJJMQ.js.map → chunk-MILI22XQ.js.map} +0 -0
- /package/dist/{chunk-YYP7WSEB.js.map → chunk-O2IMQSNA.js.map} +0 -0
- /package/dist/{chunk-QIZPZMUI.js.map → chunk-Q4UHJ5SN.js.map} +0 -0
- /package/dist/{chunk-FAHMLERK.js.map → chunk-QBLT6POL.js.map} +0 -0
- /package/dist/{chunk-BUE2D7PX.js.map → chunk-SMV2HRDU.js.map} +0 -0
- /package/dist/{chunk-SFAWCTEM.js.map → chunk-TZZBXPZK.js.map} +0 -0
- /package/dist/{chunk-UHHPLO3I.js.map → chunk-V3RWOOSO.js.map} +0 -0
- /package/dist/{chunk-Q2PU57HC.js.map → chunk-VVOLDVIR.js.map} +0 -0
- /package/dist/{chunk-TUFM4UZP.js.map → chunk-XQRFSKYM.js.map} +0 -0
- /package/dist/{chunk-GY4WYNNG.js.map → chunk-Y2RKVFKR.js.map} +0 -0
- /package/dist/{chunk-4OZNXTO4.js.map → chunk-YRXFFWG4.js.map} +0 -0
- /package/dist/{enclave-55PAM4YO.js.map → collection-facade-CS7CA2XO.js.map} +0 -0
- /package/dist/{executor-5NIMLSKZ.js.map → computed-FAD7SED4.js.map} +0 -0
- /package/dist/{executor-7UQ3BZ4F.js.map → derive-XLOJVXPU.js.map} +0 -0
- /package/dist/{dispatch-IEPAVQUM.js.map → dispatch-H4HV32AM.js.map} +0 -0
- /package/dist/{executor-AQVXV5GZ.js.map → executor-FMPVN6DT.js.map} +0 -0
- /package/dist/{export-accessible-ORMKLA3E.js.map → executor-LBBBRTN5.js.map} +0 -0
- /package/dist/{extract-partition-NKYO3WNG.js.map → executor-OSRZST2U.js.map} +0 -0
- /package/dist/{find-NOGJ25VX.js.map → export-accessible-RAOJAAU5.js.map} +0 -0
- /package/dist/{issue-3DTQ7VJJ.js.map → extract-partition-JD5CUSTG.js.map} +0 -0
- /package/dist/{ledger-EDXFFZQL.js.map → find-JRN4RNQ5.js.map} +0 -0
- /package/dist/{liberate-V35QUHJP.js.map → issue-YIVBZUTH.js.map} +0 -0
- /package/dist/{link-set-2TTM3IZC.js.map → ledger-5ZMGE44K.js.map} +0 -0
- /package/dist/{managed-secret-NCWEMPUQ.js.map → liberate-WZSZM2W6.js.map} +0 -0
- /package/dist/{noydb-62MX4QUG.js.map → link-set-MFXC24VK.js.map} +0 -0
- /package/dist/{register-HHLXEQDN.js.map → managed-secret-MFTFT22X.js.map} +0 -0
- /package/dist/{migrate-cek-5ENH64AA.js.map → migrate-cek-DZRFFOHW.js.map} +0 -0
- /package/dist/{registry-23WF3TGH.js.map → noydb-ITIGNKUF.js.map} +0 -0
- /package/dist/{registry-4AYVUNXK.js.map → query/all/index.js.map} +0 -0
- /package/dist/{registry-JS3YFQQM.js.map → register-DT5HZ2LZ.js.map} +0 -0
- /package/dist/{registry-XX2Z7B46.js.map → registry-BHRZGZHF.js.map} +0 -0
- /package/dist/{request-withdrawal-CHPATE4G.js.map → registry-ET6ZFFWA.js.map} +0 -0
- /package/dist/{revoke-7M2VW4ON.js.map → registry-HXFGLGTG.js.map} +0 -0
- /package/dist/{signer-SJQJVSM6.js.map → registry-THCPDEOD.js.map} +0 -0
- /package/dist/{stale-BZS6H2RY.js.map → request-withdrawal-AV2MJJKY.js.map} +0 -0
- /package/dist/{storage-RW4QBS23.js.map → revoke-6DWIEOCV.js.map} +0 -0
- /package/dist/{walk-ZURCH6RZ.js.map → signer-MHGW3OIL.js.map} +0 -0
- /package/dist/{withdraw-accessible-PXR4IXDX.js.map → stale-6GCFMOOR.js.map} +0 -0
|
@@ -0,0 +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":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NonAdditiveSchemaChangeError,
|
|
3
3
|
SchemaLockedError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-37EPTBBR.js";
|
|
5
5
|
|
|
6
6
|
// src/with-shape/schema-update/strategies.ts
|
|
7
7
|
function blindUpdate() {
|
|
@@ -62,4 +62,4 @@ export {
|
|
|
62
62
|
lockSchema,
|
|
63
63
|
coordinatedCutover
|
|
64
64
|
};
|
|
65
|
-
//# sourceMappingURL=chunk-
|
|
65
|
+
//# sourceMappingURL=chunk-4JPMZTJY.js.map
|
|
@@ -3,14 +3,10 @@ import {
|
|
|
3
3
|
} from "./chunk-4AOERLTV.js";
|
|
4
4
|
import {
|
|
5
5
|
buildRecordAad,
|
|
6
|
+
buildRecordEnvelope,
|
|
7
|
+
encrypt,
|
|
6
8
|
openEnvelopeJson
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import {
|
|
9
|
-
buildRecordEnvelope
|
|
10
|
-
} from "./chunk-SZJ3DLZA.js";
|
|
11
|
-
import {
|
|
12
|
-
encrypt
|
|
13
|
-
} from "./chunk-HPVZCW43.js";
|
|
9
|
+
} from "./chunk-EZGV6F47.js";
|
|
14
10
|
|
|
15
11
|
// src/with-audit/consent/consent.ts
|
|
16
12
|
var CONSENT_AUDIT_COLLECTION = "_consent_audit";
|
|
@@ -48,7 +44,7 @@ async function buildEnvelope(entry, encrypted, getDEK) {
|
|
|
48
44
|
return buildRecordEnvelope(identity, { ...body, iv, data });
|
|
49
45
|
}
|
|
50
46
|
async function decryptEntry(id, envelope, encrypted, getDEK) {
|
|
51
|
-
if (!encrypted) return JSON.parse(envelope._data);
|
|
47
|
+
if (!encrypted) return JSON.parse(envelope._data ?? "");
|
|
52
48
|
const json = await openEnvelopeJson({ collection: CONSENT_AUDIT_COLLECTION, id }, envelope, await getDEK(CONSENT_AUDIT_COLLECTION));
|
|
53
49
|
return JSON.parse(json);
|
|
54
50
|
}
|
|
@@ -66,4 +62,4 @@ export {
|
|
|
66
62
|
writeConsentEntry,
|
|
67
63
|
loadConsentEntries
|
|
68
64
|
};
|
|
69
|
-
//# sourceMappingURL=chunk-
|
|
65
|
+
//# sourceMappingURL=chunk-4T3SE27M.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-audit/consent/consent.ts"],"sourcesContent":["/**\n * Consent boundaries — per-access audit log.\n *\n * ```ts\n * const audit = await vault.withConsent(\n * { purpose: 'quarterly-review', consentHash: '7f3a...' },\n * async () => {\n * const invoices = await vault.collection<Invoice>('invoices').list()\n * return invoices\n * },\n * )\n *\n * const log = await vault.consentAudit({ since: '2026-01-01T00:00:00Z' })\n * // → entries: { actor, purpose, consentHash, ts, op, collection, id }\n * ```\n *\n * ## Contract\n *\n * Every `get` / `put` / `delete` that happens inside a `withConsent`\n * callback writes one entry to the reserved `_consent_audit`\n * collection. Entries are encrypted with the vault's consent-audit\n * DEK (separate from per-user-collection DEKs so access-log queries\n * don't require unwrapping individual collection keys). Outside a\n * `withConsent` scope, no entries are written — consent is\n * opt-in by design (GDPR Art. 7: *demonstrable*, *specific*\n * consent).\n *\n * ## Why store the hash, not the consent text?\n *\n * The `consentHash` is the sha256 of whatever consent receipt the\n * actor presented (a signed GDPR banner click, a HIPAA authorisation\n * PDF, an API-level `X-Consent-Hash` header). Storing only the hash:\n *\n * 1. Keeps the audit log small and indexable.\n * 2. Preserves zero-knowledge at the adapter — adapters see\n * ciphertext envelopes of `{ actor, purpose, consentHash, ts,\n * op, collection, id }`, never the consent record itself.\n * 3. Lets the regulator verify a presented consent doc matches\n * the logged hash at audit time without the system ever\n * possessing the doc.\n *\n * ## Concurrency\n *\n * The consent context lives on the {@link Vault} instance. Two\n * concurrent `withConsent` calls on the same Vault would stomp each\n * other — documented limitation; adopters needing per-flight scope\n * should use separate Vault instances or an AsyncLocalStorage shim.\n *\n * @module\n */\nimport type { EncryptedEnvelope, NoydbStore } from '../../kernel/types.js'\nimport { buildRecordAad, buildRecordEnvelope, encrypt, openEnvelopeJson, type EnclaveKey } from '../../capsule/index.js'\nimport { generateULID } from '../../with-pod/ulid.js'\n\n/** Reserved collection for consent-audit entries. */\nexport const CONSENT_AUDIT_COLLECTION = '_consent_audit'\n\n/**\n * The consent scope active for a block of work. Set via\n * `vault.withConsent()`; observed by the collection's access hooks.\n */\nexport interface ConsentContext {\n /**\n * What this access is for. Used by the audit query (`consentAudit\n * ({ purpose })`) and carried in the stored entry. Free-form; the\n * regulator or compliance tooling decides the vocabulary.\n */\n readonly purpose: string\n /**\n * Hex-encoded sha256 of whatever consent artefact the actor\n * presented. Stored as-is in each entry.\n */\n readonly consentHash: string\n}\n\n/**\n * Access operation recorded in an audit entry.\n *\n * Union-widening checklist (I-2) — these three sites widen together or a\n * downstream exhaustive switch breaks at its next in-range minor:\n * 1. `ConsentOp` here (public, re-exported at `index.ts`).\n * 2. `onAccess` op union on `kernel/collection-config.ts`.\n * 3. classified ctx `onAccess` op union on `port/with/classified-strategy.ts`\n * (the `ClassifiedVerifyCtx` one — the `ClassifiedRevealCtx` one stays `'reveal'`-only).\n */\nexport type ConsentOp = 'get' | 'put' | 'delete' | 'reveal' | 'verify' | 'find'\n\n/** One consent-audit record, as decrypted for the caller. */\nexport interface ConsentAuditEntry {\n /** ULID — stable insertion-order key. */\n readonly id: string\n readonly timestamp: string\n readonly actor: string\n readonly purpose: string\n readonly consentHash: string\n readonly op: ConsentOp\n readonly collection: string\n readonly recordId: string\n}\n\n/** Filter passed to `vault.consentAudit()`. */\nexport interface ConsentAuditFilter {\n /** Only entries at or after this ISO timestamp. */\n readonly since?: string\n /** Only entries at or before this ISO timestamp. */\n readonly until?: string\n /** Match entries targeting this collection. */\n readonly collection?: string\n /** Match entries written by this actor. */\n readonly actor?: string\n /** Match entries with this purpose. */\n readonly purpose?: string\n}\n\n/**\n * Write one audit entry. Called by Vault's onAccess hook when a\n * consent context is active.\n */\nexport async function writeConsentEntry(\n adapter: NoydbStore,\n vault: string,\n encrypted: boolean,\n entry: Omit<ConsentAuditEntry, 'id' | 'timestamp'>,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n): Promise<void> {\n const id = generateULID()\n const full: ConsentAuditEntry = {\n id,\n timestamp: new Date().toISOString(),\n ...entry,\n }\n const envelope = await buildEnvelope(full, encrypted, getDEK)\n await adapter.put(vault, CONSENT_AUDIT_COLLECTION, id, envelope)\n}\n\n/** Load + decrypt + filter all entries. */\nexport async function loadConsentEntries(\n adapter: NoydbStore,\n vault: string,\n encrypted: boolean,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n filter: ConsentAuditFilter = {},\n): Promise<ConsentAuditEntry[]> {\n const ids = await adapter.list(vault, CONSENT_AUDIT_COLLECTION)\n const entries: ConsentAuditEntry[] = []\n\n for (const id of ids.sort()) {\n const envelope = await adapter.get(vault, CONSENT_AUDIT_COLLECTION, id)\n if (!envelope) continue\n const entry = await decryptEntry(id, envelope, encrypted, getDEK)\n if (!matchesFilter(entry, filter)) continue\n entries.push(entry)\n }\n return entries\n}\n\n// ── internals ──────────────────────────────────────────────────────\n\nasync function buildEnvelope(\n entry: ConsentAuditEntry,\n encrypted: boolean,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n): Promise<EncryptedEnvelope> {\n const json = JSON.stringify(entry)\n // `entry.id` IS the record id the caller writes under, so identity needs no\n // threading here (#1051).\n const identity = { collection: CONSENT_AUDIT_COLLECTION, id: entry.id, version: 1 }\n const body = { ts: entry.timestamp }\n if (!encrypted) {\n return buildRecordEnvelope(identity, { ...body, iv: '', data: json })\n }\n const dek = await getDEK(CONSENT_AUDIT_COLLECTION)\n const { iv, data } = await encrypt(json, dek, buildRecordAad(identity))\n return buildRecordEnvelope(identity, { ...body, iv, data })\n}\n\nasync function decryptEntry(\n id: string,\n envelope: EncryptedEnvelope,\n encrypted: boolean,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n): Promise<ConsentAuditEntry> {\n if (!encrypted) return JSON.parse(envelope._data ?? '') as ConsentAuditEntry\n const json = await openEnvelopeJson({ collection: CONSENT_AUDIT_COLLECTION, id }, envelope, await getDEK(CONSENT_AUDIT_COLLECTION))\n return JSON.parse(json) as ConsentAuditEntry\n}\n\nfunction matchesFilter(entry: ConsentAuditEntry, f: ConsentAuditFilter): boolean {\n if (f.since && entry.timestamp < f.since) return false\n if (f.until && entry.timestamp > f.until) return false\n if (f.collection && entry.collection !== f.collection) return false\n if (f.actor && entry.actor !== f.actor) return false\n if (f.purpose && entry.purpose !== f.purpose) return false\n return true\n}\n"],"mappings":";;;;;;;;;;;AAuDO,IAAM,2BAA2B;AA+DxC,eAAsB,kBACpB,SACA,OACA,WACA,OACA,QACe;AACf,QAAM,KAAK,aAAa;AACxB,QAAM,OAA0B;AAAA,IAC9B;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,GAAG;AAAA,EACL;AACA,QAAM,WAAW,MAAM,cAAc,MAAM,WAAW,MAAM;AAC5D,QAAM,QAAQ,IAAI,OAAO,0BAA0B,IAAI,QAAQ;AACjE;AAGA,eAAsB,mBACpB,SACA,OACA,WACA,QACA,SAA6B,CAAC,GACA;AAC9B,QAAM,MAAM,MAAM,QAAQ,KAAK,OAAO,wBAAwB;AAC9D,QAAM,UAA+B,CAAC;AAEtC,aAAW,MAAM,IAAI,KAAK,GAAG;AAC3B,UAAM,WAAW,MAAM,QAAQ,IAAI,OAAO,0BAA0B,EAAE;AACtE,QAAI,CAAC,SAAU;AACf,UAAM,QAAQ,MAAM,aAAa,IAAI,UAAU,WAAW,MAAM;AAChE,QAAI,CAAC,cAAc,OAAO,MAAM,EAAG;AACnC,YAAQ,KAAK,KAAK;AAAA,EACpB;AACA,SAAO;AACT;AAIA,eAAe,cACb,OACA,WACA,QAC4B;AAC5B,QAAM,OAAO,KAAK,UAAU,KAAK;AAGjC,QAAM,WAAW,EAAE,YAAY,0BAA0B,IAAI,MAAM,IAAI,SAAS,EAAE;AAClF,QAAM,OAAO,EAAE,IAAI,MAAM,UAAU;AACnC,MAAI,CAAC,WAAW;AACd,WAAO,oBAAoB,UAAU,EAAE,GAAG,MAAM,IAAI,IAAI,MAAM,KAAK,CAAC;AAAA,EACtE;AACA,QAAM,MAAM,MAAM,OAAO,wBAAwB;AACjD,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,QAAQ,MAAM,KAAK,eAAe,QAAQ,CAAC;AACtE,SAAO,oBAAoB,UAAU,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC;AAC5D;AAEA,eAAe,aACb,IACA,UACA,WACA,QAC4B;AAC5B,MAAI,CAAC,UAAW,QAAO,KAAK,MAAM,SAAS,SAAS,EAAE;AACtD,QAAM,OAAO,MAAM,iBAAiB,EAAE,YAAY,0BAA0B,GAAG,GAAG,UAAU,MAAM,OAAO,wBAAwB,CAAC;AAClI,SAAO,KAAK,MAAM,IAAI;AACxB;AAEA,SAAS,cAAc,OAA0B,GAAgC;AAC/E,MAAI,EAAE,SAAS,MAAM,YAAY,EAAE,MAAO,QAAO;AACjD,MAAI,EAAE,SAAS,MAAM,YAAY,EAAE,MAAO,QAAO;AACjD,MAAI,EAAE,cAAc,MAAM,eAAe,EAAE,WAAY,QAAO;AAC9D,MAAI,EAAE,SAAS,MAAM,UAAU,EAAE,MAAO,QAAO;AAC/C,MAAI,EAAE,WAAW,MAAM,YAAY,EAAE,QAAS,QAAO;AACrD,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compareKeys,
|
|
3
|
+
toSortKey
|
|
4
|
+
} from "./chunk-LGNDFRYC.js";
|
|
5
|
+
import {
|
|
6
|
+
readPath
|
|
7
|
+
} from "./chunk-5WWDGFOS.js";
|
|
8
|
+
|
|
9
|
+
// src/kernel/query/relate/join-on.ts
|
|
10
|
+
var RANGE_OPS = /* @__PURE__ */ new Set(["<", "<=", ">", ">=", "between"]);
|
|
11
|
+
function isField(value) {
|
|
12
|
+
return typeof value === "string" && value.length > 0;
|
|
13
|
+
}
|
|
14
|
+
function normalizeJoinOn(on, target) {
|
|
15
|
+
if (Array.isArray(on)) {
|
|
16
|
+
const pairs = on;
|
|
17
|
+
if (pairs.length === 0) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`.joinOn("${target}"): a composite \`on\` needs at least one [leftField, rightField] pair. Pass e.g. on: [['clientId', 'clientId'], ['year', 'year']].`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
const normalized = [];
|
|
23
|
+
for (const pair of pairs) {
|
|
24
|
+
if (!Array.isArray(pair) || pair.length !== 2 || !isField(pair[0]) || !isField(pair[1])) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`.joinOn("${target}"): every composite \`on\` entry must be a [leftField, rightField] pair of non-empty field names. Received: ${JSON.stringify(pair)}.`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
normalized.push([pair[0], pair[1]]);
|
|
30
|
+
}
|
|
31
|
+
normalized.sort((a, b) => a[0] === b[0] ? a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0 : a[0] < b[0] ? -1 : 1);
|
|
32
|
+
return { kind: "composite", pairs: normalized };
|
|
33
|
+
}
|
|
34
|
+
const spec = on;
|
|
35
|
+
if (!isField(spec.left)) {
|
|
36
|
+
throw new Error(`.joinOn("${target}"): a range \`on\` needs a non-empty \`left\` field name.`);
|
|
37
|
+
}
|
|
38
|
+
if (typeof spec.op !== "string" || !RANGE_OPS.has(spec.op)) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`.joinOn("${target}"): unknown \`on\` operator ${JSON.stringify(spec.op)}. Supported operators are ${[...RANGE_OPS].join(", ")}.`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
if (spec.op === "between") {
|
|
44
|
+
if (!Array.isArray(spec.right) || spec.right.length !== 2 || !isField(spec.right[0]) || !isField(spec.right[1])) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`.joinOn("${target}"): \`between\` needs exactly two right-side boundary field names \u2014 on: { left: 'date', op: 'between', right: ['from', 'to'] }.`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return { kind: "range", left: spec.left, op: "between", right: [spec.right[0], spec.right[1]] };
|
|
50
|
+
}
|
|
51
|
+
if (!isField(spec.right)) {
|
|
52
|
+
throw new Error(`.joinOn("${target}"): the \`${spec.op}\` operator needs a single right-side field name.`);
|
|
53
|
+
}
|
|
54
|
+
return { kind: "range", left: spec.left, op: spec.op, right: [spec.right] };
|
|
55
|
+
}
|
|
56
|
+
function describeJoinOn(on) {
|
|
57
|
+
if (on.kind === "composite") return on.pairs.map(([l, r]) => `${l} = ${r}`).join(" AND ");
|
|
58
|
+
if (on.op === "between") return `${on.left} between [${on.right.join(", ")}]`;
|
|
59
|
+
return `${on.left} ${on.op} ${on.right[0]}`;
|
|
60
|
+
}
|
|
61
|
+
function joinOnDispatch(on) {
|
|
62
|
+
return on.kind === "composite" ? "join:composite-hash" : "join:sorted-range";
|
|
63
|
+
}
|
|
64
|
+
function tupleKey(values) {
|
|
65
|
+
let out = "";
|
|
66
|
+
for (const value of values) {
|
|
67
|
+
const sk = toSortKey(value, void 0);
|
|
68
|
+
if (!sk) return void 0;
|
|
69
|
+
const text = String(sk.key);
|
|
70
|
+
out += `${sk.kind}:${text.length}:${text}|`;
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
function matchDeclaredJoin(leftRows, on, rightSnapshot, maxRows, onOverflow) {
|
|
75
|
+
const out = [];
|
|
76
|
+
const push = (left, right) => {
|
|
77
|
+
if (out.length >= maxRows) onOverflow(out.length + 1);
|
|
78
|
+
out.push({ left, right });
|
|
79
|
+
};
|
|
80
|
+
if (on.kind === "composite") {
|
|
81
|
+
compositeHashJoin(leftRows, on, rightSnapshot, push);
|
|
82
|
+
} else {
|
|
83
|
+
sortedRangeJoin(leftRows, on, rightSnapshot, push);
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
function compositeHashJoin(leftRows, on, rightSnapshot, push) {
|
|
88
|
+
const leftFields = on.pairs.map((p) => p[0]);
|
|
89
|
+
const rightFields = on.pairs.map((p) => p[1]);
|
|
90
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
91
|
+
for (const record of rightSnapshot) {
|
|
92
|
+
const key = tupleKey(rightFields.map((f) => readPath(record, f)));
|
|
93
|
+
if (key === void 0) continue;
|
|
94
|
+
const bucket = buckets.get(key);
|
|
95
|
+
if (bucket) bucket.push(record);
|
|
96
|
+
else buckets.set(key, [record]);
|
|
97
|
+
}
|
|
98
|
+
for (const left of leftRows) {
|
|
99
|
+
const key = tupleKey(leftFields.map((f) => readPath(left, f)));
|
|
100
|
+
const bucket = key === void 0 ? void 0 : buckets.get(key);
|
|
101
|
+
if (!bucket || bucket.length === 0) {
|
|
102
|
+
push(left, void 0);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
for (const right of bucket) push(left, right);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function sortedRangeJoin(leftRows, on, rightSnapshot, push) {
|
|
109
|
+
const lowField = on.right[0];
|
|
110
|
+
const highField = on.op === "between" ? on.right[1] : void 0;
|
|
111
|
+
const entries = [];
|
|
112
|
+
for (const record of rightSnapshot) {
|
|
113
|
+
const key = toSortKey(readPath(record, lowField), void 0);
|
|
114
|
+
if (!key) continue;
|
|
115
|
+
const hi = highField === void 0 ? void 0 : toSortKey(readPath(record, highField), void 0);
|
|
116
|
+
if (highField !== void 0 && !hi) continue;
|
|
117
|
+
entries.push({ key, hi, record });
|
|
118
|
+
}
|
|
119
|
+
entries.sort((a, b) => compareKeys(a.key, b.key));
|
|
120
|
+
for (const left of leftRows) {
|
|
121
|
+
const probe = toSortKey(readPath(left, on.left), void 0);
|
|
122
|
+
if (!probe) {
|
|
123
|
+
push(left, void 0);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const [bandStart, bandEnd] = kindBand(entries, probe.kind);
|
|
127
|
+
const lo = lowerBound(entries, probe, bandStart, bandEnd);
|
|
128
|
+
const hi = upperBound(entries, probe, bandStart, bandEnd);
|
|
129
|
+
let matched = false;
|
|
130
|
+
const emit = (from, to, keep) => {
|
|
131
|
+
for (let i = from; i < to; i++) {
|
|
132
|
+
const entry = entries[i];
|
|
133
|
+
if (keep && !keep(entry)) continue;
|
|
134
|
+
matched = true;
|
|
135
|
+
push(left, entry.record);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
switch (on.op) {
|
|
139
|
+
// `left.field < right.field` — the right keys strictly ABOVE the probe.
|
|
140
|
+
case "<":
|
|
141
|
+
emit(hi, bandEnd);
|
|
142
|
+
break;
|
|
143
|
+
case "<=":
|
|
144
|
+
emit(lo, bandEnd);
|
|
145
|
+
break;
|
|
146
|
+
case ">":
|
|
147
|
+
emit(bandStart, lo);
|
|
148
|
+
break;
|
|
149
|
+
case ">=":
|
|
150
|
+
emit(bandStart, hi);
|
|
151
|
+
break;
|
|
152
|
+
// `right.from <= left.field <= right.to`. Sorted by `from`, so the
|
|
153
|
+
// candidates are the prefix whose `from` is at or below the probe; the
|
|
154
|
+
// `to` bound is then a filter over that prefix, which is what makes the
|
|
155
|
+
// interval case O(p) per left row rather than O(log m).
|
|
156
|
+
case "between":
|
|
157
|
+
emit(bandStart, hi, (e) => e.hi !== void 0 && compareKeys(e.hi, probe) >= 0);
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
if (!matched) push(left, void 0);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function kindBand(entries, kind) {
|
|
164
|
+
const bound = (strict) => {
|
|
165
|
+
let lo = 0;
|
|
166
|
+
let hi = entries.length;
|
|
167
|
+
while (lo < hi) {
|
|
168
|
+
const mid = lo + hi >>> 1;
|
|
169
|
+
const d = entries[mid].key.kind - kind;
|
|
170
|
+
if (strict ? d < 0 : d <= 0) lo = mid + 1;
|
|
171
|
+
else hi = mid;
|
|
172
|
+
}
|
|
173
|
+
return lo;
|
|
174
|
+
};
|
|
175
|
+
return [bound(true), bound(false)];
|
|
176
|
+
}
|
|
177
|
+
function lowerBound(entries, probe, from = 0, to = entries.length) {
|
|
178
|
+
let lo = from;
|
|
179
|
+
let hi = to;
|
|
180
|
+
while (lo < hi) {
|
|
181
|
+
const mid = lo + hi >>> 1;
|
|
182
|
+
if (compareKeys(entries[mid].key, probe) < 0) lo = mid + 1;
|
|
183
|
+
else hi = mid;
|
|
184
|
+
}
|
|
185
|
+
return lo;
|
|
186
|
+
}
|
|
187
|
+
function upperBound(entries, probe, from = 0, to = entries.length) {
|
|
188
|
+
let lo = from;
|
|
189
|
+
let hi = to;
|
|
190
|
+
while (lo < hi) {
|
|
191
|
+
const mid = lo + hi >>> 1;
|
|
192
|
+
if (compareKeys(entries[mid].key, probe) <= 0) lo = mid + 1;
|
|
193
|
+
else hi = mid;
|
|
194
|
+
}
|
|
195
|
+
return lo;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export {
|
|
199
|
+
normalizeJoinOn,
|
|
200
|
+
describeJoinOn,
|
|
201
|
+
joinOnDispatch,
|
|
202
|
+
matchDeclaredJoin
|
|
203
|
+
};
|
|
204
|
+
//# sourceMappingURL=chunk-4V3Q35WG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/kernel/query/relate/join-on.ts"],"sourcesContent":["/**\n * Declared non-equi and composite joins — `.joinOn(target, { on })` (#1339).\n *\n * `join.ts` resolves a `ref()`-declared FK against the target's `id`. That is\n * one shape of join. This module is the other two the query DSL could not\n * express at all:\n *\n * - **composite equality** — `on: [['clientId','clientId'], ['year','year']]`\n * - **range / theta** — `on: { left: 'date', op: 'between', right: ['from','to'] }`\n *\n * ⭐ THE DECLARED FORM IS THE WHOLE POINT, and it is the only reason this is\n * not `crossJoin({ on: fn })` with extra steps. `crossJoin`'s `on` is a\n * closure: it pairs arbitrary rows perfectly and serializes to the sentinel\n * `'[inline]'`, which is why a materialized view over one has its drift\n * detection disabled. A `JoinOnPlan` is plain JSON, so `serializePlan()`\n * emits it verbatim, `summarizeQueryPlan()` folds it into the MV\n * `queryHash`, and two plans differing only in their `on` are two different\n * queries — pinned by `__tests__/query-join-on.test.ts`.\n *\n * ⛔ THE NORMAL FORM IS PART OF THE HASH. `normalizeJoinOn` is the ONLY place\n * a `JoinOnSpec` becomes a `JoinOnPlan`, and it sorts composite pairs so two\n * logically identical joins hash identically (an AND of equalities is\n * commutative). Changing the normal form moves every stored `queryHash` for a\n * `joinOn` MV. Adding a new `on` KIND does not, because the leg is omitted\n * entirely when absent — the same discipline `direction` (#1289) and `inner`\n * (#1361) follow.\n *\n * **Key encoding.** Both strategies key on {@link SortKey} — the per-component\n * ordered key #1345's `CompoundIndex` already uses — rather than a second,\n * hand-rolled encoding. That buys the kind-ranking invariant for free: the\n * string `'2026'` and the number `2026` live in different kinds and never\n * match, exactly as `predicate.ts`'s `isComparable` refuses to compare them.\n * A component whose value has no order-defined type (nullish, boolean,\n * object, array) makes the whole record unkeyable and it drops out of the\n * probe — same rule as `CompoundIndex`, where a record is indexed only when\n * EVERY component has an ordered value.\n *\n * **Cost, stated honestly:**\n *\n * | strategy | build | per left row | total |\n * |---|---|---|---|\n * | `join:composite-hash` | O(m) one pass | O(1) + matches | **O(n + m + output)** |\n * | `join:sorted-range` (`< <= > >=`) | O(m log m) one sort | O(log m) + matches | **O(m log m + n log m + output)** |\n * | `join:sorted-range` (`between`) | O(m log m) | O(log m + p), p = rights whose `from` ≤ probe | **O(m log m + n·p)** — degrades to O(n·m) when every interval starts early |\n *\n * ⚠️ Neither is index-aware in the `lookupRange`/`lookupCompound` sense: a\n * `JoinableSource` exposes `snapshot()` and `lookupById()` and nothing else,\n * so the right collection's declared indexes are not reachable from here.\n * The sort/hash is built per execution over the right SNAPSHOT. Reaching a\n * real index would mean widening the published `JoinableSource` seam and\n * taking a kernel→`with-lookup` runtime edge for a service that is opt-in;\n * that is a separate decision, not a detail of this one.\n *\n * ⚠️ **A theta join EXPANDS rows**, which the ref join never does — so the\n * per-side `maxRows` ceilings are not sufficient on their own. The OUTPUT is\n * checked against the same ceiling as it is produced, and the join throws\n * `JoinTooLargeError` with `side: 'output'` rather than allocating an\n * unbounded cartesian product. That check is the difference between an error\n * and a hang.\n */\n\nimport { readPath } from '../predicate.js'\nimport { compareKeys, toSortKey, type SortKey } from '../sort-key.js'\n\n/** Comparison operators a declared range join understands. */\nexport type JoinOnOp = '<' | '<=' | '>' | '>=' | 'between'\n\n/**\n * The consumer-facing `on`, in either of the two shapes #1339 proposed.\n *\n * - `[['clientId','clientId'], ['year','year']]` — composite equality; every\n * pair must match. Each pair is `[leftField, rightField]`.\n * - `{ left, op, right }` — a comparison of ONE left field against one right\n * field (`< <= > >=`), or against a right INTERVAL for `between`, where\n * `right` names the two boundary fields `[fromField, toField]` and the\n * bounds are inclusive.\n */\nexport type JoinOnSpec =\n | readonly (readonly [string, string])[]\n | {\n readonly left: string\n readonly op: Exclude<JoinOnOp, 'between'>\n readonly right: string\n }\n | {\n readonly left: string\n readonly op: 'between'\n readonly right: readonly [string, string]\n }\n\n/**\n * The normalised, serialisable form stored on the {@link JoinLeg}. This is\n * what lands in `toPlan()` and in the MV `queryHash` — see the module header\n * before changing its shape.\n */\nexport type JoinOnPlan =\n | {\n readonly kind: 'composite'\n /** `[leftField, rightField]` pairs, sorted by left then right field. */\n readonly pairs: readonly (readonly [string, string])[]\n }\n | {\n readonly kind: 'range'\n readonly left: string\n readonly op: JoinOnOp\n /** One right field, or the two inclusive boundary fields for `between`. */\n readonly right: readonly string[]\n }\n\nconst RANGE_OPS: ReadonlySet<string> = new Set(['<', '<=', '>', '>=', 'between'])\n\nfunction isField(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0\n}\n\n/**\n * Validate and normalise a consumer `on` into its plan form.\n *\n * Every refusal is at PLAN time, with the offending shape named: an `on` that\n * cannot be serialised deterministically must never reach a `queryHash`,\n * because a hash over an invalid plan is a hash nothing can reproduce.\n */\nexport function normalizeJoinOn(on: JoinOnSpec, target: string): JoinOnPlan {\n if (Array.isArray(on)) {\n const pairs = on as readonly unknown[]\n if (pairs.length === 0) {\n throw new Error(\n `.joinOn(\"${target}\"): a composite \\`on\\` needs at least one [leftField, rightField] pair. ` +\n `Pass e.g. on: [['clientId', 'clientId'], ['year', 'year']].`,\n )\n }\n const normalized: [string, string][] = []\n for (const pair of pairs) {\n if (!Array.isArray(pair) || pair.length !== 2 || !isField(pair[0]) || !isField(pair[1])) {\n throw new Error(\n `.joinOn(\"${target}\"): every composite \\`on\\` entry must be a [leftField, rightField] pair of ` +\n `non-empty field names. Received: ${JSON.stringify(pair)}.`,\n )\n }\n normalized.push([pair[0], pair[1]])\n }\n // Sorted so a caller reordering the pairs does not change the queryHash —\n // an AND of equalities is commutative, so two such plans ARE the same\n // query and a reorder must not force every MV row to rebuild.\n normalized.sort((a, b) => (a[0] === b[0] ? (a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) : a[0] < b[0] ? -1 : 1))\n return { kind: 'composite', pairs: normalized }\n }\n\n const spec = on as { left?: unknown; op?: unknown; right?: unknown }\n if (!isField(spec.left)) {\n throw new Error(`.joinOn(\"${target}\"): a range \\`on\\` needs a non-empty \\`left\\` field name.`)\n }\n if (typeof spec.op !== 'string' || !RANGE_OPS.has(spec.op)) {\n throw new Error(\n `.joinOn(\"${target}\"): unknown \\`on\\` operator ${JSON.stringify(spec.op)}. ` +\n `Supported operators are ${[...RANGE_OPS].join(', ')}.`,\n )\n }\n if (spec.op === 'between') {\n if (!Array.isArray(spec.right) || spec.right.length !== 2 || !isField(spec.right[0]) || !isField(spec.right[1])) {\n throw new Error(\n `.joinOn(\"${target}\"): \\`between\\` needs exactly two right-side boundary field names — ` +\n `on: { left: 'date', op: 'between', right: ['from', 'to'] }.`,\n )\n }\n return { kind: 'range', left: spec.left, op: 'between', right: [spec.right[0], spec.right[1]] }\n }\n if (!isField(spec.right)) {\n throw new Error(`.joinOn(\"${target}\"): the \\`${spec.op}\\` operator needs a single right-side field name.`)\n }\n return { kind: 'range', left: spec.left, op: spec.op as JoinOnOp, right: [spec.right] }\n}\n\n/** A short, stable rendering of the predicate for `explain()`'s detail line. */\nexport function describeJoinOn(on: JoinOnPlan): string {\n if (on.kind === 'composite') return on.pairs.map(([l, r]) => `${l} = ${r}`).join(' AND ')\n if (on.op === 'between') return `${on.left} between [${on.right.join(', ')}]`\n return `${on.left} ${on.op} ${on.right[0]}`\n}\n\n/** `explain()`'s dispatch label for a declared-`on` leg. */\nexport function joinOnDispatch(on: JoinOnPlan): 'join:composite-hash' | 'join:sorted-range' {\n return on.kind === 'composite' ? 'join:composite-hash' : 'join:sorted-range'\n}\n\n/**\n * Length-prefixed tuple key. `${kind}:${len}:${value}` per component means a\n * string component may contain any byte — including the separator — without\n * colliding with a neighbouring component, which a plain `join('\u0000')`\n * cannot promise. `undefined` when ANY component has no ordered key, matching\n * `CompoundIndex`: a record with one unkeyable component is not in the index.\n */\nfunction tupleKey(values: readonly unknown[]): string | undefined {\n let out = ''\n for (const value of values) {\n const sk = toSortKey(value, undefined)\n if (!sk) return undefined\n const text = String(sk.key)\n out += `${sk.kind}:${text.length}:${text}|`\n }\n return out\n}\n\n/** One row the declared join produced: the left row and its matched right record. */\nexport interface JoinOnMatch {\n readonly left: unknown\n /** `undefined` when this left row matched nothing — the left-outer row. */\n readonly right: unknown\n}\n\n/**\n * Run a declared `on` against the right snapshot, emitting one\n * {@link JoinOnMatch} per matched pair plus one `right: undefined` match per\n * left row that matched nothing (the caller applies ref-mode / inner-drop\n * semantics, exactly as it does for the ref join).\n *\n * `onOverflow` is called the moment the produced row count would exceed the\n * ceiling; it is expected to throw. Passing the check as a callback keeps the\n * error's wording — which names the leg and the ceiling — with the leg.\n */\nexport function matchDeclaredJoin(\n leftRows: readonly unknown[],\n on: JoinOnPlan,\n rightSnapshot: readonly unknown[],\n maxRows: number,\n onOverflow: (produced: number) => never,\n): JoinOnMatch[] {\n const out: JoinOnMatch[] = []\n const push = (left: unknown, right: unknown): void => {\n if (out.length >= maxRows) onOverflow(out.length + 1)\n out.push({ left, right })\n }\n if (on.kind === 'composite') {\n compositeHashJoin(leftRows, on, rightSnapshot, push)\n } else {\n sortedRangeJoin(leftRows, on, rightSnapshot, push)\n }\n return out\n}\n\n/** Hash join over a tuple key — O(n + m + output). */\nfunction compositeHashJoin(\n leftRows: readonly unknown[],\n on: Extract<JoinOnPlan, { kind: 'composite' }>,\n rightSnapshot: readonly unknown[],\n push: (left: unknown, right: unknown) => void,\n): void {\n const leftFields = on.pairs.map(p => p[0])\n const rightFields = on.pairs.map(p => p[1])\n const buckets = new Map<string, unknown[]>()\n for (const record of rightSnapshot) {\n const key = tupleKey(rightFields.map(f => readPath(record, f)))\n if (key === undefined) continue\n const bucket = buckets.get(key)\n if (bucket) bucket.push(record)\n else buckets.set(key, [record])\n }\n for (const left of leftRows) {\n const key = tupleKey(leftFields.map(f => readPath(left, f)))\n const bucket = key === undefined ? undefined : buckets.get(key)\n if (!bucket || bucket.length === 0) {\n push(left, undefined)\n continue\n }\n for (const right of bucket) push(left, right)\n }\n}\n\ninterface RangeEntry {\n readonly key: SortKey\n /** The upper boundary key, for `between` only. */\n readonly hi: SortKey | undefined\n readonly record: unknown\n}\n\n/**\n * Nested loop over a right side sorted ONCE by the probed field.\n *\n * Entries are ordered by `compareKeys`, which ranks by kind first, so the\n * slice a probe may reach is clamped to the probe's own kind band — a string\n * probe cannot walk into the number entries, mirroring the linear scan.\n */\nfunction sortedRangeJoin(\n leftRows: readonly unknown[],\n on: Extract<JoinOnPlan, { kind: 'range' }>,\n rightSnapshot: readonly unknown[],\n push: (left: unknown, right: unknown) => void,\n): void {\n const lowField = on.right[0]!\n const highField = on.op === 'between' ? on.right[1]! : undefined\n const entries: RangeEntry[] = []\n for (const record of rightSnapshot) {\n const key = toSortKey(readPath(record, lowField), undefined)\n if (!key) continue\n const hi = highField === undefined ? undefined : toSortKey(readPath(record, highField), undefined)\n if (highField !== undefined && !hi) continue\n entries.push({ key, hi, record })\n }\n entries.sort((a, b) => compareKeys(a.key, b.key))\n\n for (const left of leftRows) {\n const probe = toSortKey(readPath(left, on.left), undefined)\n if (!probe) {\n push(left, undefined)\n continue\n }\n // The contiguous run of entries sharing the probe's kind. Everything\n // outside it is incomparable, exactly as `isComparable` says.\n const [bandStart, bandEnd] = kindBand(entries, probe.kind)\n const lo = lowerBound(entries, probe, bandStart, bandEnd)\n const hi = upperBound(entries, probe, bandStart, bandEnd)\n\n let matched = false\n const emit = (from: number, to: number, keep?: (e: RangeEntry) => boolean): void => {\n for (let i = from; i < to; i++) {\n const entry = entries[i]!\n if (keep && !keep(entry)) continue\n matched = true\n push(left, entry.record)\n }\n }\n switch (on.op) {\n // `left.field < right.field` — the right keys strictly ABOVE the probe.\n case '<': emit(hi, bandEnd); break\n case '<=': emit(lo, bandEnd); break\n case '>': emit(bandStart, lo); break\n case '>=': emit(bandStart, hi); break\n // `right.from <= left.field <= right.to`. Sorted by `from`, so the\n // candidates are the prefix whose `from` is at or below the probe; the\n // `to` bound is then a filter over that prefix, which is what makes the\n // interval case O(p) per left row rather than O(log m).\n case 'between':\n emit(bandStart, hi, e => e.hi !== undefined && compareKeys(e.hi, probe) >= 0)\n break\n }\n if (!matched) push(left, undefined)\n }\n}\n\n/**\n * The half-open range of entries whose key has the given kind. Computed by\n * kind ALONE — a sentinel `SortKey` cannot express \"smallest of this kind\",\n * because `compareKeys` falls back to `<`/`>` on mixed key types and returns\n * 0 for a number sentinel against a string key, which would silently collapse\n * the band to nothing.\n */\nfunction kindBand(entries: readonly RangeEntry[], kind: number): [number, number] {\n const bound = (strict: boolean): number => {\n let lo = 0\n let hi = entries.length\n while (lo < hi) {\n const mid = (lo + hi) >>> 1\n const d = entries[mid]!.key.kind - kind\n if (strict ? d < 0 : d <= 0) lo = mid + 1\n else hi = mid\n }\n return lo\n }\n return [bound(true), bound(false)]\n}\n\nfunction lowerBound(entries: readonly RangeEntry[], probe: SortKey, from = 0, to = entries.length): number {\n let lo = from\n let hi = to\n while (lo < hi) {\n const mid = (lo + hi) >>> 1\n if (compareKeys(entries[mid]!.key, probe) < 0) lo = mid + 1\n else hi = mid\n }\n return lo\n}\n\nfunction upperBound(entries: readonly RangeEntry[], probe: SortKey, from = 0, to = entries.length): number {\n let lo = from\n let hi = to\n while (lo < hi) {\n const mid = (lo + hi) >>> 1\n if (compareKeys(entries[mid]!.key, probe) <= 0) lo = mid + 1\n else hi = mid\n }\n return lo\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CargoNotEnabledError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-37EPTBBR.js";
|
|
4
4
|
|
|
5
5
|
// src/with-cargo/strategy.ts
|
|
6
6
|
var NO_CARGO = {
|
|
@@ -12,4 +12,4 @@ var NO_CARGO = {
|
|
|
12
12
|
export {
|
|
13
13
|
NO_CARGO
|
|
14
14
|
};
|
|
15
|
-
//# sourceMappingURL=chunk-
|
|
15
|
+
//# sourceMappingURL=chunk-52MGURXO.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildRecordEnvelope
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-EZGV6F47.js";
|
|
4
4
|
import {
|
|
5
5
|
ValidationError
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-37EPTBBR.js";
|
|
7
7
|
|
|
8
8
|
// src/with-party/directory/cover/custom.ts
|
|
9
9
|
var CUSTOM_KEY_PATTERN = /^[a-z0-9]+([.-][a-z0-9]+)+$/i;
|
|
@@ -179,7 +179,7 @@ async function loadCover(store, vault) {
|
|
|
179
179
|
const envelope = await store.get(vault, "_meta", COVER_RECORD_ID);
|
|
180
180
|
if (!envelope) return void 0;
|
|
181
181
|
try {
|
|
182
|
-
const parsed = JSON.parse(envelope._data);
|
|
182
|
+
const parsed = JSON.parse(envelope._data ?? "");
|
|
183
183
|
if (!isCover(parsed)) return void 0;
|
|
184
184
|
return parsed;
|
|
185
185
|
} catch {
|
|
@@ -230,4 +230,4 @@ export {
|
|
|
230
230
|
resolveLocale,
|
|
231
231
|
pickLocale
|
|
232
232
|
};
|
|
233
|
-
//# sourceMappingURL=chunk-
|
|
233
|
+
//# sourceMappingURL=chunk-5A4345TG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-party/directory/cover/custom.ts","../src/with-party/directory/cover/schema.ts","../src/with-party/directory/cover/storage.ts"],"sourcesContent":["/**\n * The cover's namespaced `custom` extension slot (#800) — input\n * validation, the namespace-level patch merge, and the post-merge\n * size caps.\n *\n * Like everything on the cover, `custom` is plaintext, public by\n * design, and unauthenticated — payloads are hints (display defaults,\n * routing suggestions, registry pointers), never an input to\n * security, authorization, or data-integrity decisions.\n *\n * @see https://github.com/noy-db/docs/blob/main/content/docs/services/public-envelope.md\n *\n * @module\n */\nimport { ValidationError } from '../../../kernel/errors.js'\nimport type { Cover, JsonValue, ResolvedCoverSchema } from './types.js'\n\n/**\n * Namespace keys must be reverse-DNS or package-style identifiers —\n * at least one dot/dash-separated segment (`noydb.viewer`,\n * `com.acme.registry` pass; a bare `config` fails), so two frameworks\n * can't collide on a bare word.\n */\nconst CUSTOM_KEY_PATTERN = /^[a-z0-9]+([.-][a-z0-9]+)+$/i\n\n/** Maximum nesting depth of a namespace payload (containers, not leaves). */\nconst MAX_CUSTOM_DEPTH = 8\n\n/**\n * Validate the owner-supplied `custom` input: the field must be a\n * plain object, every key must match {@link CUSTOM_KEY_PATTERN}, and\n * every value must be JSON-serializable (no functions, `undefined`,\n * symbols, bigints, or cycles) within the depth cap. `null` is\n * accepted — it is the delete-this-namespace directive and never\n * persists. Throws `ValidationError` on the first violation.\n */\nexport function validateCustomInput(custom: Record<string, JsonValue>): void {\n if (custom === null || typeof custom !== 'object' || Array.isArray(custom)) {\n throw new ValidationError(\n `setCover: custom must be a { [namespace]: value } object, got ${\n Array.isArray(custom) ? 'array' : typeof custom\n }.`,\n )\n }\n for (const [ns, value] of Object.entries(custom)) {\n if (!CUSTOM_KEY_PATTERN.test(ns)) {\n throw new ValidationError(\n `setCover: custom namespace \"${ns}\" is invalid. Keys must be ` +\n 'reverse-DNS or package-style identifiers with at least one ' +\n 'dot/dash-separated segment (e.g. \"noydb.viewer\", \"com.acme.registry\").',\n )\n }\n assertJsonValue(value, ns, 1, new Set())\n }\n}\n\nfunction assertJsonValue(\n value: unknown,\n ns: string,\n depth: number,\n seen: Set<object>,\n): void {\n if (value === null) return\n const t = typeof value\n if (t === 'string' || t === 'number' || t === 'boolean') return\n if (t !== 'object') {\n throw new ValidationError(\n `setCover: custom[\"${ns}\"] contains a non-JSON value (${t}). ` +\n 'Only strings, numbers, booleans, null, arrays, and plain objects are allowed.',\n )\n }\n const container = value as object\n if (seen.has(container)) {\n throw new ValidationError(\n `setCover: custom[\"${ns}\"] contains a circular reference.`,\n )\n }\n if (depth > MAX_CUSTOM_DEPTH) {\n throw new ValidationError(\n `setCover: custom[\"${ns}\"] nests deeper than the ${MAX_CUSTOM_DEPTH}-level cap.`,\n )\n }\n seen.add(container)\n const children = Array.isArray(container) ? container : Object.values(container)\n for (const child of children) {\n assertJsonValue(child, ns, depth + 1, seen)\n }\n seen.delete(container)\n}\n\n/**\n * Namespace-level patch merge for `setCover`: provided namespaces\n * replace their previous value, absent namespaces are preserved, and\n * an explicit `null` deletes that namespace (so `null` never persists\n * as a namespace value). A whole-`custom`-absent patch preserves the\n * previous custom, consistent with the other cover fields. Rationale:\n * framework A must never need read-modify-write of framework B's data.\n *\n * Returns a spreadable fragment — `{ custom }` when the merge leaves\n * at least one namespace, `{}` when it leaves none (the field is\n * dropped from the persisted document rather than stored empty).\n */\nexport function mergeCustom(\n existing: Record<string, JsonValue> | undefined,\n patch: Record<string, JsonValue> | undefined,\n): { custom: Record<string, JsonValue> } | Record<string, never> {\n if (patch === undefined) {\n return existing !== undefined ? { custom: existing } : {}\n }\n const merged: Record<string, JsonValue> = { ...existing }\n for (const [ns, value] of Object.entries(patch)) {\n if (value === null) {\n delete merged[ns]\n } else {\n merged[ns] = value\n }\n }\n return Object.keys(merged).length > 0 ? { custom: merged } : {}\n}\n\n/**\n * Post-merge size caps — validates the WOULD-BE-PERSISTED document\n * (after the namespace merge), so a small patch can't tip the stored\n * cover over a cap unnoticed. `maxCoverBytes` bounds the entire\n * serialized document, which also closes the unbounded locale-map\n * key-count hole for `name` / `description`. Sizes are measured as\n * `JSON.stringify` length. Throws `ValidationError` on violation.\n */\nexport function validateCoverSize(\n cover: Cover,\n schema: ResolvedCoverSchema,\n): void {\n if (cover.custom !== undefined) {\n const customSize = JSON.stringify(cover.custom).length\n if (customSize > schema.maxCustomBytes) {\n throw new ValidationError(\n `setCover: custom exceeds the ${schema.maxCustomBytes}-byte cap ` +\n `(got ${customSize} bytes serialized, after merging with the stored namespaces).`,\n )\n }\n }\n const totalSize = JSON.stringify(cover).length\n if (totalSize > schema.maxCoverBytes) {\n throw new ValidationError(\n `setCover: the cover document exceeds the ${schema.maxCoverBytes}-byte cap ` +\n `(got ${totalSize} bytes serialized).`,\n )\n }\n}\n","/**\n * Validate-on-write for the cover. Runs at every `setCover` call;\n * the developer's schema decides which fields are allowed and the\n * size caps that apply.\n *\n * @module\n */\nimport { ValidationError } from '../../../kernel/errors.js'\nimport { validateCustomInput } from './custom.js'\nimport type {\n Cover,\n CoverText,\n JsonValue,\n ResolvedCoverSchema,\n CoverField,\n} from './types.js'\n\n/** Owner-supplied input — the subset of {@link Cover} the owner can set. */\nexport interface SetCoverInput {\n readonly name?: CoverText\n readonly description?: CoverText\n readonly icon?: string\n readonly defaultLocale?: string\n /**\n * Namespace-level patch for the cover's `custom` slot (#800):\n * provided namespaces replace their previous value, absent\n * namespaces are preserved, an explicit `null` deletes that\n * namespace. Requires `'custom'` in the schema's `fields` list —\n * the `cover: true` shorthand does NOT enable it.\n */\n readonly custom?: Record<string, JsonValue>\n}\n\nconst DATA_URL_PREFIX = /^data:([a-zA-Z0-9.+-]+\\/[a-zA-Z0-9.+-]+);base64,/\n\n/**\n * Validate an owner-supplied cover input against the developer's\n * resolved schema. Throws `ValidationError` on the first violation;\n * returns void on success.\n *\n * The validator is deliberately strict: every fail mode is a hard\n * error rather than a silent drop, so the owner finds out immediately\n * which field they oversized rather than discovering a truncated\n * label months later.\n */\nexport function validateCoverInput(\n input: SetCoverInput,\n schema: ResolvedCoverSchema,\n): void {\n const allowed = new Set<CoverField>(schema.fields)\n\n // Reject any key not in the schema's allowed-field list.\n for (const key of Object.keys(input)) {\n const known: CoverField | undefined =\n key === 'name' || key === 'description' || key === 'icon' || key === 'defaultLocale' || key === 'custom'\n ? key\n : undefined\n if (!known) {\n throw new ValidationError(\n `setCover: unknown field \"${key}\". ` +\n `Allowed fields: ${[...allowed].join(', ')}.`,\n )\n }\n if (!allowed.has(known)) {\n throw new ValidationError(\n `setCover: field \"${known}\" is not enabled in this vault's schema. ` +\n `Allowed fields: ${[...allowed].join(', ')}.`,\n )\n }\n }\n\n if (input.name !== undefined) {\n validateText(input.name, 'name', schema.maxStringChars)\n }\n if (input.description !== undefined) {\n validateText(input.description, 'description', schema.maxStringChars)\n }\n if (input.icon !== undefined) {\n validateIcon(input.icon, schema)\n }\n if (input.defaultLocale !== undefined && typeof input.defaultLocale !== 'string') {\n throw new ValidationError(\n `setCover: defaultLocale must be a string (BCP-47), got ${typeof input.defaultLocale}.`,\n )\n }\n if (input.custom !== undefined) {\n validateCustomInput(input.custom)\n }\n}\n\nfunction validateText(\n value: CoverText,\n field: string,\n maxChars: number,\n): void {\n if (typeof value === 'string') {\n if (value.length > maxChars) {\n throw new ValidationError(\n `setCover: ${field} exceeds the ${maxChars}-character cap (got ${value.length}).`,\n )\n }\n return\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new ValidationError(\n `setCover: ${field} must be a string or { [locale]: string } map, got ${typeof value}.`,\n )\n }\n // Locale map. Each value must be a non-empty string within the cap.\n for (const [locale, str] of Object.entries(value)) {\n if (typeof str !== 'string') {\n throw new ValidationError(\n `setCover: ${field}[${locale}] must be a string, got ${typeof str}.`,\n )\n }\n if (str.length > maxChars) {\n throw new ValidationError(\n `setCover: ${field}[${locale}] exceeds the ${maxChars}-character cap (got ${str.length}).`,\n )\n }\n }\n}\n\nfunction validateIcon(icon: string, schema: ResolvedCoverSchema): void {\n if (typeof icon !== 'string') {\n throw new ValidationError(\n `setCover: icon must be a data: URL string, got ${typeof icon}.`,\n )\n }\n if (icon.length > schema.maxIconBytes) {\n throw new ValidationError(\n `setCover: icon exceeds the ${schema.maxIconBytes}-byte cap (got ${icon.length}).`,\n )\n }\n const m = DATA_URL_PREFIX.exec(icon)\n if (!m) {\n throw new ValidationError(\n 'setCover: icon must be a base64 data URL ' +\n '(`data:image/png;base64,…` or `data:image/svg+xml;base64,…`). ' +\n 'External URLs are not supported in v1.',\n )\n }\n const mime = m[1]!\n if (!schema.iconMimeTypes.includes(mime)) {\n throw new ValidationError(\n `setCover: icon MIME type \"${mime}\" is not allowed. ` +\n `Permitted types: ${schema.iconMimeTypes.join(', ')}.`,\n )\n }\n}\n\n/**\n * Lightweight runtime predicate — used by the bundle header\n * validator to recognise a cover without requiring it. (The wire\n * discriminator keeps its frozen name: `_noydb_public: 1`.)\n */\nexport function isCover(x: unknown): x is Cover {\n if (x === null || typeof x !== 'object' || Array.isArray(x)) return false\n const obj = x as Record<string, unknown>\n return obj['_noydb_public'] === 1 && typeof obj['version'] === 'number'\n}\n","/**\n * Persistence helpers for `_meta/public-envelope` — the cover's\n * frozen wire location. Mirrors the bypass-AES pattern used by\n * `_meta/handle` and `_meta/policy` — the document is plaintext\n * JSON, the envelope's `_iv` field is left empty.\n *\n * @module\n */\nimport { buildRecordEnvelope } from '../../../capsule/index.js'\nimport type { NoydbStore } from '../../../kernel/types.js'\nimport type { Cover } from './types.js'\nimport { isCover } from './schema.js'\n\n/**\n * Reserved id for the vault-level cover record. The VALUE is frozen\n * wire format — the record predates the #799 rename and stays at\n * `_meta/public-envelope` so existing vaults need no migration.\n */\nexport const COVER_RECORD_ID = 'public-envelope'\n\n/**\n * Read the cover from `_meta/public-envelope`. Returns `undefined`\n * when no cover has been persisted (fresh vault, or a vault written\n * before the feature was enabled). Tolerates corrupted documents —\n * a JSON parse failure surfaces as `undefined`, not a thrown error,\n * mirroring `_meta/handle`'s contract.\n */\nexport async function loadCover(\n store: NoydbStore,\n vault: string,\n): Promise<Cover | undefined> {\n const envelope = await store.get(vault, '_meta', COVER_RECORD_ID)\n if (!envelope) return undefined\n try {\n const parsed = JSON.parse(envelope._data ?? '') as unknown\n if (!isCover(parsed)) return undefined\n return parsed\n } catch {\n return undefined\n }\n}\n\n/** Persist the cover. Idempotent — overwrites any prior write. */\nexport async function saveCover(\n store: NoydbStore,\n vault: string,\n cover: Cover,\n): Promise<void> {\n const wireEnvelope = buildRecordEnvelope(\n { collection: '_meta', id: COVER_RECORD_ID, version: 1 },\n { iv: '', data: JSON.stringify(cover) },\n )\n await store.put(vault, '_meta', COVER_RECORD_ID, wireEnvelope)\n}\n\n/**\n * Public, no-key reader. Plain function, not a method on `Noydb` —\n * the whole point is it works without an authenticated session, so\n * a UI can show \"Acme 2026 Tax Records\" before unlocking.\n *\n * Resolves any `name` / `description` locale map through the supplied\n * `locale` (when provided). Omitting `locale` returns the raw\n * cover, which a multilingual picker can render as it pleases.\n */\nexport async function readCover(\n store: NoydbStore,\n vault: string,\n opts: { readonly locale?: string } = {},\n): Promise<Cover | undefined> {\n const raw = await loadCover(store, vault)\n if (!raw) return undefined\n if (opts.locale === undefined) return raw\n return resolveLocale(raw, opts.locale)\n}\n\n/**\n * Resolve the locale-map fields (`name`, `description`) of a cover\n * to plain strings for the requested locale, falling back through\n * `defaultLocale` and then any available translation.\n *\n * @internal\n */\nexport function resolveLocale(\n cover: Cover,\n locale: string,\n): Cover {\n return {\n ...cover,\n ...(cover.name !== undefined ? { name: pickLocale(cover.name, locale, cover.defaultLocale) } : {}),\n ...(cover.description !== undefined ? { description: pickLocale(cover.description, locale, cover.defaultLocale) } : {}),\n }\n}\n\n/**\n * Resolve a `string | { [locale]: string }` value to a string for a\n * given locale. Exported so the bundle reader can reuse it without\n * duplicating the fallback chain.\n *\n * **Looser than `resolveI18nText`** — the hub's record-field resolver\n * throws `LocaleNotSpecifiedError` when no translation matches, but\n * label semantics deliberately prefer \"show *something*\" over\n * throwing. The fallback chain is:\n * 1. Exact `locale` match.\n * 2. `defaultLocale` (when supplied on the cover).\n * 3. First non-empty value in the map.\n * 4. Empty string (only if every translation is empty — pathological).\n *\n * This deviation is documented in `https://github.com/noy-db/docs/blob/main/content/docs/services/public-envelope.md`.\n */\nexport function pickLocale(\n value: string | Record<string, string>,\n locale: string,\n defaultLocale: string | undefined,\n): string {\n if (typeof value === 'string') return value\n if (value[locale] !== undefined && value[locale] !== '') return value[locale]\n if (defaultLocale && value[defaultLocale] !== undefined && value[defaultLocale] !== '') {\n return value[defaultLocale]\n }\n for (const v of Object.values(value)) {\n if (v !== '') return v\n }\n return ''\n}\n"],"mappings":";;;;;;;;AAuBA,IAAM,qBAAqB;AAG3B,IAAM,mBAAmB;AAUlB,SAAS,oBAAoB,QAAyC;AAC3E,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC1E,UAAM,IAAI;AAAA,MACR,iEACE,MAAM,QAAQ,MAAM,IAAI,UAAU,OAAO,MAC3C;AAAA,IACF;AAAA,EACF;AACA,aAAW,CAAC,IAAI,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAChD,QAAI,CAAC,mBAAmB,KAAK,EAAE,GAAG;AAChC,YAAM,IAAI;AAAA,QACR,+BAA+B,EAAE;AAAA,MAGnC;AAAA,IACF;AACA,oBAAgB,OAAO,IAAI,GAAG,oBAAI,IAAI,CAAC;AAAA,EACzC;AACF;AAEA,SAAS,gBACP,OACA,IACA,OACA,MACM;AACN,MAAI,UAAU,KAAM;AACpB,QAAM,IAAI,OAAO;AACjB,MAAI,MAAM,YAAY,MAAM,YAAY,MAAM,UAAW;AACzD,MAAI,MAAM,UAAU;AAClB,UAAM,IAAI;AAAA,MACR,qBAAqB,EAAE,iCAAiC,CAAC;AAAA,IAE3D;AAAA,EACF;AACA,QAAM,YAAY;AAClB,MAAI,KAAK,IAAI,SAAS,GAAG;AACvB,UAAM,IAAI;AAAA,MACR,qBAAqB,EAAE;AAAA,IACzB;AAAA,EACF;AACA,MAAI,QAAQ,kBAAkB;AAC5B,UAAM,IAAI;AAAA,MACR,qBAAqB,EAAE,4BAA4B,gBAAgB;AAAA,IACrE;AAAA,EACF;AACA,OAAK,IAAI,SAAS;AAClB,QAAM,WAAW,MAAM,QAAQ,SAAS,IAAI,YAAY,OAAO,OAAO,SAAS;AAC/E,aAAW,SAAS,UAAU;AAC5B,oBAAgB,OAAO,IAAI,QAAQ,GAAG,IAAI;AAAA,EAC5C;AACA,OAAK,OAAO,SAAS;AACvB;AAcO,SAAS,YACd,UACA,OAC+D;AAC/D,MAAI,UAAU,QAAW;AACvB,WAAO,aAAa,SAAY,EAAE,QAAQ,SAAS,IAAI,CAAC;AAAA,EAC1D;AACA,QAAM,SAAoC,EAAE,GAAG,SAAS;AACxD,aAAW,CAAC,IAAI,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC/C,QAAI,UAAU,MAAM;AAClB,aAAO,OAAO,EAAE;AAAA,IAClB,OAAO;AACL,aAAO,EAAE,IAAI;AAAA,IACf;AAAA,EACF;AACA,SAAO,OAAO,KAAK,MAAM,EAAE,SAAS,IAAI,EAAE,QAAQ,OAAO,IAAI,CAAC;AAChE;AAUO,SAAS,kBACd,OACA,QACM;AACN,MAAI,MAAM,WAAW,QAAW;AAC9B,UAAM,aAAa,KAAK,UAAU,MAAM,MAAM,EAAE;AAChD,QAAI,aAAa,OAAO,gBAAgB;AACtC,YAAM,IAAI;AAAA,QACR,gCAAgC,OAAO,cAAc,kBAC3C,UAAU;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AACA,QAAM,YAAY,KAAK,UAAU,KAAK,EAAE;AACxC,MAAI,YAAY,OAAO,eAAe;AACpC,UAAM,IAAI;AAAA,MACR,4CAA4C,OAAO,aAAa,kBACtD,SAAS;AAAA,IACrB;AAAA,EACF;AACF;;;ACnHA,IAAM,kBAAkB;AAYjB,SAAS,mBACd,OACA,QACM;AACN,QAAM,UAAU,IAAI,IAAgB,OAAO,MAAM;AAGjD,aAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,UAAM,QACJ,QAAQ,UAAU,QAAQ,iBAAiB,QAAQ,UAAU,QAAQ,mBAAmB,QAAQ,WAC5F,MACA;AACN,QAAI,CAAC,OAAO;AACV,YAAM,IAAI;AAAA,QACR,4BAA4B,GAAG,sBACV,CAAC,GAAG,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,MAC9C;AAAA,IACF;AACA,QAAI,CAAC,QAAQ,IAAI,KAAK,GAAG;AACvB,YAAM,IAAI;AAAA,QACR,oBAAoB,KAAK,4DACJ,CAAC,GAAG,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AAEA,MAAI,MAAM,SAAS,QAAW;AAC5B,iBAAa,MAAM,MAAM,QAAQ,OAAO,cAAc;AAAA,EACxD;AACA,MAAI,MAAM,gBAAgB,QAAW;AACnC,iBAAa,MAAM,aAAa,eAAe,OAAO,cAAc;AAAA,EACtE;AACA,MAAI,MAAM,SAAS,QAAW;AAC5B,iBAAa,MAAM,MAAM,MAAM;AAAA,EACjC;AACA,MAAI,MAAM,kBAAkB,UAAa,OAAO,MAAM,kBAAkB,UAAU;AAChF,UAAM,IAAI;AAAA,MACR,0DAA0D,OAAO,MAAM,aAAa;AAAA,IACtF;AAAA,EACF;AACA,MAAI,MAAM,WAAW,QAAW;AAC9B,wBAAoB,MAAM,MAAM;AAAA,EAClC;AACF;AAEA,SAAS,aACP,OACA,OACA,UACM;AACN,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,MAAM,SAAS,UAAU;AAC3B,YAAM,IAAI;AAAA,QACR,aAAa,KAAK,gBAAgB,QAAQ,uBAAuB,MAAM,MAAM;AAAA,MAC/E;AAAA,IACF;AACA;AAAA,EACF;AACA,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACvE,UAAM,IAAI;AAAA,MACR,aAAa,KAAK,sDAAsD,OAAO,KAAK;AAAA,IACtF;AAAA,EACF;AAEA,aAAW,CAAC,QAAQ,GAAG,KAAK,OAAO,QAAQ,KAAK,GAAG;AACjD,QAAI,OAAO,QAAQ,UAAU;AAC3B,YAAM,IAAI;AAAA,QACR,aAAa,KAAK,IAAI,MAAM,2BAA2B,OAAO,GAAG;AAAA,MACnE;AAAA,IACF;AACA,QAAI,IAAI,SAAS,UAAU;AACzB,YAAM,IAAI;AAAA,QACR,aAAa,KAAK,IAAI,MAAM,iBAAiB,QAAQ,uBAAuB,IAAI,MAAM;AAAA,MACxF;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,aAAa,MAAc,QAAmC;AACrE,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM,IAAI;AAAA,MACR,kDAAkD,OAAO,IAAI;AAAA,IAC/D;AAAA,EACF;AACA,MAAI,KAAK,SAAS,OAAO,cAAc;AACrC,UAAM,IAAI;AAAA,MACR,8BAA8B,OAAO,YAAY,kBAAkB,KAAK,MAAM;AAAA,IAChF;AAAA,EACF;AACA,QAAM,IAAI,gBAAgB,KAAK,IAAI;AACnC,MAAI,CAAC,GAAG;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACA,QAAM,OAAO,EAAE,CAAC;AAChB,MAAI,CAAC,OAAO,cAAc,SAAS,IAAI,GAAG;AACxC,UAAM,IAAI;AAAA,MACR,6BAA6B,IAAI,sCACX,OAAO,cAAc,KAAK,IAAI,CAAC;AAAA,IACvD;AAAA,EACF;AACF;AAOO,SAAS,QAAQ,GAAwB;AAC9C,MAAI,MAAM,QAAQ,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,EAAG,QAAO;AACpE,QAAM,MAAM;AACZ,SAAO,IAAI,eAAe,MAAM,KAAK,OAAO,IAAI,SAAS,MAAM;AACjE;;;AC9IO,IAAM,kBAAkB;AAS/B,eAAsB,UACpB,OACA,OAC4B;AAC5B,QAAM,WAAW,MAAM,MAAM,IAAI,OAAO,SAAS,eAAe;AAChE,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,SAAS,SAAS,EAAE;AAC9C,QAAI,CAAC,QAAQ,MAAM,EAAG,QAAO;AAC7B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAsB,UACpB,OACA,OACA,OACe;AACf,QAAM,eAAe;AAAA,IACnB,EAAE,YAAY,SAAS,IAAI,iBAAiB,SAAS,EAAE;AAAA,IACvD,EAAE,IAAI,IAAI,MAAM,KAAK,UAAU,KAAK,EAAE;AAAA,EACxC;AACA,QAAM,MAAM,IAAI,OAAO,SAAS,iBAAiB,YAAY;AAC/D;AAWA,eAAsB,UACpB,OACA,OACA,OAAqC,CAAC,GACV;AAC5B,QAAM,MAAM,MAAM,UAAU,OAAO,KAAK;AACxC,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,KAAK,WAAW,OAAW,QAAO;AACtC,SAAO,cAAc,KAAK,KAAK,MAAM;AACvC;AASO,SAAS,cACd,OACA,QACO;AACP,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,WAAW,MAAM,MAAM,QAAQ,MAAM,aAAa,EAAE,IAAI,CAAC;AAAA,IAChG,GAAI,MAAM,gBAAgB,SAAY,EAAE,aAAa,WAAW,MAAM,aAAa,QAAQ,MAAM,aAAa,EAAE,IAAI,CAAC;AAAA,EACvH;AACF;AAkBO,SAAS,WACd,OACA,QACA,eACQ;AACR,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,MAAM,MAAM,MAAM,UAAa,MAAM,MAAM,MAAM,GAAI,QAAO,MAAM,MAAM;AAC5E,MAAI,iBAAiB,MAAM,aAAa,MAAM,UAAa,MAAM,aAAa,MAAM,IAAI;AACtF,WAAO,MAAM,aAAa;AAAA,EAC5B;AACA,aAAW,KAAK,OAAO,OAAO,KAAK,GAAG;AACpC,QAAI,MAAM,GAAI,QAAO;AAAA,EACvB;AACA,SAAO;AACT;","names":[]}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getHistory,
|
|
3
3
|
historyIdentity
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-LNSVGRHO.js";
|
|
5
5
|
import {
|
|
6
6
|
liveRecordIsElevated
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-FLPCETP6.js";
|
|
8
8
|
import {
|
|
9
9
|
openEnvelopeJson
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-EZGV6F47.js";
|
|
11
11
|
import {
|
|
12
12
|
ReadOnlyAtInstantError
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-37EPTBBR.js";
|
|
14
14
|
|
|
15
15
|
// src/with-commit/history/time-machine.ts
|
|
16
16
|
var VaultInstant = class {
|
|
@@ -52,7 +52,7 @@ var CollectionInstant = class {
|
|
|
52
52
|
async get(id) {
|
|
53
53
|
const found = await this.resolveVisibleEnvelope(id);
|
|
54
54
|
if (!found) return null;
|
|
55
|
-
const plaintext = this.engine.encrypted ? await openEnvelopeJson(found.ref, found.envelope, await this.engine.getDEK(this.name)) : found.envelope._data;
|
|
55
|
+
const plaintext = this.engine.encrypted ? await openEnvelopeJson(found.ref, found.envelope, await this.engine.getDEK(this.name)) : found.envelope._data ?? "";
|
|
56
56
|
return JSON.parse(plaintext);
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
@@ -208,4 +208,4 @@ export {
|
|
|
208
208
|
VaultInstant,
|
|
209
209
|
CollectionInstant
|
|
210
210
|
};
|
|
211
|
-
//# sourceMappingURL=chunk-
|
|
211
|
+
//# sourceMappingURL=chunk-5FE75J4S.js.map
|