@noy-db/hub 0.2.0-pre.10 → 0.2.0-pre.12
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 +126 -0
- package/dist/aggregate/index.cjs +289 -12
- package/dist/aggregate/index.cjs.map +1 -1
- package/dist/aggregate/index.d.cts +2 -2
- package/dist/aggregate/index.d.ts +2 -2
- package/dist/aggregate/index.js +7 -7
- package/dist/aggregate/index.js.map +1 -1
- package/dist/attestation/index.cjs.map +1 -1
- package/dist/attestation/index.d.cts +3 -3
- package/dist/attestation/index.d.ts +3 -3
- package/dist/attestation/index.js +6 -6
- package/dist/blobs/index.cjs +28 -0
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +4 -4
- package/dist/blobs/index.d.ts +4 -4
- package/dist/blobs/index.js +5 -5
- package/dist/bundle/index.cjs +1468 -19
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +5 -5
- package/dist/bundle/index.d.ts +5 -5
- package/dist/bundle/index.js +9 -9
- package/dist/{chunk-7CEGU63S.js → chunk-4BHFNKTP.js} +2 -2
- package/dist/{chunk-5OEJ6GOT.js → chunk-5ARRXIVR.js} +2 -2
- package/dist/{chunk-DRXIZOFV.js → chunk-6AD5TBF2.js} +31 -3
- package/dist/chunk-6AD5TBF2.js.map +1 -0
- package/dist/{chunk-YM7LFCG7.js → chunk-6BYBVRZU.js} +3 -3
- package/dist/{chunk-5IXJGFF2.js → chunk-7JJE3OMJ.js} +5 -5
- package/dist/{chunk-HHOO7HGH.js → chunk-7LVRIW4G.js} +4 -4
- package/dist/{chunk-O6EJ6WTI.js → chunk-AGRC7NQQ.js} +62 -2
- package/dist/chunk-AGRC7NQQ.js.map +1 -0
- package/dist/{chunk-IMYKDWB4.js → chunk-B7GGYNKQ.js} +2 -2
- package/dist/{chunk-BDV7INMP.js → chunk-BXOUVUES.js} +4 -4
- package/dist/{chunk-FO3UEG4S.js → chunk-C2CIIQRG.js} +2 -2
- package/dist/{chunk-ZROPXHJY.js → chunk-CHBXWJZQ.js} +2 -2
- package/dist/{chunk-RYIL3PI2.js → chunk-CILT6V3V.js} +2 -2
- package/dist/{chunk-PXTQPZO4.js → chunk-DLTU4M2I.js} +6 -6
- package/dist/{chunk-GAUEWM7D.js → chunk-EKNUBIIQ.js} +4 -4
- package/dist/{chunk-HQSQC2XL.js → chunk-GFPR7VJS.js} +17 -4
- package/dist/chunk-GFPR7VJS.js.map +1 -0
- package/dist/{chunk-6EOXTJS2.js → chunk-HBAJDI2N.js} +5 -5
- package/dist/{chunk-PVUUIWHY.js → chunk-HLGDYFWR.js} +10 -3
- package/dist/chunk-HLGDYFWR.js.map +1 -0
- package/dist/{chunk-RRNA5GKT.js → chunk-IEPT7HVP.js} +2 -2
- package/dist/{chunk-R233SLY3.js → chunk-IUBHXEPJ.js} +2 -2
- package/dist/{chunk-CH22FZHT.js → chunk-L6BYRCYB.js} +2 -2
- package/dist/{chunk-5OX6XVNS.js → chunk-LOA2VCMS.js} +5 -5
- package/dist/{chunk-BB27JMWB.js → chunk-LSEW3ZZ2.js} +3 -3
- package/dist/{chunk-Y26YV5R3.js → chunk-LWSD4QPT.js} +3 -3
- package/dist/{chunk-WIRRPTFH.js → chunk-LYNNZEQD.js} +1 -1
- package/dist/chunk-LYNNZEQD.js.map +1 -0
- package/dist/{chunk-26NK23DZ.js → chunk-M45IRXDM.js} +3 -3
- package/dist/{chunk-CXJG63MA.js → chunk-NP6EZT44.js} +20 -6
- package/dist/chunk-NP6EZT44.js.map +1 -0
- package/dist/{chunk-GNHAC43Q.js → chunk-O53RIZCC.js} +5 -5
- package/dist/chunk-OPDTLHFA.js +783 -0
- package/dist/chunk-OPDTLHFA.js.map +1 -0
- package/dist/{chunk-LSTBFLL2.js → chunk-P3Z5Y2TS.js} +2 -2
- package/dist/{chunk-QSOYKKMD.js → chunk-P4EDT5ZP.js} +2 -2
- package/dist/{chunk-PC6ZEDRL.js → chunk-RHQYVHFH.js} +2 -2
- package/dist/{chunk-3LPV6BXR.js → chunk-RRDWXNBQ.js} +3 -3
- package/dist/{chunk-4CLICFEY.js → chunk-SJJQKNMP.js} +4 -4
- package/dist/{chunk-TY32C732.js → chunk-SZ4N3IL5.js} +5 -5
- package/dist/{chunk-4USCAEDT.js → chunk-TMHJEYW7.js} +502 -60
- package/dist/chunk-TMHJEYW7.js.map +1 -0
- package/dist/{chunk-2N62W5YP.js → chunk-UA6G45ME.js} +3 -3
- package/dist/{chunk-6YLPHBKR.js → chunk-UOC7JMZO.js} +13 -4
- package/dist/chunk-UOC7JMZO.js.map +1 -0
- package/dist/{chunk-DAP2XL7Q.js → chunk-VOXMU6LB.js} +2 -2
- package/dist/chunk-WNRGOVLG.js +64 -0
- package/dist/chunk-WNRGOVLG.js.map +1 -0
- package/dist/{chunk-DJRWA3Q5.js → chunk-WUG3E423.js} +4 -4
- package/dist/{chunk-PM3QYWUU.js → chunk-XHM2SARW.js} +3 -3
- package/dist/{chunk-RC6SU5NO.js → chunk-XSIFXX54.js} +2 -2
- package/dist/{chunk-CXFOITNS.js → chunk-ZC7MNVYN.js} +2 -2
- package/dist/{chunk-6T2UDBKG.js → chunk-ZCFS7U4J.js} +2 -2
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +4 -4
- package/dist/consent/index.d.ts +4 -4
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-2CRLG4F4.js → crypto-AJB72OKN.js} +3 -3
- package/dist/{delegation-ZTRT2PRV.js → delegation-6FCWDRUS.js} +5 -5
- package/dist/derivations/index.cjs.map +1 -1
- package/dist/derivations/index.d.cts +5 -5
- package/dist/derivations/index.d.ts +5 -5
- package/dist/derivations/index.js +4 -4
- package/dist/{dev-unlock-BOEYl1xl.d.ts → dev-unlock-D3mpVFRc.d.ts} +1 -1
- package/dist/{dev-unlock-AglVnkPY.d.cts → dev-unlock-ckqa_Nso.d.cts} +1 -1
- package/dist/executor-7KSCEIFA.js +8 -0
- package/dist/executor-D2QMNGRJ.js +8 -0
- package/dist/executor-O5AZK7UW.js +11 -0
- package/dist/{fanout-sidecar-OKPMMPLG.js → fanout-sidecar-ZSKEQ6NI.js} +2 -2
- package/dist/guards/index.cjs +53 -1
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +12 -6
- package/dist/guards/index.d.ts +12 -6
- package/dist/guards/index.js +5 -3
- package/dist/{hash-B9m3_fhj.d.ts → hash-CTZVkXLx.d.ts} +1 -1
- package/dist/{hash-RVqz2zi8.d.cts → hash-rDSSd_oW.d.cts} +1 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +5 -5
- package/dist/history/index.d.ts +5 -5
- package/dist/history/index.js +5 -5
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +4 -4
- package/dist/i18n/index.d.ts +4 -4
- package/dist/i18n/index.js +6 -6
- package/dist/immutable-guard-C51vAHuh.d.cts +67 -0
- package/dist/immutable-guard-DyD0qg2k.d.ts +67 -0
- package/dist/index-CkFHr4OP.d.ts +1190 -0
- package/dist/index-Cmop06zJ.d.cts +1190 -0
- package/dist/index.cjs +1636 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -13
- package/dist/index.d.ts +46 -13
- package/dist/index.js +76 -44
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.js +2 -2
- package/dist/issue-YIYG4OW5.js +12 -0
- package/dist/{ledger-O7FXOG3D.js → ledger-5JMVF7PY.js} +5 -5
- package/dist/materialized-views/index.cjs.map +1 -1
- package/dist/materialized-views/index.d.cts +5 -6
- package/dist/materialized-views/index.d.ts +5 -6
- package/dist/materialized-views/index.js +6 -6
- package/dist/noydb-D5SLAJ6V.js +34 -0
- package/dist/overlay-views/index.cjs.map +1 -1
- package/dist/overlay-views/index.d.cts +5 -5
- package/dist/overlay-views/index.d.ts +5 -5
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +4 -4
- package/dist/periods/index.d.ts +4 -4
- package/dist/periods/index.js +5 -5
- package/dist/{public-envelope-HMYHZIRH.js → public-envelope-PFLZI5MO.js} +4 -4
- package/dist/query/index.cjs +293 -10
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +2 -2
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +4 -4
- package/dist/registry-BVQ5ITMF.js +8 -0
- package/dist/registry-JLP3QOLD.js +8 -0
- package/dist/{registry-ST2VNFZC.js → registry-NCY445U5.js} +3 -3
- package/dist/{revoke-S6JMSLUN.js → revoke-7RLGQWZ7.js} +6 -6
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +5 -5
- package/dist/session/index.d.ts +5 -5
- package/dist/session/index.js +3 -3
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +4 -4
- package/dist/shadow/index.d.ts +4 -4
- package/dist/shadow/index.js +2 -2
- package/dist/{signer-7NPTB3SQ.js → signer-6JF44I4A.js} +5 -5
- package/dist/snapshots/index.cjs.map +1 -1
- package/dist/snapshots/index.d.cts +4 -4
- package/dist/snapshots/index.d.ts +4 -4
- package/dist/snapshots/index.js +4 -4
- package/dist/{stale-VKXSXJF4.js → stale-UBLP3RJ3.js} +2 -2
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +4 -4
- package/dist/store/index.d.ts +4 -4
- package/dist/store/index.js +2 -2
- package/dist/strategy-rtpKDfTC.d.cts +2029 -0
- package/dist/strategy-rtpKDfTC.d.ts +2029 -0
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +3 -3
- package/dist/sync/index.d.ts +3 -3
- package/dist/sync/index.js +4 -4
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +4 -4
- package/dist/team/index.d.ts +4 -4
- package/dist/team/index.js +8 -8
- package/dist/tx/index.cjs +8 -1
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +4 -4
- package/dist/tx/index.d.ts +4 -4
- package/dist/tx/index.js +3 -3
- package/dist/{types-n2_IfwlQ.d.cts → types-BGwjsDef.d.cts} +520 -6
- package/dist/{types-CaNQm4i8.d.ts → types-DRdfwgTG.d.ts} +520 -6
- package/dist/{ulid-CLMjmyhG.d.cts → ulid-D4d0Xto3.d.cts} +1 -1
- package/dist/{ulid-B9SMWj5i.d.ts → ulid-DOTPZ5_h.d.ts} +1 -1
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.js +1 -1
- package/dist/vault-group-Z4KB75ZH.js +450 -0
- package/dist/vault-group-Z4KB75ZH.js.map +1 -0
- package/dist/{with-derivation-CVIOPTUf.d.ts → with-derivation-B082Y_WQ.d.ts} +1 -1
- package/dist/{with-derivation-aKrtS7Jj.d.cts → with-derivation-CB1EdcFF.d.cts} +1 -1
- package/dist/{with-materialized-view-C1eA1_T_.d.cts → with-materialized-view-CzRg1Dpr.d.cts} +1 -1
- package/dist/{with-materialized-view-DaYaE8-Q.d.ts → with-materialized-view-Dw4SwjKl.d.ts} +1 -1
- package/dist/{with-overlayed-view-DleJfKcV.d.cts → with-overlayed-view-C9YFKXzn.d.cts} +1 -1
- package/dist/{with-overlayed-view-DQsh2p8H.d.ts → with-overlayed-view-CaCXeW26.d.ts} +1 -1
- package/package.json +3 -3
- package/dist/chunk-2LPPNWF6.js +0 -340
- package/dist/chunk-2LPPNWF6.js.map +0 -1
- package/dist/chunk-4USCAEDT.js.map +0 -1
- package/dist/chunk-6YLPHBKR.js.map +0 -1
- package/dist/chunk-C3WE6UJY.js +0 -19
- package/dist/chunk-C3WE6UJY.js.map +0 -1
- package/dist/chunk-CXJG63MA.js.map +0 -1
- package/dist/chunk-DRXIZOFV.js.map +0 -1
- package/dist/chunk-HQSQC2XL.js.map +0 -1
- package/dist/chunk-O6EJ6WTI.js.map +0 -1
- package/dist/chunk-PVUUIWHY.js.map +0 -1
- package/dist/chunk-WIRRPTFH.js.map +0 -1
- package/dist/executor-S76VN45G.js +0 -8
- package/dist/executor-UCXLIGLW.js +0 -11
- package/dist/executor-ZCNZJMGR.js +0 -8
- package/dist/index-B8bjExET.d.cts +0 -2434
- package/dist/index-DfUbNad8.d.ts +0 -2434
- package/dist/issue-3W6IVLKH.js +0 -12
- package/dist/noydb-YAZNH5TI.js +0 -34
- package/dist/registry-UFIK7CSR.js +0 -8
- package/dist/registry-ZGYYSM5I.js +0 -8
- package/dist/strategy-CT2LCKAX.d.cts +0 -613
- package/dist/strategy-CT2LCKAX.d.ts +0 -613
- package/dist/with-guard-DZQbPzoP.d.cts +0 -18
- package/dist/with-guard-DseETUrF.d.ts +0 -18
- /package/dist/{chunk-7CEGU63S.js.map → chunk-4BHFNKTP.js.map} +0 -0
- /package/dist/{chunk-5OEJ6GOT.js.map → chunk-5ARRXIVR.js.map} +0 -0
- /package/dist/{chunk-YM7LFCG7.js.map → chunk-6BYBVRZU.js.map} +0 -0
- /package/dist/{chunk-5IXJGFF2.js.map → chunk-7JJE3OMJ.js.map} +0 -0
- /package/dist/{chunk-HHOO7HGH.js.map → chunk-7LVRIW4G.js.map} +0 -0
- /package/dist/{chunk-IMYKDWB4.js.map → chunk-B7GGYNKQ.js.map} +0 -0
- /package/dist/{chunk-BDV7INMP.js.map → chunk-BXOUVUES.js.map} +0 -0
- /package/dist/{chunk-FO3UEG4S.js.map → chunk-C2CIIQRG.js.map} +0 -0
- /package/dist/{chunk-ZROPXHJY.js.map → chunk-CHBXWJZQ.js.map} +0 -0
- /package/dist/{chunk-RYIL3PI2.js.map → chunk-CILT6V3V.js.map} +0 -0
- /package/dist/{chunk-PXTQPZO4.js.map → chunk-DLTU4M2I.js.map} +0 -0
- /package/dist/{chunk-GAUEWM7D.js.map → chunk-EKNUBIIQ.js.map} +0 -0
- /package/dist/{chunk-6EOXTJS2.js.map → chunk-HBAJDI2N.js.map} +0 -0
- /package/dist/{chunk-RRNA5GKT.js.map → chunk-IEPT7HVP.js.map} +0 -0
- /package/dist/{chunk-R233SLY3.js.map → chunk-IUBHXEPJ.js.map} +0 -0
- /package/dist/{chunk-CH22FZHT.js.map → chunk-L6BYRCYB.js.map} +0 -0
- /package/dist/{chunk-5OX6XVNS.js.map → chunk-LOA2VCMS.js.map} +0 -0
- /package/dist/{chunk-BB27JMWB.js.map → chunk-LSEW3ZZ2.js.map} +0 -0
- /package/dist/{chunk-Y26YV5R3.js.map → chunk-LWSD4QPT.js.map} +0 -0
- /package/dist/{chunk-26NK23DZ.js.map → chunk-M45IRXDM.js.map} +0 -0
- /package/dist/{chunk-GNHAC43Q.js.map → chunk-O53RIZCC.js.map} +0 -0
- /package/dist/{chunk-LSTBFLL2.js.map → chunk-P3Z5Y2TS.js.map} +0 -0
- /package/dist/{chunk-QSOYKKMD.js.map → chunk-P4EDT5ZP.js.map} +0 -0
- /package/dist/{chunk-PC6ZEDRL.js.map → chunk-RHQYVHFH.js.map} +0 -0
- /package/dist/{chunk-3LPV6BXR.js.map → chunk-RRDWXNBQ.js.map} +0 -0
- /package/dist/{chunk-4CLICFEY.js.map → chunk-SJJQKNMP.js.map} +0 -0
- /package/dist/{chunk-TY32C732.js.map → chunk-SZ4N3IL5.js.map} +0 -0
- /package/dist/{chunk-2N62W5YP.js.map → chunk-UA6G45ME.js.map} +0 -0
- /package/dist/{chunk-DAP2XL7Q.js.map → chunk-VOXMU6LB.js.map} +0 -0
- /package/dist/{chunk-DJRWA3Q5.js.map → chunk-WUG3E423.js.map} +0 -0
- /package/dist/{chunk-PM3QYWUU.js.map → chunk-XHM2SARW.js.map} +0 -0
- /package/dist/{chunk-RC6SU5NO.js.map → chunk-XSIFXX54.js.map} +0 -0
- /package/dist/{chunk-CXFOITNS.js.map → chunk-ZC7MNVYN.js.map} +0 -0
- /package/dist/{chunk-6T2UDBKG.js.map → chunk-ZCFS7U4J.js.map} +0 -0
- /package/dist/{crypto-2CRLG4F4.js.map → crypto-AJB72OKN.js.map} +0 -0
- /package/dist/{delegation-ZTRT2PRV.js.map → delegation-6FCWDRUS.js.map} +0 -0
- /package/dist/{executor-S76VN45G.js.map → executor-7KSCEIFA.js.map} +0 -0
- /package/dist/{executor-UCXLIGLW.js.map → executor-D2QMNGRJ.js.map} +0 -0
- /package/dist/{executor-ZCNZJMGR.js.map → executor-O5AZK7UW.js.map} +0 -0
- /package/dist/{fanout-sidecar-OKPMMPLG.js.map → fanout-sidecar-ZSKEQ6NI.js.map} +0 -0
- /package/dist/{issue-3W6IVLKH.js.map → issue-YIYG4OW5.js.map} +0 -0
- /package/dist/{ledger-O7FXOG3D.js.map → ledger-5JMVF7PY.js.map} +0 -0
- /package/dist/{noydb-YAZNH5TI.js.map → noydb-D5SLAJ6V.js.map} +0 -0
- /package/dist/{public-envelope-HMYHZIRH.js.map → public-envelope-PFLZI5MO.js.map} +0 -0
- /package/dist/{registry-ST2VNFZC.js.map → registry-BVQ5ITMF.js.map} +0 -0
- /package/dist/{registry-UFIK7CSR.js.map → registry-JLP3QOLD.js.map} +0 -0
- /package/dist/{registry-ZGYYSM5I.js.map → registry-NCY445U5.js.map} +0 -0
- /package/dist/{revoke-S6JMSLUN.js.map → revoke-7RLGQWZ7.js.map} +0 -0
- /package/dist/{signer-7NPTB3SQ.js.map → signer-6JF44I4A.js.map} +0 -0
- /package/dist/{stale-VKXSXJF4.js.map → stale-UBLP3RJ3.js.map} +0 -0
|
@@ -0,0 +1,783 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readPath
|
|
3
|
+
} from "./chunk-TV3YZ35S.js";
|
|
4
|
+
import {
|
|
5
|
+
GroupCardinalityError,
|
|
6
|
+
NoydbError
|
|
7
|
+
} from "./chunk-AGRC7NQQ.js";
|
|
8
|
+
|
|
9
|
+
// src/money/iso4217.ts
|
|
10
|
+
var MINOR_UNITS = {
|
|
11
|
+
// 2-decimal majors
|
|
12
|
+
EUR: 2,
|
|
13
|
+
USD: 2,
|
|
14
|
+
GBP: 2,
|
|
15
|
+
CHF: 2,
|
|
16
|
+
CAD: 2,
|
|
17
|
+
AUD: 2,
|
|
18
|
+
NZD: 2,
|
|
19
|
+
SGD: 2,
|
|
20
|
+
HKD: 2,
|
|
21
|
+
CNY: 2,
|
|
22
|
+
INR: 2,
|
|
23
|
+
BRL: 2,
|
|
24
|
+
MXN: 2,
|
|
25
|
+
ZAR: 2,
|
|
26
|
+
RUB: 2,
|
|
27
|
+
TRY: 2,
|
|
28
|
+
PLN: 2,
|
|
29
|
+
SEK: 2,
|
|
30
|
+
NOK: 2,
|
|
31
|
+
DKK: 2,
|
|
32
|
+
CZK: 2,
|
|
33
|
+
HUF: 2,
|
|
34
|
+
RON: 2,
|
|
35
|
+
ILS: 2,
|
|
36
|
+
THB: 2,
|
|
37
|
+
PHP: 2,
|
|
38
|
+
MYR: 2,
|
|
39
|
+
IDR: 2,
|
|
40
|
+
AED: 2,
|
|
41
|
+
SAR: 2,
|
|
42
|
+
QAR: 2,
|
|
43
|
+
EGP: 2,
|
|
44
|
+
// 0-decimal
|
|
45
|
+
JPY: 0,
|
|
46
|
+
KRW: 0,
|
|
47
|
+
ISK: 0,
|
|
48
|
+
CLP: 0,
|
|
49
|
+
VND: 0,
|
|
50
|
+
XOF: 0,
|
|
51
|
+
XAF: 0,
|
|
52
|
+
PYG: 0,
|
|
53
|
+
// 3-decimal
|
|
54
|
+
BHD: 3,
|
|
55
|
+
KWD: 3,
|
|
56
|
+
OMR: 3,
|
|
57
|
+
TND: 3,
|
|
58
|
+
JOD: 3,
|
|
59
|
+
IQD: 3,
|
|
60
|
+
LYD: 3
|
|
61
|
+
};
|
|
62
|
+
function scaleForCurrency(code) {
|
|
63
|
+
const v = MINOR_UNITS[code];
|
|
64
|
+
return v === void 0 ? null : v;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/money/descriptor.ts
|
|
68
|
+
var MoneyPrecisionError = class extends NoydbError {
|
|
69
|
+
constructor(field, value, scale) {
|
|
70
|
+
super(
|
|
71
|
+
"MONEY_PRECISION",
|
|
72
|
+
`money: value ${JSON.stringify(value)} for field "${field}" exceeds scale ${scale} and no rounding mode is configured`
|
|
73
|
+
);
|
|
74
|
+
this.field = field;
|
|
75
|
+
this.value = value;
|
|
76
|
+
this.scale = scale;
|
|
77
|
+
this.name = "MoneyPrecisionError";
|
|
78
|
+
}
|
|
79
|
+
field;
|
|
80
|
+
value;
|
|
81
|
+
scale;
|
|
82
|
+
};
|
|
83
|
+
var MoneyCurrencyError = class extends NoydbError {
|
|
84
|
+
constructor(currency, reason, field) {
|
|
85
|
+
super(
|
|
86
|
+
"MONEY_CURRENCY",
|
|
87
|
+
reason === "not-allowed" ? `money: currency "${currency}" is not allowed${field ? ` for field "${field}"` : ""}` : `money: no scale known for currency "${currency}"${field ? ` (field "${field}")` : ""} \u2014 pass an explicit scale / scaleOverrides`
|
|
88
|
+
);
|
|
89
|
+
this.currency = currency;
|
|
90
|
+
this.reason = reason;
|
|
91
|
+
this.field = field;
|
|
92
|
+
this.name = "MoneyCurrencyError";
|
|
93
|
+
}
|
|
94
|
+
currency;
|
|
95
|
+
reason;
|
|
96
|
+
field;
|
|
97
|
+
};
|
|
98
|
+
var MoneyUnsupportedError = class extends NoydbError {
|
|
99
|
+
constructor(field, message) {
|
|
100
|
+
super(
|
|
101
|
+
"MONEY_UNSUPPORTED",
|
|
102
|
+
message ?? `money: operation is not supported on field "${field}" \u2014 use sum() and count() and divide at the boundary`
|
|
103
|
+
);
|
|
104
|
+
this.field = field;
|
|
105
|
+
this.name = "MoneyUnsupportedError";
|
|
106
|
+
}
|
|
107
|
+
field;
|
|
108
|
+
};
|
|
109
|
+
function isMultiOptions(o) {
|
|
110
|
+
return "currencies" in o;
|
|
111
|
+
}
|
|
112
|
+
function money(options) {
|
|
113
|
+
const hasFixed = "currency" in options;
|
|
114
|
+
const hasMulti = "currencies" in options;
|
|
115
|
+
if (hasFixed && hasMulti) {
|
|
116
|
+
throw new TypeError("money: `currency` and `currencies` are mutually exclusive");
|
|
117
|
+
}
|
|
118
|
+
if (!hasFixed && !hasMulti) {
|
|
119
|
+
throw new TypeError("money: one of `currency` or `currencies` is required");
|
|
120
|
+
}
|
|
121
|
+
const rounding = options.rounding;
|
|
122
|
+
if (isMultiOptions(options)) {
|
|
123
|
+
const overrides = options.scaleOverrides ?? {};
|
|
124
|
+
const allowList = options.currencies;
|
|
125
|
+
const allows = (c) => allowList === "any" ? true : allowList.includes(c);
|
|
126
|
+
const scaleFor = (c) => {
|
|
127
|
+
if (!allows(c)) throw new MoneyCurrencyError(c, "not-allowed");
|
|
128
|
+
const s = overrides[c] ?? scaleForCurrency(c);
|
|
129
|
+
if (s === null || s === void 0) throw new MoneyCurrencyError(c, "unknown-scale");
|
|
130
|
+
return s;
|
|
131
|
+
};
|
|
132
|
+
if (allowList !== "any") for (const c of allowList) scaleFor(c);
|
|
133
|
+
const soleCurrency = () => allowList !== "any" && allowList.length === 1 ? allowList[0] : void 0;
|
|
134
|
+
return {
|
|
135
|
+
_noydbMoney: true,
|
|
136
|
+
mode: "multi",
|
|
137
|
+
options,
|
|
138
|
+
rounding,
|
|
139
|
+
fixedCurrency: void 0,
|
|
140
|
+
scaleFor,
|
|
141
|
+
allows,
|
|
142
|
+
soleCurrency
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const currency = options.currency;
|
|
146
|
+
const resolvedScale = options.scale ?? scaleForCurrency(currency);
|
|
147
|
+
if (resolvedScale === null || resolvedScale === void 0) {
|
|
148
|
+
throw new MoneyCurrencyError(currency, "unknown-scale");
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
_noydbMoney: true,
|
|
152
|
+
mode: "fixed",
|
|
153
|
+
options,
|
|
154
|
+
rounding,
|
|
155
|
+
fixedCurrency: currency,
|
|
156
|
+
scaleFor(c) {
|
|
157
|
+
if (c !== currency) throw new MoneyCurrencyError(c, "not-allowed");
|
|
158
|
+
return resolvedScale;
|
|
159
|
+
},
|
|
160
|
+
allows: (c) => c === currency,
|
|
161
|
+
soleCurrency: () => currency
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function isMoneyDescriptor(x) {
|
|
165
|
+
return typeof x === "object" && x !== null && x._noydbMoney === true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/aggregate/aggregation.ts
|
|
169
|
+
function reduceRecords(records, spec) {
|
|
170
|
+
const state = {};
|
|
171
|
+
for (const key of Object.keys(spec)) {
|
|
172
|
+
state[key] = spec[key].init();
|
|
173
|
+
}
|
|
174
|
+
for (const record of records) {
|
|
175
|
+
for (const key of Object.keys(spec)) {
|
|
176
|
+
state[key] = spec[key].step(state[key], record);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const result = {};
|
|
180
|
+
for (const key of Object.keys(spec)) {
|
|
181
|
+
result[key] = spec[key].finalize(state[key]);
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
var LiveAggregationImpl = class {
|
|
186
|
+
constructor(recompute, upstreams) {
|
|
187
|
+
this.recompute = recompute;
|
|
188
|
+
try {
|
|
189
|
+
this.value = recompute();
|
|
190
|
+
this.error = void 0;
|
|
191
|
+
} catch (err) {
|
|
192
|
+
this.value = void 0;
|
|
193
|
+
this.error = err;
|
|
194
|
+
}
|
|
195
|
+
for (const upstream of upstreams) {
|
|
196
|
+
const unsub = upstream.subscribe(() => this.refresh());
|
|
197
|
+
this.unsubscribes.push(unsub);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
recompute;
|
|
201
|
+
value;
|
|
202
|
+
error;
|
|
203
|
+
listeners = /* @__PURE__ */ new Set();
|
|
204
|
+
unsubscribes = [];
|
|
205
|
+
stopped = false;
|
|
206
|
+
refresh() {
|
|
207
|
+
if (this.stopped) return;
|
|
208
|
+
try {
|
|
209
|
+
this.value = this.recompute();
|
|
210
|
+
this.error = void 0;
|
|
211
|
+
} catch (err) {
|
|
212
|
+
this.error = err;
|
|
213
|
+
}
|
|
214
|
+
for (const listener of this.listeners) {
|
|
215
|
+
try {
|
|
216
|
+
listener();
|
|
217
|
+
} catch (err) {
|
|
218
|
+
console.warn("[noy-db] LiveAggregation listener threw:", err);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
subscribe(cb) {
|
|
223
|
+
if (this.stopped) {
|
|
224
|
+
return () => {
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
this.listeners.add(cb);
|
|
228
|
+
return () => {
|
|
229
|
+
this.listeners.delete(cb);
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
stop() {
|
|
233
|
+
if (this.stopped) return;
|
|
234
|
+
this.stopped = true;
|
|
235
|
+
for (const unsub of this.unsubscribes) {
|
|
236
|
+
try {
|
|
237
|
+
unsub();
|
|
238
|
+
} catch (err) {
|
|
239
|
+
console.warn("[noy-db] LiveAggregation upstream unsubscribe threw:", err);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
this.unsubscribes.length = 0;
|
|
243
|
+
this.listeners.clear();
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
var Aggregation = class {
|
|
247
|
+
constructor(executeRecords, spec, upstreams) {
|
|
248
|
+
this.executeRecords = executeRecords;
|
|
249
|
+
this.spec = spec;
|
|
250
|
+
this.upstreams = upstreams;
|
|
251
|
+
}
|
|
252
|
+
executeRecords;
|
|
253
|
+
spec;
|
|
254
|
+
upstreams;
|
|
255
|
+
/**
|
|
256
|
+
* Execute the query and reduce the results synchronously.
|
|
257
|
+
* Returns the reduced shape matching the spec — e.g. a spec of
|
|
258
|
+
* `{ total: sum('amount'), n: count() }` returns
|
|
259
|
+
* `{ total: number, n: number }`.
|
|
260
|
+
*/
|
|
261
|
+
run() {
|
|
262
|
+
return reduceRecords(this.executeRecords(), this.spec);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Build a reactive `LiveAggregation<R>` that re-runs the reduction
|
|
266
|
+
* whenever any upstream source notifies of a change. The initial
|
|
267
|
+
* value is computed eagerly in the constructor, so consumers can
|
|
268
|
+
* read `live.value` immediately after calling `.live()`.
|
|
269
|
+
*
|
|
270
|
+
* Always call `live.stop()` when finished — it tears down the
|
|
271
|
+
* upstream subscriptions. Vue's `onUnmounted` is the canonical
|
|
272
|
+
* place.
|
|
273
|
+
*
|
|
274
|
+
* **Implementation note:** every upstream change triggers a full
|
|
275
|
+
* re-reduction. Incremental maintenance (O(1) per delta for
|
|
276
|
+
* sum/count/avg via the reducer protocol's `remove()` method) is a
|
|
277
|
+
* planned follow-up optimization — the protocol already supports
|
|
278
|
+
* it, but the executor doesn't drive it yet. Consumers get
|
|
279
|
+
* correct, reactive values today; future PRs can switch to
|
|
280
|
+
* delta-based maintenance without changing this API.
|
|
281
|
+
*/
|
|
282
|
+
live() {
|
|
283
|
+
const recompute = () => reduceRecords(this.executeRecords(), this.spec);
|
|
284
|
+
return new LiveAggregationImpl(recompute, this.upstreams);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
function buildLiveAggregation(recompute, upstreams) {
|
|
288
|
+
return new LiveAggregationImpl(recompute, upstreams);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// src/aggregate/canonical-key.ts
|
|
292
|
+
function canonicalGroupKey(fields, row) {
|
|
293
|
+
const sorted = [...fields].sort();
|
|
294
|
+
const parts = [];
|
|
295
|
+
for (const name of sorted) {
|
|
296
|
+
const v = row[name];
|
|
297
|
+
const serialised = v === void 0 ? "undefined" : JSON.stringify(v);
|
|
298
|
+
parts.push(`${name}=${serialised}`);
|
|
299
|
+
}
|
|
300
|
+
return parts.join("|");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/money/fixed-point.ts
|
|
304
|
+
function expandExponent(s) {
|
|
305
|
+
const m = /^([+-]?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
|
|
306
|
+
if (!m) return s;
|
|
307
|
+
const sign = m[1] === "-" ? "-" : "";
|
|
308
|
+
const intp = m[2];
|
|
309
|
+
const frac = m[3] ?? "";
|
|
310
|
+
const exp = Number(m[4]);
|
|
311
|
+
const digits = intp + frac;
|
|
312
|
+
const pointPos = intp.length + exp;
|
|
313
|
+
let body;
|
|
314
|
+
if (pointPos <= 0) {
|
|
315
|
+
body = "0." + "0".repeat(-pointPos) + digits;
|
|
316
|
+
} else if (pointPos >= digits.length) {
|
|
317
|
+
body = digits + "0".repeat(pointPos - digits.length);
|
|
318
|
+
} else {
|
|
319
|
+
body = digits.slice(0, pointPos) + "." + digits.slice(pointPos);
|
|
320
|
+
}
|
|
321
|
+
return sign + body;
|
|
322
|
+
}
|
|
323
|
+
function toCanonicalDecimalString(input) {
|
|
324
|
+
let s;
|
|
325
|
+
if (typeof input === "number") {
|
|
326
|
+
if (!Number.isFinite(input)) return null;
|
|
327
|
+
s = String(input);
|
|
328
|
+
} else {
|
|
329
|
+
s = input.trim();
|
|
330
|
+
}
|
|
331
|
+
s = expandExponent(s);
|
|
332
|
+
if (s.startsWith("+")) s = s.slice(1);
|
|
333
|
+
if (!/^-?(\d+(\.\d*)?|\.\d+)$/.test(s)) return null;
|
|
334
|
+
return s;
|
|
335
|
+
}
|
|
336
|
+
function shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, mode) {
|
|
337
|
+
switch (mode) {
|
|
338
|
+
case "up":
|
|
339
|
+
return true;
|
|
340
|
+
case "down":
|
|
341
|
+
return false;
|
|
342
|
+
case "ceil":
|
|
343
|
+
return !negative;
|
|
344
|
+
case "floor":
|
|
345
|
+
return negative;
|
|
346
|
+
case "half-up":
|
|
347
|
+
return firstDiscarded >= 5;
|
|
348
|
+
case "half-down":
|
|
349
|
+
return firstDiscarded > 5 || firstDiscarded === 5 && hasMoreNonZeroAfterFirst;
|
|
350
|
+
case "half-even":
|
|
351
|
+
if (firstDiscarded > 5) return true;
|
|
352
|
+
if (firstDiscarded < 5) return false;
|
|
353
|
+
return hasMoreNonZeroAfterFirst || lastKeptDigit % 2 === 1;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
function parseToScaledInt(input, scale, rounding) {
|
|
357
|
+
const canonical = toCanonicalDecimalString(input);
|
|
358
|
+
if (canonical === null) return { ok: false, reason: "nonfinite" };
|
|
359
|
+
const negative = canonical.startsWith("-");
|
|
360
|
+
const unsigned = negative ? canonical.slice(1) : canonical;
|
|
361
|
+
const dot = unsigned.indexOf(".");
|
|
362
|
+
const intPart = dot === -1 ? unsigned : unsigned.slice(0, dot);
|
|
363
|
+
const fracPart = dot === -1 ? "" : unsigned.slice(dot + 1);
|
|
364
|
+
const intDigits = intPart === "" ? "0" : intPart;
|
|
365
|
+
if (fracPart.length <= scale) {
|
|
366
|
+
const keep2 = fracPart.padEnd(scale, "0");
|
|
367
|
+
const magnitude2 = BigInt(intDigits + keep2);
|
|
368
|
+
return { ok: true, value: negative && magnitude2 !== 0n ? -magnitude2 : magnitude2 };
|
|
369
|
+
}
|
|
370
|
+
const keep = fracPart.slice(0, scale);
|
|
371
|
+
const tail = fracPart.slice(scale);
|
|
372
|
+
const magnitudeDigits = intDigits + keep;
|
|
373
|
+
let magnitude = BigInt(magnitudeDigits);
|
|
374
|
+
if (/^0+$/.test(tail)) {
|
|
375
|
+
return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
|
|
376
|
+
}
|
|
377
|
+
if (rounding === void 0) return { ok: false, reason: "precision" };
|
|
378
|
+
const lastKeptDigit = Number(magnitudeDigits[magnitudeDigits.length - 1]);
|
|
379
|
+
const firstDiscarded = Number(tail[0]);
|
|
380
|
+
const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1));
|
|
381
|
+
if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {
|
|
382
|
+
magnitude += 1n;
|
|
383
|
+
}
|
|
384
|
+
return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
|
|
385
|
+
}
|
|
386
|
+
function formatScaledInt(value, scale) {
|
|
387
|
+
const negative = value < 0n;
|
|
388
|
+
const abs = (negative ? -value : value).toString();
|
|
389
|
+
if (scale === 0) return (negative ? "-" : "") + abs;
|
|
390
|
+
const padded = abs.padStart(scale + 1, "0");
|
|
391
|
+
const cut = padded.length - scale;
|
|
392
|
+
const intPart = padded.slice(0, cut);
|
|
393
|
+
const fracPart = padded.slice(cut);
|
|
394
|
+
return (negative ? "-" : "") + intPart + "." + fracPart;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// src/money/money-reducer.ts
|
|
398
|
+
function toScaledInt(v) {
|
|
399
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "bigint") {
|
|
400
|
+
try {
|
|
401
|
+
return BigInt(v);
|
|
402
|
+
} catch {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
function readMoney(record, field, desc) {
|
|
409
|
+
const raw = readPath(record, field);
|
|
410
|
+
if (raw === null || raw === void 0) return null;
|
|
411
|
+
if (desc.mode === "fixed") {
|
|
412
|
+
const value2 = toScaledInt(raw);
|
|
413
|
+
return value2 === null ? null : { currency: desc.fixedCurrency, value: value2 };
|
|
414
|
+
}
|
|
415
|
+
if (typeof raw !== "object") return null;
|
|
416
|
+
const o = raw;
|
|
417
|
+
if (typeof o.currency !== "string") return null;
|
|
418
|
+
const value = toScaledInt(o.amount);
|
|
419
|
+
return value === null ? null : { currency: o.currency, value };
|
|
420
|
+
}
|
|
421
|
+
function targetScaleFor(desc, currency) {
|
|
422
|
+
if (desc.allows(currency)) return desc.scaleFor(currency);
|
|
423
|
+
const s = scaleForCurrency(currency);
|
|
424
|
+
if (s === null) {
|
|
425
|
+
throw new Error(`money: cannot determine scale for conversion target "${currency}"`);
|
|
426
|
+
}
|
|
427
|
+
return s;
|
|
428
|
+
}
|
|
429
|
+
function parseRate(rate) {
|
|
430
|
+
const s = String(rate).trim();
|
|
431
|
+
const neg = s.startsWith("-");
|
|
432
|
+
const body = neg ? s.slice(1) : s;
|
|
433
|
+
const dot = body.indexOf(".");
|
|
434
|
+
const intPart = dot === -1 ? body : body.slice(0, dot);
|
|
435
|
+
const fracPart = dot === -1 ? "" : body.slice(dot + 1);
|
|
436
|
+
const int = BigInt((intPart === "" ? "0" : intPart) + fracPart);
|
|
437
|
+
return { int: neg ? -int : int, scale: fracPart.length };
|
|
438
|
+
}
|
|
439
|
+
function divRoundHalfEven(n, d) {
|
|
440
|
+
const q = n / d;
|
|
441
|
+
const r = n % d;
|
|
442
|
+
const twiceR = (r < 0n ? -r : r) * 2n;
|
|
443
|
+
if (twiceR < d) return q;
|
|
444
|
+
if (twiceR > d) return q + (n < 0n ? -1n : 1n);
|
|
445
|
+
return q % 2n === 0n ? q : q + (n < 0n ? -1n : 1n);
|
|
446
|
+
}
|
|
447
|
+
function convertScaled(value, srcScale, rate, targetScale) {
|
|
448
|
+
const { int: rateInt, scale: rateScale } = parseRate(rate);
|
|
449
|
+
const product = value * rateInt;
|
|
450
|
+
const curScale = srcScale + rateScale;
|
|
451
|
+
if (curScale === targetScale) return product;
|
|
452
|
+
if (curScale < targetScale) return product * 10n ** BigInt(targetScale - curScale);
|
|
453
|
+
return divRoundHalfEven(product, 10n ** BigInt(curScale - targetScale));
|
|
454
|
+
}
|
|
455
|
+
function finalizeSum(state, desc, convertTo, fx) {
|
|
456
|
+
if (convertTo !== void 0) {
|
|
457
|
+
if (fx === void 0) {
|
|
458
|
+
throw new Error(`money: sum convertTo "${convertTo}" requires an fx rate map`);
|
|
459
|
+
}
|
|
460
|
+
const targetScale = targetScaleFor(desc, convertTo);
|
|
461
|
+
let total = 0n;
|
|
462
|
+
for (const [cur, v] of state) {
|
|
463
|
+
if (cur === convertTo) {
|
|
464
|
+
total += convertScaled(v, desc.scaleFor(cur), 1, targetScale);
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
const rate = fx[`${cur}->${convertTo}`];
|
|
468
|
+
if (rate === void 0) {
|
|
469
|
+
throw new Error(`money: no fx rate for "${cur}->${convertTo}"`);
|
|
470
|
+
}
|
|
471
|
+
total += convertScaled(v, desc.scaleFor(cur), rate, targetScale);
|
|
472
|
+
}
|
|
473
|
+
return formatScaledInt(total, targetScale);
|
|
474
|
+
}
|
|
475
|
+
if (desc.mode === "fixed") {
|
|
476
|
+
const cur = desc.fixedCurrency;
|
|
477
|
+
return formatScaledInt(state.get(cur) ?? 0n, desc.scaleFor(cur));
|
|
478
|
+
}
|
|
479
|
+
const out = {};
|
|
480
|
+
for (const [cur, v] of state) out[cur] = formatScaledInt(v, desc.scaleFor(cur));
|
|
481
|
+
return out;
|
|
482
|
+
}
|
|
483
|
+
function moneySumReducer(field, desc, convertTo, fx) {
|
|
484
|
+
return {
|
|
485
|
+
op: "sum",
|
|
486
|
+
field,
|
|
487
|
+
init: () => /* @__PURE__ */ new Map(),
|
|
488
|
+
step: (state, record) => {
|
|
489
|
+
const m = readMoney(record, field, desc);
|
|
490
|
+
if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) + m.value);
|
|
491
|
+
return state;
|
|
492
|
+
},
|
|
493
|
+
remove: (state, record) => {
|
|
494
|
+
const m = readMoney(record, field, desc);
|
|
495
|
+
if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) - m.value);
|
|
496
|
+
return state;
|
|
497
|
+
},
|
|
498
|
+
finalize: (state) => finalizeSum(state, desc, convertTo, fx)
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function extremum(values, op) {
|
|
502
|
+
let out = values[0];
|
|
503
|
+
for (let i = 1; i < values.length; i++) {
|
|
504
|
+
const v = values[i];
|
|
505
|
+
if (op === "min" ? v < out : v > out) out = v;
|
|
506
|
+
}
|
|
507
|
+
return out;
|
|
508
|
+
}
|
|
509
|
+
function moneyMinMaxReducer(op, field, desc) {
|
|
510
|
+
return {
|
|
511
|
+
op,
|
|
512
|
+
field,
|
|
513
|
+
init: () => /* @__PURE__ */ new Map(),
|
|
514
|
+
step: (state, record) => {
|
|
515
|
+
const m = readMoney(record, field, desc);
|
|
516
|
+
if (m) {
|
|
517
|
+
const arr = state.get(m.currency);
|
|
518
|
+
if (arr) arr.push(m.value);
|
|
519
|
+
else state.set(m.currency, [m.value]);
|
|
520
|
+
}
|
|
521
|
+
return state;
|
|
522
|
+
},
|
|
523
|
+
remove: (state, record) => {
|
|
524
|
+
const m = readMoney(record, field, desc);
|
|
525
|
+
if (m) {
|
|
526
|
+
const arr = state.get(m.currency);
|
|
527
|
+
if (arr) {
|
|
528
|
+
const idx = arr.indexOf(m.value);
|
|
529
|
+
if (idx >= 0) arr.splice(idx, 1);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return state;
|
|
533
|
+
},
|
|
534
|
+
finalize: (state) => {
|
|
535
|
+
if (desc.mode === "fixed") {
|
|
536
|
+
const cur = desc.fixedCurrency;
|
|
537
|
+
const arr = state.get(cur);
|
|
538
|
+
if (!arr || arr.length === 0) return null;
|
|
539
|
+
return formatScaledInt(extremum(arr, op), desc.scaleFor(cur));
|
|
540
|
+
}
|
|
541
|
+
const out = {};
|
|
542
|
+
for (const [cur, arr] of state) {
|
|
543
|
+
if (arr.length > 0) out[cur] = formatScaledInt(extremum(arr, op), desc.scaleFor(cur));
|
|
544
|
+
}
|
|
545
|
+
return out;
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
function wrapMoneyReducers(spec, moneyFields) {
|
|
550
|
+
let changed = false;
|
|
551
|
+
const out = {};
|
|
552
|
+
for (const [key, reducer] of Object.entries(spec)) {
|
|
553
|
+
const field = reducer.field;
|
|
554
|
+
const desc = field ? moneyFields[field] : void 0;
|
|
555
|
+
if (desc && reducer.op === "avg") {
|
|
556
|
+
throw new MoneyUnsupportedError(
|
|
557
|
+
field,
|
|
558
|
+
`avg() is not supported on money field "${field}" in v1 \u2014 use sum() and count() and divide at the boundary.`
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
if (desc && (reducer.op === "sum" || reducer.op === "min" || reducer.op === "max")) {
|
|
562
|
+
changed = true;
|
|
563
|
+
out[key] = reducer.op === "sum" ? moneySumReducer(field, desc, reducer.convertTo, reducer.fx) : moneyMinMaxReducer(reducer.op, field, desc);
|
|
564
|
+
} else {
|
|
565
|
+
out[key] = reducer;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return changed ? out : spec;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// src/aggregate/groupby.ts
|
|
572
|
+
var GROUPBY_WARN_CARDINALITY = 1e4;
|
|
573
|
+
var GROUPBY_MAX_CARDINALITY = 1e5;
|
|
574
|
+
var warnedCardinalityFields = /* @__PURE__ */ new Set();
|
|
575
|
+
function warnCardinalityApproaching(fields, observed) {
|
|
576
|
+
const key = JSON.stringify([...fields].sort());
|
|
577
|
+
if (warnedCardinalityFields.has(key)) return;
|
|
578
|
+
warnedCardinalityFields.add(key);
|
|
579
|
+
const label = `[${fields.join(", ")}]`;
|
|
580
|
+
console.warn(
|
|
581
|
+
`[noy-db] .groupBy(${label}) produced ${observed} distinct groups, ${Math.round(observed / GROUPBY_MAX_CARDINALITY * 100)}% of the ${GROUPBY_MAX_CARDINALITY}-group ceiling. Narrow the query with .where() before grouping, or switch to a lower-cardinality field.`
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
function resetGroupByWarnings() {
|
|
585
|
+
warnedCardinalityFields.clear();
|
|
586
|
+
}
|
|
587
|
+
var GroupedQueryBase = class {
|
|
588
|
+
constructor(executeRecords, fieldOrFields, upstreams, dictLabelResolver, moneyFields) {
|
|
589
|
+
this.executeRecords = executeRecords;
|
|
590
|
+
this.upstreams = upstreams;
|
|
591
|
+
this.dictLabelResolver = dictLabelResolver;
|
|
592
|
+
this.moneyFields = moneyFields;
|
|
593
|
+
this.fields = typeof fieldOrFields === "string" ? [fieldOrFields] : [...fieldOrFields];
|
|
594
|
+
}
|
|
595
|
+
executeRecords;
|
|
596
|
+
upstreams;
|
|
597
|
+
dictLabelResolver;
|
|
598
|
+
moneyFields;
|
|
599
|
+
/**
|
|
600
|
+
* Field set this grouped query buckets on. Stored in declaration
|
|
601
|
+
* order — the same order is preserved on every result row by
|
|
602
|
+
* `groupAndReduce`. For the single-field constructor, this is
|
|
603
|
+
* `[field]`.
|
|
604
|
+
*/
|
|
605
|
+
fields;
|
|
606
|
+
/** Apply money-aware reducer rewriting when money fields are declared. */
|
|
607
|
+
wrapSpec(spec) {
|
|
608
|
+
return this.moneyFields ? wrapMoneyReducers(spec, this.moneyFields) : spec;
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
var GroupedQuery = class extends GroupedQueryBase {
|
|
612
|
+
/**
|
|
613
|
+
* Build a grouped aggregation. Returns a `GroupedAggregation`
|
|
614
|
+
* with `.run()`, `.runAsync()`, and `.live()` terminals — same shape
|
|
615
|
+
* as the non-grouped `.aggregate()` wrapper, just with an array
|
|
616
|
+
* result (one row per bucket) instead of a single reduced object.
|
|
617
|
+
*/
|
|
618
|
+
aggregate(spec) {
|
|
619
|
+
return new GroupedAggregation(
|
|
620
|
+
this.executeRecords,
|
|
621
|
+
this.fields,
|
|
622
|
+
this.wrapSpec(spec),
|
|
623
|
+
this.upstreams,
|
|
624
|
+
this.dictLabelResolver
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
var GroupedQueryN = class extends GroupedQueryBase {
|
|
629
|
+
aggregate(spec) {
|
|
630
|
+
return new GroupedAggregation(
|
|
631
|
+
this.executeRecords,
|
|
632
|
+
this.fields,
|
|
633
|
+
this.wrapSpec(spec),
|
|
634
|
+
this.upstreams,
|
|
635
|
+
this.dictLabelResolver
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
function groupAndReduce(records, fieldOrFields, spec) {
|
|
640
|
+
const fields = typeof fieldOrFields === "string" ? [fieldOrFields] : fieldOrFields;
|
|
641
|
+
if (fields.length === 0) {
|
|
642
|
+
throw new Error(".groupBy() requires at least one field");
|
|
643
|
+
}
|
|
644
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
645
|
+
const fieldLabel = fields.length === 1 ? fields[0] : `[${fields.join(", ")}]`;
|
|
646
|
+
for (const record of records) {
|
|
647
|
+
const keyValues = {};
|
|
648
|
+
for (const f of fields) {
|
|
649
|
+
keyValues[f] = readPath(record, f);
|
|
650
|
+
}
|
|
651
|
+
const dedupKey = canonicalGroupKey(fields, keyValues);
|
|
652
|
+
let bucket = buckets.get(dedupKey);
|
|
653
|
+
if (bucket === void 0) {
|
|
654
|
+
if (buckets.size >= GROUPBY_MAX_CARDINALITY) {
|
|
655
|
+
throw new GroupCardinalityError(
|
|
656
|
+
fieldLabel,
|
|
657
|
+
buckets.size + 1,
|
|
658
|
+
GROUPBY_MAX_CARDINALITY
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
bucket = { keyValues, records: [] };
|
|
662
|
+
buckets.set(dedupKey, bucket);
|
|
663
|
+
}
|
|
664
|
+
bucket.records.push(record);
|
|
665
|
+
}
|
|
666
|
+
if (buckets.size >= GROUPBY_WARN_CARDINALITY) {
|
|
667
|
+
warnCardinalityApproaching(fields, buckets.size);
|
|
668
|
+
}
|
|
669
|
+
const reducerKeys = Object.keys(spec);
|
|
670
|
+
const out = [];
|
|
671
|
+
for (const bucket of buckets.values()) {
|
|
672
|
+
const state = {};
|
|
673
|
+
for (const rk of reducerKeys) {
|
|
674
|
+
state[rk] = spec[rk].init();
|
|
675
|
+
}
|
|
676
|
+
for (const record of bucket.records) {
|
|
677
|
+
for (const rk of reducerKeys) {
|
|
678
|
+
state[rk] = spec[rk].step(state[rk], record);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
const row = {};
|
|
682
|
+
for (const f of fields) {
|
|
683
|
+
row[f] = bucket.keyValues[f];
|
|
684
|
+
}
|
|
685
|
+
for (const rk of reducerKeys) {
|
|
686
|
+
row[rk] = spec[rk].finalize(state[rk]);
|
|
687
|
+
}
|
|
688
|
+
out.push(row);
|
|
689
|
+
}
|
|
690
|
+
return out;
|
|
691
|
+
}
|
|
692
|
+
var GroupedAggregation = class {
|
|
693
|
+
constructor(executeRecords, fields, spec, upstreams, dictLabelResolver) {
|
|
694
|
+
this.executeRecords = executeRecords;
|
|
695
|
+
this.spec = spec;
|
|
696
|
+
this.upstreams = upstreams;
|
|
697
|
+
this.dictLabelResolver = dictLabelResolver;
|
|
698
|
+
this.fields = typeof fields === "string" ? [fields] : [...fields];
|
|
699
|
+
}
|
|
700
|
+
executeRecords;
|
|
701
|
+
spec;
|
|
702
|
+
upstreams;
|
|
703
|
+
dictLabelResolver;
|
|
704
|
+
fields;
|
|
705
|
+
/** Execute the query, group, reduce, and return an array of rows. */
|
|
706
|
+
run() {
|
|
707
|
+
return groupAndReduce(this.executeRecords(), this.fields, this.spec);
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Execute the query, group, reduce, and resolve `<field>Label` for
|
|
711
|
+
* each result row when the grouping field is a `dictKey` and a
|
|
712
|
+
* `locale` is provided. Returns `R[]` synchronously when
|
|
713
|
+
* no locale is specified (identical to `.run()`).
|
|
714
|
+
*
|
|
715
|
+
* The `<field>Label` field is appended to each row. Rows whose group
|
|
716
|
+
* key has no dictionary entry get `<field>Label: undefined`.
|
|
717
|
+
*
|
|
718
|
+
* Dict-label resolution is single-field only — multi-key groupings
|
|
719
|
+
* do not produce a `<field>Label`. The resolver is only attached
|
|
720
|
+
* by the builder when `fields.length === 1`.
|
|
721
|
+
*/
|
|
722
|
+
async runAsync(opts) {
|
|
723
|
+
const rows = groupAndReduce(this.executeRecords(), this.fields, this.spec);
|
|
724
|
+
if (!opts?.locale || !this.dictLabelResolver || this.fields.length !== 1) return rows;
|
|
725
|
+
const resolve = this.dictLabelResolver;
|
|
726
|
+
const locale = opts.locale;
|
|
727
|
+
const fallback = opts.fallback;
|
|
728
|
+
const field = this.fields[0];
|
|
729
|
+
const labelKey = `${field}Label`;
|
|
730
|
+
return Promise.all(
|
|
731
|
+
rows.map(async (row) => {
|
|
732
|
+
const key = row[field];
|
|
733
|
+
if (typeof key !== "string") return row;
|
|
734
|
+
const label = await resolve(key, locale, fallback);
|
|
735
|
+
return { ...row, [labelKey]: label };
|
|
736
|
+
})
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Build a reactive `LiveAggregation<R[]>` that re-runs the full
|
|
741
|
+
* group-and-reduce pipeline whenever any upstream source notifies
|
|
742
|
+
* of a change. Same error-isolation and idempotent-stop contract
|
|
743
|
+
* as `Aggregation.live()` — the implementation delegates to the
|
|
744
|
+
* same `LiveAggregationImpl` class by threading a fresh
|
|
745
|
+
* recompute closure through the existing constructor.
|
|
746
|
+
*
|
|
747
|
+
* uses naive full re-run on every change. Incremental
|
|
748
|
+
* per-bucket maintenance (apply `step` on inserted records,
|
|
749
|
+
* `remove` on deleted records, route by bucket key) is a future
|
|
750
|
+
* optimization — the reducer protocol admits it, but wiring
|
|
751
|
+
* delta-aware source subscriptions is a separate PR.
|
|
752
|
+
*
|
|
753
|
+
* Always call `live.stop()` when finished.
|
|
754
|
+
*/
|
|
755
|
+
live() {
|
|
756
|
+
const recompute = () => groupAndReduce(this.executeRecords(), this.fields, this.spec);
|
|
757
|
+
return buildLiveAggregation(recompute, this.upstreams);
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
export {
|
|
762
|
+
parseToScaledInt,
|
|
763
|
+
formatScaledInt,
|
|
764
|
+
scaleForCurrency,
|
|
765
|
+
MoneyPrecisionError,
|
|
766
|
+
MoneyCurrencyError,
|
|
767
|
+
MoneyUnsupportedError,
|
|
768
|
+
money,
|
|
769
|
+
isMoneyDescriptor,
|
|
770
|
+
wrapMoneyReducers,
|
|
771
|
+
reduceRecords,
|
|
772
|
+
Aggregation,
|
|
773
|
+
buildLiveAggregation,
|
|
774
|
+
canonicalGroupKey,
|
|
775
|
+
GROUPBY_WARN_CARDINALITY,
|
|
776
|
+
GROUPBY_MAX_CARDINALITY,
|
|
777
|
+
resetGroupByWarnings,
|
|
778
|
+
GroupedQuery,
|
|
779
|
+
GroupedQueryN,
|
|
780
|
+
groupAndReduce,
|
|
781
|
+
GroupedAggregation
|
|
782
|
+
};
|
|
783
|
+
//# sourceMappingURL=chunk-OPDTLHFA.js.map
|