@noy-db/hub 0.4.0-pre.4 → 0.4.0-pre.6
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/README.md +2 -2
- package/dist/api-5QVQX4HG.js +20 -0
- package/dist/attestation/index.js +18 -18
- package/dist/{backup-JL2GOTYQ.js → backup-USYSNCYM.js} +11 -11
- package/dist/blobs/index.js +15 -12
- package/dist/blobs/index.js.map +1 -1
- package/dist/broker/index.js +12 -12
- package/dist/cargo/index.js +34 -36
- package/dist/{chunk-JLHKBAFY.js → chunk-2BVW754E.js} +3 -3
- package/dist/{chunk-T7S4V52L.js → chunk-2PDBYF2O.js} +4 -4
- package/dist/{chunk-YGBIBOCR.js → chunk-2ZSC454H.js} +7 -7
- package/dist/{chunk-IGAIOGCN.js → chunk-32467U5J.js} +4 -4
- package/dist/{chunk-JA5O7UEZ.js → chunk-3GXDB3NQ.js} +6 -6
- package/dist/chunk-3GXDB3NQ.js.map +1 -0
- package/dist/{chunk-U73LHVPF.js → chunk-3S2YNMUS.js} +54 -6
- package/dist/chunk-3S2YNMUS.js.map +1 -0
- package/dist/{chunk-JW3XCZA2.js → chunk-3TZUC4WS.js} +11 -11
- package/dist/{chunk-JA5B3SLH.js → chunk-44Y3P5U6.js} +2 -2
- package/dist/{chunk-25G7U6RD.js → chunk-4B3DL75F.js} +2 -2
- package/dist/{chunk-L4OA7G5T.js → chunk-522MBPE5.js} +2 -2
- package/dist/chunk-5ABOFAHN.js +81 -0
- package/dist/chunk-5ABOFAHN.js.map +1 -0
- package/dist/chunk-5D2ALSM5.js +19 -0
- package/dist/chunk-5D2ALSM5.js.map +1 -0
- package/dist/{chunk-M25ZIEB2.js → chunk-5HSSTOOG.js} +2 -2
- package/dist/{chunk-LLVW4V7R.js → chunk-5WLDZZ6S.js} +3 -3
- package/dist/{chunk-BCOKZC4B.js → chunk-6SSWOUUS.js} +19 -19
- package/dist/chunk-6SSWOUUS.js.map +1 -0
- package/dist/{chunk-IGM563N5.js → chunk-6SYOHUHM.js} +6 -6
- package/dist/chunk-6SYOHUHM.js.map +1 -0
- package/dist/{chunk-7B4GPCX7.js → chunk-7IQWHYL2.js} +5 -5
- package/dist/{chunk-J3VW4J4E.js → chunk-7LP7FFJ2.js} +2 -2
- package/dist/chunk-7LP7FFJ2.js.map +1 -0
- package/dist/{chunk-5P5AAXYA.js → chunk-7U6AB7DZ.js} +7 -7
- package/dist/chunk-7U6AB7DZ.js.map +1 -0
- package/dist/chunk-AFHQATY4.js +47 -0
- package/dist/chunk-AFHQATY4.js.map +1 -0
- package/dist/{chunk-OWE4BNTM.js → chunk-AZXEOEQK.js} +4 -4
- package/dist/{chunk-GBEXTC6Z.js → chunk-BAAUFCJK.js} +3 -3
- package/dist/chunk-BAAUFCJK.js.map +1 -0
- package/dist/{chunk-BZEPJJOK.js → chunk-C2V7AJO2.js} +4 -4
- package/dist/chunk-CADUYBQD.js +151 -0
- package/dist/chunk-CADUYBQD.js.map +1 -0
- package/dist/{chunk-2JZ763FV.js → chunk-D4LAJ737.js} +2 -2
- package/dist/{chunk-5BHNQNBI.js → chunk-DOZFU7OZ.js} +1 -1
- package/dist/chunk-DOZFU7OZ.js.map +1 -0
- package/dist/{chunk-MEKAMERX.js → chunk-E7FSBI6I.js} +29 -29
- package/dist/chunk-E7FSBI6I.js.map +1 -0
- package/dist/{chunk-N4FZ6ITM.js → chunk-EGU4PWQW.js} +2 -2
- package/dist/chunk-EP5L47XV.js +22 -0
- package/dist/chunk-EP5L47XV.js.map +1 -0
- package/dist/{chunk-EW4HFWGJ.js → chunk-EPME33Y4.js} +6 -6
- package/dist/{chunk-W7TCXZWU.js → chunk-EWPS35AR.js} +794 -1045
- package/dist/chunk-EWPS35AR.js.map +1 -0
- package/dist/{chunk-JKWNG3TM.js → chunk-FHHYCBBP.js} +2 -2
- package/dist/{chunk-5BB3N4KU.js → chunk-G4MFQXYX.js} +2 -2
- package/dist/{chunk-KVHWEPT2.js → chunk-GHEMAVTN.js} +2 -2
- package/dist/{chunk-CGPO3ZYB.js → chunk-GQT6J4FJ.js} +15 -2
- package/dist/chunk-GQT6J4FJ.js.map +1 -0
- package/dist/{chunk-XW7NWO4A.js → chunk-GTNRMJYH.js} +2 -2
- package/dist/{chunk-YG2EG6CB.js → chunk-H5GMROCB.js} +4 -4
- package/dist/{chunk-JIMEB4C6.js → chunk-HA7P3BTB.js} +26 -177
- package/dist/chunk-HA7P3BTB.js.map +1 -0
- package/dist/{chunk-5KI3REO3.js → chunk-HDF5R2K6.js} +7 -7
- package/dist/{chunk-V3YB7RGR.js → chunk-HVPW6PCX.js} +3 -3
- package/dist/{chunk-2VUISNGP.js → chunk-HXVRRJBD.js} +5 -5
- package/dist/{chunk-ZJGZDOQO.js → chunk-ICYC5YNI.js} +6 -6
- package/dist/{chunk-KR5EGWI4.js → chunk-IUATR5UE.js} +27 -5
- package/dist/chunk-IUATR5UE.js.map +1 -0
- package/dist/{chunk-AFBJHD55.js → chunk-JGOKAZNB.js} +5 -5
- package/dist/{chunk-XGVDLQJE.js → chunk-JOCMXSR2.js} +3 -3
- package/dist/{chunk-XGVDLQJE.js.map → chunk-JOCMXSR2.js.map} +1 -1
- package/dist/{chunk-HGVFJL63.js → chunk-JTIPVW4J.js} +15 -5
- package/dist/{chunk-HGVFJL63.js.map → chunk-JTIPVW4J.js.map} +1 -1
- package/dist/{chunk-QXPOEAKR.js → chunk-KCZAFDP5.js} +2 -2
- package/dist/{chunk-MSO2C3KG.js → chunk-LC7DSMHR.js} +2 -2
- package/dist/{chunk-OVFCAS7U.js → chunk-LG2GUYRX.js} +9 -9
- package/dist/{chunk-2KL6NDD4.js → chunk-LN3MQFUE.js} +56 -4
- package/dist/chunk-LN3MQFUE.js.map +1 -0
- package/dist/{chunk-QUIG6BZT.js → chunk-M4GO4ZJK.js} +226 -36
- package/dist/chunk-M4GO4ZJK.js.map +1 -0
- package/dist/{chunk-QDNVHA2F.js → chunk-M7W2KYTL.js} +2 -2
- package/dist/{chunk-XULHFSHO.js → chunk-MLCTPWSO.js} +5 -5
- package/dist/chunk-MLCTPWSO.js.map +1 -0
- package/dist/{chunk-PEY6ZTCF.js → chunk-MMVQ3SUO.js} +9 -9
- package/dist/{chunk-PEY6ZTCF.js.map → chunk-MMVQ3SUO.js.map} +1 -1
- package/dist/{chunk-4X6DJZHY.js → chunk-MPPWL64H.js} +26 -43
- package/dist/chunk-MPPWL64H.js.map +1 -0
- package/dist/{chunk-QYJIOWKN.js → chunk-MQBFT2OT.js} +11 -9
- package/dist/chunk-MQBFT2OT.js.map +1 -0
- package/dist/{chunk-IUIVDVXM.js → chunk-MUPZ6IZX.js} +2 -2
- package/dist/{chunk-GZ2GKOF6.js → chunk-NA374QG7.js} +3 -3
- package/dist/chunk-NIKO2BBZ.js +23 -0
- package/dist/chunk-NIKO2BBZ.js.map +1 -0
- package/dist/{chunk-2U7Y7BNJ.js → chunk-ONB627HU.js} +2 -2
- package/dist/{chunk-GYB2QK5H.js → chunk-OWLZONGX.js} +2 -2
- package/dist/{chunk-YHPUDB7Q.js → chunk-OWOT7CFC.js} +2 -2
- package/dist/{chunk-VEX6G3YL.js → chunk-P4NUJVWL.js} +2 -2
- package/dist/chunk-PEOZ34EM.js +13 -0
- package/dist/chunk-PEOZ34EM.js.map +1 -0
- package/dist/{chunk-NM4LVUXH.js → chunk-PTT2NB46.js} +2 -2
- package/dist/chunk-PVEWA2DU.js +23 -0
- package/dist/chunk-PVEWA2DU.js.map +1 -0
- package/dist/{chunk-2ITLQZV5.js → chunk-PWWWMRT7.js} +2 -2
- package/dist/{chunk-FLHOTGOO.js → chunk-QVGEVZ5F.js} +3 -3
- package/dist/{chunk-HKR25MGX.js → chunk-QVIZDLAR.js} +10 -10
- package/dist/chunk-QVIZDLAR.js.map +1 -0
- package/dist/{chunk-OOTTXUNV.js → chunk-RM5EXCFF.js} +2 -2
- package/dist/{chunk-OOTTXUNV.js.map → chunk-RM5EXCFF.js.map} +1 -1
- package/dist/{chunk-OGFPMCSP.js → chunk-SBOHNJUD.js} +2 -2
- package/dist/{chunk-EDC6MLXV.js → chunk-SEU6QHH4.js} +2 -2
- package/dist/chunk-SMY5X46Z.js +14 -0
- package/dist/chunk-SMY5X46Z.js.map +1 -0
- package/dist/{chunk-F4COUZLL.js → chunk-TMR64XPL.js} +2 -2
- package/dist/{chunk-5OZCCLNC.js → chunk-TSNRZSRC.js} +2 -2
- package/dist/{chunk-2RMDVWGB.js → chunk-UNREYBZO.js} +2 -2
- package/dist/{chunk-BLB7P7LS.js → chunk-V2R2VWER.js} +176 -17
- package/dist/chunk-V2R2VWER.js.map +1 -0
- package/dist/{chunk-3XVOLB75.js → chunk-VDTWFN2P.js} +7 -5
- package/dist/chunk-VDTWFN2P.js.map +1 -0
- package/dist/chunk-VKQCVU2Z.js +21 -0
- package/dist/chunk-VKQCVU2Z.js.map +1 -0
- package/dist/{chunk-QCUH2G77.js → chunk-VNJMLOQS.js} +6 -6
- package/dist/{chunk-UEO5CHOP.js → chunk-WCJJQV2D.js} +13 -13
- package/dist/chunk-WCJJQV2D.js.map +1 -0
- package/dist/{chunk-25ZDB25Z.js → chunk-WWBC4KLB.js} +3 -3
- package/dist/chunk-WWBC4KLB.js.map +1 -0
- package/dist/{chunk-V7RGFJ3Z.js → chunk-XG7OZDOS.js} +6 -6
- package/dist/{chunk-GS3Q2KOZ.js → chunk-XONZGK6J.js} +2 -2
- package/dist/{chunk-5SZ24DL5.js → chunk-XSOGLHGY.js} +6 -6
- package/dist/{chunk-CCT4WZI6.js → chunk-XY6MNSAF.js} +5 -5
- package/dist/chunk-XY6MNSAF.js.map +1 -0
- package/dist/chunk-XZHEOSQ2.js +16 -0
- package/dist/chunk-XZHEOSQ2.js.map +1 -0
- package/dist/{chunk-NOT7XA7Q.js → chunk-YGMREQAK.js} +5 -5
- package/dist/{chunk-H4SMS4O2.js → chunk-YJ3TREAC.js} +2 -2
- package/dist/chunk-YMUFFJCO.js +23 -0
- package/dist/chunk-YMUFFJCO.js.map +1 -0
- package/dist/{chunk-E2R5NRZD.js → chunk-Z4D3YGAB.js} +2 -2
- package/dist/{chunk-WJDYZP3U.js → chunk-ZDFU34XY.js} +42 -39
- package/dist/chunk-ZDFU34XY.js.map +1 -0
- package/dist/{chunk-BZK6JEX4.js → chunk-ZF572E2M.js} +5 -5
- package/dist/{chunk-DEQ2JTBR.js → chunk-ZGEFBDPR.js} +4 -4
- package/dist/chunk-ZGEFBDPR.js.map +1 -0
- package/dist/{chunk-QAFWRGVD.js → chunk-ZHMUFYZF.js} +7 -7
- package/dist/{chunk-BVZGNMVM.js → chunk-ZHZ4PET5.js} +3 -3
- package/dist/{chunk-IZX3WZBF.js → chunk-ZM72UO3Y.js} +4 -4
- package/dist/{chunk-STR7WBOB.js → chunk-ZUDAK436.js} +2 -2
- package/dist/{chunk-ITI3SJRB.js → chunk-ZX5FNWH7.js} +2 -2
- package/dist/classified/index.js +5 -5
- package/dist/{classified-marker-7S5IEP6Q.js → classified-marker-SVDRXLD3.js} +3 -3
- package/dist/collection-facade-S5PPKZD5.js +44 -0
- package/dist/{computed-WZNKRLCY.js → computed-33DRJDOC.js} +3 -3
- package/dist/consent/index.js +13 -9
- package/dist/consent/index.js.map +1 -1
- package/dist/{cover-B5MY43CK.js → cover-ADBCTIKN.js} +4 -4
- package/dist/crdt/index.js +4 -0
- package/dist/crdt/index.js.map +1 -1
- package/dist/{dead-filter-KRRD57MR.js → dead-filter-HKDMTMVY.js} +2 -2
- package/dist/delegation-EV7LKJPH.js +25 -0
- package/dist/derivations/index.js +12 -12
- package/dist/derive-5LDJW76F.js +21 -0
- package/dist/dispatch-SPMYNXNW.js +194 -0
- package/dist/dispatch-SPMYNXNW.js.map +1 -0
- package/dist/dispatch-V5MSMQYX.js +72 -0
- package/dist/dispatch-V5MSMQYX.js.map +1 -0
- package/dist/{enclave-YFKA3GJT.js → enclave-ZA7C3H2L.js} +11 -11
- package/dist/executor-FCJEH3FU.js +9 -0
- package/dist/executor-OHET7TF7.js +9 -0
- package/dist/executor-SVHAJZ4A.js +28 -0
- package/dist/export-accessible-S35YKIBL.js +23 -0
- package/dist/extract-partition-UVHAM5WZ.js +38 -0
- package/dist/{fanout-sidecar-HJV3A7PM.js → fanout-sidecar-HRMHC34B.js} +9 -9
- package/dist/find-LEBUXVL7.js +11 -0
- package/dist/forget/index.js +9 -9
- package/dist/guards/index.js +3 -3
- package/dist/history/index.js +16 -12
- package/dist/history/index.js.map +1 -1
- package/dist/i18n/index.js +16 -14
- package/dist/i18n/index.js.map +1 -1
- package/dist/index.d.ts +17 -16
- package/dist/index.js +155 -211
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +4 -2
- package/dist/indexing/index.js.map +1 -1
- package/dist/issue-5X3SMCF4.js +19 -0
- package/dist/kernel/collection.d.ts +0 -1
- package/dist/kernel/enclave/crypto.d.ts +8 -8
- package/dist/kernel/enclave/index.d.ts +2 -2
- package/dist/kernel/errors.d.ts +20 -20
- package/dist/kernel/lazy.d.ts +30 -0
- package/dist/kernel/noydb.d.ts +53 -101
- package/dist/kernel/query/builder.d.ts +10 -10
- package/dist/kernel/query/index.d.ts +0 -6
- package/dist/kernel/query/scan-builder.d.ts +4 -4
- package/dist/kernel/types.d.ts +87 -46
- package/dist/kernel/validation.d.ts +33 -33
- package/dist/kernel/vault.d.ts +12 -149
- package/dist/kernel/via/dispatch.d.ts +7 -0
- package/dist/{ledger-SW6QQOAV.js → ledger-K6JBFL3Z.js} +11 -11
- package/dist/liberate-NHTCKBU4.js +24 -0
- package/dist/{link-set-GNGGCFI4.js → link-set-XVZY2ODC.js} +11 -11
- package/dist/materialized-views/index.js +22 -19
- package/dist/noydb-TI3RFU6M.js +78 -0
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.js +15 -13
- package/dist/pod/index.js +11 -12
- package/dist/{policy-EAXSJNOT.js → policy-QWGJB7P7.js} +5 -5
- package/dist/port/with/collection-options.d.ts +171 -0
- package/dist/port/with/collection-registries.d.ts +62 -0
- package/dist/port/with/strategies.d.ts +2 -2
- package/dist/port/with/team-strategy.d.ts +3 -2
- package/dist/portability/index.js +8 -8
- package/dist/{post-register-A7HCFBP5.js → post-register-5VHHVFJY.js} +6 -6
- package/dist/query/index.js +6 -46
- package/dist/{aggregate → reduce}/index.js +24 -17
- package/dist/reduce/index.js.map +1 -0
- package/dist/register-CEX7R7NE.js +23 -0
- package/dist/registry-7CZWF3EQ.js +9 -0
- package/dist/registry-DIKMRXUC.js +19 -0
- package/dist/registry-EWIB5NTI.js +18 -0
- package/dist/request-withdrawal-AAG57QO4.js +31 -0
- package/dist/{reveal-PB3YG544.js → reveal-7OOT5XPD.js} +6 -6
- package/dist/revoke-U5LCIJMF.js +24 -0
- package/dist/satellites/index.js +1 -1
- package/dist/sealed-record/index.js +12 -12
- package/dist/{seed-EKF4DIBB.js → seed-GVOI6QP6.js} +12 -12
- package/dist/seed-GVOI6QP6.js.map +1 -0
- package/dist/session/index.js +13 -9
- package/dist/session/index.js.map +1 -1
- package/dist/shadow/index.js +6 -2
- package/dist/shadow/index.js.map +1 -1
- package/dist/signer-NAA2OX7H.js +25 -0
- package/dist/snapshots/index.js +14 -10
- package/dist/snapshots/index.js.map +1 -1
- package/dist/{stale-4UNPHWOU.js → stale-BJO2WIN7.js} +11 -10
- package/dist/storage-FL7XXZTN.js +23 -0
- package/dist/{store-coordination-provider-3AELCZ4Y.js → store-coordination-provider-DX5HECK3.js} +3 -3
- package/dist/sync/index.js +14 -10
- package/dist/sync/index.js.map +1 -1
- package/dist/team/index.js +26 -28
- package/dist/tiers/index.js +10 -10
- package/dist/to/index.js +1 -1
- package/dist/tx/index.js +7 -3
- package/dist/tx/index.js.map +1 -1
- package/dist/util/index.js +1 -1
- package/dist/{verify-MXN66IIH.js → verify-5X37OXRY.js} +5 -5
- package/dist/via/blob/index.d.ts +2 -0
- package/dist/via/i18n/core.d.ts +1 -1
- package/dist/via/i18n/index.d.ts +2 -0
- package/dist/via/money/money-reducer.d.ts +2 -2
- package/dist/{walk-H4YE52S2.js → walk-JXMWKKQW.js} +11 -11
- package/dist/with-audit/consent/index.d.ts +2 -0
- package/dist/with-audit/periods/index.d.ts +2 -0
- package/dist/with-audit/portability/export-accessible.d.ts +4 -4
- package/dist/with-audit/portability/request-withdrawal.d.ts +3 -3
- package/dist/with-audit/portability/withdraw-accessible.d.ts +2 -2
- package/dist/with-audit/sealed-record/strategy.d.ts +1 -1
- package/dist/with-cargo/adopt-partition.d.ts +7 -7
- package/dist/with-cargo/floor.d.ts +3 -3
- package/dist/with-cargo/index.d.ts +1 -1
- package/dist/with-commit/crdt/index.d.ts +2 -0
- package/dist/with-commit/history/index.d.ts +2 -0
- package/dist/with-commit/history/time-machine.d.ts +2 -2
- package/dist/with-fork/shadow/index.d.ts +2 -0
- package/dist/with-fork/snapshots/index.d.ts +2 -0
- package/dist/with-formula/derivations/dispatch.d.ts +98 -0
- package/dist/with-formula/materialized-views/dispatch.d.ts +75 -0
- package/dist/with-formula/materialized-views/types.d.ts +7 -7
- package/dist/with-lookup/indexing/index.d.ts +2 -0
- package/dist/with-lookup/{aggregate → reduce}/active.d.ts +8 -8
- package/dist/with-lookup/{aggregate → reduce}/groupby.d.ts +21 -21
- package/dist/with-lookup/reduce/index.d.ts +34 -0
- package/dist/with-lookup/{aggregate → reduce}/reducers.d.ts +10 -10
- package/dist/with-lookup/{aggregate/aggregation.d.ts → reduce/reduction.d.ts} +21 -21
- package/dist/with-lookup/{aggregate → reduce}/strategy.d.ts +15 -15
- package/dist/with-party/custody/liberate.d.ts +3 -3
- package/dist/with-party/session/dev-unlock.d.ts +2 -2
- package/dist/with-party/session/index.d.ts +2 -0
- package/dist/with-party/session/session.d.ts +2 -2
- package/dist/with-party/sync/index.d.ts +2 -0
- package/dist/with-party/team/deed.d.ts +8 -8
- package/dist/with-party/team/index.d.ts +2 -2
- package/dist/with-party/team/keyring.d.ts +39 -20
- package/dist/with-party/team/{managed-passphrase.d.ts → managed-secret.d.ts} +27 -27
- package/dist/with-party/team/noydb-facade.d.ts +43 -42
- package/dist/with-party/team/peer-recover.d.ts +13 -13
- package/dist/with-party/team/recovery.d.ts +5 -5
- package/dist/with-party/team/rotate-recover.d.ts +30 -30
- package/dist/with-pod/bundle.d.ts +30 -30
- package/dist/with-pod/format.d.ts +2 -2
- package/dist/withdraw-accessible-GO6KQJVM.js +28 -0
- package/package.json +6 -34
- package/dist/aggregate/index.js.map +0 -1
- package/dist/api-UPAT2O3M.js +0 -20
- package/dist/as/index.js +0 -30
- package/dist/at/index.js +0 -1
- package/dist/by/index.js +0 -11
- package/dist/chunk-25ZDB25Z.js.map +0 -1
- package/dist/chunk-2KL6NDD4.js.map +0 -1
- package/dist/chunk-2YELV6TV.js +0 -1
- package/dist/chunk-3O62PVDW.js +0 -205
- package/dist/chunk-3O62PVDW.js.map +0 -1
- package/dist/chunk-3XVOLB75.js.map +0 -1
- package/dist/chunk-4X6DJZHY.js.map +0 -1
- package/dist/chunk-5BHNQNBI.js.map +0 -1
- package/dist/chunk-5P5AAXYA.js.map +0 -1
- package/dist/chunk-BCOKZC4B.js.map +0 -1
- package/dist/chunk-BLB7P7LS.js.map +0 -1
- package/dist/chunk-CCT4WZI6.js.map +0 -1
- package/dist/chunk-CGPO3ZYB.js.map +0 -1
- package/dist/chunk-DEQ2JTBR.js.map +0 -1
- package/dist/chunk-GBEXTC6Z.js.map +0 -1
- package/dist/chunk-HKR25MGX.js.map +0 -1
- package/dist/chunk-IGM563N5.js.map +0 -1
- package/dist/chunk-J3VW4J4E.js.map +0 -1
- package/dist/chunk-JA5O7UEZ.js.map +0 -1
- package/dist/chunk-JIMEB4C6.js.map +0 -1
- package/dist/chunk-KMONEHDJ.js +0 -54
- package/dist/chunk-KMONEHDJ.js.map +0 -1
- package/dist/chunk-KR5EGWI4.js.map +0 -1
- package/dist/chunk-LB7FD65R.js +0 -163
- package/dist/chunk-LB7FD65R.js.map +0 -1
- package/dist/chunk-MEKAMERX.js.map +0 -1
- package/dist/chunk-QUIG6BZT.js.map +0 -1
- package/dist/chunk-QYJIOWKN.js.map +0 -1
- package/dist/chunk-U73LHVPF.js.map +0 -1
- package/dist/chunk-UEO5CHOP.js.map +0 -1
- package/dist/chunk-W7TCXZWU.js.map +0 -1
- package/dist/chunk-WEC3JGBA.js +0 -255
- package/dist/chunk-WEC3JGBA.js.map +0 -1
- package/dist/chunk-WJDYZP3U.js.map +0 -1
- package/dist/chunk-XULHFSHO.js.map +0 -1
- package/dist/collection-facade-CMZGQ6IS.js +0 -44
- package/dist/delegation-UT76AQIG.js +0 -25
- package/dist/derive-SGRCCKVP.js +0 -21
- package/dist/executor-TEZYRDN3.js +0 -9
- package/dist/executor-WQQRU6DO.js +0 -9
- package/dist/executor-YORMIBWV.js +0 -26
- package/dist/export-accessible-HXMKW5LJ.js +0 -23
- package/dist/extract-partition-QGQ2R42J.js +0 -38
- package/dist/find-VV6PBIHK.js +0 -11
- package/dist/in/index.js +0 -1
- package/dist/issue-7JJ4Z7E2.js +0 -19
- package/dist/liberate-FK7JI2QX.js +0 -24
- package/dist/noydb-N5IW6OFZ.js +0 -70
- package/dist/on/index.js +0 -17
- package/dist/port/as/index.d.ts +0 -26
- package/dist/port/at/index.d.ts +0 -18
- package/dist/port/in/index.d.ts +0 -21
- package/dist/port/on/index.d.ts +0 -27
- package/dist/port/ui/index.d.ts +0 -21
- package/dist/port/with/index.d.ts +0 -18
- package/dist/register-YBMRLZLO.js +0 -23
- package/dist/registry-3LXKNXWI.js +0 -9
- package/dist/registry-4V4YQCXJ.js +0 -19
- package/dist/registry-ZULMLCJK.js +0 -18
- package/dist/request-withdrawal-SEVUEUDG.js +0 -31
- package/dist/request-withdrawal-SEVUEUDG.js.map +0 -1
- package/dist/revoke-5QULL5GT.js +0 -24
- package/dist/revoke-5QULL5GT.js.map +0 -1
- package/dist/seed-EKF4DIBB.js.map +0 -1
- package/dist/signer-ZMH35FT4.js +0 -25
- package/dist/signer-ZMH35FT4.js.map +0 -1
- package/dist/stale-4UNPHWOU.js.map +0 -1
- package/dist/storage-QOZKQMID.js +0 -23
- package/dist/storage-QOZKQMID.js.map +0 -1
- package/dist/ui/index.js +0 -1
- package/dist/ui/index.js.map +0 -1
- package/dist/with/index.js +0 -31
- package/dist/with/index.js.map +0 -1
- package/dist/with-lookup/aggregate/index.d.ts +0 -23
- package/dist/withdraw-accessible-HPOVNG2G.js +0 -28
- package/dist/withdraw-accessible-HPOVNG2G.js.map +0 -1
- /package/dist/{api-UPAT2O3M.js.map → api-5QVQX4HG.js.map} +0 -0
- /package/dist/{backup-JL2GOTYQ.js.map → backup-USYSNCYM.js.map} +0 -0
- /package/dist/{chunk-JLHKBAFY.js.map → chunk-2BVW754E.js.map} +0 -0
- /package/dist/{chunk-T7S4V52L.js.map → chunk-2PDBYF2O.js.map} +0 -0
- /package/dist/{chunk-YGBIBOCR.js.map → chunk-2ZSC454H.js.map} +0 -0
- /package/dist/{chunk-IGAIOGCN.js.map → chunk-32467U5J.js.map} +0 -0
- /package/dist/{chunk-JW3XCZA2.js.map → chunk-3TZUC4WS.js.map} +0 -0
- /package/dist/{chunk-JA5B3SLH.js.map → chunk-44Y3P5U6.js.map} +0 -0
- /package/dist/{chunk-25G7U6RD.js.map → chunk-4B3DL75F.js.map} +0 -0
- /package/dist/{chunk-L4OA7G5T.js.map → chunk-522MBPE5.js.map} +0 -0
- /package/dist/{chunk-M25ZIEB2.js.map → chunk-5HSSTOOG.js.map} +0 -0
- /package/dist/{chunk-LLVW4V7R.js.map → chunk-5WLDZZ6S.js.map} +0 -0
- /package/dist/{chunk-7B4GPCX7.js.map → chunk-7IQWHYL2.js.map} +0 -0
- /package/dist/{chunk-OWE4BNTM.js.map → chunk-AZXEOEQK.js.map} +0 -0
- /package/dist/{chunk-BZEPJJOK.js.map → chunk-C2V7AJO2.js.map} +0 -0
- /package/dist/{chunk-2JZ763FV.js.map → chunk-D4LAJ737.js.map} +0 -0
- /package/dist/{chunk-N4FZ6ITM.js.map → chunk-EGU4PWQW.js.map} +0 -0
- /package/dist/{chunk-EW4HFWGJ.js.map → chunk-EPME33Y4.js.map} +0 -0
- /package/dist/{chunk-JKWNG3TM.js.map → chunk-FHHYCBBP.js.map} +0 -0
- /package/dist/{chunk-5BB3N4KU.js.map → chunk-G4MFQXYX.js.map} +0 -0
- /package/dist/{chunk-KVHWEPT2.js.map → chunk-GHEMAVTN.js.map} +0 -0
- /package/dist/{chunk-XW7NWO4A.js.map → chunk-GTNRMJYH.js.map} +0 -0
- /package/dist/{chunk-YG2EG6CB.js.map → chunk-H5GMROCB.js.map} +0 -0
- /package/dist/{chunk-5KI3REO3.js.map → chunk-HDF5R2K6.js.map} +0 -0
- /package/dist/{chunk-V3YB7RGR.js.map → chunk-HVPW6PCX.js.map} +0 -0
- /package/dist/{chunk-2VUISNGP.js.map → chunk-HXVRRJBD.js.map} +0 -0
- /package/dist/{chunk-ZJGZDOQO.js.map → chunk-ICYC5YNI.js.map} +0 -0
- /package/dist/{chunk-AFBJHD55.js.map → chunk-JGOKAZNB.js.map} +0 -0
- /package/dist/{chunk-QXPOEAKR.js.map → chunk-KCZAFDP5.js.map} +0 -0
- /package/dist/{chunk-MSO2C3KG.js.map → chunk-LC7DSMHR.js.map} +0 -0
- /package/dist/{chunk-OVFCAS7U.js.map → chunk-LG2GUYRX.js.map} +0 -0
- /package/dist/{chunk-QDNVHA2F.js.map → chunk-M7W2KYTL.js.map} +0 -0
- /package/dist/{chunk-IUIVDVXM.js.map → chunk-MUPZ6IZX.js.map} +0 -0
- /package/dist/{chunk-GZ2GKOF6.js.map → chunk-NA374QG7.js.map} +0 -0
- /package/dist/{chunk-2U7Y7BNJ.js.map → chunk-ONB627HU.js.map} +0 -0
- /package/dist/{chunk-GYB2QK5H.js.map → chunk-OWLZONGX.js.map} +0 -0
- /package/dist/{chunk-YHPUDB7Q.js.map → chunk-OWOT7CFC.js.map} +0 -0
- /package/dist/{chunk-VEX6G3YL.js.map → chunk-P4NUJVWL.js.map} +0 -0
- /package/dist/{chunk-NM4LVUXH.js.map → chunk-PTT2NB46.js.map} +0 -0
- /package/dist/{chunk-2ITLQZV5.js.map → chunk-PWWWMRT7.js.map} +0 -0
- /package/dist/{chunk-FLHOTGOO.js.map → chunk-QVGEVZ5F.js.map} +0 -0
- /package/dist/{chunk-OGFPMCSP.js.map → chunk-SBOHNJUD.js.map} +0 -0
- /package/dist/{chunk-EDC6MLXV.js.map → chunk-SEU6QHH4.js.map} +0 -0
- /package/dist/{chunk-F4COUZLL.js.map → chunk-TMR64XPL.js.map} +0 -0
- /package/dist/{chunk-5OZCCLNC.js.map → chunk-TSNRZSRC.js.map} +0 -0
- /package/dist/{chunk-2RMDVWGB.js.map → chunk-UNREYBZO.js.map} +0 -0
- /package/dist/{chunk-QCUH2G77.js.map → chunk-VNJMLOQS.js.map} +0 -0
- /package/dist/{chunk-V7RGFJ3Z.js.map → chunk-XG7OZDOS.js.map} +0 -0
- /package/dist/{chunk-GS3Q2KOZ.js.map → chunk-XONZGK6J.js.map} +0 -0
- /package/dist/{chunk-5SZ24DL5.js.map → chunk-XSOGLHGY.js.map} +0 -0
- /package/dist/{chunk-NOT7XA7Q.js.map → chunk-YGMREQAK.js.map} +0 -0
- /package/dist/{chunk-H4SMS4O2.js.map → chunk-YJ3TREAC.js.map} +0 -0
- /package/dist/{chunk-E2R5NRZD.js.map → chunk-Z4D3YGAB.js.map} +0 -0
- /package/dist/{chunk-BZK6JEX4.js.map → chunk-ZF572E2M.js.map} +0 -0
- /package/dist/{chunk-QAFWRGVD.js.map → chunk-ZHMUFYZF.js.map} +0 -0
- /package/dist/{chunk-BVZGNMVM.js.map → chunk-ZHZ4PET5.js.map} +0 -0
- /package/dist/{chunk-IZX3WZBF.js.map → chunk-ZM72UO3Y.js.map} +0 -0
- /package/dist/{chunk-STR7WBOB.js.map → chunk-ZUDAK436.js.map} +0 -0
- /package/dist/{chunk-ITI3SJRB.js.map → chunk-ZX5FNWH7.js.map} +0 -0
- /package/dist/{classified-marker-7S5IEP6Q.js.map → classified-marker-SVDRXLD3.js.map} +0 -0
- /package/dist/{as/index.js.map → collection-facade-S5PPKZD5.js.map} +0 -0
- /package/dist/{at/index.js.map → computed-33DRJDOC.js.map} +0 -0
- /package/dist/{by/index.js.map → cover-ADBCTIKN.js.map} +0 -0
- /package/dist/{dead-filter-KRRD57MR.js.map → dead-filter-HKDMTMVY.js.map} +0 -0
- /package/dist/{chunk-2YELV6TV.js.map → delegation-EV7LKJPH.js.map} +0 -0
- /package/dist/{collection-facade-CMZGQ6IS.js.map → derive-5LDJW76F.js.map} +0 -0
- /package/dist/{computed-WZNKRLCY.js.map → enclave-ZA7C3H2L.js.map} +0 -0
- /package/dist/{cover-B5MY43CK.js.map → executor-FCJEH3FU.js.map} +0 -0
- /package/dist/{delegation-UT76AQIG.js.map → executor-OHET7TF7.js.map} +0 -0
- /package/dist/{derive-SGRCCKVP.js.map → executor-SVHAJZ4A.js.map} +0 -0
- /package/dist/{enclave-YFKA3GJT.js.map → export-accessible-S35YKIBL.js.map} +0 -0
- /package/dist/{executor-TEZYRDN3.js.map → extract-partition-UVHAM5WZ.js.map} +0 -0
- /package/dist/{fanout-sidecar-HJV3A7PM.js.map → fanout-sidecar-HRMHC34B.js.map} +0 -0
- /package/dist/{executor-WQQRU6DO.js.map → find-LEBUXVL7.js.map} +0 -0
- /package/dist/{executor-YORMIBWV.js.map → issue-5X3SMCF4.js.map} +0 -0
- /package/dist/{export-accessible-HXMKW5LJ.js.map → ledger-K6JBFL3Z.js.map} +0 -0
- /package/dist/{extract-partition-QGQ2R42J.js.map → liberate-NHTCKBU4.js.map} +0 -0
- /package/dist/{find-VV6PBIHK.js.map → link-set-XVZY2ODC.js.map} +0 -0
- /package/dist/{in/index.js.map → noydb-TI3RFU6M.js.map} +0 -0
- /package/dist/{issue-7JJ4Z7E2.js.map → policy-QWGJB7P7.js.map} +0 -0
- /package/dist/{post-register-A7HCFBP5.js.map → post-register-5VHHVFJY.js.map} +0 -0
- /package/dist/{ledger-SW6QQOAV.js.map → register-CEX7R7NE.js.map} +0 -0
- /package/dist/{liberate-FK7JI2QX.js.map → registry-7CZWF3EQ.js.map} +0 -0
- /package/dist/{link-set-GNGGCFI4.js.map → registry-DIKMRXUC.js.map} +0 -0
- /package/dist/{noydb-N5IW6OFZ.js.map → registry-EWIB5NTI.js.map} +0 -0
- /package/dist/{on/index.js.map → request-withdrawal-AAG57QO4.js.map} +0 -0
- /package/dist/{reveal-PB3YG544.js.map → reveal-7OOT5XPD.js.map} +0 -0
- /package/dist/{policy-EAXSJNOT.js.map → revoke-U5LCIJMF.js.map} +0 -0
- /package/dist/{register-YBMRLZLO.js.map → signer-NAA2OX7H.js.map} +0 -0
- /package/dist/{registry-3LXKNXWI.js.map → stale-BJO2WIN7.js.map} +0 -0
- /package/dist/{registry-4V4YQCXJ.js.map → storage-FL7XXZTN.js.map} +0 -0
- /package/dist/{store-coordination-provider-3AELCZ4Y.js.map → store-coordination-provider-DX5HECK3.js.map} +0 -0
- /package/dist/{verify-MXN66IIH.js.map → verify-5X37OXRY.js.map} +0 -0
- /package/dist/{walk-H4YE52S2.js.map → walk-JXMWKKQW.js.map} +0 -0
- /package/dist/with-lookup/{aggregate → reduce}/canonical-key.d.ts +0 -0
- /package/dist/{registry-ZULMLCJK.js.map → withdraw-accessible-GO6KQJVM.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NoydbError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6SYOHUHM.js";
|
|
4
4
|
|
|
5
5
|
// src/with-formula/computed/index.ts
|
|
6
6
|
var ComputedFieldError = class extends NoydbError {
|
|
@@ -35,4 +35,4 @@ export {
|
|
|
35
35
|
ComputedFieldError,
|
|
36
36
|
evalComputedFields
|
|
37
37
|
};
|
|
38
|
-
//# sourceMappingURL=chunk-
|
|
38
|
+
//# sourceMappingURL=chunk-FHHYCBBP.js.map
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-LP7BEXCT.js";
|
|
6
6
|
import {
|
|
7
7
|
bufferToBase64
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7U6AB7DZ.js";
|
|
9
9
|
|
|
10
10
|
// src/kernel/enclave/classify/bidx.ts
|
|
11
11
|
var subtle = globalThis.crypto.subtle;
|
|
@@ -66,4 +66,4 @@ export {
|
|
|
66
66
|
mintBidxTag,
|
|
67
67
|
computeBidxTarget
|
|
68
68
|
};
|
|
69
|
-
//# sourceMappingURL=chunk-
|
|
69
|
+
//# sourceMappingURL=chunk-G4MFQXYX.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ValidationError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6SYOHUHM.js";
|
|
4
4
|
|
|
5
5
|
// src/with-formula/derivations/with-derivation.ts
|
|
6
6
|
function withDerivation(spec) {
|
|
@@ -126,4 +126,4 @@ export {
|
|
|
126
126
|
withDerivation,
|
|
127
127
|
withRollup
|
|
128
128
|
};
|
|
129
|
-
//# sourceMappingURL=chunk-
|
|
129
|
+
//# sourceMappingURL=chunk-GHEMAVTN.js.map
|
|
@@ -5,7 +5,19 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
FieldNotQueryableError,
|
|
7
7
|
IndexRequiredError
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-6SYOHUHM.js";
|
|
9
|
+
|
|
10
|
+
// src/with-lookup/indexing/strategy.ts
|
|
11
|
+
var NO_INDEXING = {
|
|
12
|
+
createState() {
|
|
13
|
+
return DISABLED_STATE;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var DISABLED_STATE = {
|
|
17
|
+
isEnabled: false,
|
|
18
|
+
getEagerIndexes: () => null,
|
|
19
|
+
getPersistedIndexes: () => null
|
|
20
|
+
};
|
|
9
21
|
|
|
10
22
|
// src/with-lookup/indexing/persisted-indexes.ts
|
|
11
23
|
var IDX_PREFIX = "_idx/";
|
|
@@ -512,6 +524,7 @@ function compareValues(a, b) {
|
|
|
512
524
|
}
|
|
513
525
|
|
|
514
526
|
export {
|
|
527
|
+
NO_INDEXING,
|
|
515
528
|
IDX_PREFIX,
|
|
516
529
|
encodeIdxId,
|
|
517
530
|
decodeIdxId,
|
|
@@ -521,4 +534,4 @@ export {
|
|
|
521
534
|
PersistedCollectionIndex,
|
|
522
535
|
LazyQuery
|
|
523
536
|
};
|
|
524
|
-
//# sourceMappingURL=chunk-
|
|
537
|
+
//# sourceMappingURL=chunk-GQT6J4FJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-lookup/indexing/strategy.ts","../src/with-lookup/indexing/persisted-indexes.ts","../src/with-lookup/indexing/lazy-builder.ts"],"sourcesContent":["/**\n * Strategy seam between core Collection and the optional indexing\n * service. Core imports `IndexStrategy` and `IndexState` as\n * TYPE-ONLY symbols and `NO_INDEXING` as a tiny runtime stub.\n *\n * The heavy classes — `CollectionIndexes`, `PersistedCollectionIndex`,\n * `LazyQuery` — are only instantiated inside the `withIndexing()`\n * factory under `./active.ts`, which in turn is only reachable through\n * the `@noy-db/hub/indexing` subpath export. A consumer that never\n * imports the subpath ships none of those classes in their bundle\n * (ESM tree-shaking + hub's `\"sideEffects\": false`).\n *\n * @internal\n */\n\nimport type { CollectionIndexes, IndexDef } from './eager-indexes.js'\nimport type { PersistedCollectionIndex } from './persisted-indexes.js'\n\n/**\n * Per-collection container for whatever mirrors the active strategy\n * decided to materialize. Both accessors may return `null` — they do\n * for `NO_INDEXING`, and `getEagerIndexes` returns null in a\n * lazy-mode collection even when indexing is active (lazy uses the\n * persisted mirror instead).\n *\n * `isEnabled` is a cheap guard so collection code can short-circuit\n * the full indexing path without inspecting either mirror.\n *\n * @internal\n */\nexport interface IndexState {\n readonly isEnabled: boolean\n getEagerIndexes(): CollectionIndexes | null\n getPersistedIndexes(): PersistedCollectionIndex | null\n}\n\n/**\n * Factory that builds one `IndexState` per Collection. Called exactly\n * once inside each Collection constructor with the declared\n * `IndexDef[]` and the lazy-mode flag (so lazy collections get the\n * persisted mirror and eager collections get the in-memory one).\n *\n * @internal\n */\nexport interface IndexStrategy {\n createState(args: {\n readonly defs: readonly IndexDef[]\n readonly lazy: boolean\n }): IndexState\n}\n\n/**\n * No-indexing stub. Every Collection defaults to this; it returns a\n * cheap `IndexState` whose mirrors are both `null`. Collection code\n * null-checks both accessors and short-circuits, so no indexing code\n * path runs and the heavy classes never arrive in the bundle.\n *\n * @internal\n */\nexport const NO_INDEXING: IndexStrategy = {\n createState() {\n return DISABLED_STATE\n },\n}\n\nconst DISABLED_STATE: IndexState = {\n isEnabled: false,\n getEagerIndexes: () => null,\n getPersistedIndexes: () => null,\n}\n","/**\n * Persistent, encrypted secondary indexes for lazy-mode collections.\n *\n * Parallel to the in-memory `CollectionIndexes` used by eager mode (see\n * `packages/hub/src/query/indexes.ts`): same logical surface, but entries\n * are materialised as encrypted side-car records (`_idx/<field>/<recordId>`)\n * and bulk-loaded into an in-memory mirror on first query.\n *\n * This module only owns the id-namespace convention, the in-memory mirror,\n * and the typed errors. Write-path integration (PR 2 / ), query-planner\n * dispatch (PR 3 / , PR 4 / ), and the rebuild/reconcile utilities\n * (PR 5 / ) live in other files.\n *\n * See the design spec for the full architecture + threat model.\n */\n\n/**\n * Reserved id prefix for encrypted index side-car records.\n * Matches the existing `_keyring`, `_ledger_deltas/…`, `_meta/handle`\n * conventions inside a collection's id namespace.\n */\nexport const IDX_PREFIX = '_idx/' as const\n\n/**\n * Encode the side-car record id for a (field, recordId) pair.\n *\n * Format: `_idx/<field>/<recordId>` — no escaping. Field names may contain\n * dots (for dotted-path access consistent with eager-mode `readPath`);\n * record ids may contain slashes. The first two slash-separated segments\n * are `_idx` and the field; everything after the *second* slash is the\n * record id verbatim.\n */\nexport function encodeIdxId(field: string, recordId: string): string {\n return `${IDX_PREFIX}${field}/${recordId}`\n}\n\n/**\n * Decode a side-car id back into `{ field, recordId }`, or `null` if the\n * input is not a well-formed idx id. A well-formed id is:\n * - prefixed with `_idx/`\n * - contains a field segment (non-empty, no slashes)\n * - contains a record-id segment (non-empty, may contain slashes)\n */\nexport function decodeIdxId(id: string): { field: string; recordId: string } | null {\n if (!id.startsWith(IDX_PREFIX)) return null\n const rest = id.slice(IDX_PREFIX.length)\n const firstSlash = rest.indexOf('/')\n if (firstSlash <= 0) return null\n const field = rest.slice(0, firstSlash)\n const recordId = rest.slice(firstSlash + 1)\n if (recordId.length === 0) return null\n return { field, recordId }\n}\n\n/**\n * Fast-path predicate for discriminating side-car ids from regular record\n * ids and other reserved namespaces. Used by the hub to filter `list()`\n * results during bulk-load of the in-memory mirror.\n */\nexport function isIdxId(id: string): boolean {\n return decodeIdxId(id) !== null\n}\n\n/**\n * Sorted-value entry returned by `orderedBy()`. Mirrors the body shape\n * used by the write path — but `orderedBy` emits them already sorted by\n * `value` in the requested direction. Consumers (PR 4 / ) treat the\n * array as immutable and paginate via a numeric offset.\n *\n * **Note on `value`:** as of, this is the ORIGINAL TYPED\n * value (number, Date, boolean, etc.), not the stringified bucket key.\n * That's what lets range predicates and `orderedBy` compare numerically\n * instead of stumbling into `'10' < '2'` on `String(n)`.\n */\nexport interface OrderedEntry {\n readonly recordId: string\n readonly value: unknown\n}\n\n/**\n * Bulk-load row shape accepted by `ingest()`. The `value` field is the\n * decrypted index body's `value` field verbatim.\n */\nexport interface IngestRow {\n readonly recordId: string\n readonly value: unknown\n}\n\n/**\n * In-memory mirror of the persisted index side-car records for a single\n * collection. Populated by bulk-loading `_idx/<field>/*` ids on first\n * query and maintained incrementally by `Collection.put()` / `.delete()`\n * via `upsert()` / `remove()`.\n *\n * API surface is deliberately parallel to `CollectionIndexes` (eager mode)\n * so the query planner in PR 3/4 can dispatch to either polymorphically.\n *\n * Lifecycle:\n * - `declare(field)` — accept the field as indexable (idempotent)\n * - `ingest(field, rows[])` — bulk-load from decrypted index bodies\n * - `upsert(recordId, field, newValue, previousValue)` — incremental update\n * - `remove(recordId, field, value)` — incremental remove\n * - `lookupEqual(field, value)` / `lookupIn(field, values)` — equality reads\n * - `orderedBy(field, dir)` — sorted iteration for orderBy\n * - `clear()` — drop all buckets (invalidation / rotation)\n */\n/**\n * Per-field storage: the equality bucket map AND a parallel table of typed\n * values keyed by recordId. The typed table exists so range predicates\n * and `orderedBy` can compare on the original typed value rather\n * than the stringified bucket key — String(10) < String(2) is the classic\n * landmine `stringifyKey` introduces for numeric fields.\n */\ninterface PersistedFieldState {\n readonly buckets: Map<string, Set<string>>\n readonly values: Map<string, unknown>\n}\n\n/**\n * Structured index definition. Single-field indexes carry just a field\n * name; composite indexes carry the ordered list of fields and\n * the synthetic `key` (= fields joined by `COMPOSITE_DELIMITER`) used\n * as the bucket-map key and side-car envelope id segment.\n */\nexport type PersistedIndexDef =\n | { readonly kind: 'single'; readonly field: string; readonly key: string }\n | { readonly kind: 'composite'; readonly fields: readonly string[]; readonly key: string }\n\n/**\n * Delimiter used to synthesize a composite-index key from an ordered\n * field list. Intentionally a character that is extremely unusual in\n * JavaScript object keys (`|`) so collision with a literal field name\n * is vanishingly rare in practice. Composite declarations whose field\n * names contain `|` are rejected at declare-time with an explicit\n * error.\n */\nexport const COMPOSITE_DELIMITER = '|'\n\nexport function compositeKey(fields: readonly string[]): string {\n return fields.join(COMPOSITE_DELIMITER)\n}\n\nexport class PersistedCollectionIndex {\n private readonly indexes = new Map<string, PersistedFieldState>()\n private readonly defs = new Map<string, PersistedIndexDef>()\n\n /**\n * Per-field bucket-key canonicalizer (#677 — lazy twin of #672's\n * `CollectionIndexes.setCanonicalizer`), registered ONCE via\n * {@link setCanonicalizer} when the collection wires indexing up\n * (money-aware via `ViaPipeline.canonicalizeIndexKey`). Consulted by\n * EVERY bucket-mutation site — `ingest`/`upsert`/`remove` — so bucket\n * membership is symmetric: a legacy non-canonical stored value (e.g.\n * `'0100'`) and a canonical one (`'100'`) land in the SAME bucket,\n * mirroring `eager-indexes.ts`'s `addToIndex`/`removeFromIndex`.\n */\n private canonicalize?: (field: string, value: unknown) => string | undefined\n\n /**\n * Register the bucket-key canonicalizer. Called once, where the\n * collection constructs its indexing state — safe to call again (the\n * closure is simply replaced) but there is normally only one caller.\n */\n setCanonicalizer(fn: (field: string, value: unknown) => string | undefined): void {\n this.canonicalize = fn\n }\n\n /**\n * Declare a single-field index. Subsequent `upsert` / `ingest` calls\n * populate the in-memory mirror; calls before `declare` are no-ops\n * (tolerant bulk-load ordering). Idempotent.\n */\n declare(field: string): void {\n if (this.indexes.has(field)) return\n this.indexes.set(field, { buckets: new Map(), values: new Map() })\n this.defs.set(field, { kind: 'single', field, key: field })\n }\n\n /**\n * Declare a composite (multi-field) index. The synthetic\n * key is `fields.join('|')`; it doubles as the in-memory map key and\n * the `_idx/<key>/<recordId>` side-car field segment. Callers upsert\n * and lookup via the same `key` as single-field indexes, just with a\n * tuple value (JSON-stringified for bucketing).\n */\n declareComposite(fields: readonly string[]): void {\n if (fields.length === 0) {\n throw new Error('declareComposite: fields array must be non-empty')\n }\n for (const f of fields) {\n if (f.includes(COMPOSITE_DELIMITER)) {\n throw new Error(\n `declareComposite: field \"${f}\" contains the composite delimiter ` +\n `\"${COMPOSITE_DELIMITER}\" — pick a different field name or open an ` +\n `issue to add hash-based composite keys.`,\n )\n }\n }\n const key = compositeKey(fields)\n if (this.indexes.has(key)) return\n this.indexes.set(key, { buckets: new Map(), values: new Map() })\n this.defs.set(key, { kind: 'composite', fields: [...fields], key })\n }\n\n /**\n * Every declared index's structured definition. Collection walks this\n * when materialising side-cars on put/delete so it can extract a\n * single-field value or a composite tuple appropriately.\n */\n definitions(): PersistedIndexDef[] {\n return [...this.defs.values()]\n }\n\n /** True if `field` has been declared as indexable on this mirror. */\n has(field: string): boolean {\n return this.indexes.has(field)\n }\n\n /** All declared field names, in declaration order. */\n fields(): string[] {\n return [...this.indexes.keys()]\n }\n\n /**\n * Bulk-load the mirror from decrypted index bodies. Intended to be\n * called once per field after reading the collection's `_idx/<field>/*`\n * side-cars. Safe to call twice with the same rows — bucket Sets\n * deduplicate recordIds. If `field` is not declared, this is a no-op\n * (tolerates the case where bulk-load runs before `declare()` lands).\n */\n ingest(field: string, rows: readonly IngestRow[]): void {\n const state = this.indexes.get(field)\n if (!state) return\n for (const row of rows) {\n addToState(state, row.recordId, row.value, field, this.canonicalize)\n }\n }\n\n /**\n * Incrementally update a record's index entry for one field. Called by\n * `Collection.put()` after the main write succeeds. If\n * `previousValue` is non-null, the record is removed from the old\n * bucket first — this is the update path. Pass `null` for fresh adds.\n * No-op if the field is not declared.\n */\n upsert(recordId: string, field: string, newValue: unknown, previousValue: unknown): void {\n const state = this.indexes.get(field)\n if (!state) return\n if (previousValue !== null && previousValue !== undefined) {\n removeFromState(state, recordId, previousValue, field, this.canonicalize)\n }\n addToState(state, recordId, newValue, field, this.canonicalize)\n }\n\n /**\n * Remove a record from the index for one field. Called by\n * `Collection.delete()`. No-op if the field is not declared or\n * the record isn't in the bucket. Empty buckets are dropped to keep\n * the Map clean.\n */\n remove(recordId: string, field: string, value: unknown): void {\n const state = this.indexes.get(field)\n if (!state) return\n removeFromState(state, recordId, value, field, this.canonicalize)\n }\n\n /**\n * Drop all bucket data while preserving field declarations. Called on\n * invalidation (incoming sync changes, keyring rotation) — the next\n * query re-populates via `ingest`.\n */\n clear(): void {\n for (const state of this.indexes.values()) {\n state.buckets.clear()\n state.values.clear()\n }\n }\n\n /**\n * Equality lookup — return the set of record ids whose `field` matches\n * `value`. Returns `null` if the field is not declared (caller falls\n * back to scan or throws `IndexRequiredError`). Returns a shared empty\n * set if the field is declared but no record matches — that set MUST\n * NOT be mutated by the caller.\n *\n * `value` is bucketed via the plain {@link stringifyKey} — it is NOT\n * canonicalized here. Callers on a Via-covered field (money) canonicalize\n * `value` themselves BEFORE calling (see `lazy-builder.ts`'s\n * `resolveCandidateIds()`, #677) — the SAME caller-canonicalizes split\n * `eager-indexes.ts`'s `lookupEqual`/`lookupIn` use (see `kernel/query/\n * builder.ts`'s `candidateRecords()`), so probing here twice never\n * double-canonicalizes.\n */\n lookupEqual(field: string, value: unknown): ReadonlySet<string> | null {\n const state = this.indexes.get(field)\n if (!state) return null\n const key = stringifyKey(value)\n return state.buckets.get(key) ?? EMPTY_SET\n }\n\n /**\n * Set lookup — return the union of record ids whose `field` matches any\n * of `values`. Returns `null` if the field is not declared. Returns a\n * fresh (non-shared) Set — safe for the caller to mutate. Same\n * caller-canonicalizes contract as {@link lookupEqual} (#677).\n */\n lookupIn(field: string, values: readonly unknown[]): ReadonlySet<string> | null {\n const state = this.indexes.get(field)\n if (!state) return null\n const out = new Set<string>()\n for (const value of values) {\n const bucket = state.buckets.get(stringifyKey(value))\n if (bucket) for (const id of bucket) out.add(id)\n }\n return out\n }\n\n /**\n * Range lookup. Return record ids whose indexed value\n * satisfies the predicate. Comparison happens on the ORIGINAL TYPED\n * value carried in `state.values` — so numeric `<` sorts numerically,\n * not lexicographically on `String(n)`. Returns `null` if the field\n * is not declared.\n *\n * Supported ops: `'<'`, `'<='`, `'>'`, `'>='`, `'between'`. For\n * `'between'`, `value` is `[lo, hi]` and both bounds are inclusive\n * (matches the eager-mode operator contract in `predicate.ts`).\n *\n * NOT canonicalized (#677): this method compares `value` against the\n * ORIGINAL TYPED `state.values`, not a stringified bucket key, so a\n * `canonicalizeIndexKey`-shaped (`string | undefined`) result would not\n * even type-check as a bound here. This does NOT mirror eager mode the\n * way `lookupEqual`/`lookupIn` do: `moneyIndexProbe` (`via/money/\n * where.ts`) never emits a range probe, so EAGER mode's `candidateRecords`\n * never calls an index for a range clause and always scans.\n *\n * LAZY mode's `resolveCandidateIds()` (`lazy-builder.ts`) no longer\n * dispatches a Via-covered (e.g. money) range clause through THIS method\n * at all (#684) — it enumerates the field's indexed ids via `orderedBy()`\n * instead and leaves the raw/typed comparison here for the (now\n * Via-aware) post-filter to redo correctly in scaled-int space. A\n * NON-via range clause still dispatches through this method exactly as\n * before — the raw/typed comparison here is correct and sufficient for\n * fields with no decode-transforming Via binding.\n */\n lookupRange(\n field: string,\n op: '<' | '<=' | '>' | '>=' | 'between',\n value: unknown,\n ): ReadonlySet<string> | null {\n const state = this.indexes.get(field)\n if (!state) return null\n const out = new Set<string>()\n for (const [recordId, live] of state.values) {\n if (live === undefined || live === null) continue\n if (matchesRange(live, op, value)) out.add(recordId)\n }\n return out\n }\n\n /**\n * Sorted iteration — return every entry on `field` as an\n * `OrderedEntry[]`, sorted by the ORIGINAL TYPED value (no more\n * `'10' < '2'` surprises on numeric fields). Consumers paginate with\n * a numeric offset. `OrderedEntry.value` is the typed value.\n */\n orderedBy(field: string, dir: 'asc' | 'desc'): readonly OrderedEntry[] | null {\n const state = this.indexes.get(field)\n if (!state) return null\n const entries: OrderedEntry[] = []\n for (const [recordId, value] of state.values) {\n entries.push({ recordId, value })\n }\n entries.sort((a, b) => compareTyped(a.value, b.value))\n if (dir === 'desc') entries.reverse()\n return entries\n }\n}\n\nconst EMPTY_SET: ReadonlySet<string> = new Set()\n\n/**\n * Canonicalize a value into a bucket key. Deliberately identical to the\n * eager-mode `stringifyKey` in `query/indexes.ts` so semantics match. When\n * `query/indexes.ts` changes its coercion rules, update this in lockstep.\n *\n * null / undefined values are NOT indexed — callers who pass them to\n * `upsert` / `remove` short-circuit before reaching this function; the\n * sentinel here exists only to make `lookupEqual(field, null)` return\n * an empty bucket (rather than matching some arbitrary record).\n */\nfunction stringifyKey(value: unknown): string {\n if (value === null || value === undefined) return '\\0NULL\\0'\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n if (value instanceof Date) return value.toISOString()\n // composite index values are tuple arrays. JSON.stringify\n // gives a delimiter-safe, order-preserving canonical form so buckets\n // for `['c-A', '2026-Q1']` and `['c-A', '2026-Q2']` never collide.\n if (Array.isArray(value)) {\n const parts: string[] = []\n for (const el of value) parts.push(stringifyKey(el))\n return JSON.stringify(parts)\n }\n return '\\0OBJECT\\0'\n}\n\nfunction addToState(\n state: PersistedFieldState,\n recordId: string,\n value: unknown,\n field: string,\n canonicalize?: (field: string, value: unknown) => string | undefined,\n): void {\n if (value === null || value === undefined) return\n const key = canonicalize?.(field, value) ?? stringifyKey(value)\n let bucket = state.buckets.get(key)\n if (!bucket) {\n bucket = new Set()\n state.buckets.set(key, bucket)\n }\n bucket.add(recordId)\n state.values.set(recordId, value)\n}\n\nfunction removeFromState(\n state: PersistedFieldState,\n recordId: string,\n value: unknown,\n field: string,\n canonicalize?: (field: string, value: unknown) => string | undefined,\n): void {\n if (value === null || value === undefined) return\n const key = canonicalize?.(field, value) ?? stringifyKey(value)\n const bucket = state.buckets.get(key)\n if (bucket) {\n bucket.delete(recordId)\n if (bucket.size === 0) state.buckets.delete(key)\n }\n state.values.delete(recordId)\n}\n\n/**\n * Range-predicate comparator. Runs on the ORIGINAL TYPED value so numeric\n * fields sort numerically (not lexicographically on `String(n)`). ISO-8601\n * date strings already sort correctly lexicographically; Date instances\n * compare via `getTime()` before the string branch to keep the contract\n * honest regardless of which form survived serialization.\n */\nfunction matchesRange(\n live: unknown,\n op: '<' | '<=' | '>' | '>=' | 'between',\n bound: unknown,\n): boolean {\n if (op === 'between') {\n if (!Array.isArray(bound) || bound.length !== 2) return false\n return compareTyped(live, bound[0]) >= 0 && compareTyped(live, bound[1]) <= 0\n }\n const cmp = compareTyped(live, bound)\n switch (op) {\n case '<': return cmp < 0\n case '<=': return cmp <= 0\n case '>': return cmp > 0\n case '>=': return cmp >= 0\n }\n}\n\nfunction compareTyped(a: unknown, b: unknown): number {\n if (a === undefined || a === null) return b === undefined || b === null ? 0 : 1\n if (b === undefined || b === null) return -1\n if (typeof a === 'number' && typeof b === 'number') return a - b\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime()\n if (typeof a === 'string' && typeof b === 'string') return a < b ? -1 : a > b ? 1 : 0\n if (typeof a === 'boolean' && typeof b === 'boolean') {\n return a === b ? 0 : a ? 1 : -1\n }\n // Mixed/unsupported types: deliberately treat as equal so sort stays\n // stable. Matches the eager-mode `compareValues` contract in\n // builder.ts — we don't silently coerce arbitrary objects to strings\n // (which would be meaningless) nor throw (which would be hostile).\n return 0\n}\n","/**\n * Lazy-mode query builder.\n *\n * Companion to `Query<T>` in `builder.ts`, but built for collections in lazy\n * mode where `snapshot()` is unavailable — records live in the adapter and\n * are pulled on demand. Dispatches through `PersistedCollectionIndex` to\n * resolve a candidate record-id set, then decrypts only those records.\n *\n * Scope:\n * - `.where(field, '==' | 'in', value)` — dispatched through the index\n * - `.where(field, other-op, value)` — evaluated against the decrypted\n * candidate set (non-indexed ops still require the field to be indexed\n * — we need SOMETHING to scope the candidate set)\n * - `.orderBy(field, dir?)` — dispatched through `orderedBy` when no\n * `==`/`in` clause is present; otherwise applied as an in-memory sort\n * over the candidate set\n * - `.limit(n)` / `.offset(n)` — page slice after filtering\n * - `.toArray()` / `.first()` / `.count()` — terminals\n *\n * Every field referenced by a where or orderBy clause MUST be indexed;\n * otherwise `toArray()` throws `IndexRequiredError`. This is deliberate:\n * silent scan-fallback would hide the very performance cliff that lazy-mode\n * indexes exist to prevent (see `docs/architecture.md` §indexes).\n */\n\nimport type { Clause, FieldClause, Operator } from '../../kernel/query/predicate.js'\nimport { evaluateClause, readPath } from '../../kernel/query/predicate.js'\nimport type { PersistedCollectionIndex } from './persisted-indexes.js'\nimport { IndexRequiredError, FieldNotQueryableError } from '../../kernel/errors.js'\nimport type { QueryField } from '../../kernel/types.js'\nimport type { ViaPipeline } from '../../kernel/via/pipeline.js'\n\nexport interface LazyOrderBy {\n readonly field: string\n readonly direction: 'asc' | 'desc'\n}\n\n/**\n * Source abstraction the LazyQuery runs against. Collection implements it.\n * Kept minimal so the builder stays test-friendly.\n */\nexport interface LazyQuerySource<T> {\n readonly collectionName: string\n readonly persistedIndexes: PersistedCollectionIndex\n /**\n * Per-field bucket-key canonicalizer (#677 — lazy twin of the eager\n * `candidateRecords()` probe-resolution in `kernel/query/builder.ts`).\n * Consulted in `resolveCandidateIds()` for `==`/`in` clause values\n * BEFORE calling `lookupEqual`/`lookupIn`, so a mixed-era stored value\n * (money) probes the SAME canonical bucket the write path now\n * populates. A narrow closure — not the whole `ViaPipeline` — keeps\n * the with-lookup/kernel seam thin. `undefined` (no via, or no binding\n * covers the field) is the common case: falls back to the raw value,\n * unchanged from today.\n */\n canonicalizeIndexKey?(field: string, value: unknown): string | undefined\n /** Ensure `_idx/<field>/*` side-cars have been bulk-loaded into the mirror. */\n ensurePersistedIndexesLoaded(): Promise<void>\n /**\n * Decrypt one record by id in RAW (pre-`present()`, stored-form) shape,\n * or return null if it's gone (#684 — the raw-fetch seam). Used by the\n * post-filter (`toArray()`) so a `clause.via.evaluate` (e.g. money) sees\n * the same operand/actual-value space as eager's `filterRecords` — the\n * DECODED form (e.g. money's canonical decimal) is only materialized\n * for survivors, via {@link decodeRecord}.\n */\n getRawRecord(id: string): Promise<unknown>\n /**\n * Apply this collection's locale/virtual-field decode (`present()`) to a\n * RAW record already known to match — the output-side twin of\n * {@link getRawRecord}. Mirrors eager's `Query.decodeVia`.\n */\n decodeRecord(record: unknown): Promise<T>\n /**\n * This collection's compiled Via pipeline (money, i18n, …), read lazily\n * (a method, not a captured value) so a late `_setVia` (#666) is honored\n * — same closure discipline as {@link canonicalizeIndexKey}. `undefined`\n * when the collection has no Via-covered fields.\n */\n via(): ViaPipeline | undefined\n}\n\ninterface LazyPlan {\n readonly clauses: readonly FieldClause[]\n readonly orderBy: readonly LazyOrderBy[]\n readonly limit: number | undefined\n readonly offset: number\n}\n\nconst EMPTY_PLAN: LazyPlan = {\n clauses: [],\n orderBy: [],\n limit: undefined,\n offset: 0,\n}\n\nexport class LazyQuery<T, S extends keyof T = never, Q extends keyof T & string = never> {\n private readonly source: LazyQuerySource<T>\n private readonly plan: LazyPlan\n\n constructor(source: LazyQuerySource<T>, plan: LazyPlan = EMPTY_PLAN) {\n this.source = source\n this.plan = plan\n }\n\n where<V>(field: QueryField<T, S, Q>, op: Operator, value: V): LazyQuery<T, S, Q> {\n // A Via-covered field (e.g. money) compares in major units, BigInt-exact\n // in scaled space — same build-time operand rewrite + `clause.via`\n // attachment as `Query.where()` (`kernel/query/builder.ts`) and\n // `ScanBuilder.where()` (`kernel/query/scan-builder.ts`). Before #684\n // this method built a bare clause with no `clause.via` at all, so the\n // post-filter fell through to the generic (non-Via-aware) comparison —\n // see `toArray()` below for the raw-record post-filter this feeds.\n const via = this.source.via()\n // Consults the Via pipeline's posture BEFORE building a clause — same\n // gate as `Query.where()` (builder.ts) / `ScanBuilder.where()`\n // (scan-builder.ts): a field whose posture is `queryable: 'none'`\n // (e.g. a `blobFields` slot) throws `FieldNotQueryableError` here, at\n // the call site, instead of building a bare clause that later surfaces\n // as a deferred `IndexRequiredError` from `toArray()`.\n if (via?.postureFor(field)?.queryable === 'none') throw new FieldNotQueryableError(field)\n const viaClause = via?.buildClause(field, op, value)\n const clause: FieldClause = viaClause\n ? {\n type: 'field',\n field,\n op,\n value,\n via: {\n brand: viaClause.brand,\n payload: viaClause.payload,\n evaluate: (actual: unknown, evalOp: string) => via!.evaluateClause(viaClause, actual, evalOp),\n indexValue: via!.indexProbe(viaClause, op),\n },\n }\n : { type: 'field', field, op, value }\n return new LazyQuery<T, S, Q>(this.source, {\n ...this.plan,\n clauses: [...this.plan.clauses, clause],\n })\n }\n\n orderBy(field: QueryField<T, S>, direction: 'asc' | 'desc' = 'asc'): LazyQuery<T, S, Q> {\n return new LazyQuery<T, S, Q>(this.source, {\n ...this.plan,\n orderBy: [...this.plan.orderBy, { field, direction }],\n })\n }\n\n limit(n: number): LazyQuery<T, S, Q> {\n return new LazyQuery<T, S, Q>(this.source, { ...this.plan, limit: n })\n }\n\n offset(n: number): LazyQuery<T, S, Q> {\n return new LazyQuery<T, S, Q>(this.source, { ...this.plan, offset: n })\n }\n\n async toArray(): Promise<T[]> {\n await this.source.ensurePersistedIndexesLoaded()\n\n const touchedFields = collectTouchedFields(this.plan)\n const missingFields = touchedFields.filter(f => !isFieldIndexed(f, this.source.persistedIndexes))\n if (missingFields.length > 0) {\n throw new IndexRequiredError({\n collection: this.source.collectionName,\n touchedFields,\n missingFields,\n })\n }\n\n const candidateIds = this.resolveCandidateIds()\n if (candidateIds === null) {\n // No usable driver — every touched field is indexed but no clause\n // pins the candidate set. This happens when a query only uses\n // operators other than `==`/`in` and no `orderBy` clause is\n // present — we refuse to enumerate the whole index, because that\n // defeats the purpose of lazy mode.\n throw new IndexRequiredError({\n collection: this.source.collectionName,\n touchedFields,\n missingFields: touchedFields,\n })\n }\n\n // #684: post-filter the RAW record (mirrors eager's filterRecords on the\n // raw snapshot) so `clause.via.evaluate` sees the same stored-form\n // operand/actual-value space `buildClause` quantized into at where()\n // time. Survivors stay RAW here — #695 sorts them via-aware in that\n // same stored-form space (mirroring eager's `sortRecords(result, ...,\n // source.via, ...)` in `kernel/query/builder.ts`) before anything is\n // decoded (`present()`).\n const survivors: T[] = []\n for (const id of candidateIds) {\n const raw = await this.source.getRawRecord(id)\n if (raw === null) continue\n if (!matchesAll(raw, this.plan.clauses)) continue\n survivors.push(raw as T)\n }\n\n // #695: sort the RAW survivors via-aware — a money field's stored form\n // (scaled-integer string) sorts lexicographically wrong under the\n // generic comparator ('10.00' < '2.00' once decoded); `via.compareForOrder`\n // compares in the correct (BigInt-exact scaled) space, same as eager.\n const sorted = this.plan.orderBy.length > 0\n ? sortRecords(survivors, this.plan.orderBy, this.source.via())\n : survivors\n\n const offset = this.plan.offset > 0 ? this.plan.offset : 0\n const page = this.plan.limit === undefined\n ? sorted.slice(offset)\n : sorted.slice(offset, offset + this.plan.limit)\n\n // Decode only the final page — fewer decodes than the pre-#695 code,\n // which decoded every survivor before sorting/slicing.\n return Promise.all(page.map(r => this.source.decodeRecord(r)))\n }\n\n async first(): Promise<T | null> {\n const out = await this.limit(1).toArray()\n return out.length > 0 ? out[0]! : null\n }\n\n async count(): Promise<number> {\n const out = await this.toArray()\n return out.length\n }\n\n /**\n * Resolve the candidate record-id set to decrypt. Returns null when the\n * query has no usable driver — no `==`/`in` clause and no `orderBy`\n * clause that can scope the scan. Callers interpret null as\n * IndexRequiredError (see `toArray`).\n */\n private resolveCandidateIds(): readonly string[] | null {\n const idx = this.source.persistedIndexes\n\n // prefer a composite index when the query's `==`\n // clauses cover every field of one declared composite. The\n // composite mirror lookup is O(matches) vs single-field +\n // post-filter on the decrypted candidate set.\n const eqMap = new Map<string, unknown>()\n const viaFields = new Set<string>()\n for (const clause of this.plan.clauses) {\n if (clause.op === '==') {\n eqMap.set(clause.field, clause.value)\n // #696: the composite mirror buckets on `stringifyKey(tuple)` built\n // from the RAW clause values — the per-field money canonicalizer\n // (`canonicalizeIndexKey`) only ever keys off a single field name,\n // never the joined composite key, so a Via-covered field's tuple\n // slot never lands in the same bucket a canonical write produced.\n // Track which `==` fields are Via-covered so the composite branch\n // below can skip them and fall through to the single-field\n // Via-aware path (already correct for money) instead.\n if (clause.via) viaFields.add(clause.field)\n }\n }\n if (eqMap.size >= 2) {\n for (const def of idx.definitions()) {\n if (def.kind !== 'composite') continue\n if (def.fields.some(f => viaFields.has(f))) continue\n if (def.fields.every(f => eqMap.has(f))) {\n const tuple = def.fields.map(f => eqMap.get(f))\n const ids = idx.lookupEqual(def.key, tuple)\n if (ids) return [...ids]\n }\n }\n }\n\n for (const clause of this.plan.clauses) {\n if (clause.op === '==') {\n if (clause.via) {\n // #684: prefer `clause.via.indexValue` — the same STORED-form\n // probe operand eager's `candidateRecords()` resolves first\n // (`kernel/query/builder.ts`). `undefined` means the binding\n // declined to probe this op/payload (e.g. money multi-mode) — no\n // sound equality bucket exists. Eager's `candidateRecords()`\n // handles this by skipping the index-eligibility check for the\n // clause and falling back to a full scan (`builder.ts:1176`); the\n // lazy equivalent (#684 review-fix 2) is the same move the RANGE branch\n // below already makes — enumerate the field's full indexed id\n // set via `orderedBy` and let the (now via-aware) post-filter in\n // `toArray()` decide, instead of throwing `IndexRequiredError`.\n if (clause.via.indexValue === undefined) {\n const entries = idx.orderedBy(clause.field, 'asc')\n if (entries) return entries.map(e => e.recordId)\n continue\n }\n const ids = idx.lookupEqual(clause.field, clause.via.indexValue)\n if (ids) return [...ids]\n continue\n }\n // #677: canonicalize the probe value BEFORE the lookup — same\n // \"resolve before lookupEqual\" shape as eager's `candidateRecords()`,\n // but through the narrower `canonicalizeIndexKey` seam, used only on\n // this NON-via path. `undefined` (no via coverage) falls back to the\n // raw clause value, unchanged from today.\n const probe = this.source.canonicalizeIndexKey?.(clause.field, clause.value) ?? clause.value\n const ids = idx.lookupEqual(clause.field, probe)\n if (ids) return [...ids]\n } else if (clause.op === 'in' && Array.isArray(clause.value)) {\n if (clause.via) {\n // Same #684 review-fix 2 fallback as the `==` branch above — a via\n // payload that declines to probe (e.g. money multi-mode) still\n // has a sound candidate superset via `orderedBy`.\n if (clause.via.indexValue === undefined) {\n const entries = idx.orderedBy(clause.field, 'asc')\n if (entries) return entries.map(e => e.recordId)\n continue\n }\n if (!Array.isArray(clause.via.indexValue)) continue\n const ids = idx.lookupIn(clause.field, clause.via.indexValue)\n if (ids) return [...ids]\n continue\n }\n const probes = clause.value.map(v => this.source.canonicalizeIndexKey?.(clause.field, v) ?? v)\n const ids = idx.lookupIn(clause.field, probes)\n if (ids) return [...ids]\n } else if (isRangeOp(clause.op)) {\n if (clause.via) {\n // #684: a Via-covered range clause (e.g. money) cannot trust\n // `lookupRange` — it compares the ORIGINAL TYPED stored value,\n // wrong space for a binding whose stored form isn't directly\n // orderable that way (money: BigInt-exact scaled-int compare).\n // No binding emits a range `indexProbe` either (see\n // `moneyIndexProbe`), so there's no sound bucket lookup at all.\n // Enumerate the field's full indexed id set via `orderedBy` — the\n // same superset a scan would consider, just scoped to indexed ids\n // — and let the (now via-aware) post-filter in `toArray()` apply\n // the correct scaled-space comparison. Replaces the old\n // \"always trust lookupRange, no scan fallback\" gap.\n const entries = idx.orderedBy(clause.field, 'asc')\n if (entries) return entries.map(e => e.recordId)\n continue\n }\n // range predicates on a NON-via indexed field dispatch through\n // `lookupRange`, which compares on the original typed value (no\n // numeric-lexicographic landmines).\n const ids = idx.lookupRange(clause.field, clause.op, clause.value)\n if (ids) return [...ids]\n }\n }\n\n // No equality/range driver — try to scope via orderBy.\n if (this.plan.orderBy.length > 0) {\n const primary = this.plan.orderBy[0]!\n const entries = idx.orderedBy(primary.field, primary.direction)\n if (entries) return entries.map(e => e.recordId)\n }\n\n return null\n }\n}\n\n/**\n * True if the given field name is covered by either a single-field\n * index or appears as a component of a declared composite index.\n * Composite coverage is sufficient for the missing-field check because\n * composite writes also maintain the in-memory mirror — the range /\n * orderBy / single-equality lookup paths fall through to decrypted\n * candidates that still get post-filtered by the composite clause.\n */\nfunction isFieldIndexed(field: string, idx: PersistedCollectionIndex): boolean {\n if (idx.has(field)) return true\n for (const def of idx.definitions()) {\n if (def.kind === 'composite' && def.fields.includes(field)) return true\n }\n return false\n}\n\nfunction isRangeOp(op: Operator): op is '<' | '<=' | '>' | '>=' | 'between' {\n return op === '<' || op === '<=' || op === '>' || op === '>=' || op === 'between'\n}\n\nfunction collectTouchedFields(plan: LazyPlan): string[] {\n const seen = new Set<string>()\n for (const c of plan.clauses) seen.add(c.field)\n for (const o of plan.orderBy) seen.add(o.field)\n return [...seen]\n}\n\nfunction matchesAll(record: unknown, clauses: readonly Clause[]): boolean {\n for (const c of clauses) {\n if (!evaluateClause(record, c)) return false\n }\n return true\n}\n\nfunction sortRecords<T>(records: T[], orderBy: readonly LazyOrderBy[], via?: ViaPipeline): T[] {\n return [...records].sort((a, b) => {\n for (const { field, direction } of orderBy) {\n const av = readPath(a, field)\n const bv = readPath(b, field)\n // #695: a Via-covered field (e.g. money) may store a representation\n // the generic comparator would order wrong — ask the pipeline for an\n // exact ordering first, same as eager's `sortRecords` in\n // `kernel/query/builder.ts`. `undefined` (no via, or the field isn't\n // covered) falls back to the generic comparator, unchanged from today.\n const viaCmp = via?.compareForOrder(field, av, bv)\n const cmp = viaCmp !== undefined ? viaCmp : compareValues(av, bv)\n if (cmp !== 0) return direction === 'asc' ? cmp : -cmp\n }\n return 0\n })\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a === undefined || a === null) return b === undefined || b === null ? 0 : 1\n if (b === undefined || b === null) return -1\n if (typeof a === 'number' && typeof b === 'number') return a - b\n if (typeof a === 'string' && typeof b === 'string') return a < b ? -1 : a > b ? 1 : 0\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime()\n return 0\n}\n"],"mappings":"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","names":["ids"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AttestationNotEnabledError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6SYOHUHM.js";
|
|
4
4
|
|
|
5
5
|
// src/with-audit/attestation/strategy.ts
|
|
6
6
|
var NO_ATTESTATION = {
|
|
@@ -27,4 +27,4 @@ var NO_ATTESTATION = {
|
|
|
27
27
|
export {
|
|
28
28
|
NO_ATTESTATION
|
|
29
29
|
};
|
|
30
|
-
//# sourceMappingURL=chunk-
|
|
30
|
+
//# sourceMappingURL=chunk-GTNRMJYH.js.map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalize,
|
|
3
3
|
derivePersistedSchema
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SEU6QHH4.js";
|
|
5
5
|
import {
|
|
6
6
|
loadPersistedSchemaEntry,
|
|
7
7
|
savePersistedSchema
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-32467U5J.js";
|
|
9
9
|
import {
|
|
10
10
|
ConflictError
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-6SYOHUHM.js";
|
|
12
12
|
|
|
13
13
|
// src/with-shape/schema-update/delta.ts
|
|
14
14
|
function computeSchemaDelta(stored, fresh, collection) {
|
|
@@ -164,4 +164,4 @@ export {
|
|
|
164
164
|
persistClassifiedMarker,
|
|
165
165
|
persistSatelliteMarker
|
|
166
166
|
};
|
|
167
|
-
//# sourceMappingURL=chunk-
|
|
167
|
+
//# sourceMappingURL=chunk-H5GMROCB.js.map
|
|
@@ -1,152 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
canonicalGroupKey
|
|
3
|
+
} from "./chunk-XZHEOSQ2.js";
|
|
4
|
+
import {
|
|
5
|
+
reducerBuilder
|
|
6
|
+
} from "./chunk-CADUYBQD.js";
|
|
1
7
|
import {
|
|
2
8
|
readPath
|
|
3
9
|
} from "./chunk-IGJCYUT5.js";
|
|
4
10
|
import {
|
|
5
11
|
applyI18nLocale
|
|
6
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-JOCMXSR2.js";
|
|
7
13
|
import {
|
|
8
14
|
viaBinder
|
|
9
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-ZUDAK436.js";
|
|
10
16
|
import {
|
|
11
17
|
GroupCardinalityError
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
|
|
14
|
-
// src/with-lookup/aggregate/reducers.ts
|
|
15
|
-
function count(opts) {
|
|
16
|
-
const _seed = opts?.seed;
|
|
17
|
-
void _seed;
|
|
18
|
-
return {
|
|
19
|
-
op: "count",
|
|
20
|
-
init: () => 0,
|
|
21
|
-
step: (state) => state + 1,
|
|
22
|
-
remove: (state) => state - 1,
|
|
23
|
-
finalize: (state) => state,
|
|
24
|
-
merge: (a, b) => a + b
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
function sum(field, opts) {
|
|
28
|
-
const _seed = opts?.seed;
|
|
29
|
-
void _seed;
|
|
30
|
-
return {
|
|
31
|
-
op: "sum",
|
|
32
|
-
field,
|
|
33
|
-
// Money-only metadata, read by `wrapMoneyReducers`. No effect on a
|
|
34
|
-
// generic numeric sum.
|
|
35
|
-
...opts?.convertTo !== void 0 ? { convertTo: opts.convertTo } : {},
|
|
36
|
-
...opts?.fx !== void 0 ? { fx: opts.fx } : {},
|
|
37
|
-
init: () => 0,
|
|
38
|
-
step: (state, record) => state + readNumber(record, field),
|
|
39
|
-
remove: (state, record) => state - readNumber(record, field),
|
|
40
|
-
finalize: (state) => state,
|
|
41
|
-
merge: (a, b) => a + b
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
function avg(field, opts) {
|
|
45
|
-
const _seed = opts?.seed;
|
|
46
|
-
void _seed;
|
|
47
|
-
return {
|
|
48
|
-
op: "avg",
|
|
49
|
-
field,
|
|
50
|
-
init: () => ({ sum: 0, count: 0 }),
|
|
51
|
-
step: (state, record) => ({
|
|
52
|
-
sum: state.sum + readNumber(record, field),
|
|
53
|
-
count: state.count + 1
|
|
54
|
-
}),
|
|
55
|
-
remove: (state, record) => ({
|
|
56
|
-
sum: state.sum - readNumber(record, field),
|
|
57
|
-
count: state.count - 1
|
|
58
|
-
}),
|
|
59
|
-
finalize: (state) => state.count === 0 ? null : state.sum / state.count,
|
|
60
|
-
merge: (a, b) => ({ sum: a.sum + b.sum, count: a.count + b.count })
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
function pushValue(state, value) {
|
|
64
|
-
return { values: [...state.values, value] };
|
|
65
|
-
}
|
|
66
|
-
function removeValue(state, value) {
|
|
67
|
-
const idx = state.values.indexOf(value);
|
|
68
|
-
if (idx < 0) return state;
|
|
69
|
-
const next = state.values.slice();
|
|
70
|
-
next.splice(idx, 1);
|
|
71
|
-
return { values: next };
|
|
72
|
-
}
|
|
73
|
-
function min(field, opts) {
|
|
74
|
-
const _seed = opts?.seed;
|
|
75
|
-
void _seed;
|
|
76
|
-
return {
|
|
77
|
-
op: "min",
|
|
78
|
-
field,
|
|
79
|
-
init: () => ({ values: [] }),
|
|
80
|
-
step: (state, record) => pushValue(state, readNumber(record, field)),
|
|
81
|
-
remove: (state, record) => removeValue(state, readNumber(record, field)),
|
|
82
|
-
finalize: (state) => {
|
|
83
|
-
if (state.values.length === 0) return null;
|
|
84
|
-
let out = state.values[0];
|
|
85
|
-
for (let i = 1; i < state.values.length; i++) {
|
|
86
|
-
const v = state.values[i];
|
|
87
|
-
if (v < out) out = v;
|
|
88
|
-
}
|
|
89
|
-
return out;
|
|
90
|
-
},
|
|
91
|
-
merge: (a, b) => ({ values: [...a.values, ...b.values] })
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
function max(field, opts) {
|
|
95
|
-
const _seed = opts?.seed;
|
|
96
|
-
void _seed;
|
|
97
|
-
return {
|
|
98
|
-
op: "max",
|
|
99
|
-
field,
|
|
100
|
-
init: () => ({ values: [] }),
|
|
101
|
-
step: (state, record) => pushValue(state, readNumber(record, field)),
|
|
102
|
-
remove: (state, record) => removeValue(state, readNumber(record, field)),
|
|
103
|
-
finalize: (state) => {
|
|
104
|
-
if (state.values.length === 0) return null;
|
|
105
|
-
let out = state.values[0];
|
|
106
|
-
for (let i = 1; i < state.values.length; i++) {
|
|
107
|
-
const v = state.values[i];
|
|
108
|
-
if (v > out) out = v;
|
|
109
|
-
}
|
|
110
|
-
return out;
|
|
111
|
-
},
|
|
112
|
-
merge: (a, b) => ({ values: [...a.values, ...b.values] })
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
function moneySum(field, opts) {
|
|
116
|
-
return sum(field, opts);
|
|
117
|
-
}
|
|
118
|
-
function moneyMin(field, opts) {
|
|
119
|
-
return min(field, opts);
|
|
120
|
-
}
|
|
121
|
-
function moneyMax(field, opts) {
|
|
122
|
-
return max(field, opts);
|
|
123
|
-
}
|
|
124
|
-
var reducerBuilder = {
|
|
125
|
-
count,
|
|
126
|
-
// The generic F parameter on sum/min/max in the interface is type-only; the
|
|
127
|
-
// standalone factories are plain `(field: string)` functions. With M=never the
|
|
128
|
-
// conditional return collapses to the numeric branch, matching the factories'
|
|
129
|
-
// return type, but TypeScript cannot verify that collapse for a generic method
|
|
130
|
-
// signature — so each factory is cast directly to the method's type.
|
|
131
|
-
sum,
|
|
132
|
-
avg,
|
|
133
|
-
min,
|
|
134
|
-
max,
|
|
135
|
-
moneySum,
|
|
136
|
-
moneyMin,
|
|
137
|
-
moneyMax
|
|
138
|
-
};
|
|
139
|
-
function readNumber(record, field) {
|
|
140
|
-
const value = readPath(record, field);
|
|
141
|
-
if (typeof value === "object" && value !== null && "amount" in value && "currency" in value) {
|
|
142
|
-
throw new Error(
|
|
143
|
-
`aggregate: field "${field}" holds a money value but was not money-aware \u2014 declare it in the collection's moneyFields so sum/min/max stay exact`
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
147
|
-
}
|
|
18
|
+
} from "./chunk-6SYOHUHM.js";
|
|
148
19
|
|
|
149
|
-
// src/with-lookup/
|
|
20
|
+
// src/with-lookup/reduce/reduction.ts
|
|
150
21
|
function reduceRecords(records, spec) {
|
|
151
22
|
const state = {};
|
|
152
23
|
for (const key of Object.keys(spec)) {
|
|
@@ -196,7 +67,7 @@ var LiveAggregationImpl = class {
|
|
|
196
67
|
try {
|
|
197
68
|
listener();
|
|
198
69
|
} catch (err) {
|
|
199
|
-
console.warn("[noy-db]
|
|
70
|
+
console.warn("[noy-db] LiveReduction listener threw:", err);
|
|
200
71
|
}
|
|
201
72
|
}
|
|
202
73
|
}
|
|
@@ -217,14 +88,14 @@ var LiveAggregationImpl = class {
|
|
|
217
88
|
try {
|
|
218
89
|
unsub();
|
|
219
90
|
} catch (err) {
|
|
220
|
-
console.warn("[noy-db]
|
|
91
|
+
console.warn("[noy-db] LiveReduction upstream unsubscribe threw:", err);
|
|
221
92
|
}
|
|
222
93
|
}
|
|
223
94
|
this.unsubscribes.length = 0;
|
|
224
95
|
this.listeners.clear();
|
|
225
96
|
}
|
|
226
97
|
};
|
|
227
|
-
var
|
|
98
|
+
var Reduction = class {
|
|
228
99
|
constructor(executeRecords, spec, upstreams) {
|
|
229
100
|
this.executeRecords = executeRecords;
|
|
230
101
|
this.spec = spec;
|
|
@@ -243,7 +114,7 @@ var Aggregation = class {
|
|
|
243
114
|
return reduceRecords(this.executeRecords(), this.spec);
|
|
244
115
|
}
|
|
245
116
|
/**
|
|
246
|
-
* Build a reactive `
|
|
117
|
+
* Build a reactive `LiveReduction<R>` that re-runs the reduction
|
|
247
118
|
* whenever any upstream source notifies of a change. The initial
|
|
248
119
|
* value is computed eagerly in the constructor, so consumers can
|
|
249
120
|
* read `live.value` immediately after calling `.live()`.
|
|
@@ -265,23 +136,11 @@ var Aggregation = class {
|
|
|
265
136
|
return new LiveAggregationImpl(recompute, this.upstreams);
|
|
266
137
|
}
|
|
267
138
|
};
|
|
268
|
-
function
|
|
139
|
+
function buildLiveReduction(recompute, upstreams) {
|
|
269
140
|
return new LiveAggregationImpl(recompute, upstreams);
|
|
270
141
|
}
|
|
271
142
|
|
|
272
|
-
// src/with-lookup/
|
|
273
|
-
function canonicalGroupKey(fields, row) {
|
|
274
|
-
const sorted = [...fields].sort();
|
|
275
|
-
const parts = [];
|
|
276
|
-
for (const name of sorted) {
|
|
277
|
-
const v = row[name];
|
|
278
|
-
const serialised = v === void 0 ? "undefined" : JSON.stringify(v);
|
|
279
|
-
parts.push(`${name}=${serialised}`);
|
|
280
|
-
}
|
|
281
|
-
return parts.join("|");
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// src/with-lookup/aggregate/groupby.ts
|
|
143
|
+
// src/with-lookup/reduce/groupby.ts
|
|
285
144
|
var GROUPBY_WARN_CARDINALITY = 1e4;
|
|
286
145
|
var GROUPBY_MAX_CARDINALITY = 1e5;
|
|
287
146
|
var warnedCardinalityFields = /* @__PURE__ */ new Set();
|
|
@@ -324,7 +183,7 @@ var GroupedQueryBase = class {
|
|
|
324
183
|
var GroupedQuery = class extends GroupedQueryBase {
|
|
325
184
|
aggregate(specOrBuild) {
|
|
326
185
|
const spec = typeof specOrBuild === "function" ? specOrBuild(reducerBuilder) : specOrBuild;
|
|
327
|
-
return new
|
|
186
|
+
return new GroupedReduction(
|
|
328
187
|
this.executeRecords,
|
|
329
188
|
this.fields,
|
|
330
189
|
this.wrapSpec(spec),
|
|
@@ -336,7 +195,7 @@ var GroupedQuery = class extends GroupedQueryBase {
|
|
|
336
195
|
var GroupedQueryN = class extends GroupedQueryBase {
|
|
337
196
|
aggregate(specOrBuild) {
|
|
338
197
|
const spec = typeof specOrBuild === "function" ? specOrBuild(reducerBuilder) : specOrBuild;
|
|
339
|
-
return new
|
|
198
|
+
return new GroupedReduction(
|
|
340
199
|
this.executeRecords,
|
|
341
200
|
this.fields,
|
|
342
201
|
this.wrapSpec(spec),
|
|
@@ -401,7 +260,7 @@ function groupAndReduce(records, fieldOrFields, spec, moneyFields) {
|
|
|
401
260
|
}
|
|
402
261
|
return out;
|
|
403
262
|
}
|
|
404
|
-
var
|
|
263
|
+
var GroupedReduction = class {
|
|
405
264
|
constructor(executeRecords, fields, spec, upstreams, dictLabelResolver) {
|
|
406
265
|
this.executeRecords = executeRecords;
|
|
407
266
|
this.spec = spec;
|
|
@@ -469,10 +328,10 @@ var GroupedAggregation = class {
|
|
|
469
328
|
);
|
|
470
329
|
}
|
|
471
330
|
/**
|
|
472
|
-
* Build a reactive `
|
|
331
|
+
* Build a reactive `LiveReduction<R[]>` that re-runs the full
|
|
473
332
|
* group-and-reduce pipeline whenever any upstream source notifies
|
|
474
333
|
* of a change. Same error-isolation and idempotent-stop contract
|
|
475
|
-
* as `
|
|
334
|
+
* as `Reduction.live()` — the implementation delegates to the
|
|
476
335
|
* same `LiveAggregationImpl` class by threading a fresh
|
|
477
336
|
* recompute closure through the existing constructor.
|
|
478
337
|
*
|
|
@@ -486,30 +345,20 @@ var GroupedAggregation = class {
|
|
|
486
345
|
*/
|
|
487
346
|
live() {
|
|
488
347
|
const recompute = () => groupAndReduce(this.executeRecords(), this.fields, this.spec);
|
|
489
|
-
return
|
|
348
|
+
return buildLiveReduction(recompute, this.upstreams);
|
|
490
349
|
}
|
|
491
350
|
};
|
|
492
351
|
|
|
493
352
|
export {
|
|
494
|
-
count,
|
|
495
|
-
sum,
|
|
496
|
-
avg,
|
|
497
|
-
min,
|
|
498
|
-
max,
|
|
499
|
-
moneySum,
|
|
500
|
-
moneyMin,
|
|
501
|
-
moneyMax,
|
|
502
|
-
reducerBuilder,
|
|
503
353
|
reduceRecords,
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
canonicalGroupKey,
|
|
354
|
+
Reduction,
|
|
355
|
+
buildLiveReduction,
|
|
507
356
|
GROUPBY_WARN_CARDINALITY,
|
|
508
357
|
GROUPBY_MAX_CARDINALITY,
|
|
509
358
|
resetGroupByWarnings,
|
|
510
359
|
GroupedQuery,
|
|
511
360
|
GroupedQueryN,
|
|
512
361
|
groupAndReduce,
|
|
513
|
-
|
|
362
|
+
GroupedReduction
|
|
514
363
|
};
|
|
515
|
-
//# sourceMappingURL=chunk-
|
|
364
|
+
//# sourceMappingURL=chunk-HA7P3BTB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-lookup/reduce/reduction.ts","../src/with-lookup/reduce/groupby.ts"],"sourcesContent":["/**\n * Aggregate execution — the runtime behind `Query.aggregate()`.\n *\n * takes an `ReduceSpec` (a record of named reducers\n * built from `reducers.ts`) and runs every reducer over the records\n * produced by the underlying query. Two terminal surfaces:\n *\n * - `.run(): R` — synchronous one-shot reduction. Matches the\n * existing `Query.toArray()` / `.first()` / `.count()` style.\n * - `.live(): LiveReduction<R>` — reactive primitive that\n * re-runs the reduction whenever the query's source notifies of\n * a change. uses naive full re-run; incremental delta\n * maintenance is admitted by the reducer protocol (`remove()`)\n * but not wired to the executor yet — a follow-up optimization\n * can switch from full re-run to delta-based without breaking\n * the public API. Consumers get correct, reactive values today.\n *\n * The `Reduction<R>` wrapper is deliberately tiny — it exists so\n * `.aggregate(spec)` can be chained with either `.run()` or `.live()`\n * without the builder needing two separate terminal methods. It\n * holds the closure over the query execution (produces the current\n * matching record set) and the spec, and stitches them together in\n * either mode.\n *\n * This file depends ONLY on `reducers.ts` — it has no knowledge of\n * the `Query` class. Tests can therefore exercise the reduction\n * surface with plain record arrays, without spinning up a Collection.\n */\n\nimport type { Reducer } from './reducers.js'\n\n/**\n * A named set of reducers, keyed by output field name. Each key\n * becomes a field on the aggregated result.\n *\n * ```ts\n * const spec = {\n * total: sum('amount'),\n * n: count(),\n * avgAmount: avg('amount'),\n * }\n * ```\n */\nexport type ReduceSpec = Readonly<Record<string, Reducer<unknown, unknown>>>\n\n/**\n * Map an `ReduceSpec` to its reduced result shape — each key\n * carries the finalized result type from its reducer. A spec built\n * from `{ total: sum('amount'), n: count() }` yields a result of\n * `{ total: number, n: number }`.\n *\n * This uses a mapped type with a conditional to extract `R` from\n * each `Reducer<R, _>`. The `infer` captures the user-visible result\n * type, discarding the internal state type `S`.\n */\nexport type ReduceResult<Spec extends ReduceSpec> = {\n [K in keyof Spec]: Spec[K] extends Reducer<infer R, unknown> ? R : never\n}\n\n/**\n * Pure reduction over a record array. Runs every reducer's\n * `init → step* → finalize` pipeline exactly once over the records.\n *\n * Called by `Reduction.run()` and by the live-mode refresh path.\n * Exported for tests and for future `scan().aggregate()` reuse\n * — the streaming path will call the same reducer protocol with a\n * per-page loop instead of a single array.\n */\nexport function reduceRecords<Spec extends ReduceSpec>(\n records: readonly unknown[],\n spec: Spec,\n): ReduceResult<Spec> {\n // Per-slot state, keyed by the spec's output field name.\n const state: Record<string, unknown> = {}\n for (const key of Object.keys(spec)) {\n state[key] = spec[key]!.init()\n }\n for (const record of records) {\n for (const key of Object.keys(spec)) {\n state[key] = spec[key]!.step(state[key], record)\n }\n }\n const result: Record<string, unknown> = {}\n for (const key of Object.keys(spec)) {\n result[key] = spec[key]!.finalize(state[key])\n }\n return result as ReduceResult<Spec>\n}\n\n/**\n * A minimal reactive primitive for reduction results.\n *\n * Same spirit as the `LiveQuery` in : frame-agnostic, a plain\n * object with `value` / `error` fields and a `subscribe(cb)`\n * notification channel that Vue / React / Solid adapters wrap in\n * their own primitive. Intentionally NOT a Promise — reductions\n * have a well-defined \"current value\" at every instant, and the\n * reactive consumer wants to read that value synchronously.\n *\n * Error semantics mirror `LiveQuery`: if a re-run throws, the\n * previous successful `value` is preserved and the error is stored\n * in `error` so consumers can render an error state without losing\n * the last-known-good result. The throw does NOT propagate out of\n * the source's change handler (which would tear down the upstream\n * emitter).\n *\n * `stop()` tears down the upstream subscription. It is idempotent —\n * calling it multiple times is safe — and subscribe calls after\n * stop are no-ops (they immediately return a no-op unsubscribe).\n * Always call `stop()` when done; Vue's `onUnmounted` is the\n * canonical place. Raw consumers must do it themselves.\n */\nexport interface LiveReduction<R> {\n /** Current reduced value. Undefined only if the first compute threw. */\n readonly value: R | undefined\n /** Last execution error, if any. Cleared on the next successful run. */\n readonly error: unknown\n /** Notify on every recomputation (success or error). Returns unsubscribe. */\n subscribe(cb: () => void): () => void\n /** Tear down the upstream subscription. Idempotent. */\n stop(): void\n}\n\n/**\n * Upstream change-notification hook for live reduction.\n *\n * Matches the shape that `QuerySource.subscribe` already uses — a\n * single method that accepts a callback and returns an unsubscribe\n * function. The `Reduction` wrapper collects upstreams from the\n * query's source and wires them into a single re-run trigger.\n */\nexport interface ReductionUpstream {\n subscribe(cb: () => void): () => void\n}\n\n/**\n * Internal implementation of `LiveReduction`. Not exported —\n * consumers get the interface only. The class wraps a `recompute`\n * closure (which runs the full reduction and returns the new value)\n * and a list of upstreams (sources whose changes should trigger a\n * re-run).\n *\n * Error isolation: if an individual listener callback throws, the\n * other listeners still fire and the error is logged to the warn\n * channel. This matches `LiveQuery` from and keeps one misbehaving\n * consumer from tearing down the whole live reduction.\n */\nclass LiveAggregationImpl<R> implements LiveReduction<R> {\n public value: R | undefined\n public error: unknown\n private readonly listeners = new Set<() => void>()\n private readonly unsubscribes: Array<() => void> = []\n private stopped = false\n\n constructor(\n private readonly recompute: () => R,\n upstreams: readonly ReductionUpstream[],\n ) {\n // Initial computation — surface any error through the `error`\n // field rather than letting the constructor throw, so consumers\n // can always construct a LiveReduction and check its state\n // afterwards. Throwing from a constructor would force every\n // caller to wrap in try/catch, which is the opposite of the\n // \"reactive value with error state\" ergonomics we want.\n try {\n this.value = recompute()\n this.error = undefined\n } catch (err) {\n this.value = undefined\n this.error = err\n }\n\n // Wire up upstream subscriptions. Each one triggers a full\n // recomputation; we don't attempt incremental updates in.\n for (const upstream of upstreams) {\n const unsub = upstream.subscribe(() => this.refresh())\n this.unsubscribes.push(unsub)\n }\n }\n\n private refresh(): void {\n if (this.stopped) return\n try {\n this.value = this.recompute()\n this.error = undefined\n } catch (err) {\n // Preserve the previous successful value — consumers render an\n // error state using `error` without losing the last-known-good\n // number. This matches LiveQuery's error-preservation contract.\n this.error = err\n }\n for (const listener of this.listeners) {\n try {\n listener()\n } catch (err) {\n // Isolate listener errors so one bad consumer can't tear\n // down every other subscriber on the same reduction.\n console.warn('[noy-db] LiveReduction listener threw:', err)\n }\n }\n }\n\n subscribe(cb: () => void): () => void {\n if (this.stopped) {\n // No-op after stop. Returning a harmless unsubscribe lets\n // consumers use the same teardown pattern unconditionally.\n return () => {}\n }\n this.listeners.add(cb)\n return () => {\n this.listeners.delete(cb)\n }\n }\n\n stop(): void {\n if (this.stopped) return\n this.stopped = true\n for (const unsub of this.unsubscribes) {\n try {\n unsub()\n } catch (err) {\n console.warn('[noy-db] LiveReduction upstream unsubscribe threw:', err)\n }\n }\n this.unsubscribes.length = 0\n this.listeners.clear()\n }\n}\n\n/**\n * Chainable wrapper returned by `Query.aggregate(spec)`. Holds the\n * execute-records closure and the spec; terminal methods (`run`,\n * `live`) stitch them together in either mode.\n *\n * Why a wrapper instead of two terminal methods on `Query` directly?\n *\n * The `.aggregate(spec)` call is where the spec is bound — both\n * `.run()` and `.live()` need the same spec, and the consumer's\n * fluent style is `query.where(...).aggregate(spec).run()` or\n * `.aggregate(spec).live()`. Wrapping lets the spec be named once\n * and reused for either terminal, and keeps the `Query` class\n * from growing a pair of near-duplicate method overloads\n * (`reduceRun` / `reduceLive`) that would be harder to\n * discover.\n */\nexport class Reduction<R> {\n constructor(\n private readonly executeRecords: () => readonly unknown[],\n private readonly spec: ReduceSpec,\n private readonly upstreams: readonly ReductionUpstream[],\n ) {}\n\n /**\n * Execute the query and reduce the results synchronously.\n * Returns the reduced shape matching the spec — e.g. a spec of\n * `{ total: sum('amount'), n: count() }` returns\n * `{ total: number, n: number }`.\n */\n run(): R {\n return reduceRecords(this.executeRecords(), this.spec) as unknown as R\n }\n\n /**\n * Build a reactive `LiveReduction<R>` that re-runs the reduction\n * whenever any upstream source notifies of a change. The initial\n * value is computed eagerly in the constructor, so consumers can\n * read `live.value` immediately after calling `.live()`.\n *\n * Always call `live.stop()` when finished — it tears down the\n * upstream subscriptions. Vue's `onUnmounted` is the canonical\n * place.\n *\n * **Implementation note:** every upstream change triggers a full\n * re-reduction. Incremental maintenance (O(1) per delta for\n * sum/count/avg via the reducer protocol's `remove()` method) is a\n * planned follow-up optimization — the protocol already supports\n * it, but the executor doesn't drive it yet. Consumers get\n * correct, reactive values today; future PRs can switch to\n * delta-based maintenance without changing this API.\n */\n live(): LiveReduction<R> {\n const recompute = (): R =>\n reduceRecords(this.executeRecords(), this.spec) as unknown as R\n return new LiveAggregationImpl<R>(recompute, this.upstreams)\n }\n}\n\n/**\n * Build a `LiveReduction<V>` from a recompute closure and a list\n * of upstreams. Exposed so sibling files in the query DSL\n * (currently `groupby.ts`) can reuse the reactive primitive\n * without reaching into `LiveAggregationImpl` directly. This keeps\n * the implementation class private while still allowing planned\n * composition with `.groupBy().aggregate().live()`.\n */\nexport function buildLiveReduction<V>(\n recompute: () => V,\n upstreams: readonly ReductionUpstream[],\n): LiveReduction<V> {\n return new LiveAggregationImpl<V>(recompute, upstreams)\n}\n","/**\n * Query DSL `.groupBy()` —.\n *\n * Chains after `.where()` / `.filter()` / `.or()` / `.and()` on a\n * Query and before a reducer spec, so consumers can compute\n * per-bucket aggregates without folding in userland:\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 * Execution pipeline:\n *\n * 1. Run the query's where/filter clauses (same candidate /\n * filter pipeline as `.aggregate()` directly on Query).\n * 2. Partition the matching records into buckets keyed by\n * `readPath(record, field)`. JS `Map` preserves insertion\n * order, so the first-seen key for a bucket determines its\n * position in the result array — consumers who want a\n * specific ordering should `.sort()` downstream.\n * 3. Enforce cardinality: warn once per field at 10% of the cap\n * (10_000 buckets), throw `GroupCardinalityError` at 100% of\n * the cap (100_000 buckets).\n * 4. For each bucket, build a per-group reducer state and\n * step every record in the bucket through it.\n * 5. Emit one result row per bucket, shaped as\n * `{ [field]: key, ...reduced }`.\n *\n * **Null / undefined keys:** `Map` distinguishes `null` from\n * `undefined`, so records with a missing group field get their own\n * bucket, and records with an explicit `null` value get a separate\n * bucket from that. Consumers who want them merged can coalesce\n * upstream with `.filter()`.\n *\n * **Live mode:** `.groupBy().aggregate().live()` re-runs the full\n * grouping pipeline on every source change. Per-bucket incremental\n * delta maintenance is a future optimization — the reducer\n * protocol's `remove()` hook admits it, but ships naive\n * re-grouping for simplicity.\n *\n * **Type-level stable-key narrowing:** when\n * `dictKey` lands, `groupBy<DictField>()` will narrow the group key\n * type to the stable dictionary key rather than the resolved locale\n * label. That prevents grouping by the locale-resolved label,\n * which would produce different buckets per reader. types the\n * key as `unknown` at the result shape; the dictKey narrowing\n * layers on top without an API break.\n *\n * Partition-awareness seam: when partitioned collections land,\n * per-partition grouping will need to merge sub-results across\n * partitions. The reducer protocol's `{ seed }` parameter\n * (already plumbed through in `reducers.ts`) is the mechanism —\n * groupBy doesn't need its own seam for the moment, because it\n * delegates to the reducer protocol for all per-bucket state.\n */\n\nimport { readPath } from '../../kernel/query/predicate.js'\nimport type {\n ReduceSpec,\n ReduceResult,\n ReductionUpstream,\n LiveReduction,\n} from './reduction.js'\nimport { buildLiveReduction } from './reduction.js'\nimport type { ReducerBuilder } from './reducers.js'\nimport { reducerBuilder } from './reducers.js'\nimport { canonicalGroupKey } from './canonical-key.js'\nimport { GroupCardinalityError } from '../../kernel/errors.js'\nimport type { MoneyDescriptor } from '../../via/money/descriptor.js'\nimport type { ViaPipeline } from '../../kernel/via/pipeline.js'\nimport { viaBinder } from '../../kernel/via/index.js'\nimport { applyI18nLocale, type I18nTextDescriptor } from '../../via/i18n/core.js'\n\n/**\n * Cardinality thresholds for `.groupBy()`. The warn threshold gives\n * consumers a heads-up before the hard error; the cap is a fixed\n * constant in (not overridable). A `{ maxGroups }` override\n * can be added later without a break if a real consumer asks.\n */\nexport const GROUPBY_WARN_CARDINALITY = 10_000\nexport const GROUPBY_MAX_CARDINALITY = 100_000\n\n/**\n * One-shot warning dedup per-field-set — reactive dashboards\n * re-executing the same grouped query should produce the warning\n * once, not once per re-fire. Keyed on the sorted JSON of grouping\n * field names so `.groupBy('a', 'b')` and `.groupBy('b', 'a')`\n * share the same dedup slot (their result tuples are isomorphic).\n */\nconst warnedCardinalityFields = new Set<string>()\nfunction warnCardinalityApproaching(\n fields: readonly string[],\n observed: number,\n): void {\n const key = JSON.stringify([...fields].sort())\n if (warnedCardinalityFields.has(key)) return\n warnedCardinalityFields.add(key)\n const label = `[${fields.join(', ')}]`\n console.warn(\n `[noy-db] .groupBy(${label}) produced ${observed} distinct groups, ` +\n `${Math.round((observed / GROUPBY_MAX_CARDINALITY) * 100)}% of the ` +\n `${GROUPBY_MAX_CARDINALITY}-group ceiling. Narrow the query with ` +\n `.where() before grouping, or switch to a lower-cardinality field.`,\n )\n}\n\n/**\n * Test-only: clear the per-field cardinality warning dedup between\n * tests. Production code never calls this — matching the\n * `resetJoinWarnings` pattern in `join.ts`.\n */\nexport function resetGroupByWarnings(): void {\n warnedCardinalityFields.clear()\n}\n\n/**\n * Result row shape for a grouped reduction. Each row carries the\n * group key value under the grouping field name plus every reducer\n * output from the spec.\n *\n * types the group key as `unknown` at the result shape — the\n * runtime read via `readPath` can return any value, and narrowing\n * to a specific type would require the caller to assert at the\n * call site. `dictKey` narrowing layers on top of this by\n * adding an overload that constrains `F` when the grouping field\n * is a `dictKey`.\n */\nexport type GroupedRow<F extends string, R> = { [K in F]: unknown } & R\n\n/**\n * Multi-key variant — result-row shape for variadic\n * `.groupBy(...fields)`. Every grouped field name appears on the row\n * (typed as `unknown` for the same reason as `GroupedRow`), plus the\n * reducer outputs from the spec.\n */\nexport type GroupedRowN<F extends readonly string[], R> =\n { [K in F[number]]: unknown } & R\n\n/**\n * Shared base class for the chainable grouped-query wrappers. Holds\n * the constructor + protected fields that both single-key\n * `GroupedQuery<T, F>` and variadic `GroupedQueryN<T, F>` need; each\n * subclass only overrides `aggregate()` with its own result-row\n * generic.\n *\n * Not exported — implementation detail. Adding `.having()` /\n * `.live()` / `.orderByGroup()` etc. in the future lands here once\n * and both subclasses pick it up automatically.\n *\n * @internal\n */\nabstract class GroupedQueryBase {\n /**\n * Field set this grouped query buckets on. Stored in declaration\n * order — the same order is preserved on every result row by\n * `groupAndReduce`. For the single-field constructor, this is\n * `[field]`.\n */\n protected readonly fields: readonly string[]\n\n constructor(\n protected readonly executeRecords: () => readonly unknown[],\n fieldOrFields: string | readonly string[],\n protected readonly upstreams: readonly ReductionUpstream[],\n /**\n * Optional dict label resolver attached by the query builder when\n * the grouping field is a dictKey. Variadic groupings always pass\n * `undefined` — `<field>Label` projection has no meaningful shape\n * for composite keys.\n */\n protected readonly dictLabelResolver?: (\n key: string,\n locale: string,\n fallback?: string | readonly string[],\n ) => Promise<string | undefined>,\n /**\n * The backing collection's compiled Via pipeline — used to rewrite\n * `sum`/`min`/`max` over Via-covered fields (e.g. money) into exact\n * BigInt reducers when `.aggregate(spec)` is terminated.\n */\n protected readonly via?: ViaPipeline,\n ) {\n this.fields =\n typeof fieldOrFields === 'string' ? [fieldOrFields] : [...fieldOrFields]\n }\n\n /** Apply Via-aware reducer rewriting (e.g. money) when the source declares one. */\n protected wrapSpec<Spec extends ReduceSpec>(spec: Spec): Spec {\n return this.via ? this.via.wrapReducers(spec) : spec\n }\n}\n\n/**\n * Chainable wrapper returned by `Query.groupBy(field)`. Terminates\n * with `.aggregate(spec)` which returns a `GroupedReduction`.\n *\n * Kept minimal — the only operation on a grouped query is\n * reduction. Ordering, limiting, and further filtering belong on\n * the underlying `Query` before `.groupBy()` is called; applying\n * them post-group would be a different operation (`having` /\n * `groupOrderBy`), out of scope for.\n */\nexport class GroupedQuery<T, F extends string, S extends keyof T = never, M extends keyof T & string = never> extends GroupedQueryBase {\n /**\n * Build a grouped reduction. Returns a `GroupedReduction`\n * with `.run()`, `.runAsync()`, and `.live()` terminals — same shape\n * as the non-grouped `.aggregate()` wrapper, just with an array\n * result (one row per bucket) instead of a single reduced object.\n *\n * The builder overload `aggregate(b => spec)` types `b` as\n * `ReducerBuilder<T, S, M>`, so field-taking reducers (`sum`, `avg`,\n * `min`, `max`) refuse any field listed in the collection's\n * `sensitive` option at compile time, and `sum`/`min`/`max` over a\n * declared `moneyFields` (`M`) member return a `MoneyString`. The\n * bare-spec overload is preserved for backward compatibility.\n */\n aggregate<Spec extends ReduceSpec>(spec: Spec): GroupedReduction<GroupedRow<F, ReduceResult<Spec>>>\n aggregate<Spec extends ReduceSpec>(build: (b: ReducerBuilder<T, S, M>) => Spec): GroupedReduction<GroupedRow<F, ReduceResult<Spec>>>\n aggregate<Spec extends ReduceSpec>(\n specOrBuild: Spec | ((b: ReducerBuilder<T, S, M>) => Spec),\n ): GroupedReduction<GroupedRow<F, ReduceResult<Spec>>> {\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 : specOrBuild\n // T is phantom on the wrapper so consumers can still see the\n // source row type on hover. Reference it to keep lint quiet.\n void undefined as T | undefined\n return new GroupedReduction<GroupedRow<F, ReduceResult<Spec>>>(\n this.executeRecords,\n this.fields,\n this.wrapSpec(spec),\n this.upstreams,\n this.dictLabelResolver,\n )\n }\n}\n\n/**\n * Variadic-keyed sibling of `GroupedQuery<T, F>`. Constructed by the\n * multi-arg `Query.groupBy(...fields)` overload. The runtime shape is\n * identical — only the type-level result-row narrowing differs.\n */\nexport class GroupedQueryN<T, F extends readonly string[], S extends keyof T = never, M extends keyof T & string = never> extends GroupedQueryBase {\n aggregate<Spec extends ReduceSpec>(spec: Spec): GroupedReduction<GroupedRowN<F, ReduceResult<Spec>>>\n aggregate<Spec extends ReduceSpec>(build: (b: ReducerBuilder<T, S, M>) => Spec): GroupedReduction<GroupedRowN<F, ReduceResult<Spec>>>\n aggregate<Spec extends ReduceSpec>(\n specOrBuild: Spec | ((b: ReducerBuilder<T, S, M>) => Spec),\n ): GroupedReduction<GroupedRowN<F, ReduceResult<Spec>>> {\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 : specOrBuild\n void undefined as T | undefined\n return new GroupedReduction<GroupedRowN<F, ReduceResult<Spec>>>(\n this.executeRecords,\n this.fields,\n this.wrapSpec(spec),\n this.upstreams,\n this.dictLabelResolver,\n )\n }\n}\n\n/**\n * Execute the group-and-reduce pipeline. Pure function over a\n * record array and a spec — shared by `GroupedReduction.run()`\n * and the live-mode refresh path. Exported for tests and for any\n * future `scan().groupBy().aggregate()` reuse.\n *\n * Enforces the cardinality cap incrementally during the partition\n * loop, so a runaway grouping throws at the moment the 100_001st\n * bucket would be created — the consumer doesn't have to wait for\n * the full partition to materialize before the error fires.\n */\nexport function groupAndReduce<R>(\n records: readonly unknown[],\n fieldOrFields: string | readonly string[],\n spec: ReduceSpec,\n moneyFields?: Record<string, MoneyDescriptor>,\n): R[] {\n const fields: readonly string[] =\n typeof fieldOrFields === 'string' ? [fieldOrFields] : fieldOrFields\n if (fields.length === 0) {\n throw new Error('.groupBy() requires at least one field')\n }\n\n // Money-aware reduction: when the caller declares money descriptors\n // for output/intermediate fields, rewrite any `sum`/`min`/`max` over\n // them into exact BigInt reducers before bucketing. Omitted → spec\n // passes through unchanged (backward compatible). The chainable\n // `GroupedQuery` path already wraps upstream via `wrapSpec`; this\n // covers direct `groupAndReduce` callers (UNION-form MVs) that have\n // no Query wrapper to do it. Builds a transient money binding via the\n // kernel's Via port rather than a Query-attached pipeline — `moneyFields`\n // here is the MV spec's OWN descriptor map, not a collection's.\n if (moneyFields) {\n spec = viaBinder('money')({ moneyFields }).wrapReducers!(spec) as ReduceSpec\n }\n\n // Bucket value is { keyValues, records } so the output row can stamp\n // every grouped field in DECLARATION ORDER. Map preserves insertion\n // order natively (ES2015), so first-seen keys determine ordering.\n interface Bucket {\n keyValues: Record<string, unknown>\n records: unknown[]\n }\n const buckets = new Map<string, Bucket>()\n // Field-label string for error messages — matches the variadic\n // surface (`[a, b]` for multi-key, `\"k\"` for single-key back-compat).\n const fieldLabel = fields.length === 1 ? fields[0]! : `[${fields.join(', ')}]`\n\n for (const record of records) {\n // Read each field's value into a row object, then canonicalise.\n const keyValues: Record<string, unknown> = {}\n for (const f of fields) {\n keyValues[f] = readPath(record, f)\n }\n const dedupKey = canonicalGroupKey(fields, keyValues)\n let bucket = buckets.get(dedupKey)\n if (bucket === undefined) {\n if (buckets.size >= GROUPBY_MAX_CARDINALITY) {\n throw new GroupCardinalityError(\n fieldLabel,\n buckets.size + 1,\n GROUPBY_MAX_CARDINALITY,\n )\n }\n bucket = { keyValues, records: [] }\n buckets.set(dedupKey, bucket)\n }\n bucket.records.push(record)\n }\n\n if (buckets.size >= GROUPBY_WARN_CARDINALITY) {\n warnCardinalityApproaching(fields, buckets.size)\n }\n\n // Reduce each bucket through the spec. Same init/step/finalize\n // pipeline as `reduceRecords` in aggregate.ts, but one state per\n // bucket. Inlining the loop here keeps the per-bucket path tight\n // — calling `reduceRecords` per bucket would recompute\n // `Object.keys(spec)` once per bucket unnecessarily.\n const reducerKeys = Object.keys(spec)\n const out: R[] = []\n for (const bucket of buckets.values()) {\n const state: Record<string, unknown> = {}\n for (const rk of reducerKeys) {\n state[rk] = spec[rk]!.init()\n }\n for (const record of bucket.records) {\n for (const rk of reducerKeys) {\n state[rk] = spec[rk]!.step(state[rk], record)\n }\n }\n // Stamp grouped fields FIRST, in declaration order — this is\n // tested via `Object.keys(row).slice(0, fields.length)`.\n const row: Record<string, unknown> = {}\n for (const f of fields) {\n row[f] = bucket.keyValues[f]\n }\n for (const rk of reducerKeys) {\n row[rk] = spec[rk]!.finalize(state[rk])\n }\n out.push(row as unknown as R)\n }\n return out\n}\n\n/**\n * Grouped reduction wrapper — the `.groupBy(field).aggregate(spec)`\n * terminal. Shape mirrors `Reduction<R>` from aggregate.ts: two\n * terminals (`.run()` and `.live()`), spec bound at construction\n * time, upstreams collected for live mode.\n *\n * The generic `R` is the per-row result shape (i.e. a single\n * grouped row), and the terminals return `R[]` — one row per\n * bucket.\n */\nexport class GroupedReduction<R> {\n private readonly fields: readonly string[]\n\n constructor(\n private readonly executeRecords: () => readonly unknown[],\n fields: string | readonly string[],\n private readonly spec: ReduceSpec,\n private readonly upstreams: readonly ReductionUpstream[],\n /**\n * Optional dict label resolver for `<field>Label` projection\n *. Present when the grouping field is a dictKey.\n */\n private readonly dictLabelResolver?: (\n key: string,\n locale: string,\n fallback?: string | readonly string[],\n ) => Promise<string | undefined>,\n ) {\n this.fields = typeof fields === 'string' ? [fields] : [...fields]\n }\n\n /**\n * Execute the query, group, reduce, and return an array of rows.\n *\n * `opts` (query-form MV grouping): when a `locale` + `i18nFields` are\n * given, the declared group-key `i18nText` fields are resolved to that locale\n * at the `mv` layer BEFORE bucketing — so an i18n group key is a stable string\n * instead of a raw `{locale}` map. The MV executor passes the MV's\n * `i18nLocale`/`i18nFields`; ordinary `.run()` callers pass nothing and are\n * unaffected.\n */\n run(opts?: { locale?: string; i18nFields?: Record<string, I18nTextDescriptor> }): R[] {\n let records = this.executeRecords()\n if (opts?.locale !== undefined && opts.i18nFields !== undefined) {\n const groupI18n: Record<string, I18nTextDescriptor> = {}\n for (const f of this.fields) {\n const d = opts.i18nFields[f]\n if (d !== undefined) groupI18n[f] = d\n }\n if (Object.keys(groupI18n).length > 0) {\n records = records.map((r) => applyI18nLocale(r as Record<string, unknown>, groupI18n, opts.locale!, undefined, 'mv'))\n }\n }\n return groupAndReduce<R>(records, this.fields, this.spec)\n }\n\n /**\n * Execute the query, group, reduce, and resolve `<field>Label` for\n * each result row when the grouping field is a `dictKey` and a\n * `locale` is provided. Returns `R[]` synchronously when\n * no locale is specified (identical to `.run()`).\n *\n * The `<field>Label` field is appended to each row. Rows whose group\n * key has no dictionary entry get `<field>Label: undefined`.\n *\n * Dict-label resolution is single-field only — multi-key groupings\n * do not produce a `<field>Label`. The resolver is only attached\n * by the builder when `fields.length === 1`.\n */\n async runAsync(opts?: {\n locale?: string\n fallback?: string | readonly string[]\n }): Promise<R[]> {\n const rows = groupAndReduce<R>(this.executeRecords(), this.fields, this.spec)\n if (!opts?.locale || !this.dictLabelResolver || this.fields.length !== 1) return rows\n\n const resolve = this.dictLabelResolver\n const locale = opts.locale\n const fallback = opts.fallback\n const field = this.fields[0]!\n const labelKey = `${field}Label`\n\n return Promise.all(\n rows.map(async (row) => {\n const key = (row as Record<string, unknown>)[field]\n if (typeof key !== 'string') return row\n const label = await resolve(key, locale, fallback)\n return { ...(row as Record<string, unknown>), [labelKey]: label } as unknown as R\n }),\n )\n }\n\n /**\n * Build a reactive `LiveReduction<R[]>` that re-runs the full\n * group-and-reduce pipeline whenever any upstream source notifies\n * of a change. Same error-isolation and idempotent-stop contract\n * as `Reduction.live()` — the implementation delegates to the\n * same `LiveAggregationImpl` class by threading a fresh\n * recompute closure through the existing constructor.\n *\n * uses naive full re-run on every change. Incremental\n * per-bucket maintenance (apply `step` on inserted records,\n * `remove` on deleted records, route by bucket key) is a future\n * optimization — the reducer protocol admits it, but wiring\n * delta-aware source subscriptions is a separate PR.\n *\n * Always call `live.stop()` when finished.\n */\n live(): LiveReduction<R[]> {\n const recompute = (): R[] =>\n groupAndReduce<R>(this.executeRecords(), this.fields, this.spec)\n return buildLiveReduction<R[]>(recompute, this.upstreams)\n }\n}\n"],"mappings":"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","names":[]}
|