@noy-db/hub 0.2.0-pre.14 → 0.2.0-pre.16
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/dist/aggregate/index.cjs +160 -64
- 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 +3 -3
- package/dist/attestation/index.cjs.map +1 -1
- package/dist/attestation/index.d.cts +5 -5
- package/dist/attestation/index.d.ts +5 -5
- package/dist/attestation/index.js +4 -4
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +6 -6
- package/dist/blobs/index.d.ts +6 -6
- package/dist/blobs/index.js +3 -3
- package/dist/bundle/index.cjs +607 -114
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +7 -7
- package/dist/bundle/index.d.ts +7 -7
- package/dist/bundle/index.js +7 -7
- package/dist/{chunk-BIYRQQV6.js → chunk-3YWP3WBP.js} +3 -3
- package/dist/{chunk-VU7SWWT5.js → chunk-42FEUPZQ.js} +10 -6
- package/dist/chunk-42FEUPZQ.js.map +1 -0
- package/dist/{chunk-ACKFRSAH.js → chunk-667MB6AH.js} +132 -54
- package/dist/chunk-667MB6AH.js.map +1 -0
- package/dist/{chunk-A5ZOOZFB.js → chunk-6H2ZUNR7.js} +2 -2
- package/dist/{chunk-7HT2MEZ5.js → chunk-7BQ4QWYX.js} +3 -3
- package/dist/{chunk-DQU36Q7I.js → chunk-7Z7KSVA5.js} +13 -4
- package/dist/chunk-7Z7KSVA5.js.map +1 -0
- package/dist/{chunk-WBAYSNUQ.js → chunk-BI6ETQPF.js} +2 -2
- package/dist/{chunk-56DJ7JVK.js → chunk-BR3AMFGS.js} +2 -2
- package/dist/chunk-CJORTUJ2.js +524 -0
- package/dist/chunk-CJORTUJ2.js.map +1 -0
- package/dist/{chunk-YNTBADIY.js → chunk-CZI2A4MQ.js} +2 -2
- package/dist/{chunk-COFPAMX6.js → chunk-DLZ2ONOD.js} +3 -3
- package/dist/{chunk-KGCORI4L.js → chunk-DUREQF5W.js} +266 -66
- package/dist/chunk-DUREQF5W.js.map +1 -0
- package/dist/{chunk-PE4AQGFH.js → chunk-E2CDVKMH.js} +3 -3
- package/dist/{chunk-GC4V7RU7.js → chunk-F3BPIPLS.js} +1 -1
- package/dist/{chunk-GC4V7RU7.js.map → chunk-F3BPIPLS.js.map} +1 -1
- package/dist/{chunk-L2FE64BU.js → chunk-FFXM3ZIF.js} +2 -2
- package/dist/{chunk-5LQG6ZO2.js → chunk-G4SCICH5.js} +8 -3
- package/dist/chunk-G4SCICH5.js.map +1 -0
- package/dist/{chunk-WGHU7BLI.js → chunk-GNI5STXQ.js} +2 -2
- package/dist/{chunk-UWNYBOOO.js → chunk-HBXJ37ZY.js} +11 -5
- package/dist/chunk-HBXJ37ZY.js.map +1 -0
- package/dist/{chunk-NP6EZT44.js → chunk-IQLVUT37.js} +2 -2
- package/dist/{chunk-7PS7EOCF.js → chunk-IXBIFDEW.js} +2 -2
- package/dist/{chunk-LX3CB26H.js → chunk-KABJXG2F.js} +2 -2
- package/dist/{chunk-3EWA37FV.js → chunk-L2BNJ6HM.js} +32 -276
- package/dist/chunk-L2BNJ6HM.js.map +1 -0
- package/dist/{chunk-DKO2QFSA.js → chunk-OB2ZJQ2D.js} +2 -2
- package/dist/{chunk-4PEFEETV.js → chunk-OMAMZKKD.js} +2 -2
- package/dist/{chunk-EGD5DXFT.js → chunk-OQSRJG6A.js} +13 -1
- package/dist/chunk-OQSRJG6A.js.map +1 -0
- package/dist/{chunk-KI6HAJWL.js → chunk-QSUK7YWK.js} +2 -2
- package/dist/{chunk-YHPM5D7Y.js → chunk-QVIEAYTP.js} +61 -2
- package/dist/chunk-QVIEAYTP.js.map +1 -0
- package/dist/{chunk-NSCVNK5K.js → chunk-SCJPI4Z5.js} +3 -3
- package/dist/{chunk-ZWTNWAO4.js → chunk-TKIY625R.js} +13 -3
- package/dist/chunk-TKIY625R.js.map +1 -0
- package/dist/{chunk-OHVFWCJP.js → chunk-VLMPU56Q.js} +48 -18
- package/dist/chunk-VLMPU56Q.js.map +1 -0
- package/dist/{chunk-6AJBSQU4.js → chunk-XL35NSEN.js} +2 -2
- package/dist/{chunk-WIBHRONM.js → chunk-XWH4MXIU.js} +2 -2
- package/dist/consent/index.d.cts +6 -6
- package/dist/consent/index.d.ts +6 -6
- package/dist/derivations/index.cjs +24 -3
- package/dist/derivations/index.cjs.map +1 -1
- package/dist/derivations/index.d.cts +7 -7
- package/dist/derivations/index.d.ts +7 -7
- package/dist/derivations/index.js +2 -2
- package/dist/{dev-unlock-BF4OSxRv.d.cts → dev-unlock-8XzcD2Z4.d.cts} +1 -1
- package/dist/{dev-unlock-DV7ujTCI.d.ts → dev-unlock-DR3upLd1.d.ts} +1 -1
- package/dist/executor-AZLS3KBK.js +11 -0
- package/dist/{fanout-sidecar-N6OJX6QR.js → fanout-sidecar-67CMI3UT.js} +2 -2
- package/dist/guards/index.cjs +9 -5
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +7 -7
- package/dist/guards/index.d.ts +7 -7
- package/dist/guards/index.js +1 -1
- package/dist/{hash-DswxkLtW.d.ts → hash-CDjye9KV.d.ts} +1 -1
- package/dist/{hash-BcF5WQXl.d.cts → hash-DuQ88_5W.d.cts} +1 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +7 -7
- package/dist/history/index.d.ts +7 -7
- package/dist/history/index.js +2 -2
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +6 -6
- package/dist/i18n/index.d.ts +6 -6
- package/dist/i18n/index.js +3 -3
- package/dist/{immutable-guard-7KqslW2K.d.cts → immutable-guard-CRPvu24K.d.cts} +16 -1
- package/dist/{immutable-guard-C8IYdzfu.d.ts → immutable-guard-Dov3WvwF.d.ts} +16 -1
- package/dist/{index-Cqzp4tt9.d.ts → index-C8Bk3-VF.d.cts} +11 -3
- package/dist/{index-CUVOMtgg.d.cts → index-nP99bXLg.d.ts} +11 -3
- package/dist/index.cjs +840 -122
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +146 -15
- package/dist/index.d.ts +146 -15
- package/dist/index.js +153 -35
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs +92 -31
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.d.cts +3 -3
- package/dist/indexing/index.d.ts +3 -3
- package/dist/indexing/index.js +3 -3
- package/dist/{issue-ADVS4OVP.js → issue-RZP3VI6O.js} +4 -4
- package/dist/{lazy-builder-D5GU14TS.d.ts → lazy-builder-ChSqcF5t.d.ts} +1 -1
- package/dist/{lazy-builder-Ci5_YG73.d.cts → lazy-builder-eYZzLEL1.d.cts} +1 -1
- package/dist/{ledger-CWSE3BLF.js → ledger-A3LL253R.js} +3 -3
- package/dist/materialized-views/index.cjs +409 -7
- package/dist/materialized-views/index.cjs.map +1 -1
- package/dist/materialized-views/index.d.cts +7 -7
- package/dist/materialized-views/index.d.ts +7 -7
- package/dist/materialized-views/index.js +6 -6
- package/dist/noydb-WCMY2ZOW.js +35 -0
- package/dist/overlay-views/index.cjs +47 -17
- package/dist/overlay-views/index.cjs.map +1 -1
- package/dist/overlay-views/index.d.cts +28 -10
- package/dist/overlay-views/index.d.ts +28 -10
- package/dist/overlay-views/index.js +1 -1
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +6 -6
- package/dist/periods/index.d.ts +6 -6
- package/dist/periods/index.js +3 -3
- package/dist/{predicate-Bt5ft-9c.d.cts → predicate-BmhBSPCH.d.cts} +59 -2
- package/dist/{predicate-Bt5ft-9c.d.ts → predicate-BmhBSPCH.d.ts} +59 -2
- package/dist/{public-envelope-SYHEYQ3X.js → public-envelope-YP2UWMLG.js} +3 -3
- package/dist/query/index.cjs +604 -205
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +3 -3
- package/dist/query/index.d.ts +3 -3
- package/dist/query/index.js +5 -5
- package/dist/{registry-XGLNADIE.js → registry-EB6SISTA.js} +2 -2
- package/dist/{registry-DK5YWAAA.js → registry-UTA4CLQS.js} +2 -2
- package/dist/{revoke-ZDFKMR5E.js → revoke-HNMQZSCL.js} +4 -4
- package/dist/session/index.d.cts +7 -7
- package/dist/session/index.d.ts +7 -7
- package/dist/shadow/index.d.cts +6 -6
- package/dist/shadow/index.d.ts +6 -6
- package/dist/{signer-P5D7Y72U.js → signer-DCMNKXSF.js} +3 -3
- package/dist/snapshots/index.d.cts +6 -6
- package/dist/snapshots/index.d.ts +6 -6
- package/dist/snapshots/index.js +3 -3
- package/dist/{stale-7FRJVHN6.js → stale-W5PQTRYH.js} +2 -2
- package/dist/store/index.d.cts +6 -6
- package/dist/store/index.d.ts +6 -6
- package/dist/{strategy-CrS7PnbE.d.cts → strategy-BtW8fAjz.d.cts} +2 -2
- package/dist/{strategy-CrS7PnbE.d.ts → strategy-BtW8fAjz.d.ts} +2 -2
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +5 -5
- package/dist/sync/index.d.ts +5 -5
- package/dist/sync/index.js +2 -2
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +6 -6
- package/dist/team/index.d.ts +6 -6
- package/dist/team/index.js +5 -5
- package/dist/tx/index.cjs +66 -3
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +24 -8
- package/dist/tx/index.d.ts +24 -8
- package/dist/tx/index.js +7 -3
- package/dist/tx/index.js.map +1 -1
- package/dist/{types-V5R2-pd4.d.cts → types-Bze6vkwm.d.cts} +391 -144
- package/dist/{types-BFHQUjdy.d.ts → types-DrmBTscX.d.ts} +391 -144
- package/dist/{ulid-p2nKiiKg.d.ts → ulid-DbBVrNSt.d.ts} +1 -1
- package/dist/{ulid-CwNf9e6-.d.cts → ulid-DfZlAh0u.d.cts} +1 -1
- package/dist/{vault-group-W7QC4UYW.js → vault-group-DX2HFQMX.js} +3 -3
- package/dist/{with-derivation-C9K43BOB.d.cts → with-derivation-CCqAchD5.d.cts} +1 -1
- package/dist/{with-derivation-Ds9yZgCj.d.ts → with-derivation-_lySGdlm.d.ts} +1 -1
- package/dist/{with-materialized-view-DgQcAjYv.d.cts → with-materialized-view--4PsvMDu.d.cts} +1 -1
- package/dist/{with-materialized-view-DwR4jkV5.d.ts → with-materialized-view-QT1Tp7NO.d.ts} +1 -1
- package/dist/{with-overlayed-view-ByyhHdVr.d.ts → with-overlayed-view-BEXfpzSb.d.ts} +1 -1
- package/dist/{with-overlayed-view-7-rUB3vD.d.cts → with-overlayed-view-DlH5qmeB.d.cts} +1 -1
- package/package.json +3 -3
- package/dist/chunk-3EWA37FV.js.map +0 -1
- package/dist/chunk-5LQG6ZO2.js.map +0 -1
- package/dist/chunk-ACKFRSAH.js.map +0 -1
- package/dist/chunk-DQU36Q7I.js.map +0 -1
- package/dist/chunk-EGD5DXFT.js.map +0 -1
- package/dist/chunk-KGCORI4L.js.map +0 -1
- package/dist/chunk-OHVFWCJP.js.map +0 -1
- package/dist/chunk-TV3YZ35S.js +0 -90
- package/dist/chunk-TV3YZ35S.js.map +0 -1
- package/dist/chunk-UWNYBOOO.js.map +0 -1
- package/dist/chunk-VU7SWWT5.js.map +0 -1
- package/dist/chunk-YHPM5D7Y.js.map +0 -1
- package/dist/chunk-ZWTNWAO4.js.map +0 -1
- package/dist/executor-723ZP6TH.js +0 -11
- package/dist/noydb-VZ4JVW55.js +0 -35
- /package/dist/{chunk-BIYRQQV6.js.map → chunk-3YWP3WBP.js.map} +0 -0
- /package/dist/{chunk-A5ZOOZFB.js.map → chunk-6H2ZUNR7.js.map} +0 -0
- /package/dist/{chunk-7HT2MEZ5.js.map → chunk-7BQ4QWYX.js.map} +0 -0
- /package/dist/{chunk-WBAYSNUQ.js.map → chunk-BI6ETQPF.js.map} +0 -0
- /package/dist/{chunk-56DJ7JVK.js.map → chunk-BR3AMFGS.js.map} +0 -0
- /package/dist/{chunk-YNTBADIY.js.map → chunk-CZI2A4MQ.js.map} +0 -0
- /package/dist/{chunk-COFPAMX6.js.map → chunk-DLZ2ONOD.js.map} +0 -0
- /package/dist/{chunk-PE4AQGFH.js.map → chunk-E2CDVKMH.js.map} +0 -0
- /package/dist/{chunk-L2FE64BU.js.map → chunk-FFXM3ZIF.js.map} +0 -0
- /package/dist/{chunk-WGHU7BLI.js.map → chunk-GNI5STXQ.js.map} +0 -0
- /package/dist/{chunk-NP6EZT44.js.map → chunk-IQLVUT37.js.map} +0 -0
- /package/dist/{chunk-7PS7EOCF.js.map → chunk-IXBIFDEW.js.map} +0 -0
- /package/dist/{chunk-LX3CB26H.js.map → chunk-KABJXG2F.js.map} +0 -0
- /package/dist/{chunk-DKO2QFSA.js.map → chunk-OB2ZJQ2D.js.map} +0 -0
- /package/dist/{chunk-4PEFEETV.js.map → chunk-OMAMZKKD.js.map} +0 -0
- /package/dist/{chunk-KI6HAJWL.js.map → chunk-QSUK7YWK.js.map} +0 -0
- /package/dist/{chunk-NSCVNK5K.js.map → chunk-SCJPI4Z5.js.map} +0 -0
- /package/dist/{chunk-6AJBSQU4.js.map → chunk-XL35NSEN.js.map} +0 -0
- /package/dist/{chunk-WIBHRONM.js.map → chunk-XWH4MXIU.js.map} +0 -0
- /package/dist/{executor-723ZP6TH.js.map → executor-AZLS3KBK.js.map} +0 -0
- /package/dist/{fanout-sidecar-N6OJX6QR.js.map → fanout-sidecar-67CMI3UT.js.map} +0 -0
- /package/dist/{issue-ADVS4OVP.js.map → issue-RZP3VI6O.js.map} +0 -0
- /package/dist/{ledger-CWSE3BLF.js.map → ledger-A3LL253R.js.map} +0 -0
- /package/dist/{noydb-VZ4JVW55.js.map → noydb-WCMY2ZOW.js.map} +0 -0
- /package/dist/{public-envelope-SYHEYQ3X.js.map → public-envelope-YP2UWMLG.js.map} +0 -0
- /package/dist/{registry-DK5YWAAA.js.map → registry-EB6SISTA.js.map} +0 -0
- /package/dist/{registry-XGLNADIE.js.map → registry-UTA4CLQS.js.map} +0 -0
- /package/dist/{revoke-ZDFKMR5E.js.map → revoke-HNMQZSCL.js.map} +0 -0
- /package/dist/{signer-P5D7Y72U.js.map → signer-DCMNKXSF.js.map} +0 -0
- /package/dist/{stale-7FRJVHN6.js.map → stale-W5PQTRYH.js.map} +0 -0
- /package/dist/{vault-group-W7QC4UYW.js.map → vault-group-DX2HFQMX.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -2397,6 +2397,30 @@ function parseToScaledInt(input, scale, rounding) {
|
|
|
2397
2397
|
}
|
|
2398
2398
|
return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
|
|
2399
2399
|
}
|
|
2400
|
+
function decimalScaleOf(input) {
|
|
2401
|
+
const s = toCanonicalDecimalString(input);
|
|
2402
|
+
if (s === null) return null;
|
|
2403
|
+
const dot = s.indexOf(".");
|
|
2404
|
+
return dot === -1 ? 0 : s.length - dot - 1;
|
|
2405
|
+
}
|
|
2406
|
+
function rescaleScaledInt(value, fromScale, toScale, rounding = "half-up") {
|
|
2407
|
+
if (toScale >= fromScale) return value * 10n ** BigInt(toScale - fromScale);
|
|
2408
|
+
const drop = fromScale - toScale;
|
|
2409
|
+
const negative = value < 0n;
|
|
2410
|
+
const absStr = (negative ? -value : value).toString().padStart(drop + 1, "0");
|
|
2411
|
+
const keptStr = absStr.slice(0, absStr.length - drop);
|
|
2412
|
+
const tail = absStr.slice(absStr.length - drop);
|
|
2413
|
+
let magnitude = BigInt(keptStr);
|
|
2414
|
+
if (!/^0+$/.test(tail)) {
|
|
2415
|
+
const lastKeptDigit = Number(keptStr[keptStr.length - 1]);
|
|
2416
|
+
const firstDiscarded = Number(tail[0]);
|
|
2417
|
+
const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1));
|
|
2418
|
+
if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {
|
|
2419
|
+
magnitude += 1n;
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
return negative && magnitude !== 0n ? -magnitude : magnitude;
|
|
2423
|
+
}
|
|
2400
2424
|
function formatScaledInt(value, scale) {
|
|
2401
2425
|
const negative = value < 0n;
|
|
2402
2426
|
const abs = (negative ? -value : value).toString();
|
|
@@ -2616,6 +2640,146 @@ var init_tiers = __esm({
|
|
|
2616
2640
|
}
|
|
2617
2641
|
});
|
|
2618
2642
|
|
|
2643
|
+
// src/money/where.ts
|
|
2644
|
+
function isMoneyValueObject2(v) {
|
|
2645
|
+
return typeof v === "object" && v !== null && "currency" in v;
|
|
2646
|
+
}
|
|
2647
|
+
function parseOperand(field, raw, desc) {
|
|
2648
|
+
let amount;
|
|
2649
|
+
let currency;
|
|
2650
|
+
if (desc.mode === "fixed") {
|
|
2651
|
+
currency = desc.fixedCurrency;
|
|
2652
|
+
amount = raw;
|
|
2653
|
+
} else if (isMoneyValueObject2(raw)) {
|
|
2654
|
+
currency = String(raw.currency);
|
|
2655
|
+
amount = raw.amount;
|
|
2656
|
+
} else {
|
|
2657
|
+
const sole = desc.soleCurrency();
|
|
2658
|
+
if (sole === void 0) {
|
|
2659
|
+
throw new MoneyUnsupportedError(
|
|
2660
|
+
`where("${field}"): field is multi-currency \u2014 compare against { amount, currency }, not a bare amount`
|
|
2661
|
+
);
|
|
2662
|
+
}
|
|
2663
|
+
currency = sole;
|
|
2664
|
+
amount = raw;
|
|
2665
|
+
}
|
|
2666
|
+
if (typeof amount !== "number" && typeof amount !== "string") {
|
|
2667
|
+
throw new MoneyUnsupportedError(
|
|
2668
|
+
`where("${field}"): operand ${JSON.stringify(raw)} is not a money amount`
|
|
2669
|
+
);
|
|
2670
|
+
}
|
|
2671
|
+
const r = parseToScaledInt(amount, desc.scaleFor(currency), desc.rounding);
|
|
2672
|
+
if (!r.ok) {
|
|
2673
|
+
throw new MoneyUnsupportedError(
|
|
2674
|
+
`where("${field}"): operand ${JSON.stringify(amount)} is not a finite decimal`
|
|
2675
|
+
);
|
|
2676
|
+
}
|
|
2677
|
+
return { scaled: r.value.toString(), currency };
|
|
2678
|
+
}
|
|
2679
|
+
function moneyFieldClause(field, op, value, desc) {
|
|
2680
|
+
switch (op) {
|
|
2681
|
+
case "==":
|
|
2682
|
+
case "!=":
|
|
2683
|
+
case "<":
|
|
2684
|
+
case "<=":
|
|
2685
|
+
case ">":
|
|
2686
|
+
case ">=": {
|
|
2687
|
+
const e = parseOperand(field, value, desc);
|
|
2688
|
+
return withMoney(field, op, value, desc, [e]);
|
|
2689
|
+
}
|
|
2690
|
+
case "between": {
|
|
2691
|
+
if (!Array.isArray(value) || value.length !== 2) {
|
|
2692
|
+
throw new MoneyUnsupportedError(`where("${field}"): 'between' needs a [lo, hi] tuple`);
|
|
2693
|
+
}
|
|
2694
|
+
const lo = parseOperand(field, value[0], desc);
|
|
2695
|
+
const hi = parseOperand(field, value[1], desc);
|
|
2696
|
+
if (lo.currency !== hi.currency) {
|
|
2697
|
+
throw new MoneyUnsupportedError(
|
|
2698
|
+
`where("${field}"): 'between' bounds mix currencies (${lo.currency} vs ${hi.currency})`
|
|
2699
|
+
);
|
|
2700
|
+
}
|
|
2701
|
+
return withMoney(field, op, value, desc, [lo, hi]);
|
|
2702
|
+
}
|
|
2703
|
+
case "in": {
|
|
2704
|
+
if (!Array.isArray(value)) {
|
|
2705
|
+
throw new MoneyUnsupportedError(`where("${field}"): 'in' needs an array of amounts`);
|
|
2706
|
+
}
|
|
2707
|
+
return withMoney(field, op, value, desc, value.map((v) => parseOperand(field, v, desc)));
|
|
2708
|
+
}
|
|
2709
|
+
default:
|
|
2710
|
+
throw new MoneyUnsupportedError(
|
|
2711
|
+
`where("${field}"): operator '${op}' is not supported on a money field`
|
|
2712
|
+
);
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
function withMoney(field, op, originalValue, desc, entries) {
|
|
2716
|
+
const money2 = { mode: desc.mode, entries };
|
|
2717
|
+
const value = desc.mode !== "fixed" ? originalValue : entries.length === 1 && op !== "in" && op !== "between" ? entries[0].scaled : entries.map((e) => e.scaled);
|
|
2718
|
+
return { type: "field", field, op, value, money: money2 };
|
|
2719
|
+
}
|
|
2720
|
+
function readStored(actual, operand) {
|
|
2721
|
+
let amount;
|
|
2722
|
+
let currency;
|
|
2723
|
+
if (operand.mode === "fixed") {
|
|
2724
|
+
if (typeof actual !== "string" && typeof actual !== "number") return null;
|
|
2725
|
+
amount = actual;
|
|
2726
|
+
currency = operand.entries[0]?.currency ?? "";
|
|
2727
|
+
} else {
|
|
2728
|
+
if (!isMoneyValueObject2(actual)) return null;
|
|
2729
|
+
if (typeof actual.currency !== "string") return null;
|
|
2730
|
+
amount = actual.amount;
|
|
2731
|
+
currency = actual.currency;
|
|
2732
|
+
}
|
|
2733
|
+
if (typeof amount !== "string" && typeof amount !== "number") return null;
|
|
2734
|
+
try {
|
|
2735
|
+
return { scaled: BigInt(amount).toString(), currency };
|
|
2736
|
+
} catch {
|
|
2737
|
+
return null;
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
function evaluateMoneyClause(actual, op, operand) {
|
|
2741
|
+
const stored = readStored(actual, operand);
|
|
2742
|
+
if (stored === null) return op === "!=";
|
|
2743
|
+
const a = BigInt(stored.scaled);
|
|
2744
|
+
if (op === "in") {
|
|
2745
|
+
return operand.entries.some(
|
|
2746
|
+
(e2) => e2.currency === stored.currency && BigInt(e2.scaled) === a
|
|
2747
|
+
);
|
|
2748
|
+
}
|
|
2749
|
+
if (op === "between") {
|
|
2750
|
+
const [lo, hi] = operand.entries;
|
|
2751
|
+
if (!lo || !hi || lo.currency !== stored.currency) return false;
|
|
2752
|
+
return a >= BigInt(lo.scaled) && a <= BigInt(hi.scaled);
|
|
2753
|
+
}
|
|
2754
|
+
const e = operand.entries[0];
|
|
2755
|
+
if (!e) return false;
|
|
2756
|
+
if (e.currency !== stored.currency) return op === "!=";
|
|
2757
|
+
const b = BigInt(e.scaled);
|
|
2758
|
+
switch (op) {
|
|
2759
|
+
case "==":
|
|
2760
|
+
return a === b;
|
|
2761
|
+
case "!=":
|
|
2762
|
+
return a !== b;
|
|
2763
|
+
case "<":
|
|
2764
|
+
return a < b;
|
|
2765
|
+
case "<=":
|
|
2766
|
+
return a <= b;
|
|
2767
|
+
case ">":
|
|
2768
|
+
return a > b;
|
|
2769
|
+
case ">=":
|
|
2770
|
+
return a >= b;
|
|
2771
|
+
default:
|
|
2772
|
+
return false;
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
var init_where = __esm({
|
|
2776
|
+
"src/money/where.ts"() {
|
|
2777
|
+
"use strict";
|
|
2778
|
+
init_fixed_point();
|
|
2779
|
+
init_descriptor();
|
|
2780
|
+
}
|
|
2781
|
+
});
|
|
2782
|
+
|
|
2619
2783
|
// src/query/predicate.ts
|
|
2620
2784
|
function readPath(record, path) {
|
|
2621
2785
|
if (record === null || record === void 0) return void 0;
|
|
@@ -2633,6 +2797,7 @@ function readPath(record, path) {
|
|
|
2633
2797
|
function evaluateFieldClause(record, clause) {
|
|
2634
2798
|
const actual = readPath(record, clause.field);
|
|
2635
2799
|
const { op, value } = clause;
|
|
2800
|
+
if (clause.money) return evaluateMoneyClause(actual, op, clause.money);
|
|
2636
2801
|
switch (op) {
|
|
2637
2802
|
case "==":
|
|
2638
2803
|
return actual === value;
|
|
@@ -2673,14 +2838,14 @@ function isComparable(a, b) {
|
|
|
2673
2838
|
if (a instanceof Date && b instanceof Date) return true;
|
|
2674
2839
|
return false;
|
|
2675
2840
|
}
|
|
2676
|
-
function evaluateClause(record, clause) {
|
|
2841
|
+
function evaluateClause(record, clause, fnRecord) {
|
|
2677
2842
|
switch (clause.type) {
|
|
2678
2843
|
case "field":
|
|
2679
2844
|
return evaluateFieldClause(record, clause);
|
|
2680
2845
|
case "filter":
|
|
2681
|
-
return clause.fn(record);
|
|
2846
|
+
return clause.fn(fnRecord !== void 0 ? fnRecord : record);
|
|
2682
2847
|
case "wherePredicate":
|
|
2683
|
-
return clause.fn(record, clause.ctx);
|
|
2848
|
+
return clause.fn(fnRecord !== void 0 ? fnRecord : record, clause.ctx);
|
|
2684
2849
|
case "crossJoin":
|
|
2685
2850
|
throw new Error(
|
|
2686
2851
|
`evaluateClause: 'crossJoin' clauses are expansion primitives and are not evaluated per-record. This is a query planner routing error \u2014 crossJoin clauses must be extracted from the clause list before calling evaluateClause or filterRecords.`
|
|
@@ -2688,31 +2853,48 @@ function evaluateClause(record, clause) {
|
|
|
2688
2853
|
case "group":
|
|
2689
2854
|
if (clause.op === "and") {
|
|
2690
2855
|
for (const child of clause.clauses) {
|
|
2691
|
-
if (!evaluateClause(record, child)) return false;
|
|
2856
|
+
if (!evaluateClause(record, child, fnRecord)) return false;
|
|
2692
2857
|
}
|
|
2693
2858
|
return true;
|
|
2694
2859
|
} else {
|
|
2695
2860
|
for (const child of clause.clauses) {
|
|
2696
|
-
if (evaluateClause(record, child)) return true;
|
|
2861
|
+
if (evaluateClause(record, child, fnRecord)) return true;
|
|
2697
2862
|
}
|
|
2698
2863
|
return false;
|
|
2699
2864
|
}
|
|
2700
2865
|
}
|
|
2701
2866
|
}
|
|
2867
|
+
function hasFnClause(clauses) {
|
|
2868
|
+
for (const c of clauses) {
|
|
2869
|
+
if (c.type === "filter" || c.type === "wherePredicate") return true;
|
|
2870
|
+
if (c.type === "group" && hasFnClause(c.clauses)) return true;
|
|
2871
|
+
}
|
|
2872
|
+
return false;
|
|
2873
|
+
}
|
|
2702
2874
|
var init_predicate = __esm({
|
|
2703
2875
|
"src/query/predicate.ts"() {
|
|
2704
2876
|
"use strict";
|
|
2877
|
+
init_where();
|
|
2705
2878
|
}
|
|
2706
2879
|
});
|
|
2707
2880
|
|
|
2708
2881
|
// src/money/money-reducer.ts
|
|
2709
|
-
function
|
|
2710
|
-
if (typeof v === "
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2882
|
+
function toScaledIntFromAny(v, scale) {
|
|
2883
|
+
if (typeof v === "bigint") return v;
|
|
2884
|
+
if (typeof v === "number") {
|
|
2885
|
+
const r = parseToScaledInt(v, scale);
|
|
2886
|
+
return r.ok ? r.value : null;
|
|
2887
|
+
}
|
|
2888
|
+
if (typeof v === "string") {
|
|
2889
|
+
if (!v.includes(".")) {
|
|
2890
|
+
try {
|
|
2891
|
+
return BigInt(v);
|
|
2892
|
+
} catch {
|
|
2893
|
+
return null;
|
|
2894
|
+
}
|
|
2715
2895
|
}
|
|
2896
|
+
const r = parseToScaledInt(v, scale);
|
|
2897
|
+
return r.ok ? r.value : null;
|
|
2716
2898
|
}
|
|
2717
2899
|
return null;
|
|
2718
2900
|
}
|
|
@@ -2720,13 +2902,15 @@ function readMoney(record, field, desc) {
|
|
|
2720
2902
|
const raw = readPath(record, field);
|
|
2721
2903
|
if (raw === null || raw === void 0) return null;
|
|
2722
2904
|
if (desc.mode === "fixed") {
|
|
2723
|
-
const
|
|
2724
|
-
|
|
2905
|
+
const cur = desc.fixedCurrency;
|
|
2906
|
+
const value2 = toScaledIntFromAny(raw, desc.scaleFor(cur));
|
|
2907
|
+
return value2 === null ? null : { currency: cur, value: value2 };
|
|
2725
2908
|
}
|
|
2726
2909
|
if (typeof raw !== "object") return null;
|
|
2727
2910
|
const o = raw;
|
|
2728
2911
|
if (typeof o.currency !== "string") return null;
|
|
2729
|
-
const
|
|
2912
|
+
const scale = desc.allows(o.currency) ? desc.scaleFor(o.currency) : 0;
|
|
2913
|
+
const value = toScaledIntFromAny(o.amount, scale);
|
|
2730
2914
|
return value === null ? null : { currency: o.currency, value };
|
|
2731
2915
|
}
|
|
2732
2916
|
function targetScaleFor(desc, currency) {
|
|
@@ -3044,11 +3228,14 @@ function warnCardinalityApproaching(fields, observed) {
|
|
|
3044
3228
|
`[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.`
|
|
3045
3229
|
);
|
|
3046
3230
|
}
|
|
3047
|
-
function groupAndReduce(records, fieldOrFields, spec) {
|
|
3231
|
+
function groupAndReduce(records, fieldOrFields, spec, moneyFields) {
|
|
3048
3232
|
const fields = typeof fieldOrFields === "string" ? [fieldOrFields] : fieldOrFields;
|
|
3049
3233
|
if (fields.length === 0) {
|
|
3050
3234
|
throw new Error(".groupBy() requires at least one field");
|
|
3051
3235
|
}
|
|
3236
|
+
if (moneyFields) {
|
|
3237
|
+
spec = wrapMoneyReducers(spec, moneyFields);
|
|
3238
|
+
}
|
|
3052
3239
|
const buckets = /* @__PURE__ */ new Map();
|
|
3053
3240
|
const fieldLabel = fields.length === 1 ? fields[0] : `[${fields.join(", ")}]`;
|
|
3054
3241
|
for (const record of records) {
|
|
@@ -3491,10 +3678,14 @@ function summarizeQueryPlan(query) {
|
|
|
3491
3678
|
});
|
|
3492
3679
|
}
|
|
3493
3680
|
function summarizeUnionPlan(spec) {
|
|
3494
|
-
const arms = (spec.unionSources ?? []).map((s) =>
|
|
3681
|
+
const arms = (spec.unionSources ?? []).map((s) => {
|
|
3682
|
+
const joins = s.join?.length ? `[${s.join.map((j) => `${j.field}\u2192${j.as}`).join(";")}]` : "";
|
|
3683
|
+
return `${s.collection}${joins}`;
|
|
3684
|
+
}).join(",");
|
|
3495
3685
|
const groupBy = Array.isArray(spec.groupBy) ? [...spec.groupBy].sort().join(",") : typeof spec.groupBy === "string" ? spec.groupBy : "";
|
|
3496
3686
|
const aggKeys = spec.aggregate ? Object.keys(spec.aggregate).sort().join(",") : "";
|
|
3497
|
-
|
|
3687
|
+
const moneyKeys = spec.moneyFields ? Object.keys(spec.moneyFields).sort().join(",") : "";
|
|
3688
|
+
return `union(${arms})|groupBy(${groupBy})|aggregate(${aggKeys})|money(${moneyKeys})`;
|
|
3498
3689
|
}
|
|
3499
3690
|
var init_dependency_analyzer = __esm({
|
|
3500
3691
|
"src/materialized-views/dependency-analyzer.ts"() {
|
|
@@ -3621,6 +3812,7 @@ var init_registry = __esm({
|
|
|
3621
3812
|
let isQuery = false;
|
|
3622
3813
|
if (spec.unionSources) {
|
|
3623
3814
|
dependencies = new Set(spec.unionSources.map((s) => s.collection));
|
|
3815
|
+
if (spec.sources) for (const s of spec.sources) dependencies.add(s);
|
|
3624
3816
|
queryPlanSummary = summarizeUnionPlan(spec);
|
|
3625
3817
|
} else {
|
|
3626
3818
|
const q = spec.query(dbForQuery);
|
|
@@ -3775,7 +3967,13 @@ async function materializeUnionResult(spec, db) {
|
|
|
3775
3967
|
const unified = [];
|
|
3776
3968
|
for (const arm of spec.unionSources) {
|
|
3777
3969
|
const coll = db.collection(arm.collection);
|
|
3778
|
-
|
|
3970
|
+
let q = coll.query();
|
|
3971
|
+
if (arm.join?.length) {
|
|
3972
|
+
for (const leg of arm.join) {
|
|
3973
|
+
q = q.join(leg.field, { as: leg.as, maxRows: leg.maxRows, strategy: leg.strategy });
|
|
3974
|
+
}
|
|
3975
|
+
}
|
|
3976
|
+
const sourceRows = q.toArray();
|
|
3779
3977
|
for (const r of sourceRows) {
|
|
3780
3978
|
const mapped = arm.map(r);
|
|
3781
3979
|
if (mapped == null) continue;
|
|
@@ -3792,7 +3990,7 @@ async function materializeUnionResult(spec, db) {
|
|
|
3792
3990
|
}
|
|
3793
3991
|
return [...seen.values()];
|
|
3794
3992
|
}
|
|
3795
|
-
return groupAndReduce(unified, groupFields, spec.aggregate);
|
|
3993
|
+
return groupAndReduce(unified, groupFields, spec.aggregate, spec.moneyFields);
|
|
3796
3994
|
}
|
|
3797
3995
|
async function listOutputIds(outputColl) {
|
|
3798
3996
|
const cAny = outputColl;
|
|
@@ -4347,11 +4545,15 @@ var init_read_only_facade = __esm({
|
|
|
4347
4545
|
});
|
|
4348
4546
|
|
|
4349
4547
|
// src/derivations/strategy-hash.ts
|
|
4350
|
-
async function computeStrategyHash(source, outputKeys, derive) {
|
|
4548
|
+
async function computeStrategyHash(source, outputKeys, derive, sources) {
|
|
4351
4549
|
const canonical2 = JSON.stringify({
|
|
4352
4550
|
source,
|
|
4353
4551
|
outputs: [...outputKeys].sort(),
|
|
4354
|
-
derive: derive.toString()
|
|
4552
|
+
derive: derive.toString(),
|
|
4553
|
+
// Declared sibling sources (#344) — adding/removing a trigger
|
|
4554
|
+
// collection invalidates cached derived records. Omitted when empty
|
|
4555
|
+
// so strategies without siblings keep their existing hash.
|
|
4556
|
+
...sources?.length ? { sources: [...sources].sort() } : {}
|
|
4355
4557
|
});
|
|
4356
4558
|
const bytes = new TextEncoder().encode(canonical2);
|
|
4357
4559
|
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
@@ -4380,11 +4582,16 @@ var init_registry3 = __esm({
|
|
|
4380
4582
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4381
4583
|
async register(spec) {
|
|
4382
4584
|
const outputKeys = Object.keys(spec.outputs);
|
|
4383
|
-
const strategyHash = await computeStrategyHash(spec.source, outputKeys, spec.derive);
|
|
4585
|
+
const strategyHash = await computeStrategyHash(spec.source, outputKeys, spec.derive, spec.sources);
|
|
4384
4586
|
const reg = { spec, strategyHash };
|
|
4385
4587
|
const fromSource = this._bySource.get(spec.source);
|
|
4386
4588
|
if (fromSource) fromSource.push(reg);
|
|
4387
4589
|
else this._bySource.set(spec.source, [reg]);
|
|
4590
|
+
for (const extra of spec.sources ?? []) {
|
|
4591
|
+
const fromExtra = this._bySource.get(extra);
|
|
4592
|
+
if (fromExtra) fromExtra.push(reg);
|
|
4593
|
+
else this._bySource.set(extra, [reg]);
|
|
4594
|
+
}
|
|
4388
4595
|
for (const key of outputKeys) {
|
|
4389
4596
|
const output = spec.outputs[key];
|
|
4390
4597
|
if (!output) continue;
|
|
@@ -5577,9 +5784,11 @@ __export(src_exports, {
|
|
|
5577
5784
|
WeakPassphraseError: () => WeakPassphraseError,
|
|
5578
5785
|
activeSessionCount: () => activeSessionCount,
|
|
5579
5786
|
additiveOnly: () => additiveOnly,
|
|
5787
|
+
allocate: () => allocate,
|
|
5580
5788
|
applyI18nLocale: () => applyI18nLocale,
|
|
5581
5789
|
applyJoins: () => applyJoins,
|
|
5582
5790
|
applyPatch: () => applyPatch,
|
|
5791
|
+
asMoney: () => asMoney,
|
|
5583
5792
|
assertStrongPassphrase: () => assertStrongPassphrase,
|
|
5584
5793
|
assertTierAccess: () => assertTierAccess,
|
|
5585
5794
|
avg: () => avg,
|
|
@@ -5656,6 +5865,7 @@ __export(src_exports, {
|
|
|
5656
5865
|
isI18nTextDescriptor: () => isI18nTextDescriptor,
|
|
5657
5866
|
isMagicLinkGrantExpired: () => isMagicLinkGrantExpired,
|
|
5658
5867
|
isMoneyDescriptor: () => isMoneyDescriptor,
|
|
5868
|
+
isMoneyString: () => isMoneyString,
|
|
5659
5869
|
isPreCompressed: () => isPreCompressed,
|
|
5660
5870
|
isPublicEnvelope: () => isPublicEnvelope,
|
|
5661
5871
|
isSessionAlive: () => isSessionAlive,
|
|
@@ -5688,6 +5898,8 @@ __export(src_exports, {
|
|
|
5688
5898
|
mintShamirRecoveryEntry: () => mintShamirRecoveryEntry,
|
|
5689
5899
|
mintWrappedDeksBlob: () => mintWrappedDeksBlob,
|
|
5690
5900
|
money: () => money,
|
|
5901
|
+
moneyNumber: () => moneyNumber,
|
|
5902
|
+
mulRate: () => mulRate,
|
|
5691
5903
|
paddedIndex: () => paddedIndex,
|
|
5692
5904
|
parseBytes: () => parseBytes,
|
|
5693
5905
|
parseIndex: () => parseIndex,
|
|
@@ -5714,6 +5926,7 @@ __export(src_exports, {
|
|
|
5714
5926
|
resolveI18nText: () => resolveI18nText,
|
|
5715
5927
|
resolvePolicy: () => resolvePolicy,
|
|
5716
5928
|
resolvePublicEnvelopeSchema: () => resolveSchema,
|
|
5929
|
+
resolveSequenceKey: () => resolveSequenceKey,
|
|
5717
5930
|
resolveSession: () => resolveSession,
|
|
5718
5931
|
revokeAllSessions: () => revokeAllSessions,
|
|
5719
5932
|
revokeDelegation: () => revokeDelegation,
|
|
@@ -7812,6 +8025,21 @@ init_crypto();
|
|
|
7812
8025
|
init_errors();
|
|
7813
8026
|
var SEQUENCE_COLLECTION = "_sequences";
|
|
7814
8027
|
var MAX_NEXT_ATTEMPTS = 16;
|
|
8028
|
+
function resolveSequenceKey(series, opts) {
|
|
8029
|
+
const partition = opts?.partition;
|
|
8030
|
+
if (!partition || partition.length === 0) return series;
|
|
8031
|
+
const parts = partition.map((p) => {
|
|
8032
|
+
if (typeof p === "number" && !Number.isFinite(p)) {
|
|
8033
|
+
throw new ValidationError(`sequence partition component must be a finite number, got ${p}`);
|
|
8034
|
+
}
|
|
8035
|
+
const s = String(p);
|
|
8036
|
+
if (s === "") {
|
|
8037
|
+
throw new ValidationError("sequence partition component must not be empty");
|
|
8038
|
+
}
|
|
8039
|
+
return encodeURIComponent(s);
|
|
8040
|
+
});
|
|
8041
|
+
return `${series}\0${parts.join("/")}`;
|
|
8042
|
+
}
|
|
7815
8043
|
async function sleepBackoff(attempt) {
|
|
7816
8044
|
const ceil = Math.min(2 ** attempt, 32);
|
|
7817
8045
|
const ms = Math.floor(Math.random() * ceil);
|
|
@@ -7842,7 +8070,8 @@ var SequenceStore = class {
|
|
|
7842
8070
|
handle(name) {
|
|
7843
8071
|
return {
|
|
7844
8072
|
next: () => this.next(name),
|
|
7845
|
-
peek: () => this.peek(name)
|
|
8073
|
+
peek: () => this.peek(name),
|
|
8074
|
+
seedTo: (n) => this.seedTo(name, n)
|
|
7846
8075
|
};
|
|
7847
8076
|
}
|
|
7848
8077
|
assertOnline() {
|
|
@@ -7895,6 +8124,30 @@ var SequenceStore = class {
|
|
|
7895
8124
|
void lastConflict;
|
|
7896
8125
|
throw new SequenceContentionError(name, MAX_NEXT_ATTEMPTS);
|
|
7897
8126
|
}
|
|
8127
|
+
async seedTo(name, n) {
|
|
8128
|
+
this.assertOnline();
|
|
8129
|
+
if (n <= 0) return;
|
|
8130
|
+
let lastConflict;
|
|
8131
|
+
for (let attempt = 0; attempt < MAX_NEXT_ATTEMPTS; attempt++) {
|
|
8132
|
+
const { env, value } = await this.read(name);
|
|
8133
|
+
if (value >= n) return;
|
|
8134
|
+
const expectedVersion = env?._v ?? 0;
|
|
8135
|
+
const envelope = await this.encryptState({ value: n }, expectedVersion + 1);
|
|
8136
|
+
try {
|
|
8137
|
+
await this.adapter.put(this.vault, SEQUENCE_COLLECTION, name, envelope, expectedVersion);
|
|
8138
|
+
return;
|
|
8139
|
+
} catch (err) {
|
|
8140
|
+
if (err instanceof ConflictError) {
|
|
8141
|
+
lastConflict = err;
|
|
8142
|
+
if (attempt < MAX_NEXT_ATTEMPTS - 1) await sleepBackoff(attempt);
|
|
8143
|
+
continue;
|
|
8144
|
+
}
|
|
8145
|
+
throw err;
|
|
8146
|
+
}
|
|
8147
|
+
}
|
|
8148
|
+
void lastConflict;
|
|
8149
|
+
throw new SequenceContentionError(name, MAX_NEXT_ATTEMPTS);
|
|
8150
|
+
}
|
|
7898
8151
|
};
|
|
7899
8152
|
|
|
7900
8153
|
// src/numbering/descriptor.ts
|
|
@@ -11579,6 +11832,111 @@ function applyI18nLocale(record, i18nFields, locale, fallback, layer = "read") {
|
|
|
11579
11832
|
// src/money/normalize.ts
|
|
11580
11833
|
init_fixed_point();
|
|
11581
11834
|
init_descriptor();
|
|
11835
|
+
|
|
11836
|
+
// src/money/paths.ts
|
|
11837
|
+
init_errors();
|
|
11838
|
+
var SEGMENT_RE = /^(\*|[^.[\]*]+)(\[\])?$/;
|
|
11839
|
+
var parseCache = /* @__PURE__ */ new Map();
|
|
11840
|
+
function parseMoneyPath(path) {
|
|
11841
|
+
const cached = parseCache.get(path);
|
|
11842
|
+
if (cached) return cached;
|
|
11843
|
+
if (typeof path !== "string" || path.length === 0) {
|
|
11844
|
+
throw new ValidationError("moneyFields: path must be a non-empty string");
|
|
11845
|
+
}
|
|
11846
|
+
const segments = [];
|
|
11847
|
+
for (const part of path.split(".")) {
|
|
11848
|
+
const m = SEGMENT_RE.exec(part);
|
|
11849
|
+
if (!m) {
|
|
11850
|
+
throw new ValidationError(
|
|
11851
|
+
`moneyFields: invalid path "${path}" \u2014 segment "${part}" must be a key, "key[]", "*", or "*[]"`
|
|
11852
|
+
);
|
|
11853
|
+
}
|
|
11854
|
+
const array = m[2] === "[]";
|
|
11855
|
+
segments.push(
|
|
11856
|
+
m[1] === "*" ? { kind: "wildcard", array } : { kind: "key", key: m[1], array }
|
|
11857
|
+
);
|
|
11858
|
+
}
|
|
11859
|
+
parseCache.set(path, segments);
|
|
11860
|
+
return segments;
|
|
11861
|
+
}
|
|
11862
|
+
function isSimpleMoneyPath(path) {
|
|
11863
|
+
return !path.includes(".") && !path.includes("[") && !path.includes("*");
|
|
11864
|
+
}
|
|
11865
|
+
function validateMoneyFieldPaths(moneyFields) {
|
|
11866
|
+
for (const path of Object.keys(moneyFields)) parseMoneyPath(path);
|
|
11867
|
+
}
|
|
11868
|
+
function transformAtMoneyPath(node, path, segments, index, visit, lenient) {
|
|
11869
|
+
if (node === null || node === void 0) return node;
|
|
11870
|
+
const seg = segments[index];
|
|
11871
|
+
const last = index === segments.length - 1;
|
|
11872
|
+
if (seg.kind === "key") {
|
|
11873
|
+
if (typeof node !== "object" || Array.isArray(node)) {
|
|
11874
|
+
if (lenient) return node;
|
|
11875
|
+
throw new ValidationError(
|
|
11876
|
+
`moneyFields: path "${path}" expected an object at segment "${seg.key}", got ${Array.isArray(node) ? "an array" : typeof node}`
|
|
11877
|
+
);
|
|
11878
|
+
}
|
|
11879
|
+
const obj2 = node;
|
|
11880
|
+
if (!(seg.key in obj2) || obj2[seg.key] === null || obj2[seg.key] === void 0) return node;
|
|
11881
|
+
if (seg.array) {
|
|
11882
|
+
const arr = obj2[seg.key];
|
|
11883
|
+
if (!Array.isArray(arr)) {
|
|
11884
|
+
if (lenient) return node;
|
|
11885
|
+
throw new ValidationError(
|
|
11886
|
+
`moneyFields: path "${path}" declares "${seg.key}[]" but the value is not an array`
|
|
11887
|
+
);
|
|
11888
|
+
}
|
|
11889
|
+
const cloned = [...arr];
|
|
11890
|
+
if (last) {
|
|
11891
|
+
for (let i = 0; i < cloned.length; i++) visit(cloned, i);
|
|
11892
|
+
} else {
|
|
11893
|
+
for (let i = 0; i < cloned.length; i++) {
|
|
11894
|
+
cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient);
|
|
11895
|
+
}
|
|
11896
|
+
}
|
|
11897
|
+
return { ...obj2, [seg.key]: cloned };
|
|
11898
|
+
}
|
|
11899
|
+
const clone3 = { ...obj2 };
|
|
11900
|
+
if (last) {
|
|
11901
|
+
visit(clone3, seg.key);
|
|
11902
|
+
} else {
|
|
11903
|
+
clone3[seg.key] = transformAtMoneyPath(clone3[seg.key], path, segments, index + 1, visit, lenient);
|
|
11904
|
+
}
|
|
11905
|
+
return clone3;
|
|
11906
|
+
}
|
|
11907
|
+
if (seg.array) {
|
|
11908
|
+
if (!Array.isArray(node)) {
|
|
11909
|
+
if (lenient) return node;
|
|
11910
|
+
throw new ValidationError(`moneyFields: path "${path}" declares "*[]" but the value is not an array`);
|
|
11911
|
+
}
|
|
11912
|
+
const cloned = [...node];
|
|
11913
|
+
if (last) {
|
|
11914
|
+
for (let i = 0; i < cloned.length; i++) visit(cloned, i);
|
|
11915
|
+
} else {
|
|
11916
|
+
for (let i = 0; i < cloned.length; i++) {
|
|
11917
|
+
cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient);
|
|
11918
|
+
}
|
|
11919
|
+
}
|
|
11920
|
+
return cloned;
|
|
11921
|
+
}
|
|
11922
|
+
if (typeof node !== "object" || Array.isArray(node)) {
|
|
11923
|
+
if (lenient) return node;
|
|
11924
|
+
throw new ValidationError(
|
|
11925
|
+
`moneyFields: path "${path}" applies "*" to a non-object (${Array.isArray(node) ? 'array \u2014 use "*[]"' : typeof node})`
|
|
11926
|
+
);
|
|
11927
|
+
}
|
|
11928
|
+
const obj = node;
|
|
11929
|
+
const clone2 = { ...obj };
|
|
11930
|
+
for (const key of Object.keys(obj)) {
|
|
11931
|
+
const v = clone2[key];
|
|
11932
|
+
if (v === null || v === void 0) continue;
|
|
11933
|
+
if (last) visit(clone2, key);
|
|
11934
|
+
else clone2[key] = transformAtMoneyPath(v, path, segments, index + 1, visit, lenient);
|
|
11935
|
+
}
|
|
11936
|
+
return clone2;
|
|
11937
|
+
}
|
|
11938
|
+
|
|
11939
|
+
// src/money/normalize.ts
|
|
11582
11940
|
function isMoneyValueObject(v) {
|
|
11583
11941
|
return typeof v === "object" && v !== null && "currency" in v;
|
|
11584
11942
|
}
|
|
@@ -11590,33 +11948,68 @@ function quantizeAmount(field, input, scale, rounding) {
|
|
|
11590
11948
|
}
|
|
11591
11949
|
return r.value.toString();
|
|
11592
11950
|
}
|
|
11951
|
+
function canonicalizeStoredMoney(record, moneyFields) {
|
|
11952
|
+
if (record === null || record === void 0) return record;
|
|
11953
|
+
if (!moneyFields || Object.keys(moneyFields).length === 0) return record;
|
|
11954
|
+
return decodeMoneyFields(record, moneyFields, "raw");
|
|
11955
|
+
}
|
|
11956
|
+
function canonicalizeIncomingMoney(record, moneyFields) {
|
|
11957
|
+
if (!moneyFields || Object.keys(moneyFields).length === 0) return record;
|
|
11958
|
+
try {
|
|
11959
|
+
return decodeMoneyFields(
|
|
11960
|
+
quantizeMoneyFields(record, moneyFields),
|
|
11961
|
+
moneyFields,
|
|
11962
|
+
"raw"
|
|
11963
|
+
);
|
|
11964
|
+
} catch {
|
|
11965
|
+
return record;
|
|
11966
|
+
}
|
|
11967
|
+
}
|
|
11968
|
+
function quantizeValue(field, raw, desc) {
|
|
11969
|
+
if (desc.mode === "fixed") {
|
|
11970
|
+
const currency2 = desc.fixedCurrency;
|
|
11971
|
+
return quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
|
|
11972
|
+
}
|
|
11973
|
+
let amount;
|
|
11974
|
+
let currency;
|
|
11975
|
+
if (isMoneyValueObject(raw)) {
|
|
11976
|
+
currency = String(raw.currency);
|
|
11977
|
+
amount = raw.amount;
|
|
11978
|
+
} else {
|
|
11979
|
+
const sole = desc.soleCurrency();
|
|
11980
|
+
if (sole === void 0) {
|
|
11981
|
+
throw new TypeError(
|
|
11982
|
+
`money: field "${field}" is multi-currency \u2014 write { amount, currency }, not a bare amount`
|
|
11983
|
+
);
|
|
11984
|
+
}
|
|
11985
|
+
currency = sole;
|
|
11986
|
+
amount = raw;
|
|
11987
|
+
}
|
|
11988
|
+
const scale = desc.scaleFor(currency);
|
|
11989
|
+
return { amount: quantizeAmount(field, amount, scale, desc.rounding), currency };
|
|
11990
|
+
}
|
|
11593
11991
|
function quantizeMoneyFields(record, moneyFields) {
|
|
11594
|
-
|
|
11595
|
-
for (const [
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
out[field] = quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
|
|
11992
|
+
let out = { ...record };
|
|
11993
|
+
for (const [path, desc] of Object.entries(moneyFields)) {
|
|
11994
|
+
if (isSimpleMoneyPath(path)) {
|
|
11995
|
+
const raw = out[path];
|
|
11996
|
+
if (raw === null || raw === void 0) continue;
|
|
11997
|
+
out[path] = quantizeValue(path, raw, desc);
|
|
11601
11998
|
continue;
|
|
11602
11999
|
}
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
amount = raw;
|
|
11617
|
-
}
|
|
11618
|
-
const scale = desc.scaleFor(currency);
|
|
11619
|
-
out[field] = { amount: quantizeAmount(field, amount, scale, desc.rounding), currency };
|
|
12000
|
+
out = transformAtMoneyPath(
|
|
12001
|
+
out,
|
|
12002
|
+
path,
|
|
12003
|
+
parseMoneyPath(path),
|
|
12004
|
+
0,
|
|
12005
|
+
(container, key) => {
|
|
12006
|
+
const raw = container[key];
|
|
12007
|
+
if (raw === null || raw === void 0) return;
|
|
12008
|
+
container[key] = quantizeValue(path, raw, desc);
|
|
12009
|
+
},
|
|
12010
|
+
/* lenient */
|
|
12011
|
+
false
|
|
12012
|
+
);
|
|
11620
12013
|
}
|
|
11621
12014
|
return out;
|
|
11622
12015
|
}
|
|
@@ -11629,33 +12022,70 @@ function formatCurrency(decimal, currency, scale, locale) {
|
|
|
11629
12022
|
});
|
|
11630
12023
|
return fmt.format(decimal);
|
|
11631
12024
|
}
|
|
12025
|
+
function decodeValue(stored, desc) {
|
|
12026
|
+
let currency;
|
|
12027
|
+
let scaledIntString;
|
|
12028
|
+
if (desc.mode === "fixed") {
|
|
12029
|
+
if (typeof stored !== "string" && typeof stored !== "number") return null;
|
|
12030
|
+
currency = desc.fixedCurrency;
|
|
12031
|
+
scaledIntString = String(stored);
|
|
12032
|
+
} else {
|
|
12033
|
+
if (!isMoneyValueObject(stored)) return null;
|
|
12034
|
+
const amount = stored.amount;
|
|
12035
|
+
if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") return null;
|
|
12036
|
+
currency = stored.currency;
|
|
12037
|
+
scaledIntString = String(amount);
|
|
12038
|
+
}
|
|
12039
|
+
const scale = desc.scaleFor(currency);
|
|
12040
|
+
let decimal;
|
|
12041
|
+
try {
|
|
12042
|
+
decimal = formatScaledInt(BigInt(scaledIntString), scale);
|
|
12043
|
+
} catch {
|
|
12044
|
+
return null;
|
|
12045
|
+
}
|
|
12046
|
+
return {
|
|
12047
|
+
decoded: desc.mode === "fixed" ? decimal : { amount: decimal, currency },
|
|
12048
|
+
decimal,
|
|
12049
|
+
currency,
|
|
12050
|
+
scale
|
|
12051
|
+
};
|
|
12052
|
+
}
|
|
11632
12053
|
function decodeMoneyFields(record, moneyFields, locale) {
|
|
11633
|
-
|
|
12054
|
+
let out = { ...record };
|
|
11634
12055
|
const format = locale !== "raw";
|
|
11635
12056
|
const fmtLocale = typeof locale === "string" && locale !== "raw" ? locale : "en-US";
|
|
11636
|
-
for (const [
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") continue;
|
|
11649
|
-
currency = stored.currency;
|
|
11650
|
-
scaledIntString = String(amount);
|
|
11651
|
-
}
|
|
11652
|
-
const scale = desc.scaleFor(currency);
|
|
11653
|
-
const decimal = formatScaledInt(BigInt(scaledIntString), scale);
|
|
11654
|
-
out[field] = desc.mode === "fixed" ? decimal : { amount: decimal, currency };
|
|
11655
|
-
if (format) {
|
|
11656
|
-
out[`${field}Formatted`] = formatCurrency(decimal, currency, scale, fmtLocale);
|
|
11657
|
-
out[`${field}Number`] = Number(decimal);
|
|
12057
|
+
for (const [path, desc] of Object.entries(moneyFields)) {
|
|
12058
|
+
if (isSimpleMoneyPath(path)) {
|
|
12059
|
+
const stored = out[path];
|
|
12060
|
+
if (stored === null || stored === void 0) continue;
|
|
12061
|
+
const r = decodeValue(stored, desc);
|
|
12062
|
+
if (r === null) continue;
|
|
12063
|
+
out[path] = r.decoded;
|
|
12064
|
+
if (format) {
|
|
12065
|
+
out[`${path}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale);
|
|
12066
|
+
out[`${path}Number`] = Number(r.decimal);
|
|
12067
|
+
}
|
|
12068
|
+
continue;
|
|
11658
12069
|
}
|
|
12070
|
+
out = transformAtMoneyPath(
|
|
12071
|
+
out,
|
|
12072
|
+
path,
|
|
12073
|
+
parseMoneyPath(path),
|
|
12074
|
+
0,
|
|
12075
|
+
(container, key) => {
|
|
12076
|
+
const stored = container[key];
|
|
12077
|
+
if (stored === null || stored === void 0) return;
|
|
12078
|
+
const r = decodeValue(stored, desc);
|
|
12079
|
+
if (r === null) return;
|
|
12080
|
+
container[key] = r.decoded;
|
|
12081
|
+
if (format && typeof key === "string" && !Array.isArray(container)) {
|
|
12082
|
+
container[`${key}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale);
|
|
12083
|
+
container[`${key}Number`] = Number(r.decimal);
|
|
12084
|
+
}
|
|
12085
|
+
},
|
|
12086
|
+
/* lenient */
|
|
12087
|
+
true
|
|
12088
|
+
);
|
|
11659
12089
|
}
|
|
11660
12090
|
return out;
|
|
11661
12091
|
}
|
|
@@ -12008,6 +12438,7 @@ var NO_AGGREGATE = {
|
|
|
12008
12438
|
|
|
12009
12439
|
// src/query/builder.ts
|
|
12010
12440
|
init_money_reducer();
|
|
12441
|
+
init_where();
|
|
12011
12442
|
var EMPTY_PLAN = {
|
|
12012
12443
|
clauses: [],
|
|
12013
12444
|
orderBy: [],
|
|
@@ -12099,9 +12530,18 @@ var Query = class _Query {
|
|
|
12099
12530
|
this.predicates
|
|
12100
12531
|
);
|
|
12101
12532
|
}
|
|
12102
|
-
/**
|
|
12533
|
+
/**
|
|
12534
|
+
* Add a field comparison. Multiple where() calls are AND-combined.
|
|
12535
|
+
*
|
|
12536
|
+
* A declared money field compares in MAJOR units (#336): the operand
|
|
12537
|
+
* (`10000`, `'10000.00'`, or `{ amount, currency }` in multi mode) is
|
|
12538
|
+
* quantized into stored scaled-int space at build time and evaluated
|
|
12539
|
+
* BigInt-exact per record. A malformed operand or a string operator
|
|
12540
|
+
* (`contains`/`startsWith`) throws here, at the call site.
|
|
12541
|
+
*/
|
|
12103
12542
|
where(field, op, value) {
|
|
12104
|
-
const
|
|
12543
|
+
const desc = this.source.moneyFields?.[field];
|
|
12544
|
+
const clause = desc ? moneyFieldClause(field, op, value, desc) : { type: "field", field, op, value };
|
|
12105
12545
|
return new _Query(
|
|
12106
12546
|
this.source,
|
|
12107
12547
|
{ ...this.plan, clauses: [...this.plan.clauses, clause] },
|
|
@@ -12427,7 +12867,7 @@ var Query = class _Query {
|
|
|
12427
12867
|
}
|
|
12428
12868
|
const { candidates, remainingClauses } = candidateRecords(this.source, this.plan.clauses);
|
|
12429
12869
|
if (remainingClauses.length === 0) return candidates.length;
|
|
12430
|
-
return filterRecords(candidates, remainingClauses).length;
|
|
12870
|
+
return filterRecords(candidates, remainingClauses, fnViewDecoder(this.source)).length;
|
|
12431
12871
|
}
|
|
12432
12872
|
/**
|
|
12433
12873
|
* Reduce the matching records through a named set of reducers.
|
|
@@ -12484,7 +12924,7 @@ var Query = class _Query {
|
|
|
12484
12924
|
return executeClausePipeline(source, clauses, joinCtx);
|
|
12485
12925
|
}
|
|
12486
12926
|
const { candidates, remainingClauses } = candidateRecords(source, clauses);
|
|
12487
|
-
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses);
|
|
12927
|
+
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
|
|
12488
12928
|
};
|
|
12489
12929
|
const upstreams = [];
|
|
12490
12930
|
if (source.subscribe) {
|
|
@@ -12507,7 +12947,7 @@ var Query = class _Query {
|
|
|
12507
12947
|
return executeClausePipeline(source, clauses, joinCtx);
|
|
12508
12948
|
}
|
|
12509
12949
|
const { candidates, remainingClauses } = candidateRecords(source, clauses);
|
|
12510
|
-
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses);
|
|
12950
|
+
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
|
|
12511
12951
|
};
|
|
12512
12952
|
const upstreams = [];
|
|
12513
12953
|
if (source.subscribe) {
|
|
@@ -12659,7 +13099,7 @@ function executePlanWithSource(source, plan, joinContext) {
|
|
|
12659
13099
|
result = executeClausePipeline(source, plan.clauses, joinContext);
|
|
12660
13100
|
} else {
|
|
12661
13101
|
const { candidates, remainingClauses } = candidateRecords(source, plan.clauses);
|
|
12662
|
-
result = remainingClauses.length === 0 ? [...candidates] : filterRecords(candidates, remainingClauses);
|
|
13102
|
+
result = remainingClauses.length === 0 ? [...candidates] : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
|
|
12663
13103
|
}
|
|
12664
13104
|
if (plan.orderBy.length > 0) {
|
|
12665
13105
|
result = sortRecords(result, plan.orderBy);
|
|
@@ -12682,6 +13122,7 @@ function candidateRecords(source, clauses) {
|
|
|
12682
13122
|
const clause = clauses[i];
|
|
12683
13123
|
if (clause.type !== "field") continue;
|
|
12684
13124
|
if (!indexes.has(clause.field)) continue;
|
|
13125
|
+
if (clause.money?.mode === "multi") continue;
|
|
12685
13126
|
let ids = null;
|
|
12686
13127
|
if (clause.op === "==") {
|
|
12687
13128
|
ids = indexes.lookupEqual(clause.field, clause.value);
|
|
@@ -12727,13 +13168,20 @@ function executePlan(records, plan) {
|
|
|
12727
13168
|
}
|
|
12728
13169
|
return result;
|
|
12729
13170
|
}
|
|
12730
|
-
function
|
|
13171
|
+
function fnViewDecoder(source) {
|
|
13172
|
+
const mf = source.moneyFields;
|
|
13173
|
+
if (!mf || Object.keys(mf).length === 0) return void 0;
|
|
13174
|
+
return (r) => decodeMoneyFields(r, mf, "raw");
|
|
13175
|
+
}
|
|
13176
|
+
function filterRecords(records, clauses, decodeForFns) {
|
|
12731
13177
|
if (clauses.length === 0) return [...records];
|
|
13178
|
+
const needsFnView = decodeForFns !== void 0 && hasFnClause(clauses);
|
|
12732
13179
|
const out = [];
|
|
12733
13180
|
for (const r of records) {
|
|
13181
|
+
const fnView = needsFnView ? decodeForFns(r) : void 0;
|
|
12734
13182
|
let matches = true;
|
|
12735
13183
|
for (const clause of clauses) {
|
|
12736
|
-
if (!evaluateClause(r, clause)) {
|
|
13184
|
+
if (!evaluateClause(r, clause, fnView)) {
|
|
12737
13185
|
matches = false;
|
|
12738
13186
|
break;
|
|
12739
13187
|
}
|
|
@@ -12745,10 +13193,11 @@ function filterRecords(records, clauses) {
|
|
|
12745
13193
|
function executeClausePipeline(source, clauses, joinContext) {
|
|
12746
13194
|
let rel = [...source.snapshot()];
|
|
12747
13195
|
let filterBatch = [];
|
|
13196
|
+
const decodeForFns = fnViewDecoder(source);
|
|
12748
13197
|
for (const clause of clauses) {
|
|
12749
13198
|
if (clause.type === "crossJoin") {
|
|
12750
13199
|
if (filterBatch.length > 0) {
|
|
12751
|
-
rel = filterRecords(rel, filterBatch);
|
|
13200
|
+
rel = filterRecords(rel, filterBatch, decodeForFns);
|
|
12752
13201
|
filterBatch = [];
|
|
12753
13202
|
}
|
|
12754
13203
|
const rightSource = joinContext.resolveSource(clause.target);
|
|
@@ -12761,7 +13210,7 @@ function executeClausePipeline(source, clauses, joinContext) {
|
|
|
12761
13210
|
}
|
|
12762
13211
|
}
|
|
12763
13212
|
if (filterBatch.length > 0) {
|
|
12764
|
-
rel = filterRecords(rel, filterBatch);
|
|
13213
|
+
rel = filterRecords(rel, filterBatch, decodeForFns);
|
|
12765
13214
|
}
|
|
12766
13215
|
return rel;
|
|
12767
13216
|
}
|
|
@@ -13179,6 +13628,7 @@ init_groupby();
|
|
|
13179
13628
|
// src/query/scan-builder.ts
|
|
13180
13629
|
init_predicate();
|
|
13181
13630
|
init_errors();
|
|
13631
|
+
init_where();
|
|
13182
13632
|
var DEFAULT_SCAN_PAGE_SIZE = 100;
|
|
13183
13633
|
var ScanBuilder = class _ScanBuilder {
|
|
13184
13634
|
pageProvider;
|
|
@@ -13242,7 +13692,8 @@ var ScanBuilder = class _ScanBuilder {
|
|
|
13242
13692
|
* evaluates clauses per record in O(1) per clause.
|
|
13243
13693
|
*/
|
|
13244
13694
|
where(field, op, value) {
|
|
13245
|
-
const
|
|
13695
|
+
const desc = this.moneyFields?.[field];
|
|
13696
|
+
const clause = desc ? moneyFieldClause(field, op, value, desc) : { type: "field", field, op, value };
|
|
13246
13697
|
return new _ScanBuilder(
|
|
13247
13698
|
this.pageProvider,
|
|
13248
13699
|
this.pageSize,
|
|
@@ -13588,8 +14039,9 @@ var ScanBuilder = class _ScanBuilder {
|
|
|
13588
14039
|
*/
|
|
13589
14040
|
recordMatches(record) {
|
|
13590
14041
|
if (this.clauses.length === 0) return true;
|
|
14042
|
+
const fnView = this.moneyFields && Object.keys(this.moneyFields).length > 0 && hasFnClause(this.clauses) ? this.decodeMoney(record) : void 0;
|
|
13591
14043
|
for (const clause of this.clauses) {
|
|
13592
|
-
if (!evaluateClause(record, clause)) return false;
|
|
14044
|
+
if (!evaluateClause(record, clause, fnView)) return false;
|
|
13593
14045
|
}
|
|
13594
14046
|
return true;
|
|
13595
14047
|
}
|
|
@@ -14222,7 +14674,7 @@ var TxCollection = class {
|
|
|
14222
14674
|
this._ctx._ops.push(op);
|
|
14223
14675
|
}
|
|
14224
14676
|
};
|
|
14225
|
-
async function runTransaction(db, fn, options) {
|
|
14677
|
+
async function runTransaction(db, fn, options, txInvariants) {
|
|
14226
14678
|
if (options?.amendment) {
|
|
14227
14679
|
if (typeof options.reason !== "string" || options.reason.trim().length === 0) {
|
|
14228
14680
|
throw new ValidationError(
|
|
@@ -14246,12 +14698,19 @@ async function runTransaction(db, fn, options) {
|
|
|
14246
14698
|
}
|
|
14247
14699
|
const priorEnvelopes = /* @__PURE__ */ new Map();
|
|
14248
14700
|
const store = db._store;
|
|
14701
|
+
const invariants = txInvariants ?? [];
|
|
14702
|
+
const watchedScopes = new Set(invariants.map((i) => i.scope));
|
|
14703
|
+
const plainBefore = /* @__PURE__ */ new Map();
|
|
14249
14704
|
for (const op of ctx._ops) {
|
|
14250
14705
|
const key = keyOf(op);
|
|
14251
14706
|
if (!priorEnvelopes.has(key)) {
|
|
14252
14707
|
const env = await store.get(op.vaultName, op.collectionName, op.id);
|
|
14253
14708
|
priorEnvelopes.set(key, env);
|
|
14254
14709
|
}
|
|
14710
|
+
if (watchedScopes.has(op.collectionName) && !plainBefore.has(key)) {
|
|
14711
|
+
const prior = await db.vault(op.vaultName).collection(op.collectionName).get(op.id);
|
|
14712
|
+
plainBefore.set(key, prior ?? null);
|
|
14713
|
+
}
|
|
14255
14714
|
if (op.expectedVersion !== void 0) {
|
|
14256
14715
|
const env = priorEnvelopes.get(key) ?? null;
|
|
14257
14716
|
const actual = env?._v ?? 0;
|
|
@@ -14349,6 +14808,58 @@ async function runTransaction(db, fn, options) {
|
|
|
14349
14808
|
);
|
|
14350
14809
|
}
|
|
14351
14810
|
}
|
|
14811
|
+
if (invariants.length > 0) {
|
|
14812
|
+
const lastOp = /* @__PURE__ */ new Map();
|
|
14813
|
+
const order = [];
|
|
14814
|
+
for (const op of ctx._ops) {
|
|
14815
|
+
const key = keyOf(op);
|
|
14816
|
+
if (!lastOp.has(key)) order.push(key);
|
|
14817
|
+
lastOp.set(key, op);
|
|
14818
|
+
}
|
|
14819
|
+
const changesByScope = /* @__PURE__ */ new Map();
|
|
14820
|
+
const scopeVault = /* @__PURE__ */ new Map();
|
|
14821
|
+
for (const key of order) {
|
|
14822
|
+
const op = lastOp.get(key);
|
|
14823
|
+
if (!watchedScopes.has(op.collectionName)) continue;
|
|
14824
|
+
const before = plainBefore.get(key) ?? null;
|
|
14825
|
+
const after = op.type === "delete" ? null : op.record ?? null;
|
|
14826
|
+
const change = { before, after };
|
|
14827
|
+
const arr = changesByScope.get(op.collectionName);
|
|
14828
|
+
if (arr) arr.push(change);
|
|
14829
|
+
else changesByScope.set(op.collectionName, [change]);
|
|
14830
|
+
scopeVault.set(op.collectionName, op.vaultName);
|
|
14831
|
+
}
|
|
14832
|
+
try {
|
|
14833
|
+
for (const inv of invariants) {
|
|
14834
|
+
const changes = changesByScope.get(inv.scope);
|
|
14835
|
+
if (changes === void 0 || changes.length === 0) continue;
|
|
14836
|
+
const vaultName = scopeVault.get(inv.scope);
|
|
14837
|
+
const v = db.vault(vaultName);
|
|
14838
|
+
const facade = v._getReadOnlyFacade() ?? {
|
|
14839
|
+
collection(name) {
|
|
14840
|
+
const c = v.collection(name);
|
|
14841
|
+
return {
|
|
14842
|
+
get: (id) => c.get(id),
|
|
14843
|
+
list: () => c.list(),
|
|
14844
|
+
query: () => c.query()
|
|
14845
|
+
};
|
|
14846
|
+
}
|
|
14847
|
+
};
|
|
14848
|
+
const ctxForInv = {
|
|
14849
|
+
existing: null,
|
|
14850
|
+
vault: facade,
|
|
14851
|
+
userId: v.userId,
|
|
14852
|
+
role: v.role
|
|
14853
|
+
};
|
|
14854
|
+
await inv.check(changes, ctxForInv);
|
|
14855
|
+
}
|
|
14856
|
+
} catch (err) {
|
|
14857
|
+
await revertExecuted(ctx._executed, store, db);
|
|
14858
|
+
throw err instanceof InvariantError ? err : new InvariantError(
|
|
14859
|
+
err instanceof Error ? err.message : `invariant violated: ${String(err)}`
|
|
14860
|
+
);
|
|
14861
|
+
}
|
|
14862
|
+
}
|
|
14352
14863
|
return bodyResult;
|
|
14353
14864
|
}
|
|
14354
14865
|
async function revertExecuted(executed, store, db) {
|
|
@@ -14753,6 +15264,7 @@ var Collection = class {
|
|
|
14753
15264
|
this.joinResolver = opts.joinResolver;
|
|
14754
15265
|
this.i18nFields = opts.i18nFields;
|
|
14755
15266
|
this.dictKeyFields = opts.dictKeyFields;
|
|
15267
|
+
if (opts.moneyFields) validateMoneyFieldPaths(opts.moneyFields);
|
|
14756
15268
|
this.moneyFields = opts.moneyFields;
|
|
14757
15269
|
this.computed = opts.computed;
|
|
14758
15270
|
this.dictLabelResolver = opts.dictLabelResolver;
|
|
@@ -14886,7 +15398,9 @@ var Collection = class {
|
|
|
14886
15398
|
* declaration; this reconciles that ordering. First-wins. Not public.
|
|
14887
15399
|
*/
|
|
14888
15400
|
_applyMoneyFields(moneyFields) {
|
|
14889
|
-
if (this.moneyFields
|
|
15401
|
+
if (this.moneyFields !== void 0) return;
|
|
15402
|
+
validateMoneyFieldPaths(moneyFields);
|
|
15403
|
+
this.moneyFields = moneyFields;
|
|
14890
15404
|
}
|
|
14891
15405
|
/** @internal — attach computed fields post-construction. See {@link _applyMoneyFields}. */
|
|
14892
15406
|
_applyComputed(computed) {
|
|
@@ -15053,6 +15567,7 @@ var Collection = class {
|
|
|
15053
15567
|
if (!hasWritePermission(this.keyring, this.name)) {
|
|
15054
15568
|
throw new ReadOnlyError();
|
|
15055
15569
|
}
|
|
15570
|
+
record = canonicalizeIncomingMoney(record, this.moneyFields);
|
|
15056
15571
|
if (this.subsystemBus?.hasGateHandlers("beforePut")) {
|
|
15057
15572
|
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
15058
15573
|
let existingRecord = null;
|
|
@@ -15069,7 +15584,7 @@ var Collection = class {
|
|
|
15069
15584
|
collection: this.name,
|
|
15070
15585
|
docId: id,
|
|
15071
15586
|
incoming: record,
|
|
15072
|
-
existing: existingRecord,
|
|
15587
|
+
existing: canonicalizeStoredMoney(existingRecord, this.moneyFields),
|
|
15073
15588
|
existingVersion: existingEnv?._v ?? 0,
|
|
15074
15589
|
existingTs: existingEnv?._ts,
|
|
15075
15590
|
userId: this.keyring.userId,
|
|
@@ -15338,7 +15853,7 @@ var Collection = class {
|
|
|
15338
15853
|
*/
|
|
15339
15854
|
async dispatchDerivations(id, record, version) {
|
|
15340
15855
|
if (this.derivationSource === void 0) return;
|
|
15341
|
-
const incoming = record;
|
|
15856
|
+
const incoming = canonicalizeStoredMoney(record, this.moneyFields);
|
|
15342
15857
|
if (incoming && typeof incoming === "object" && "_derivedFrom" in incoming) return;
|
|
15343
15858
|
const registry = this.derivationSource.registry();
|
|
15344
15859
|
const strategies = registry.strategiesForSource(this.name);
|
|
@@ -15350,9 +15865,18 @@ var Collection = class {
|
|
|
15350
15865
|
if (DerivationExecutor2 === null) {
|
|
15351
15866
|
({ DerivationExecutor: DerivationExecutor2 } = await Promise.resolve().then(() => (init_executor2(), executor_exports2)));
|
|
15352
15867
|
}
|
|
15353
|
-
|
|
15868
|
+
let sourceWithId;
|
|
15869
|
+
let sourceVersion = version;
|
|
15870
|
+
if (spec.source === this.name) {
|
|
15871
|
+
sourceWithId = { ...incoming, id };
|
|
15872
|
+
} else {
|
|
15873
|
+
const primary = await this.derivationSource.getCollection(spec.source).get(id);
|
|
15874
|
+
if (primary === null || primary === void 0) continue;
|
|
15875
|
+
sourceWithId = { ...primary, id };
|
|
15876
|
+
sourceVersion = 0;
|
|
15877
|
+
}
|
|
15354
15878
|
const ctx = { vault: this.derivationSource.getReadOnlyFacade() };
|
|
15355
|
-
const result = await DerivationExecutor2.run(spec, sourceWithId,
|
|
15879
|
+
const result = await DerivationExecutor2.run(spec, sourceWithId, sourceVersion, strategyHash, ctx);
|
|
15356
15880
|
for (const key of Object.keys(spec.outputs)) {
|
|
15357
15881
|
const out = result.outputs[key];
|
|
15358
15882
|
if (!out) continue;
|
|
@@ -15571,7 +16095,7 @@ var Collection = class {
|
|
|
15571
16095
|
vault: this.vault,
|
|
15572
16096
|
collection: this.name,
|
|
15573
16097
|
docId: id,
|
|
15574
|
-
existing: existingRecord,
|
|
16098
|
+
existing: canonicalizeStoredMoney(existingRecord, this.moneyFields),
|
|
15575
16099
|
existingVersion: existingEnv._v,
|
|
15576
16100
|
existingTs: existingEnv._ts,
|
|
15577
16101
|
internal,
|
|
@@ -17335,37 +17859,34 @@ var OverlayedCollection = class {
|
|
|
17335
17859
|
/** Get the merged row by id. */
|
|
17336
17860
|
async get(id) {
|
|
17337
17861
|
const overlayRow = await this.overlayCollection.get(id);
|
|
17338
|
-
if (overlayRow !== null && this.shadowPredicateApplies(overlayRow)) {
|
|
17339
|
-
return overlayRow;
|
|
17340
|
-
}
|
|
17341
17862
|
const baseRow = await this.baseCollection.get(id);
|
|
17342
|
-
|
|
17343
|
-
return null;
|
|
17863
|
+
return this.mergeRows(overlayRow, baseRow);
|
|
17344
17864
|
}
|
|
17345
17865
|
/** List union of base + overlay ids, applying the merge per row. */
|
|
17346
17866
|
async list() {
|
|
17347
17867
|
const baseRows = await this.baseCollection.list();
|
|
17348
17868
|
const overlayRows = await this.overlayCollection.list();
|
|
17349
|
-
const merged = /* @__PURE__ */ new Map();
|
|
17350
17869
|
const idOf = (row) => {
|
|
17351
17870
|
if (this.baseRowKey) return this.baseRowKey(row);
|
|
17352
17871
|
const idField = row.id;
|
|
17353
17872
|
return typeof idField === "string" ? idField : "";
|
|
17354
17873
|
};
|
|
17874
|
+
const baseById = /* @__PURE__ */ new Map();
|
|
17875
|
+
const overlayById = /* @__PURE__ */ new Map();
|
|
17355
17876
|
for (const row of baseRows) {
|
|
17356
17877
|
const id = idOf(row);
|
|
17357
|
-
if (id)
|
|
17878
|
+
if (id) baseById.set(id, row);
|
|
17358
17879
|
}
|
|
17359
17880
|
for (const row of overlayRows) {
|
|
17360
17881
|
const id = idOf(row);
|
|
17361
|
-
if (
|
|
17362
|
-
if (this.shadowPredicateApplies(row)) {
|
|
17363
|
-
merged.set(id, row);
|
|
17364
|
-
} else if (!merged.has(id)) {
|
|
17365
|
-
continue;
|
|
17366
|
-
}
|
|
17882
|
+
if (id) overlayById.set(id, row);
|
|
17367
17883
|
}
|
|
17368
|
-
|
|
17884
|
+
const out = [];
|
|
17885
|
+
for (const id of /* @__PURE__ */ new Set([...baseById.keys(), ...overlayById.keys()])) {
|
|
17886
|
+
const merged = this.mergeRows(overlayById.get(id) ?? null, baseById.get(id) ?? null);
|
|
17887
|
+
if (merged !== null) out.push(merged);
|
|
17888
|
+
}
|
|
17889
|
+
return out;
|
|
17369
17890
|
}
|
|
17370
17891
|
/**
|
|
17371
17892
|
* Write to the overlay. Two forms:
|
|
@@ -17405,9 +17926,42 @@ var OverlayedCollection = class {
|
|
|
17405
17926
|
async delete(id) {
|
|
17406
17927
|
await this.overlayCollection.delete(id);
|
|
17407
17928
|
}
|
|
17408
|
-
/**
|
|
17409
|
-
|
|
17410
|
-
|
|
17929
|
+
/**
|
|
17930
|
+
* Merge a single id's overlay + base rows into the visible row.
|
|
17931
|
+
*
|
|
17932
|
+
* Priority (first match wins):
|
|
17933
|
+
* 1. Binary shadow win — overlay present AND
|
|
17934
|
+
* `overlay[shadowField] === shadowValue` → return the overlay row
|
|
17935
|
+
* entirely. This stays FIRST so the original binary behaviour is
|
|
17936
|
+
* unchanged whether or not `mergeMode` is configured.
|
|
17937
|
+
* 2. Field-level merge — overlay present, `mergeMode` configured,
|
|
17938
|
+
* and a rule whose `whenStatus` equals `overlay[shadowField]`.
|
|
17939
|
+
* The matched rule pulls its `overlayFields` (those present on
|
|
17940
|
+
* the overlay row) on top of the base row. With no base row, the
|
|
17941
|
+
* overlay row is returned as-is.
|
|
17942
|
+
* 3. Fallback — return the base row (possibly `null`). An
|
|
17943
|
+
* overlay-only row that qualifies under neither (1) nor (2) and
|
|
17944
|
+
* has no base is therefore NOT surfaced.
|
|
17945
|
+
*/
|
|
17946
|
+
mergeRows(overlayRow, baseRow) {
|
|
17947
|
+
const shadowField = this.spec.shadowField;
|
|
17948
|
+
if (overlayRow !== null && overlayRow[shadowField] === this.spec.shadowValue) {
|
|
17949
|
+
return overlayRow;
|
|
17950
|
+
}
|
|
17951
|
+
if (overlayRow !== null && this.spec.mergeMode) {
|
|
17952
|
+
const status = overlayRow[shadowField];
|
|
17953
|
+
const rule = this.spec.mergeMode.rules.find((r) => r.whenStatus === status);
|
|
17954
|
+
if (rule) {
|
|
17955
|
+
if (baseRow === null) return overlayRow;
|
|
17956
|
+
const overlaySrc = overlayRow;
|
|
17957
|
+
const picked = {};
|
|
17958
|
+
for (const field of rule.overlayFields) {
|
|
17959
|
+
if (field in overlaySrc) picked[field] = overlaySrc[field];
|
|
17960
|
+
}
|
|
17961
|
+
return { ...baseRow, ...picked };
|
|
17962
|
+
}
|
|
17963
|
+
}
|
|
17964
|
+
return baseRow;
|
|
17411
17965
|
}
|
|
17412
17966
|
// ─── Throw-stubs for the unimplemented Collection<T> surface ───────
|
|
17413
17967
|
//
|
|
@@ -19831,17 +20385,23 @@ var Vault = class {
|
|
|
19831
20385
|
* const cur = await vault.sequence('invoice-2026').peek() // current value, no allocation
|
|
19832
20386
|
* ```
|
|
19833
20387
|
*/
|
|
19834
|
-
sequence(
|
|
19835
|
-
if (
|
|
20388
|
+
sequence(series, opts) {
|
|
20389
|
+
if (series.includes("\0")) {
|
|
20390
|
+
throw new ValidationError(`sequence("${series}"): series name must not contain a null byte (\\x00).`);
|
|
20391
|
+
}
|
|
20392
|
+
if (this.numberingConfigs.has(series)) {
|
|
19836
20393
|
const eng = this.deferred();
|
|
19837
20394
|
return {
|
|
19838
|
-
next: async (
|
|
19839
|
-
if (!
|
|
19840
|
-
throw new ValidationError(`sequence("${
|
|
20395
|
+
next: async (nextOpts) => {
|
|
20396
|
+
if (!nextOpts?.for) {
|
|
20397
|
+
throw new ValidationError(`sequence("${series}") is a deferred-numbering series; call next({ for: recordId }).`);
|
|
19841
20398
|
}
|
|
19842
|
-
return (await eng.enqueue(
|
|
20399
|
+
return (await eng.enqueue(series, nextOpts.for)).assigned;
|
|
19843
20400
|
},
|
|
19844
|
-
peek: () => eng.peek(
|
|
20401
|
+
peek: () => eng.peek(series),
|
|
20402
|
+
seedTo: () => {
|
|
20403
|
+
throw new ValidationError(`sequence("${series}") is a deferred-numbering series; seedTo is CAS-only.`);
|
|
20404
|
+
}
|
|
19845
20405
|
};
|
|
19846
20406
|
}
|
|
19847
20407
|
if (!this.sequenceStore) {
|
|
@@ -19853,7 +20413,7 @@ var Vault = class {
|
|
|
19853
20413
|
actor: this.keyring.userId
|
|
19854
20414
|
});
|
|
19855
20415
|
}
|
|
19856
|
-
return this.sequenceStore.handle(
|
|
20416
|
+
return this.sequenceStore.handle(resolveSequenceKey(series, opts));
|
|
19857
20417
|
}
|
|
19858
20418
|
/** @internal — lazily build the deferred-numbering engine with a cache-coherent stamp. */
|
|
19859
20419
|
deferred() {
|
|
@@ -20145,9 +20705,24 @@ var Vault = class {
|
|
|
20145
20705
|
});
|
|
20146
20706
|
}
|
|
20147
20707
|
if (rule.mode === "cascade") {
|
|
20708
|
+
const txCtx = this.noydb._activeTxContextOrNull;
|
|
20148
20709
|
for (const match of matches) {
|
|
20149
20710
|
const matchId = match["id"] ?? null;
|
|
20150
20711
|
if (matchId === null) continue;
|
|
20712
|
+
if (txCtx !== null) {
|
|
20713
|
+
const prior = await this.adapter.get(this.name, rule.collection, matchId);
|
|
20714
|
+
if (prior !== null) {
|
|
20715
|
+
txCtx._executed.push({
|
|
20716
|
+
op: {
|
|
20717
|
+
type: "delete",
|
|
20718
|
+
vaultName: this.name,
|
|
20719
|
+
collectionName: rule.collection,
|
|
20720
|
+
id: matchId
|
|
20721
|
+
},
|
|
20722
|
+
priorEnvelope: prior
|
|
20723
|
+
});
|
|
20724
|
+
}
|
|
20725
|
+
}
|
|
20151
20726
|
await fromCollection.delete(matchId);
|
|
20152
20727
|
}
|
|
20153
20728
|
}
|
|
@@ -22346,7 +22921,7 @@ var NOT_ENABLED6 = new Error(
|
|
|
22346
22921
|
'Multi-record transactions require the tx strategy. Import `{ withTransactions }` from "@noy-db/hub/tx" and pass it to `createNoydb({ txStrategy: withTransactions() })`.'
|
|
22347
22922
|
);
|
|
22348
22923
|
var NO_TX = {
|
|
22349
|
-
async runTransaction() {
|
|
22924
|
+
async runTransaction(_db, _fn, _options, _txInvariants) {
|
|
22350
22925
|
throw NOT_ENABLED6;
|
|
22351
22926
|
},
|
|
22352
22927
|
async runDryRun() {
|
|
@@ -25664,7 +26239,7 @@ function recordId2(record) {
|
|
|
25664
26239
|
return typeof id === "string" ? id : "";
|
|
25665
26240
|
}
|
|
25666
26241
|
function immutableGuard(config) {
|
|
25667
|
-
const { collection, after, appendOnly, amendmentRoles } = config;
|
|
26242
|
+
const { collection, after, appendOnly, amendmentRoles, amendmentInvariant } = config;
|
|
25668
26243
|
if (appendOnly && after !== void 0) {
|
|
25669
26244
|
throw new ValidationError("immutableGuard: `after` and `appendOnly` are mutually exclusive");
|
|
25670
26245
|
}
|
|
@@ -25690,12 +26265,16 @@ function immutableGuard(config) {
|
|
|
25690
26265
|
}
|
|
25691
26266
|
},
|
|
25692
26267
|
// The authorized override: inside an amendment transaction the
|
|
25693
|
-
// check/onDelete are skipped and the change is ledgered.
|
|
25694
|
-
// invariant — the amendment itself is the
|
|
26268
|
+
// check/onDelete are skipped and the change is ledgered. By default
|
|
26269
|
+
// there is no extra invariant — the amendment itself is the
|
|
26270
|
+
// sanctioned exception. Callers may supply `amendmentInvariant` to
|
|
26271
|
+
// keep a constraint inviolable even under amendment (e.g. forbid
|
|
26272
|
+
// deletes, or preserve a cross-record sum); a throw reverts the
|
|
26273
|
+
// amendment and surfaces as `InvariantError`.
|
|
25695
26274
|
amendment: {
|
|
25696
26275
|
roles: amendmentRoles ?? ["admin", "owner"],
|
|
25697
|
-
invariant: () => {
|
|
25698
|
-
}
|
|
26276
|
+
invariant: amendmentInvariant ?? (() => {
|
|
26277
|
+
})
|
|
25699
26278
|
}
|
|
25700
26279
|
};
|
|
25701
26280
|
return withGuard(spec);
|
|
@@ -25716,6 +26295,18 @@ function withDerivation(spec) {
|
|
|
25716
26295
|
if (typeof spec.derive !== "function") {
|
|
25717
26296
|
throw new ValidationError("withDerivation: derive must be a function");
|
|
25718
26297
|
}
|
|
26298
|
+
if (spec.sources !== void 0) {
|
|
26299
|
+
for (const extra of spec.sources) {
|
|
26300
|
+
if (typeof extra !== "string" || extra.length === 0) {
|
|
26301
|
+
throw new ValidationError("withDerivation: each entry in sources[] must be a non-empty string");
|
|
26302
|
+
}
|
|
26303
|
+
if (extra === spec.source) {
|
|
26304
|
+
throw new ValidationError(
|
|
26305
|
+
`withDerivation: sources[] must not contain the primary source "${spec.source}"`
|
|
26306
|
+
);
|
|
26307
|
+
}
|
|
26308
|
+
}
|
|
26309
|
+
}
|
|
25719
26310
|
const lifecycleMode = typeof spec.lifecycle === "string" ? spec.lifecycle : spec.lifecycle.mode;
|
|
25720
26311
|
for (const [outputKey, outputSpec] of Object.entries(spec.outputs)) {
|
|
25721
26312
|
if (outputSpec.shape === "array") {
|
|
@@ -25768,9 +26359,9 @@ function withMaterializedView(spec) {
|
|
|
25768
26359
|
throw new ValidationError("withMaterializedView: query must be a function returning a Query<T>");
|
|
25769
26360
|
}
|
|
25770
26361
|
if (spec.unionSources) {
|
|
25771
|
-
if (spec.unionSources.length <
|
|
26362
|
+
if (spec.unionSources.length < 1) {
|
|
25772
26363
|
throw new MaterializedViewConfigError(
|
|
25773
|
-
"unionSources requires at least
|
|
26364
|
+
"unionSources requires at least 1 source collection"
|
|
25774
26365
|
);
|
|
25775
26366
|
}
|
|
25776
26367
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -25802,6 +26393,16 @@ function withMaterializedView(spec) {
|
|
|
25802
26393
|
`withMaterializedView "${spec.name}": UNION strategy with aggregate requires groupBy \u2014 use groupBy to declare the bucketing keys, or remove aggregate for a pure dedup MV`
|
|
25803
26394
|
);
|
|
25804
26395
|
}
|
|
26396
|
+
if (spec.moneyFields && !spec.aggregate) {
|
|
26397
|
+
throw new MaterializedViewConfigError(
|
|
26398
|
+
`withMaterializedView "${spec.name}": moneyFields requires aggregate \u2014 moneyFields rewrites sum/min/max reducers over money output fields, so it is meaningless without an aggregate spec`
|
|
26399
|
+
);
|
|
26400
|
+
}
|
|
26401
|
+
if (spec.unionSources.some((s) => s.join && s.join.length > 0) && (!spec.sources || spec.sources.length === 0)) {
|
|
26402
|
+
throw new MaterializedViewConfigError(
|
|
26403
|
+
`withMaterializedView "${spec.name}": a unionSources arm declares join(s) but no \`sources\` are listed \u2014 declare sources: [...] with the right-side (join-target) collection names so writes to them trigger MV refresh`
|
|
26404
|
+
);
|
|
26405
|
+
}
|
|
25805
26406
|
if (spec.predicates) {
|
|
25806
26407
|
throw new MaterializedViewConfigError(
|
|
25807
26408
|
`withMaterializedView "${spec.name}": predicates are not supported on UNION strategies \u2014 UNION mode does not use a Query<T> chain, so .wherePredicate() cannot fire. Use the query() form, or open an issue if per-arm predicates are needed`
|
|
@@ -25862,6 +26463,117 @@ init_errors();
|
|
|
25862
26463
|
init_descriptor();
|
|
25863
26464
|
init_iso4217();
|
|
25864
26465
|
|
|
26466
|
+
// src/money/arith.ts
|
|
26467
|
+
init_fixed_point();
|
|
26468
|
+
init_descriptor();
|
|
26469
|
+
function parseAmount(label, amount, scale, rounding) {
|
|
26470
|
+
const r = parseToScaledInt(amount, scale, rounding);
|
|
26471
|
+
if (!r.ok) {
|
|
26472
|
+
throw new MoneyUnsupportedError(
|
|
26473
|
+
r.reason === "precision" ? `${label}: amount ${JSON.stringify(amount)} has more precision than scale ${scale} and no rounding mode is configured` : `${label}: amount ${JSON.stringify(amount)} is not a finite decimal`
|
|
26474
|
+
);
|
|
26475
|
+
}
|
|
26476
|
+
return r.value;
|
|
26477
|
+
}
|
|
26478
|
+
function resolveScale(label, amount, explicit) {
|
|
26479
|
+
if (explicit !== void 0) {
|
|
26480
|
+
if (!Number.isInteger(explicit) || explicit < 0) {
|
|
26481
|
+
throw new MoneyUnsupportedError(`${label}: scale must be a non-negative integer`);
|
|
26482
|
+
}
|
|
26483
|
+
return explicit;
|
|
26484
|
+
}
|
|
26485
|
+
const inferred = decimalScaleOf(amount);
|
|
26486
|
+
if (inferred === null) {
|
|
26487
|
+
throw new MoneyUnsupportedError(`${label}: amount ${JSON.stringify(amount)} is not a finite decimal`);
|
|
26488
|
+
}
|
|
26489
|
+
return inferred;
|
|
26490
|
+
}
|
|
26491
|
+
function mulRate(amount, rate, opts = {}) {
|
|
26492
|
+
const scale = resolveScale("mulRate", amount, opts.scale);
|
|
26493
|
+
const rounding = opts.rounding ?? "half-up";
|
|
26494
|
+
const a = parseAmount("mulRate", amount, scale, rounding);
|
|
26495
|
+
const rateScale = decimalScaleOf(rate);
|
|
26496
|
+
if (rateScale === null) {
|
|
26497
|
+
throw new MoneyUnsupportedError(`mulRate: rate ${JSON.stringify(rate)} is not a finite decimal`);
|
|
26498
|
+
}
|
|
26499
|
+
const r = parseToScaledInt(rate, rateScale);
|
|
26500
|
+
if (!r.ok) {
|
|
26501
|
+
throw new MoneyUnsupportedError(`mulRate: rate ${JSON.stringify(rate)} is not a finite decimal`);
|
|
26502
|
+
}
|
|
26503
|
+
const product = a * r.value;
|
|
26504
|
+
return formatScaledInt(rescaleScaledInt(product, scale + rateScale, scale, rounding), scale);
|
|
26505
|
+
}
|
|
26506
|
+
function allocate(amount, weights, opts = {}) {
|
|
26507
|
+
if (weights.length === 0) {
|
|
26508
|
+
throw new MoneyUnsupportedError("allocate: weights must not be empty");
|
|
26509
|
+
}
|
|
26510
|
+
const scale = resolveScale("allocate", amount, opts.scale);
|
|
26511
|
+
const a = parseAmount("allocate", amount, scale);
|
|
26512
|
+
let weightScale = 0;
|
|
26513
|
+
for (const w of weights) {
|
|
26514
|
+
const s = decimalScaleOf(w);
|
|
26515
|
+
if (s === null) {
|
|
26516
|
+
throw new MoneyUnsupportedError(`allocate: weight ${JSON.stringify(w)} is not a finite decimal`);
|
|
26517
|
+
}
|
|
26518
|
+
if (s > weightScale) weightScale = s;
|
|
26519
|
+
}
|
|
26520
|
+
const scaledWeights = weights.map((w) => {
|
|
26521
|
+
const r = parseToScaledInt(w, weightScale);
|
|
26522
|
+
if (!r.ok || r.value < 0n) {
|
|
26523
|
+
throw new MoneyUnsupportedError(`allocate: weight ${JSON.stringify(w)} must be a non-negative decimal`);
|
|
26524
|
+
}
|
|
26525
|
+
return r.value;
|
|
26526
|
+
});
|
|
26527
|
+
const sumW = scaledWeights.reduce((acc, w) => acc + w, 0n);
|
|
26528
|
+
if (sumW === 0n) {
|
|
26529
|
+
throw new MoneyUnsupportedError("allocate: weights must not all be zero");
|
|
26530
|
+
}
|
|
26531
|
+
const negative = a < 0n;
|
|
26532
|
+
const mag = negative ? -a : a;
|
|
26533
|
+
const base = [];
|
|
26534
|
+
const remainders = [];
|
|
26535
|
+
let distributed = 0n;
|
|
26536
|
+
for (let i = 0; i < scaledWeights.length; i++) {
|
|
26537
|
+
const product = mag * scaledWeights[i];
|
|
26538
|
+
const share = product / sumW;
|
|
26539
|
+
base.push(share);
|
|
26540
|
+
distributed += share;
|
|
26541
|
+
remainders.push({ index: i, rem: product % sumW });
|
|
26542
|
+
}
|
|
26543
|
+
let leftover = mag - distributed;
|
|
26544
|
+
remainders.sort((x, y) => y.rem > x.rem ? 1 : y.rem < x.rem ? -1 : x.index - y.index);
|
|
26545
|
+
for (const { index } of remainders) {
|
|
26546
|
+
if (leftover === 0n) break;
|
|
26547
|
+
base[index] = base[index] + 1n;
|
|
26548
|
+
leftover -= 1n;
|
|
26549
|
+
}
|
|
26550
|
+
return base.map((p) => formatScaledInt(negative && p !== 0n ? -p : p, scale));
|
|
26551
|
+
}
|
|
26552
|
+
|
|
26553
|
+
// src/money/branded.ts
|
|
26554
|
+
init_fixed_point();
|
|
26555
|
+
init_descriptor();
|
|
26556
|
+
function asMoney(value) {
|
|
26557
|
+
if (!isMoneyLike(value)) {
|
|
26558
|
+
throw new MoneyUnsupportedError(`asMoney: ${JSON.stringify(value)} is not a finite decimal`);
|
|
26559
|
+
}
|
|
26560
|
+
return String(value).trim();
|
|
26561
|
+
}
|
|
26562
|
+
function isMoneyString(value) {
|
|
26563
|
+
return typeof value === "string" && isMoneyLike(value);
|
|
26564
|
+
}
|
|
26565
|
+
function moneyNumber(value) {
|
|
26566
|
+
if (!isMoneyLike(value)) {
|
|
26567
|
+
throw new MoneyUnsupportedError(`moneyNumber: ${JSON.stringify(value)} is not a finite decimal`);
|
|
26568
|
+
}
|
|
26569
|
+
return Number(value);
|
|
26570
|
+
}
|
|
26571
|
+
function isMoneyLike(value) {
|
|
26572
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
26573
|
+
if (typeof value !== "string") return false;
|
|
26574
|
+
return decimalScaleOf(value) !== null;
|
|
26575
|
+
}
|
|
26576
|
+
|
|
25865
26577
|
// src/i18n/script.ts
|
|
25866
26578
|
init_errors();
|
|
25867
26579
|
var LATIN_BASE = /* @__PURE__ */ new Set([
|
|
@@ -26804,9 +27516,11 @@ function shortJSON(value) {
|
|
|
26804
27516
|
WeakPassphraseError,
|
|
26805
27517
|
activeSessionCount,
|
|
26806
27518
|
additiveOnly,
|
|
27519
|
+
allocate,
|
|
26807
27520
|
applyI18nLocale,
|
|
26808
27521
|
applyJoins,
|
|
26809
27522
|
applyPatch,
|
|
27523
|
+
asMoney,
|
|
26810
27524
|
assertStrongPassphrase,
|
|
26811
27525
|
assertTierAccess,
|
|
26812
27526
|
avg,
|
|
@@ -26883,6 +27597,7 @@ function shortJSON(value) {
|
|
|
26883
27597
|
isI18nTextDescriptor,
|
|
26884
27598
|
isMagicLinkGrantExpired,
|
|
26885
27599
|
isMoneyDescriptor,
|
|
27600
|
+
isMoneyString,
|
|
26886
27601
|
isPreCompressed,
|
|
26887
27602
|
isPublicEnvelope,
|
|
26888
27603
|
isSessionAlive,
|
|
@@ -26915,6 +27630,8 @@ function shortJSON(value) {
|
|
|
26915
27630
|
mintShamirRecoveryEntry,
|
|
26916
27631
|
mintWrappedDeksBlob,
|
|
26917
27632
|
money,
|
|
27633
|
+
moneyNumber,
|
|
27634
|
+
mulRate,
|
|
26918
27635
|
paddedIndex,
|
|
26919
27636
|
parseBytes,
|
|
26920
27637
|
parseIndex,
|
|
@@ -26941,6 +27658,7 @@ function shortJSON(value) {
|
|
|
26941
27658
|
resolveI18nText,
|
|
26942
27659
|
resolvePolicy,
|
|
26943
27660
|
resolvePublicEnvelopeSchema,
|
|
27661
|
+
resolveSequenceKey,
|
|
26944
27662
|
resolveSession,
|
|
26945
27663
|
revokeAllSessions,
|
|
26946
27664
|
revokeDelegation,
|