@noy-db/hub 0.2.0-pre.14 → 0.2.0-pre.15
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 +56 -56
- 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.d.cts +5 -5
- package/dist/attestation/index.d.ts +5 -5
- package/dist/blobs/index.d.cts +6 -6
- package/dist/blobs/index.d.ts +6 -6
- package/dist/bundle/index.cjs +426 -68
- 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 +1 -1
- package/dist/{chunk-3EWA37FV.js → chunk-3EWXMOK3.js} +7 -266
- package/dist/chunk-3EWXMOK3.js.map +1 -0
- package/dist/{chunk-ACKFRSAH.js → chunk-7EFFHEN5.js} +26 -19
- package/dist/chunk-7EFFHEN5.js.map +1 -0
- package/dist/{chunk-UWNYBOOO.js → chunk-C5T5AFWN.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-KGCORI4L.js → chunk-EYVQHAGH.js} +266 -66
- package/dist/chunk-EYVQHAGH.js.map +1 -0
- package/dist/{chunk-NP6EZT44.js → chunk-IQLVUT37.js} +2 -2
- package/dist/{chunk-4PEFEETV.js → chunk-KIP6JLTF.js} +2 -2
- package/dist/{chunk-ZWTNWAO4.js → chunk-NU6Q3FOR.js} +3 -3
- package/dist/chunk-NU6Q3FOR.js.map +1 -0
- 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.d.cts +7 -7
- package/dist/derivations/index.d.ts +7 -7
- package/dist/{dev-unlock-DV7ujTCI.d.ts → dev-unlock-iAS8z9jc.d.ts} +1 -1
- package/dist/{dev-unlock-BF4OSxRv.d.cts → dev-unlock-nVkuRLLe.d.cts} +1 -1
- package/dist/{executor-723ZP6TH.js → executor-HSSRXDOB.js} +4 -4
- package/dist/guards/index.d.cts +7 -7
- package/dist/guards/index.d.ts +7 -7
- package/dist/{hash-BcF5WQXl.d.cts → hash-Cv6byZs7.d.cts} +1 -1
- package/dist/{hash-DswxkLtW.d.ts → hash-DHOnRarj.d.ts} +1 -1
- package/dist/history/index.d.cts +7 -7
- package/dist/history/index.d.ts +7 -7
- package/dist/i18n/index.d.cts +6 -6
- package/dist/i18n/index.d.ts +6 -6
- package/dist/{immutable-guard-C8IYdzfu.d.ts → immutable-guard-BehB1YGB.d.ts} +1 -1
- package/dist/{immutable-guard-7KqslW2K.d.cts → immutable-guard-yBEOYmif.d.cts} +1 -1
- package/dist/{index-CUVOMtgg.d.cts → index-D95VK1Qy.d.cts} +11 -3
- package/dist/{index-Cqzp4tt9.d.ts → index-XNB2r6bX.d.ts} +11 -3
- package/dist/index.cjs +567 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +145 -15
- package/dist/index.d.ts +145 -15
- package/dist/index.js +132 -16
- 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/{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/materialized-views/index.cjs +2 -2
- 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 +5 -5
- package/dist/{noydb-VZ4JVW55.js → noydb-GZGFBA4E.js} +8 -8
- package/dist/overlay-views/index.d.cts +7 -7
- package/dist/overlay-views/index.d.ts +7 -7
- package/dist/periods/index.d.cts +6 -6
- package/dist/periods/index.d.ts +6 -6
- 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/query/index.cjs +580 -195
- 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/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/snapshots/index.d.cts +6 -6
- package/dist/snapshots/index.d.ts +6 -6
- package/dist/{stale-7FRJVHN6.js → stale-JH67FU57.js} +2 -2
- package/dist/store/index.d.cts +6 -6
- package/dist/store/index.d.ts +6 -6
- package/dist/{strategy-CrS7PnbE.d.ts → strategy-CbneC7bS.d.cts} +1 -1
- package/dist/{strategy-CrS7PnbE.d.cts → strategy-CbneC7bS.d.ts} +1 -1
- package/dist/sync/index.d.cts +5 -5
- package/dist/sync/index.d.ts +5 -5
- package/dist/team/index.d.cts +6 -6
- package/dist/team/index.d.ts +6 -6
- package/dist/tx/index.d.cts +6 -6
- package/dist/tx/index.d.ts +6 -6
- package/dist/{types-BFHQUjdy.d.ts → types-4t1-tWS4.d.ts} +22 -7
- package/dist/{types-V5R2-pd4.d.cts → types-BpPV5uyy.d.cts} +22 -7
- package/dist/{ulid-CwNf9e6-.d.cts → ulid-CiPrpGqm.d.cts} +1 -1
- package/dist/{ulid-p2nKiiKg.d.ts → ulid-DAfenvFd.d.ts} +1 -1
- package/dist/{vault-group-W7QC4UYW.js → vault-group-KOM7QRJG.js} +3 -3
- package/dist/{with-derivation-C9K43BOB.d.cts → with-derivation-DBqJB3dQ.d.cts} +1 -1
- package/dist/{with-derivation-Ds9yZgCj.d.ts → with-derivation-OK9M2sJE.d.ts} +1 -1
- package/dist/{with-materialized-view-DwR4jkV5.d.ts → with-materialized-view-Dt-ufPWQ.d.ts} +1 -1
- package/dist/{with-materialized-view-DgQcAjYv.d.cts → with-materialized-view-NzuxYPDF.d.cts} +1 -1
- package/dist/{with-overlayed-view-7-rUB3vD.d.cts → with-overlayed-view-CC0_ocy-.d.cts} +1 -1
- package/dist/{with-overlayed-view-ByyhHdVr.d.ts → with-overlayed-view-eDvMs6LO.d.ts} +1 -1
- package/package.json +3 -3
- package/dist/chunk-3EWA37FV.js.map +0 -1
- package/dist/chunk-ACKFRSAH.js.map +0 -1
- package/dist/chunk-KGCORI4L.js.map +0 -1
- package/dist/chunk-TV3YZ35S.js +0 -90
- package/dist/chunk-TV3YZ35S.js.map +0 -1
- package/dist/chunk-ZWTNWAO4.js.map +0 -1
- /package/dist/{chunk-UWNYBOOO.js.map → chunk-C5T5AFWN.js.map} +0 -0
- /package/dist/{chunk-YNTBADIY.js.map → chunk-CZI2A4MQ.js.map} +0 -0
- /package/dist/{chunk-NP6EZT44.js.map → chunk-IQLVUT37.js.map} +0 -0
- /package/dist/{chunk-4PEFEETV.js.map → chunk-KIP6JLTF.js.map} +0 -0
- /package/dist/{chunk-WIBHRONM.js.map → chunk-XWH4MXIU.js.map} +0 -0
- /package/dist/{executor-723ZP6TH.js.map → executor-HSSRXDOB.js.map} +0 -0
- /package/dist/{noydb-VZ4JVW55.js.map → noydb-GZGFBA4E.js.map} +0 -0
- /package/dist/{stale-7FRJVHN6.js.map → stale-JH67FU57.js.map} +0 -0
- /package/dist/{vault-group-W7QC4UYW.js.map → vault-group-KOM7QRJG.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,20 +2853,28 @@ 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
|
|
|
@@ -5577,9 +5750,11 @@ __export(src_exports, {
|
|
|
5577
5750
|
WeakPassphraseError: () => WeakPassphraseError,
|
|
5578
5751
|
activeSessionCount: () => activeSessionCount,
|
|
5579
5752
|
additiveOnly: () => additiveOnly,
|
|
5753
|
+
allocate: () => allocate,
|
|
5580
5754
|
applyI18nLocale: () => applyI18nLocale,
|
|
5581
5755
|
applyJoins: () => applyJoins,
|
|
5582
5756
|
applyPatch: () => applyPatch,
|
|
5757
|
+
asMoney: () => asMoney,
|
|
5583
5758
|
assertStrongPassphrase: () => assertStrongPassphrase,
|
|
5584
5759
|
assertTierAccess: () => assertTierAccess,
|
|
5585
5760
|
avg: () => avg,
|
|
@@ -5656,6 +5831,7 @@ __export(src_exports, {
|
|
|
5656
5831
|
isI18nTextDescriptor: () => isI18nTextDescriptor,
|
|
5657
5832
|
isMagicLinkGrantExpired: () => isMagicLinkGrantExpired,
|
|
5658
5833
|
isMoneyDescriptor: () => isMoneyDescriptor,
|
|
5834
|
+
isMoneyString: () => isMoneyString,
|
|
5659
5835
|
isPreCompressed: () => isPreCompressed,
|
|
5660
5836
|
isPublicEnvelope: () => isPublicEnvelope,
|
|
5661
5837
|
isSessionAlive: () => isSessionAlive,
|
|
@@ -5688,6 +5864,8 @@ __export(src_exports, {
|
|
|
5688
5864
|
mintShamirRecoveryEntry: () => mintShamirRecoveryEntry,
|
|
5689
5865
|
mintWrappedDeksBlob: () => mintWrappedDeksBlob,
|
|
5690
5866
|
money: () => money,
|
|
5867
|
+
moneyNumber: () => moneyNumber,
|
|
5868
|
+
mulRate: () => mulRate,
|
|
5691
5869
|
paddedIndex: () => paddedIndex,
|
|
5692
5870
|
parseBytes: () => parseBytes,
|
|
5693
5871
|
parseIndex: () => parseIndex,
|
|
@@ -11579,6 +11757,111 @@ function applyI18nLocale(record, i18nFields, locale, fallback, layer = "read") {
|
|
|
11579
11757
|
// src/money/normalize.ts
|
|
11580
11758
|
init_fixed_point();
|
|
11581
11759
|
init_descriptor();
|
|
11760
|
+
|
|
11761
|
+
// src/money/paths.ts
|
|
11762
|
+
init_errors();
|
|
11763
|
+
var SEGMENT_RE = /^(\*|[^.[\]*]+)(\[\])?$/;
|
|
11764
|
+
var parseCache = /* @__PURE__ */ new Map();
|
|
11765
|
+
function parseMoneyPath(path) {
|
|
11766
|
+
const cached = parseCache.get(path);
|
|
11767
|
+
if (cached) return cached;
|
|
11768
|
+
if (typeof path !== "string" || path.length === 0) {
|
|
11769
|
+
throw new ValidationError("moneyFields: path must be a non-empty string");
|
|
11770
|
+
}
|
|
11771
|
+
const segments = [];
|
|
11772
|
+
for (const part of path.split(".")) {
|
|
11773
|
+
const m = SEGMENT_RE.exec(part);
|
|
11774
|
+
if (!m) {
|
|
11775
|
+
throw new ValidationError(
|
|
11776
|
+
`moneyFields: invalid path "${path}" \u2014 segment "${part}" must be a key, "key[]", "*", or "*[]"`
|
|
11777
|
+
);
|
|
11778
|
+
}
|
|
11779
|
+
const array = m[2] === "[]";
|
|
11780
|
+
segments.push(
|
|
11781
|
+
m[1] === "*" ? { kind: "wildcard", array } : { kind: "key", key: m[1], array }
|
|
11782
|
+
);
|
|
11783
|
+
}
|
|
11784
|
+
parseCache.set(path, segments);
|
|
11785
|
+
return segments;
|
|
11786
|
+
}
|
|
11787
|
+
function isSimpleMoneyPath(path) {
|
|
11788
|
+
return !path.includes(".") && !path.includes("[") && !path.includes("*");
|
|
11789
|
+
}
|
|
11790
|
+
function validateMoneyFieldPaths(moneyFields) {
|
|
11791
|
+
for (const path of Object.keys(moneyFields)) parseMoneyPath(path);
|
|
11792
|
+
}
|
|
11793
|
+
function transformAtMoneyPath(node, path, segments, index, visit, lenient) {
|
|
11794
|
+
if (node === null || node === void 0) return node;
|
|
11795
|
+
const seg = segments[index];
|
|
11796
|
+
const last = index === segments.length - 1;
|
|
11797
|
+
if (seg.kind === "key") {
|
|
11798
|
+
if (typeof node !== "object" || Array.isArray(node)) {
|
|
11799
|
+
if (lenient) return node;
|
|
11800
|
+
throw new ValidationError(
|
|
11801
|
+
`moneyFields: path "${path}" expected an object at segment "${seg.key}", got ${Array.isArray(node) ? "an array" : typeof node}`
|
|
11802
|
+
);
|
|
11803
|
+
}
|
|
11804
|
+
const obj2 = node;
|
|
11805
|
+
if (!(seg.key in obj2) || obj2[seg.key] === null || obj2[seg.key] === void 0) return node;
|
|
11806
|
+
if (seg.array) {
|
|
11807
|
+
const arr = obj2[seg.key];
|
|
11808
|
+
if (!Array.isArray(arr)) {
|
|
11809
|
+
if (lenient) return node;
|
|
11810
|
+
throw new ValidationError(
|
|
11811
|
+
`moneyFields: path "${path}" declares "${seg.key}[]" but the value is not an array`
|
|
11812
|
+
);
|
|
11813
|
+
}
|
|
11814
|
+
const cloned = [...arr];
|
|
11815
|
+
if (last) {
|
|
11816
|
+
for (let i = 0; i < cloned.length; i++) visit(cloned, i);
|
|
11817
|
+
} else {
|
|
11818
|
+
for (let i = 0; i < cloned.length; i++) {
|
|
11819
|
+
cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient);
|
|
11820
|
+
}
|
|
11821
|
+
}
|
|
11822
|
+
return { ...obj2, [seg.key]: cloned };
|
|
11823
|
+
}
|
|
11824
|
+
const clone3 = { ...obj2 };
|
|
11825
|
+
if (last) {
|
|
11826
|
+
visit(clone3, seg.key);
|
|
11827
|
+
} else {
|
|
11828
|
+
clone3[seg.key] = transformAtMoneyPath(clone3[seg.key], path, segments, index + 1, visit, lenient);
|
|
11829
|
+
}
|
|
11830
|
+
return clone3;
|
|
11831
|
+
}
|
|
11832
|
+
if (seg.array) {
|
|
11833
|
+
if (!Array.isArray(node)) {
|
|
11834
|
+
if (lenient) return node;
|
|
11835
|
+
throw new ValidationError(`moneyFields: path "${path}" declares "*[]" but the value is not an array`);
|
|
11836
|
+
}
|
|
11837
|
+
const cloned = [...node];
|
|
11838
|
+
if (last) {
|
|
11839
|
+
for (let i = 0; i < cloned.length; i++) visit(cloned, i);
|
|
11840
|
+
} else {
|
|
11841
|
+
for (let i = 0; i < cloned.length; i++) {
|
|
11842
|
+
cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient);
|
|
11843
|
+
}
|
|
11844
|
+
}
|
|
11845
|
+
return cloned;
|
|
11846
|
+
}
|
|
11847
|
+
if (typeof node !== "object" || Array.isArray(node)) {
|
|
11848
|
+
if (lenient) return node;
|
|
11849
|
+
throw new ValidationError(
|
|
11850
|
+
`moneyFields: path "${path}" applies "*" to a non-object (${Array.isArray(node) ? 'array \u2014 use "*[]"' : typeof node})`
|
|
11851
|
+
);
|
|
11852
|
+
}
|
|
11853
|
+
const obj = node;
|
|
11854
|
+
const clone2 = { ...obj };
|
|
11855
|
+
for (const key of Object.keys(obj)) {
|
|
11856
|
+
const v = clone2[key];
|
|
11857
|
+
if (v === null || v === void 0) continue;
|
|
11858
|
+
if (last) visit(clone2, key);
|
|
11859
|
+
else clone2[key] = transformAtMoneyPath(v, path, segments, index + 1, visit, lenient);
|
|
11860
|
+
}
|
|
11861
|
+
return clone2;
|
|
11862
|
+
}
|
|
11863
|
+
|
|
11864
|
+
// src/money/normalize.ts
|
|
11582
11865
|
function isMoneyValueObject(v) {
|
|
11583
11866
|
return typeof v === "object" && v !== null && "currency" in v;
|
|
11584
11867
|
}
|
|
@@ -11590,33 +11873,68 @@ function quantizeAmount(field, input, scale, rounding) {
|
|
|
11590
11873
|
}
|
|
11591
11874
|
return r.value.toString();
|
|
11592
11875
|
}
|
|
11876
|
+
function canonicalizeStoredMoney(record, moneyFields) {
|
|
11877
|
+
if (record === null || record === void 0) return record;
|
|
11878
|
+
if (!moneyFields || Object.keys(moneyFields).length === 0) return record;
|
|
11879
|
+
return decodeMoneyFields(record, moneyFields, "raw");
|
|
11880
|
+
}
|
|
11881
|
+
function canonicalizeIncomingMoney(record, moneyFields) {
|
|
11882
|
+
if (!moneyFields || Object.keys(moneyFields).length === 0) return record;
|
|
11883
|
+
try {
|
|
11884
|
+
return decodeMoneyFields(
|
|
11885
|
+
quantizeMoneyFields(record, moneyFields),
|
|
11886
|
+
moneyFields,
|
|
11887
|
+
"raw"
|
|
11888
|
+
);
|
|
11889
|
+
} catch {
|
|
11890
|
+
return record;
|
|
11891
|
+
}
|
|
11892
|
+
}
|
|
11893
|
+
function quantizeValue(field, raw, desc) {
|
|
11894
|
+
if (desc.mode === "fixed") {
|
|
11895
|
+
const currency2 = desc.fixedCurrency;
|
|
11896
|
+
return quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
|
|
11897
|
+
}
|
|
11898
|
+
let amount;
|
|
11899
|
+
let currency;
|
|
11900
|
+
if (isMoneyValueObject(raw)) {
|
|
11901
|
+
currency = String(raw.currency);
|
|
11902
|
+
amount = raw.amount;
|
|
11903
|
+
} else {
|
|
11904
|
+
const sole = desc.soleCurrency();
|
|
11905
|
+
if (sole === void 0) {
|
|
11906
|
+
throw new TypeError(
|
|
11907
|
+
`money: field "${field}" is multi-currency \u2014 write { amount, currency }, not a bare amount`
|
|
11908
|
+
);
|
|
11909
|
+
}
|
|
11910
|
+
currency = sole;
|
|
11911
|
+
amount = raw;
|
|
11912
|
+
}
|
|
11913
|
+
const scale = desc.scaleFor(currency);
|
|
11914
|
+
return { amount: quantizeAmount(field, amount, scale, desc.rounding), currency };
|
|
11915
|
+
}
|
|
11593
11916
|
function quantizeMoneyFields(record, moneyFields) {
|
|
11594
|
-
|
|
11595
|
-
for (const [
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
out[field] = quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
|
|
11917
|
+
let out = { ...record };
|
|
11918
|
+
for (const [path, desc] of Object.entries(moneyFields)) {
|
|
11919
|
+
if (isSimpleMoneyPath(path)) {
|
|
11920
|
+
const raw = out[path];
|
|
11921
|
+
if (raw === null || raw === void 0) continue;
|
|
11922
|
+
out[path] = quantizeValue(path, raw, desc);
|
|
11601
11923
|
continue;
|
|
11602
11924
|
}
|
|
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 };
|
|
11925
|
+
out = transformAtMoneyPath(
|
|
11926
|
+
out,
|
|
11927
|
+
path,
|
|
11928
|
+
parseMoneyPath(path),
|
|
11929
|
+
0,
|
|
11930
|
+
(container, key) => {
|
|
11931
|
+
const raw = container[key];
|
|
11932
|
+
if (raw === null || raw === void 0) return;
|
|
11933
|
+
container[key] = quantizeValue(path, raw, desc);
|
|
11934
|
+
},
|
|
11935
|
+
/* lenient */
|
|
11936
|
+
false
|
|
11937
|
+
);
|
|
11620
11938
|
}
|
|
11621
11939
|
return out;
|
|
11622
11940
|
}
|
|
@@ -11629,33 +11947,70 @@ function formatCurrency(decimal, currency, scale, locale) {
|
|
|
11629
11947
|
});
|
|
11630
11948
|
return fmt.format(decimal);
|
|
11631
11949
|
}
|
|
11950
|
+
function decodeValue(stored, desc) {
|
|
11951
|
+
let currency;
|
|
11952
|
+
let scaledIntString;
|
|
11953
|
+
if (desc.mode === "fixed") {
|
|
11954
|
+
if (typeof stored !== "string" && typeof stored !== "number") return null;
|
|
11955
|
+
currency = desc.fixedCurrency;
|
|
11956
|
+
scaledIntString = String(stored);
|
|
11957
|
+
} else {
|
|
11958
|
+
if (!isMoneyValueObject(stored)) return null;
|
|
11959
|
+
const amount = stored.amount;
|
|
11960
|
+
if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") return null;
|
|
11961
|
+
currency = stored.currency;
|
|
11962
|
+
scaledIntString = String(amount);
|
|
11963
|
+
}
|
|
11964
|
+
const scale = desc.scaleFor(currency);
|
|
11965
|
+
let decimal;
|
|
11966
|
+
try {
|
|
11967
|
+
decimal = formatScaledInt(BigInt(scaledIntString), scale);
|
|
11968
|
+
} catch {
|
|
11969
|
+
return null;
|
|
11970
|
+
}
|
|
11971
|
+
return {
|
|
11972
|
+
decoded: desc.mode === "fixed" ? decimal : { amount: decimal, currency },
|
|
11973
|
+
decimal,
|
|
11974
|
+
currency,
|
|
11975
|
+
scale
|
|
11976
|
+
};
|
|
11977
|
+
}
|
|
11632
11978
|
function decodeMoneyFields(record, moneyFields, locale) {
|
|
11633
|
-
|
|
11979
|
+
let out = { ...record };
|
|
11634
11980
|
const format = locale !== "raw";
|
|
11635
11981
|
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);
|
|
11982
|
+
for (const [path, desc] of Object.entries(moneyFields)) {
|
|
11983
|
+
if (isSimpleMoneyPath(path)) {
|
|
11984
|
+
const stored = out[path];
|
|
11985
|
+
if (stored === null || stored === void 0) continue;
|
|
11986
|
+
const r = decodeValue(stored, desc);
|
|
11987
|
+
if (r === null) continue;
|
|
11988
|
+
out[path] = r.decoded;
|
|
11989
|
+
if (format) {
|
|
11990
|
+
out[`${path}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale);
|
|
11991
|
+
out[`${path}Number`] = Number(r.decimal);
|
|
11992
|
+
}
|
|
11993
|
+
continue;
|
|
11658
11994
|
}
|
|
11995
|
+
out = transformAtMoneyPath(
|
|
11996
|
+
out,
|
|
11997
|
+
path,
|
|
11998
|
+
parseMoneyPath(path),
|
|
11999
|
+
0,
|
|
12000
|
+
(container, key) => {
|
|
12001
|
+
const stored = container[key];
|
|
12002
|
+
if (stored === null || stored === void 0) return;
|
|
12003
|
+
const r = decodeValue(stored, desc);
|
|
12004
|
+
if (r === null) return;
|
|
12005
|
+
container[key] = r.decoded;
|
|
12006
|
+
if (format && typeof key === "string" && !Array.isArray(container)) {
|
|
12007
|
+
container[`${key}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale);
|
|
12008
|
+
container[`${key}Number`] = Number(r.decimal);
|
|
12009
|
+
}
|
|
12010
|
+
},
|
|
12011
|
+
/* lenient */
|
|
12012
|
+
true
|
|
12013
|
+
);
|
|
11659
12014
|
}
|
|
11660
12015
|
return out;
|
|
11661
12016
|
}
|
|
@@ -12008,6 +12363,7 @@ var NO_AGGREGATE = {
|
|
|
12008
12363
|
|
|
12009
12364
|
// src/query/builder.ts
|
|
12010
12365
|
init_money_reducer();
|
|
12366
|
+
init_where();
|
|
12011
12367
|
var EMPTY_PLAN = {
|
|
12012
12368
|
clauses: [],
|
|
12013
12369
|
orderBy: [],
|
|
@@ -12099,9 +12455,18 @@ var Query = class _Query {
|
|
|
12099
12455
|
this.predicates
|
|
12100
12456
|
);
|
|
12101
12457
|
}
|
|
12102
|
-
/**
|
|
12458
|
+
/**
|
|
12459
|
+
* Add a field comparison. Multiple where() calls are AND-combined.
|
|
12460
|
+
*
|
|
12461
|
+
* A declared money field compares in MAJOR units (#336): the operand
|
|
12462
|
+
* (`10000`, `'10000.00'`, or `{ amount, currency }` in multi mode) is
|
|
12463
|
+
* quantized into stored scaled-int space at build time and evaluated
|
|
12464
|
+
* BigInt-exact per record. A malformed operand or a string operator
|
|
12465
|
+
* (`contains`/`startsWith`) throws here, at the call site.
|
|
12466
|
+
*/
|
|
12103
12467
|
where(field, op, value) {
|
|
12104
|
-
const
|
|
12468
|
+
const desc = this.source.moneyFields?.[field];
|
|
12469
|
+
const clause = desc ? moneyFieldClause(field, op, value, desc) : { type: "field", field, op, value };
|
|
12105
12470
|
return new _Query(
|
|
12106
12471
|
this.source,
|
|
12107
12472
|
{ ...this.plan, clauses: [...this.plan.clauses, clause] },
|
|
@@ -12427,7 +12792,7 @@ var Query = class _Query {
|
|
|
12427
12792
|
}
|
|
12428
12793
|
const { candidates, remainingClauses } = candidateRecords(this.source, this.plan.clauses);
|
|
12429
12794
|
if (remainingClauses.length === 0) return candidates.length;
|
|
12430
|
-
return filterRecords(candidates, remainingClauses).length;
|
|
12795
|
+
return filterRecords(candidates, remainingClauses, fnViewDecoder(this.source)).length;
|
|
12431
12796
|
}
|
|
12432
12797
|
/**
|
|
12433
12798
|
* Reduce the matching records through a named set of reducers.
|
|
@@ -12484,7 +12849,7 @@ var Query = class _Query {
|
|
|
12484
12849
|
return executeClausePipeline(source, clauses, joinCtx);
|
|
12485
12850
|
}
|
|
12486
12851
|
const { candidates, remainingClauses } = candidateRecords(source, clauses);
|
|
12487
|
-
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses);
|
|
12852
|
+
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
|
|
12488
12853
|
};
|
|
12489
12854
|
const upstreams = [];
|
|
12490
12855
|
if (source.subscribe) {
|
|
@@ -12507,7 +12872,7 @@ var Query = class _Query {
|
|
|
12507
12872
|
return executeClausePipeline(source, clauses, joinCtx);
|
|
12508
12873
|
}
|
|
12509
12874
|
const { candidates, remainingClauses } = candidateRecords(source, clauses);
|
|
12510
|
-
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses);
|
|
12875
|
+
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
|
|
12511
12876
|
};
|
|
12512
12877
|
const upstreams = [];
|
|
12513
12878
|
if (source.subscribe) {
|
|
@@ -12659,7 +13024,7 @@ function executePlanWithSource(source, plan, joinContext) {
|
|
|
12659
13024
|
result = executeClausePipeline(source, plan.clauses, joinContext);
|
|
12660
13025
|
} else {
|
|
12661
13026
|
const { candidates, remainingClauses } = candidateRecords(source, plan.clauses);
|
|
12662
|
-
result = remainingClauses.length === 0 ? [...candidates] : filterRecords(candidates, remainingClauses);
|
|
13027
|
+
result = remainingClauses.length === 0 ? [...candidates] : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
|
|
12663
13028
|
}
|
|
12664
13029
|
if (plan.orderBy.length > 0) {
|
|
12665
13030
|
result = sortRecords(result, plan.orderBy);
|
|
@@ -12682,6 +13047,7 @@ function candidateRecords(source, clauses) {
|
|
|
12682
13047
|
const clause = clauses[i];
|
|
12683
13048
|
if (clause.type !== "field") continue;
|
|
12684
13049
|
if (!indexes.has(clause.field)) continue;
|
|
13050
|
+
if (clause.money?.mode === "multi") continue;
|
|
12685
13051
|
let ids = null;
|
|
12686
13052
|
if (clause.op === "==") {
|
|
12687
13053
|
ids = indexes.lookupEqual(clause.field, clause.value);
|
|
@@ -12727,13 +13093,20 @@ function executePlan(records, plan) {
|
|
|
12727
13093
|
}
|
|
12728
13094
|
return result;
|
|
12729
13095
|
}
|
|
12730
|
-
function
|
|
13096
|
+
function fnViewDecoder(source) {
|
|
13097
|
+
const mf = source.moneyFields;
|
|
13098
|
+
if (!mf || Object.keys(mf).length === 0) return void 0;
|
|
13099
|
+
return (r) => decodeMoneyFields(r, mf, "raw");
|
|
13100
|
+
}
|
|
13101
|
+
function filterRecords(records, clauses, decodeForFns) {
|
|
12731
13102
|
if (clauses.length === 0) return [...records];
|
|
13103
|
+
const needsFnView = decodeForFns !== void 0 && hasFnClause(clauses);
|
|
12732
13104
|
const out = [];
|
|
12733
13105
|
for (const r of records) {
|
|
13106
|
+
const fnView = needsFnView ? decodeForFns(r) : void 0;
|
|
12734
13107
|
let matches = true;
|
|
12735
13108
|
for (const clause of clauses) {
|
|
12736
|
-
if (!evaluateClause(r, clause)) {
|
|
13109
|
+
if (!evaluateClause(r, clause, fnView)) {
|
|
12737
13110
|
matches = false;
|
|
12738
13111
|
break;
|
|
12739
13112
|
}
|
|
@@ -12745,10 +13118,11 @@ function filterRecords(records, clauses) {
|
|
|
12745
13118
|
function executeClausePipeline(source, clauses, joinContext) {
|
|
12746
13119
|
let rel = [...source.snapshot()];
|
|
12747
13120
|
let filterBatch = [];
|
|
13121
|
+
const decodeForFns = fnViewDecoder(source);
|
|
12748
13122
|
for (const clause of clauses) {
|
|
12749
13123
|
if (clause.type === "crossJoin") {
|
|
12750
13124
|
if (filterBatch.length > 0) {
|
|
12751
|
-
rel = filterRecords(rel, filterBatch);
|
|
13125
|
+
rel = filterRecords(rel, filterBatch, decodeForFns);
|
|
12752
13126
|
filterBatch = [];
|
|
12753
13127
|
}
|
|
12754
13128
|
const rightSource = joinContext.resolveSource(clause.target);
|
|
@@ -12761,7 +13135,7 @@ function executeClausePipeline(source, clauses, joinContext) {
|
|
|
12761
13135
|
}
|
|
12762
13136
|
}
|
|
12763
13137
|
if (filterBatch.length > 0) {
|
|
12764
|
-
rel = filterRecords(rel, filterBatch);
|
|
13138
|
+
rel = filterRecords(rel, filterBatch, decodeForFns);
|
|
12765
13139
|
}
|
|
12766
13140
|
return rel;
|
|
12767
13141
|
}
|
|
@@ -13179,6 +13553,7 @@ init_groupby();
|
|
|
13179
13553
|
// src/query/scan-builder.ts
|
|
13180
13554
|
init_predicate();
|
|
13181
13555
|
init_errors();
|
|
13556
|
+
init_where();
|
|
13182
13557
|
var DEFAULT_SCAN_PAGE_SIZE = 100;
|
|
13183
13558
|
var ScanBuilder = class _ScanBuilder {
|
|
13184
13559
|
pageProvider;
|
|
@@ -13242,7 +13617,8 @@ var ScanBuilder = class _ScanBuilder {
|
|
|
13242
13617
|
* evaluates clauses per record in O(1) per clause.
|
|
13243
13618
|
*/
|
|
13244
13619
|
where(field, op, value) {
|
|
13245
|
-
const
|
|
13620
|
+
const desc = this.moneyFields?.[field];
|
|
13621
|
+
const clause = desc ? moneyFieldClause(field, op, value, desc) : { type: "field", field, op, value };
|
|
13246
13622
|
return new _ScanBuilder(
|
|
13247
13623
|
this.pageProvider,
|
|
13248
13624
|
this.pageSize,
|
|
@@ -13588,8 +13964,9 @@ var ScanBuilder = class _ScanBuilder {
|
|
|
13588
13964
|
*/
|
|
13589
13965
|
recordMatches(record) {
|
|
13590
13966
|
if (this.clauses.length === 0) return true;
|
|
13967
|
+
const fnView = this.moneyFields && Object.keys(this.moneyFields).length > 0 && hasFnClause(this.clauses) ? this.decodeMoney(record) : void 0;
|
|
13591
13968
|
for (const clause of this.clauses) {
|
|
13592
|
-
if (!evaluateClause(record, clause)) return false;
|
|
13969
|
+
if (!evaluateClause(record, clause, fnView)) return false;
|
|
13593
13970
|
}
|
|
13594
13971
|
return true;
|
|
13595
13972
|
}
|
|
@@ -14753,6 +15130,7 @@ var Collection = class {
|
|
|
14753
15130
|
this.joinResolver = opts.joinResolver;
|
|
14754
15131
|
this.i18nFields = opts.i18nFields;
|
|
14755
15132
|
this.dictKeyFields = opts.dictKeyFields;
|
|
15133
|
+
if (opts.moneyFields) validateMoneyFieldPaths(opts.moneyFields);
|
|
14756
15134
|
this.moneyFields = opts.moneyFields;
|
|
14757
15135
|
this.computed = opts.computed;
|
|
14758
15136
|
this.dictLabelResolver = opts.dictLabelResolver;
|
|
@@ -14886,7 +15264,9 @@ var Collection = class {
|
|
|
14886
15264
|
* declaration; this reconciles that ordering. First-wins. Not public.
|
|
14887
15265
|
*/
|
|
14888
15266
|
_applyMoneyFields(moneyFields) {
|
|
14889
|
-
if (this.moneyFields
|
|
15267
|
+
if (this.moneyFields !== void 0) return;
|
|
15268
|
+
validateMoneyFieldPaths(moneyFields);
|
|
15269
|
+
this.moneyFields = moneyFields;
|
|
14890
15270
|
}
|
|
14891
15271
|
/** @internal — attach computed fields post-construction. See {@link _applyMoneyFields}. */
|
|
14892
15272
|
_applyComputed(computed) {
|
|
@@ -15053,6 +15433,7 @@ var Collection = class {
|
|
|
15053
15433
|
if (!hasWritePermission(this.keyring, this.name)) {
|
|
15054
15434
|
throw new ReadOnlyError();
|
|
15055
15435
|
}
|
|
15436
|
+
record = canonicalizeIncomingMoney(record, this.moneyFields);
|
|
15056
15437
|
if (this.subsystemBus?.hasGateHandlers("beforePut")) {
|
|
15057
15438
|
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
15058
15439
|
let existingRecord = null;
|
|
@@ -15069,7 +15450,7 @@ var Collection = class {
|
|
|
15069
15450
|
collection: this.name,
|
|
15070
15451
|
docId: id,
|
|
15071
15452
|
incoming: record,
|
|
15072
|
-
existing: existingRecord,
|
|
15453
|
+
existing: canonicalizeStoredMoney(existingRecord, this.moneyFields),
|
|
15073
15454
|
existingVersion: existingEnv?._v ?? 0,
|
|
15074
15455
|
existingTs: existingEnv?._ts,
|
|
15075
15456
|
userId: this.keyring.userId,
|
|
@@ -15338,7 +15719,7 @@ var Collection = class {
|
|
|
15338
15719
|
*/
|
|
15339
15720
|
async dispatchDerivations(id, record, version) {
|
|
15340
15721
|
if (this.derivationSource === void 0) return;
|
|
15341
|
-
const incoming = record;
|
|
15722
|
+
const incoming = canonicalizeStoredMoney(record, this.moneyFields);
|
|
15342
15723
|
if (incoming && typeof incoming === "object" && "_derivedFrom" in incoming) return;
|
|
15343
15724
|
const registry = this.derivationSource.registry();
|
|
15344
15725
|
const strategies = registry.strategiesForSource(this.name);
|
|
@@ -15571,7 +15952,7 @@ var Collection = class {
|
|
|
15571
15952
|
vault: this.vault,
|
|
15572
15953
|
collection: this.name,
|
|
15573
15954
|
docId: id,
|
|
15574
|
-
existing: existingRecord,
|
|
15955
|
+
existing: canonicalizeStoredMoney(existingRecord, this.moneyFields),
|
|
15575
15956
|
existingVersion: existingEnv._v,
|
|
15576
15957
|
existingTs: existingEnv._ts,
|
|
15577
15958
|
internal,
|
|
@@ -25768,9 +26149,9 @@ function withMaterializedView(spec) {
|
|
|
25768
26149
|
throw new ValidationError("withMaterializedView: query must be a function returning a Query<T>");
|
|
25769
26150
|
}
|
|
25770
26151
|
if (spec.unionSources) {
|
|
25771
|
-
if (spec.unionSources.length <
|
|
26152
|
+
if (spec.unionSources.length < 1) {
|
|
25772
26153
|
throw new MaterializedViewConfigError(
|
|
25773
|
-
"unionSources requires at least
|
|
26154
|
+
"unionSources requires at least 1 source collection"
|
|
25774
26155
|
);
|
|
25775
26156
|
}
|
|
25776
26157
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -25862,6 +26243,117 @@ init_errors();
|
|
|
25862
26243
|
init_descriptor();
|
|
25863
26244
|
init_iso4217();
|
|
25864
26245
|
|
|
26246
|
+
// src/money/arith.ts
|
|
26247
|
+
init_fixed_point();
|
|
26248
|
+
init_descriptor();
|
|
26249
|
+
function parseAmount(label, amount, scale, rounding) {
|
|
26250
|
+
const r = parseToScaledInt(amount, scale, rounding);
|
|
26251
|
+
if (!r.ok) {
|
|
26252
|
+
throw new MoneyUnsupportedError(
|
|
26253
|
+
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`
|
|
26254
|
+
);
|
|
26255
|
+
}
|
|
26256
|
+
return r.value;
|
|
26257
|
+
}
|
|
26258
|
+
function resolveScale(label, amount, explicit) {
|
|
26259
|
+
if (explicit !== void 0) {
|
|
26260
|
+
if (!Number.isInteger(explicit) || explicit < 0) {
|
|
26261
|
+
throw new MoneyUnsupportedError(`${label}: scale must be a non-negative integer`);
|
|
26262
|
+
}
|
|
26263
|
+
return explicit;
|
|
26264
|
+
}
|
|
26265
|
+
const inferred = decimalScaleOf(amount);
|
|
26266
|
+
if (inferred === null) {
|
|
26267
|
+
throw new MoneyUnsupportedError(`${label}: amount ${JSON.stringify(amount)} is not a finite decimal`);
|
|
26268
|
+
}
|
|
26269
|
+
return inferred;
|
|
26270
|
+
}
|
|
26271
|
+
function mulRate(amount, rate, opts = {}) {
|
|
26272
|
+
const scale = resolveScale("mulRate", amount, opts.scale);
|
|
26273
|
+
const rounding = opts.rounding ?? "half-up";
|
|
26274
|
+
const a = parseAmount("mulRate", amount, scale, rounding);
|
|
26275
|
+
const rateScale = decimalScaleOf(rate);
|
|
26276
|
+
if (rateScale === null) {
|
|
26277
|
+
throw new MoneyUnsupportedError(`mulRate: rate ${JSON.stringify(rate)} is not a finite decimal`);
|
|
26278
|
+
}
|
|
26279
|
+
const r = parseToScaledInt(rate, rateScale);
|
|
26280
|
+
if (!r.ok) {
|
|
26281
|
+
throw new MoneyUnsupportedError(`mulRate: rate ${JSON.stringify(rate)} is not a finite decimal`);
|
|
26282
|
+
}
|
|
26283
|
+
const product = a * r.value;
|
|
26284
|
+
return formatScaledInt(rescaleScaledInt(product, scale + rateScale, scale, rounding), scale);
|
|
26285
|
+
}
|
|
26286
|
+
function allocate(amount, weights, opts = {}) {
|
|
26287
|
+
if (weights.length === 0) {
|
|
26288
|
+
throw new MoneyUnsupportedError("allocate: weights must not be empty");
|
|
26289
|
+
}
|
|
26290
|
+
const scale = resolveScale("allocate", amount, opts.scale);
|
|
26291
|
+
const a = parseAmount("allocate", amount, scale);
|
|
26292
|
+
let weightScale = 0;
|
|
26293
|
+
for (const w of weights) {
|
|
26294
|
+
const s = decimalScaleOf(w);
|
|
26295
|
+
if (s === null) {
|
|
26296
|
+
throw new MoneyUnsupportedError(`allocate: weight ${JSON.stringify(w)} is not a finite decimal`);
|
|
26297
|
+
}
|
|
26298
|
+
if (s > weightScale) weightScale = s;
|
|
26299
|
+
}
|
|
26300
|
+
const scaledWeights = weights.map((w) => {
|
|
26301
|
+
const r = parseToScaledInt(w, weightScale);
|
|
26302
|
+
if (!r.ok || r.value < 0n) {
|
|
26303
|
+
throw new MoneyUnsupportedError(`allocate: weight ${JSON.stringify(w)} must be a non-negative decimal`);
|
|
26304
|
+
}
|
|
26305
|
+
return r.value;
|
|
26306
|
+
});
|
|
26307
|
+
const sumW = scaledWeights.reduce((acc, w) => acc + w, 0n);
|
|
26308
|
+
if (sumW === 0n) {
|
|
26309
|
+
throw new MoneyUnsupportedError("allocate: weights must not all be zero");
|
|
26310
|
+
}
|
|
26311
|
+
const negative = a < 0n;
|
|
26312
|
+
const mag = negative ? -a : a;
|
|
26313
|
+
const base = [];
|
|
26314
|
+
const remainders = [];
|
|
26315
|
+
let distributed = 0n;
|
|
26316
|
+
for (let i = 0; i < scaledWeights.length; i++) {
|
|
26317
|
+
const product = mag * scaledWeights[i];
|
|
26318
|
+
const share = product / sumW;
|
|
26319
|
+
base.push(share);
|
|
26320
|
+
distributed += share;
|
|
26321
|
+
remainders.push({ index: i, rem: product % sumW });
|
|
26322
|
+
}
|
|
26323
|
+
let leftover = mag - distributed;
|
|
26324
|
+
remainders.sort((x, y) => y.rem > x.rem ? 1 : y.rem < x.rem ? -1 : x.index - y.index);
|
|
26325
|
+
for (const { index } of remainders) {
|
|
26326
|
+
if (leftover === 0n) break;
|
|
26327
|
+
base[index] = base[index] + 1n;
|
|
26328
|
+
leftover -= 1n;
|
|
26329
|
+
}
|
|
26330
|
+
return base.map((p) => formatScaledInt(negative && p !== 0n ? -p : p, scale));
|
|
26331
|
+
}
|
|
26332
|
+
|
|
26333
|
+
// src/money/branded.ts
|
|
26334
|
+
init_fixed_point();
|
|
26335
|
+
init_descriptor();
|
|
26336
|
+
function asMoney(value) {
|
|
26337
|
+
if (!isMoneyLike(value)) {
|
|
26338
|
+
throw new MoneyUnsupportedError(`asMoney: ${JSON.stringify(value)} is not a finite decimal`);
|
|
26339
|
+
}
|
|
26340
|
+
return String(value).trim();
|
|
26341
|
+
}
|
|
26342
|
+
function isMoneyString(value) {
|
|
26343
|
+
return typeof value === "string" && isMoneyLike(value);
|
|
26344
|
+
}
|
|
26345
|
+
function moneyNumber(value) {
|
|
26346
|
+
if (!isMoneyLike(value)) {
|
|
26347
|
+
throw new MoneyUnsupportedError(`moneyNumber: ${JSON.stringify(value)} is not a finite decimal`);
|
|
26348
|
+
}
|
|
26349
|
+
return Number(value);
|
|
26350
|
+
}
|
|
26351
|
+
function isMoneyLike(value) {
|
|
26352
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
26353
|
+
if (typeof value !== "string") return false;
|
|
26354
|
+
return decimalScaleOf(value) !== null;
|
|
26355
|
+
}
|
|
26356
|
+
|
|
25865
26357
|
// src/i18n/script.ts
|
|
25866
26358
|
init_errors();
|
|
25867
26359
|
var LATIN_BASE = /* @__PURE__ */ new Set([
|
|
@@ -26804,9 +27296,11 @@ function shortJSON(value) {
|
|
|
26804
27296
|
WeakPassphraseError,
|
|
26805
27297
|
activeSessionCount,
|
|
26806
27298
|
additiveOnly,
|
|
27299
|
+
allocate,
|
|
26807
27300
|
applyI18nLocale,
|
|
26808
27301
|
applyJoins,
|
|
26809
27302
|
applyPatch,
|
|
27303
|
+
asMoney,
|
|
26810
27304
|
assertStrongPassphrase,
|
|
26811
27305
|
assertTierAccess,
|
|
26812
27306
|
avg,
|
|
@@ -26883,6 +27377,7 @@ function shortJSON(value) {
|
|
|
26883
27377
|
isI18nTextDescriptor,
|
|
26884
27378
|
isMagicLinkGrantExpired,
|
|
26885
27379
|
isMoneyDescriptor,
|
|
27380
|
+
isMoneyString,
|
|
26886
27381
|
isPreCompressed,
|
|
26887
27382
|
isPublicEnvelope,
|
|
26888
27383
|
isSessionAlive,
|
|
@@ -26915,6 +27410,8 @@ function shortJSON(value) {
|
|
|
26915
27410
|
mintShamirRecoveryEntry,
|
|
26916
27411
|
mintWrappedDeksBlob,
|
|
26917
27412
|
money,
|
|
27413
|
+
moneyNumber,
|
|
27414
|
+
mulRate,
|
|
26918
27415
|
paddedIndex,
|
|
26919
27416
|
parseBytes,
|
|
26920
27417
|
parseIndex,
|