@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/bundle/index.cjs
CHANGED
|
@@ -3894,6 +3894,115 @@ var init_descriptor = __esm({
|
|
|
3894
3894
|
}
|
|
3895
3895
|
});
|
|
3896
3896
|
|
|
3897
|
+
// src/money/paths.ts
|
|
3898
|
+
function parseMoneyPath(path) {
|
|
3899
|
+
const cached = parseCache.get(path);
|
|
3900
|
+
if (cached) return cached;
|
|
3901
|
+
if (typeof path !== "string" || path.length === 0) {
|
|
3902
|
+
throw new ValidationError("moneyFields: path must be a non-empty string");
|
|
3903
|
+
}
|
|
3904
|
+
const segments = [];
|
|
3905
|
+
for (const part of path.split(".")) {
|
|
3906
|
+
const m = SEGMENT_RE.exec(part);
|
|
3907
|
+
if (!m) {
|
|
3908
|
+
throw new ValidationError(
|
|
3909
|
+
`moneyFields: invalid path "${path}" \u2014 segment "${part}" must be a key, "key[]", "*", or "*[]"`
|
|
3910
|
+
);
|
|
3911
|
+
}
|
|
3912
|
+
const array = m[2] === "[]";
|
|
3913
|
+
segments.push(
|
|
3914
|
+
m[1] === "*" ? { kind: "wildcard", array } : { kind: "key", key: m[1], array }
|
|
3915
|
+
);
|
|
3916
|
+
}
|
|
3917
|
+
parseCache.set(path, segments);
|
|
3918
|
+
return segments;
|
|
3919
|
+
}
|
|
3920
|
+
function isSimpleMoneyPath(path) {
|
|
3921
|
+
return !path.includes(".") && !path.includes("[") && !path.includes("*");
|
|
3922
|
+
}
|
|
3923
|
+
function validateMoneyFieldPaths(moneyFields) {
|
|
3924
|
+
for (const path of Object.keys(moneyFields)) parseMoneyPath(path);
|
|
3925
|
+
}
|
|
3926
|
+
function transformAtMoneyPath(node, path, segments, index, visit, lenient) {
|
|
3927
|
+
if (node === null || node === void 0) return node;
|
|
3928
|
+
const seg = segments[index];
|
|
3929
|
+
const last = index === segments.length - 1;
|
|
3930
|
+
if (seg.kind === "key") {
|
|
3931
|
+
if (typeof node !== "object" || Array.isArray(node)) {
|
|
3932
|
+
if (lenient) return node;
|
|
3933
|
+
throw new ValidationError(
|
|
3934
|
+
`moneyFields: path "${path}" expected an object at segment "${seg.key}", got ${Array.isArray(node) ? "an array" : typeof node}`
|
|
3935
|
+
);
|
|
3936
|
+
}
|
|
3937
|
+
const obj2 = node;
|
|
3938
|
+
if (!(seg.key in obj2) || obj2[seg.key] === null || obj2[seg.key] === void 0) return node;
|
|
3939
|
+
if (seg.array) {
|
|
3940
|
+
const arr = obj2[seg.key];
|
|
3941
|
+
if (!Array.isArray(arr)) {
|
|
3942
|
+
if (lenient) return node;
|
|
3943
|
+
throw new ValidationError(
|
|
3944
|
+
`moneyFields: path "${path}" declares "${seg.key}[]" but the value is not an array`
|
|
3945
|
+
);
|
|
3946
|
+
}
|
|
3947
|
+
const cloned = [...arr];
|
|
3948
|
+
if (last) {
|
|
3949
|
+
for (let i = 0; i < cloned.length; i++) visit(cloned, i);
|
|
3950
|
+
} else {
|
|
3951
|
+
for (let i = 0; i < cloned.length; i++) {
|
|
3952
|
+
cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient);
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
return { ...obj2, [seg.key]: cloned };
|
|
3956
|
+
}
|
|
3957
|
+
const clone3 = { ...obj2 };
|
|
3958
|
+
if (last) {
|
|
3959
|
+
visit(clone3, seg.key);
|
|
3960
|
+
} else {
|
|
3961
|
+
clone3[seg.key] = transformAtMoneyPath(clone3[seg.key], path, segments, index + 1, visit, lenient);
|
|
3962
|
+
}
|
|
3963
|
+
return clone3;
|
|
3964
|
+
}
|
|
3965
|
+
if (seg.array) {
|
|
3966
|
+
if (!Array.isArray(node)) {
|
|
3967
|
+
if (lenient) return node;
|
|
3968
|
+
throw new ValidationError(`moneyFields: path "${path}" declares "*[]" but the value is not an array`);
|
|
3969
|
+
}
|
|
3970
|
+
const cloned = [...node];
|
|
3971
|
+
if (last) {
|
|
3972
|
+
for (let i = 0; i < cloned.length; i++) visit(cloned, i);
|
|
3973
|
+
} else {
|
|
3974
|
+
for (let i = 0; i < cloned.length; i++) {
|
|
3975
|
+
cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient);
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
return cloned;
|
|
3979
|
+
}
|
|
3980
|
+
if (typeof node !== "object" || Array.isArray(node)) {
|
|
3981
|
+
if (lenient) return node;
|
|
3982
|
+
throw new ValidationError(
|
|
3983
|
+
`moneyFields: path "${path}" applies "*" to a non-object (${Array.isArray(node) ? 'array \u2014 use "*[]"' : typeof node})`
|
|
3984
|
+
);
|
|
3985
|
+
}
|
|
3986
|
+
const obj = node;
|
|
3987
|
+
const clone2 = { ...obj };
|
|
3988
|
+
for (const key of Object.keys(obj)) {
|
|
3989
|
+
const v = clone2[key];
|
|
3990
|
+
if (v === null || v === void 0) continue;
|
|
3991
|
+
if (last) visit(clone2, key);
|
|
3992
|
+
else clone2[key] = transformAtMoneyPath(v, path, segments, index + 1, visit, lenient);
|
|
3993
|
+
}
|
|
3994
|
+
return clone2;
|
|
3995
|
+
}
|
|
3996
|
+
var SEGMENT_RE, parseCache;
|
|
3997
|
+
var init_paths = __esm({
|
|
3998
|
+
"src/money/paths.ts"() {
|
|
3999
|
+
"use strict";
|
|
4000
|
+
init_errors();
|
|
4001
|
+
SEGMENT_RE = /^(\*|[^.[\]*]+)(\[\])?$/;
|
|
4002
|
+
parseCache = /* @__PURE__ */ new Map();
|
|
4003
|
+
}
|
|
4004
|
+
});
|
|
4005
|
+
|
|
3897
4006
|
// src/money/normalize.ts
|
|
3898
4007
|
function isMoneyValueObject(v) {
|
|
3899
4008
|
return typeof v === "object" && v !== null && "currency" in v;
|
|
@@ -3906,33 +4015,68 @@ function quantizeAmount(field, input, scale, rounding) {
|
|
|
3906
4015
|
}
|
|
3907
4016
|
return r.value.toString();
|
|
3908
4017
|
}
|
|
4018
|
+
function canonicalizeStoredMoney(record, moneyFields) {
|
|
4019
|
+
if (record === null || record === void 0) return record;
|
|
4020
|
+
if (!moneyFields || Object.keys(moneyFields).length === 0) return record;
|
|
4021
|
+
return decodeMoneyFields(record, moneyFields, "raw");
|
|
4022
|
+
}
|
|
4023
|
+
function canonicalizeIncomingMoney(record, moneyFields) {
|
|
4024
|
+
if (!moneyFields || Object.keys(moneyFields).length === 0) return record;
|
|
4025
|
+
try {
|
|
4026
|
+
return decodeMoneyFields(
|
|
4027
|
+
quantizeMoneyFields(record, moneyFields),
|
|
4028
|
+
moneyFields,
|
|
4029
|
+
"raw"
|
|
4030
|
+
);
|
|
4031
|
+
} catch {
|
|
4032
|
+
return record;
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
function quantizeValue(field, raw, desc) {
|
|
4036
|
+
if (desc.mode === "fixed") {
|
|
4037
|
+
const currency2 = desc.fixedCurrency;
|
|
4038
|
+
return quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
|
|
4039
|
+
}
|
|
4040
|
+
let amount;
|
|
4041
|
+
let currency;
|
|
4042
|
+
if (isMoneyValueObject(raw)) {
|
|
4043
|
+
currency = String(raw.currency);
|
|
4044
|
+
amount = raw.amount;
|
|
4045
|
+
} else {
|
|
4046
|
+
const sole = desc.soleCurrency();
|
|
4047
|
+
if (sole === void 0) {
|
|
4048
|
+
throw new TypeError(
|
|
4049
|
+
`money: field "${field}" is multi-currency \u2014 write { amount, currency }, not a bare amount`
|
|
4050
|
+
);
|
|
4051
|
+
}
|
|
4052
|
+
currency = sole;
|
|
4053
|
+
amount = raw;
|
|
4054
|
+
}
|
|
4055
|
+
const scale = desc.scaleFor(currency);
|
|
4056
|
+
return { amount: quantizeAmount(field, amount, scale, desc.rounding), currency };
|
|
4057
|
+
}
|
|
3909
4058
|
function quantizeMoneyFields(record, moneyFields) {
|
|
3910
|
-
|
|
3911
|
-
for (const [
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
out[field] = quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
|
|
4059
|
+
let out = { ...record };
|
|
4060
|
+
for (const [path, desc] of Object.entries(moneyFields)) {
|
|
4061
|
+
if (isSimpleMoneyPath(path)) {
|
|
4062
|
+
const raw = out[path];
|
|
4063
|
+
if (raw === null || raw === void 0) continue;
|
|
4064
|
+
out[path] = quantizeValue(path, raw, desc);
|
|
3917
4065
|
continue;
|
|
3918
4066
|
}
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
amount = raw;
|
|
3933
|
-
}
|
|
3934
|
-
const scale = desc.scaleFor(currency);
|
|
3935
|
-
out[field] = { amount: quantizeAmount(field, amount, scale, desc.rounding), currency };
|
|
4067
|
+
out = transformAtMoneyPath(
|
|
4068
|
+
out,
|
|
4069
|
+
path,
|
|
4070
|
+
parseMoneyPath(path),
|
|
4071
|
+
0,
|
|
4072
|
+
(container, key) => {
|
|
4073
|
+
const raw = container[key];
|
|
4074
|
+
if (raw === null || raw === void 0) return;
|
|
4075
|
+
container[key] = quantizeValue(path, raw, desc);
|
|
4076
|
+
},
|
|
4077
|
+
/* lenient */
|
|
4078
|
+
false
|
|
4079
|
+
);
|
|
3936
4080
|
}
|
|
3937
4081
|
return out;
|
|
3938
4082
|
}
|
|
@@ -3945,33 +4089,70 @@ function formatCurrency(decimal, currency, scale, locale) {
|
|
|
3945
4089
|
});
|
|
3946
4090
|
return fmt.format(decimal);
|
|
3947
4091
|
}
|
|
4092
|
+
function decodeValue(stored, desc) {
|
|
4093
|
+
let currency;
|
|
4094
|
+
let scaledIntString;
|
|
4095
|
+
if (desc.mode === "fixed") {
|
|
4096
|
+
if (typeof stored !== "string" && typeof stored !== "number") return null;
|
|
4097
|
+
currency = desc.fixedCurrency;
|
|
4098
|
+
scaledIntString = String(stored);
|
|
4099
|
+
} else {
|
|
4100
|
+
if (!isMoneyValueObject(stored)) return null;
|
|
4101
|
+
const amount = stored.amount;
|
|
4102
|
+
if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") return null;
|
|
4103
|
+
currency = stored.currency;
|
|
4104
|
+
scaledIntString = String(amount);
|
|
4105
|
+
}
|
|
4106
|
+
const scale = desc.scaleFor(currency);
|
|
4107
|
+
let decimal;
|
|
4108
|
+
try {
|
|
4109
|
+
decimal = formatScaledInt(BigInt(scaledIntString), scale);
|
|
4110
|
+
} catch {
|
|
4111
|
+
return null;
|
|
4112
|
+
}
|
|
4113
|
+
return {
|
|
4114
|
+
decoded: desc.mode === "fixed" ? decimal : { amount: decimal, currency },
|
|
4115
|
+
decimal,
|
|
4116
|
+
currency,
|
|
4117
|
+
scale
|
|
4118
|
+
};
|
|
4119
|
+
}
|
|
3948
4120
|
function decodeMoneyFields(record, moneyFields, locale) {
|
|
3949
|
-
|
|
4121
|
+
let out = { ...record };
|
|
3950
4122
|
const format = locale !== "raw";
|
|
3951
4123
|
const fmtLocale = typeof locale === "string" && locale !== "raw" ? locale : "en-US";
|
|
3952
|
-
for (const [
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") continue;
|
|
3965
|
-
currency = stored.currency;
|
|
3966
|
-
scaledIntString = String(amount);
|
|
3967
|
-
}
|
|
3968
|
-
const scale = desc.scaleFor(currency);
|
|
3969
|
-
const decimal = formatScaledInt(BigInt(scaledIntString), scale);
|
|
3970
|
-
out[field] = desc.mode === "fixed" ? decimal : { amount: decimal, currency };
|
|
3971
|
-
if (format) {
|
|
3972
|
-
out[`${field}Formatted`] = formatCurrency(decimal, currency, scale, fmtLocale);
|
|
3973
|
-
out[`${field}Number`] = Number(decimal);
|
|
4124
|
+
for (const [path, desc] of Object.entries(moneyFields)) {
|
|
4125
|
+
if (isSimpleMoneyPath(path)) {
|
|
4126
|
+
const stored = out[path];
|
|
4127
|
+
if (stored === null || stored === void 0) continue;
|
|
4128
|
+
const r = decodeValue(stored, desc);
|
|
4129
|
+
if (r === null) continue;
|
|
4130
|
+
out[path] = r.decoded;
|
|
4131
|
+
if (format) {
|
|
4132
|
+
out[`${path}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale);
|
|
4133
|
+
out[`${path}Number`] = Number(r.decimal);
|
|
4134
|
+
}
|
|
4135
|
+
continue;
|
|
3974
4136
|
}
|
|
4137
|
+
out = transformAtMoneyPath(
|
|
4138
|
+
out,
|
|
4139
|
+
path,
|
|
4140
|
+
parseMoneyPath(path),
|
|
4141
|
+
0,
|
|
4142
|
+
(container, key) => {
|
|
4143
|
+
const stored = container[key];
|
|
4144
|
+
if (stored === null || stored === void 0) return;
|
|
4145
|
+
const r = decodeValue(stored, desc);
|
|
4146
|
+
if (r === null) return;
|
|
4147
|
+
container[key] = r.decoded;
|
|
4148
|
+
if (format && typeof key === "string" && !Array.isArray(container)) {
|
|
4149
|
+
container[`${key}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale);
|
|
4150
|
+
container[`${key}Number`] = Number(r.decimal);
|
|
4151
|
+
}
|
|
4152
|
+
},
|
|
4153
|
+
/* lenient */
|
|
4154
|
+
true
|
|
4155
|
+
);
|
|
3975
4156
|
}
|
|
3976
4157
|
return out;
|
|
3977
4158
|
}
|
|
@@ -3980,6 +4161,7 @@ var init_normalize = __esm({
|
|
|
3980
4161
|
"use strict";
|
|
3981
4162
|
init_fixed_point();
|
|
3982
4163
|
init_descriptor();
|
|
4164
|
+
init_paths();
|
|
3983
4165
|
}
|
|
3984
4166
|
});
|
|
3985
4167
|
|
|
@@ -4152,6 +4334,146 @@ var init_strategy3 = __esm({
|
|
|
4152
4334
|
}
|
|
4153
4335
|
});
|
|
4154
4336
|
|
|
4337
|
+
// src/money/where.ts
|
|
4338
|
+
function isMoneyValueObject2(v) {
|
|
4339
|
+
return typeof v === "object" && v !== null && "currency" in v;
|
|
4340
|
+
}
|
|
4341
|
+
function parseOperand(field, raw, desc) {
|
|
4342
|
+
let amount;
|
|
4343
|
+
let currency;
|
|
4344
|
+
if (desc.mode === "fixed") {
|
|
4345
|
+
currency = desc.fixedCurrency;
|
|
4346
|
+
amount = raw;
|
|
4347
|
+
} else if (isMoneyValueObject2(raw)) {
|
|
4348
|
+
currency = String(raw.currency);
|
|
4349
|
+
amount = raw.amount;
|
|
4350
|
+
} else {
|
|
4351
|
+
const sole = desc.soleCurrency();
|
|
4352
|
+
if (sole === void 0) {
|
|
4353
|
+
throw new MoneyUnsupportedError(
|
|
4354
|
+
`where("${field}"): field is multi-currency \u2014 compare against { amount, currency }, not a bare amount`
|
|
4355
|
+
);
|
|
4356
|
+
}
|
|
4357
|
+
currency = sole;
|
|
4358
|
+
amount = raw;
|
|
4359
|
+
}
|
|
4360
|
+
if (typeof amount !== "number" && typeof amount !== "string") {
|
|
4361
|
+
throw new MoneyUnsupportedError(
|
|
4362
|
+
`where("${field}"): operand ${JSON.stringify(raw)} is not a money amount`
|
|
4363
|
+
);
|
|
4364
|
+
}
|
|
4365
|
+
const r = parseToScaledInt(amount, desc.scaleFor(currency), desc.rounding);
|
|
4366
|
+
if (!r.ok) {
|
|
4367
|
+
throw new MoneyUnsupportedError(
|
|
4368
|
+
`where("${field}"): operand ${JSON.stringify(amount)} is not a finite decimal`
|
|
4369
|
+
);
|
|
4370
|
+
}
|
|
4371
|
+
return { scaled: r.value.toString(), currency };
|
|
4372
|
+
}
|
|
4373
|
+
function moneyFieldClause(field, op, value, desc) {
|
|
4374
|
+
switch (op) {
|
|
4375
|
+
case "==":
|
|
4376
|
+
case "!=":
|
|
4377
|
+
case "<":
|
|
4378
|
+
case "<=":
|
|
4379
|
+
case ">":
|
|
4380
|
+
case ">=": {
|
|
4381
|
+
const e = parseOperand(field, value, desc);
|
|
4382
|
+
return withMoney(field, op, value, desc, [e]);
|
|
4383
|
+
}
|
|
4384
|
+
case "between": {
|
|
4385
|
+
if (!Array.isArray(value) || value.length !== 2) {
|
|
4386
|
+
throw new MoneyUnsupportedError(`where("${field}"): 'between' needs a [lo, hi] tuple`);
|
|
4387
|
+
}
|
|
4388
|
+
const lo = parseOperand(field, value[0], desc);
|
|
4389
|
+
const hi = parseOperand(field, value[1], desc);
|
|
4390
|
+
if (lo.currency !== hi.currency) {
|
|
4391
|
+
throw new MoneyUnsupportedError(
|
|
4392
|
+
`where("${field}"): 'between' bounds mix currencies (${lo.currency} vs ${hi.currency})`
|
|
4393
|
+
);
|
|
4394
|
+
}
|
|
4395
|
+
return withMoney(field, op, value, desc, [lo, hi]);
|
|
4396
|
+
}
|
|
4397
|
+
case "in": {
|
|
4398
|
+
if (!Array.isArray(value)) {
|
|
4399
|
+
throw new MoneyUnsupportedError(`where("${field}"): 'in' needs an array of amounts`);
|
|
4400
|
+
}
|
|
4401
|
+
return withMoney(field, op, value, desc, value.map((v) => parseOperand(field, v, desc)));
|
|
4402
|
+
}
|
|
4403
|
+
default:
|
|
4404
|
+
throw new MoneyUnsupportedError(
|
|
4405
|
+
`where("${field}"): operator '${op}' is not supported on a money field`
|
|
4406
|
+
);
|
|
4407
|
+
}
|
|
4408
|
+
}
|
|
4409
|
+
function withMoney(field, op, originalValue, desc, entries) {
|
|
4410
|
+
const money = { mode: desc.mode, entries };
|
|
4411
|
+
const value = desc.mode !== "fixed" ? originalValue : entries.length === 1 && op !== "in" && op !== "between" ? entries[0].scaled : entries.map((e) => e.scaled);
|
|
4412
|
+
return { type: "field", field, op, value, money };
|
|
4413
|
+
}
|
|
4414
|
+
function readStored(actual, operand) {
|
|
4415
|
+
let amount;
|
|
4416
|
+
let currency;
|
|
4417
|
+
if (operand.mode === "fixed") {
|
|
4418
|
+
if (typeof actual !== "string" && typeof actual !== "number") return null;
|
|
4419
|
+
amount = actual;
|
|
4420
|
+
currency = operand.entries[0]?.currency ?? "";
|
|
4421
|
+
} else {
|
|
4422
|
+
if (!isMoneyValueObject2(actual)) return null;
|
|
4423
|
+
if (typeof actual.currency !== "string") return null;
|
|
4424
|
+
amount = actual.amount;
|
|
4425
|
+
currency = actual.currency;
|
|
4426
|
+
}
|
|
4427
|
+
if (typeof amount !== "string" && typeof amount !== "number") return null;
|
|
4428
|
+
try {
|
|
4429
|
+
return { scaled: BigInt(amount).toString(), currency };
|
|
4430
|
+
} catch {
|
|
4431
|
+
return null;
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4434
|
+
function evaluateMoneyClause(actual, op, operand) {
|
|
4435
|
+
const stored = readStored(actual, operand);
|
|
4436
|
+
if (stored === null) return op === "!=";
|
|
4437
|
+
const a = BigInt(stored.scaled);
|
|
4438
|
+
if (op === "in") {
|
|
4439
|
+
return operand.entries.some(
|
|
4440
|
+
(e2) => e2.currency === stored.currency && BigInt(e2.scaled) === a
|
|
4441
|
+
);
|
|
4442
|
+
}
|
|
4443
|
+
if (op === "between") {
|
|
4444
|
+
const [lo, hi] = operand.entries;
|
|
4445
|
+
if (!lo || !hi || lo.currency !== stored.currency) return false;
|
|
4446
|
+
return a >= BigInt(lo.scaled) && a <= BigInt(hi.scaled);
|
|
4447
|
+
}
|
|
4448
|
+
const e = operand.entries[0];
|
|
4449
|
+
if (!e) return false;
|
|
4450
|
+
if (e.currency !== stored.currency) return op === "!=";
|
|
4451
|
+
const b = BigInt(e.scaled);
|
|
4452
|
+
switch (op) {
|
|
4453
|
+
case "==":
|
|
4454
|
+
return a === b;
|
|
4455
|
+
case "!=":
|
|
4456
|
+
return a !== b;
|
|
4457
|
+
case "<":
|
|
4458
|
+
return a < b;
|
|
4459
|
+
case "<=":
|
|
4460
|
+
return a <= b;
|
|
4461
|
+
case ">":
|
|
4462
|
+
return a > b;
|
|
4463
|
+
case ">=":
|
|
4464
|
+
return a >= b;
|
|
4465
|
+
default:
|
|
4466
|
+
return false;
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4469
|
+
var init_where = __esm({
|
|
4470
|
+
"src/money/where.ts"() {
|
|
4471
|
+
"use strict";
|
|
4472
|
+
init_fixed_point();
|
|
4473
|
+
init_descriptor();
|
|
4474
|
+
}
|
|
4475
|
+
});
|
|
4476
|
+
|
|
4155
4477
|
// src/query/predicate.ts
|
|
4156
4478
|
function readPath(record, path) {
|
|
4157
4479
|
if (record === null || record === void 0) return void 0;
|
|
@@ -4169,6 +4491,7 @@ function readPath(record, path) {
|
|
|
4169
4491
|
function evaluateFieldClause(record, clause) {
|
|
4170
4492
|
const actual = readPath(record, clause.field);
|
|
4171
4493
|
const { op, value } = clause;
|
|
4494
|
+
if (clause.money) return evaluateMoneyClause(actual, op, clause.money);
|
|
4172
4495
|
switch (op) {
|
|
4173
4496
|
case "==":
|
|
4174
4497
|
return actual === value;
|
|
@@ -4209,14 +4532,14 @@ function isComparable(a, b) {
|
|
|
4209
4532
|
if (a instanceof Date && b instanceof Date) return true;
|
|
4210
4533
|
return false;
|
|
4211
4534
|
}
|
|
4212
|
-
function evaluateClause(record, clause) {
|
|
4535
|
+
function evaluateClause(record, clause, fnRecord) {
|
|
4213
4536
|
switch (clause.type) {
|
|
4214
4537
|
case "field":
|
|
4215
4538
|
return evaluateFieldClause(record, clause);
|
|
4216
4539
|
case "filter":
|
|
4217
|
-
return clause.fn(record);
|
|
4540
|
+
return clause.fn(fnRecord !== void 0 ? fnRecord : record);
|
|
4218
4541
|
case "wherePredicate":
|
|
4219
|
-
return clause.fn(record, clause.ctx);
|
|
4542
|
+
return clause.fn(fnRecord !== void 0 ? fnRecord : record, clause.ctx);
|
|
4220
4543
|
case "crossJoin":
|
|
4221
4544
|
throw new Error(
|
|
4222
4545
|
`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.`
|
|
@@ -4224,20 +4547,28 @@ function evaluateClause(record, clause) {
|
|
|
4224
4547
|
case "group":
|
|
4225
4548
|
if (clause.op === "and") {
|
|
4226
4549
|
for (const child of clause.clauses) {
|
|
4227
|
-
if (!evaluateClause(record, child)) return false;
|
|
4550
|
+
if (!evaluateClause(record, child, fnRecord)) return false;
|
|
4228
4551
|
}
|
|
4229
4552
|
return true;
|
|
4230
4553
|
} else {
|
|
4231
4554
|
for (const child of clause.clauses) {
|
|
4232
|
-
if (evaluateClause(record, child)) return true;
|
|
4555
|
+
if (evaluateClause(record, child, fnRecord)) return true;
|
|
4233
4556
|
}
|
|
4234
4557
|
return false;
|
|
4235
4558
|
}
|
|
4236
4559
|
}
|
|
4237
4560
|
}
|
|
4561
|
+
function hasFnClause(clauses) {
|
|
4562
|
+
for (const c of clauses) {
|
|
4563
|
+
if (c.type === "filter" || c.type === "wherePredicate") return true;
|
|
4564
|
+
if (c.type === "group" && hasFnClause(c.clauses)) return true;
|
|
4565
|
+
}
|
|
4566
|
+
return false;
|
|
4567
|
+
}
|
|
4238
4568
|
var init_predicate = __esm({
|
|
4239
4569
|
"src/query/predicate.ts"() {
|
|
4240
4570
|
"use strict";
|
|
4571
|
+
init_where();
|
|
4241
4572
|
}
|
|
4242
4573
|
});
|
|
4243
4574
|
|
|
@@ -4503,13 +4834,22 @@ var init_strategy4 = __esm({
|
|
|
4503
4834
|
});
|
|
4504
4835
|
|
|
4505
4836
|
// src/money/money-reducer.ts
|
|
4506
|
-
function
|
|
4507
|
-
if (typeof v === "
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4837
|
+
function toScaledIntFromAny(v, scale) {
|
|
4838
|
+
if (typeof v === "bigint") return v;
|
|
4839
|
+
if (typeof v === "number") {
|
|
4840
|
+
const r = parseToScaledInt(v, scale);
|
|
4841
|
+
return r.ok ? r.value : null;
|
|
4842
|
+
}
|
|
4843
|
+
if (typeof v === "string") {
|
|
4844
|
+
if (!v.includes(".")) {
|
|
4845
|
+
try {
|
|
4846
|
+
return BigInt(v);
|
|
4847
|
+
} catch {
|
|
4848
|
+
return null;
|
|
4849
|
+
}
|
|
4512
4850
|
}
|
|
4851
|
+
const r = parseToScaledInt(v, scale);
|
|
4852
|
+
return r.ok ? r.value : null;
|
|
4513
4853
|
}
|
|
4514
4854
|
return null;
|
|
4515
4855
|
}
|
|
@@ -4517,13 +4857,15 @@ function readMoney(record, field, desc) {
|
|
|
4517
4857
|
const raw = readPath(record, field);
|
|
4518
4858
|
if (raw === null || raw === void 0) return null;
|
|
4519
4859
|
if (desc.mode === "fixed") {
|
|
4520
|
-
const
|
|
4521
|
-
|
|
4860
|
+
const cur = desc.fixedCurrency;
|
|
4861
|
+
const value2 = toScaledIntFromAny(raw, desc.scaleFor(cur));
|
|
4862
|
+
return value2 === null ? null : { currency: cur, value: value2 };
|
|
4522
4863
|
}
|
|
4523
4864
|
if (typeof raw !== "object") return null;
|
|
4524
4865
|
const o = raw;
|
|
4525
4866
|
if (typeof o.currency !== "string") return null;
|
|
4526
|
-
const
|
|
4867
|
+
const scale = desc.allows(o.currency) ? desc.scaleFor(o.currency) : 0;
|
|
4868
|
+
const value = toScaledIntFromAny(o.amount, scale);
|
|
4527
4869
|
return value === null ? null : { currency: o.currency, value };
|
|
4528
4870
|
}
|
|
4529
4871
|
function targetScaleFor(desc, currency) {
|
|
@@ -4698,7 +5040,7 @@ function executePlanWithSource(source, plan, joinContext) {
|
|
|
4698
5040
|
result = executeClausePipeline(source, plan.clauses, joinContext);
|
|
4699
5041
|
} else {
|
|
4700
5042
|
const { candidates, remainingClauses } = candidateRecords(source, plan.clauses);
|
|
4701
|
-
result = remainingClauses.length === 0 ? [...candidates] : filterRecords(candidates, remainingClauses);
|
|
5043
|
+
result = remainingClauses.length === 0 ? [...candidates] : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
|
|
4702
5044
|
}
|
|
4703
5045
|
if (plan.orderBy.length > 0) {
|
|
4704
5046
|
result = sortRecords(result, plan.orderBy);
|
|
@@ -4721,6 +5063,7 @@ function candidateRecords(source, clauses) {
|
|
|
4721
5063
|
const clause = clauses[i];
|
|
4722
5064
|
if (clause.type !== "field") continue;
|
|
4723
5065
|
if (!indexes.has(clause.field)) continue;
|
|
5066
|
+
if (clause.money?.mode === "multi") continue;
|
|
4724
5067
|
let ids = null;
|
|
4725
5068
|
if (clause.op === "==") {
|
|
4726
5069
|
ids = indexes.lookupEqual(clause.field, clause.value);
|
|
@@ -4748,13 +5091,20 @@ function materializeIds(ids, lookupById) {
|
|
|
4748
5091
|
}
|
|
4749
5092
|
return out;
|
|
4750
5093
|
}
|
|
4751
|
-
function
|
|
5094
|
+
function fnViewDecoder(source) {
|
|
5095
|
+
const mf = source.moneyFields;
|
|
5096
|
+
if (!mf || Object.keys(mf).length === 0) return void 0;
|
|
5097
|
+
return (r) => decodeMoneyFields(r, mf, "raw");
|
|
5098
|
+
}
|
|
5099
|
+
function filterRecords(records, clauses, decodeForFns) {
|
|
4752
5100
|
if (clauses.length === 0) return [...records];
|
|
5101
|
+
const needsFnView = decodeForFns !== void 0 && hasFnClause(clauses);
|
|
4753
5102
|
const out = [];
|
|
4754
5103
|
for (const r of records) {
|
|
5104
|
+
const fnView = needsFnView ? decodeForFns(r) : void 0;
|
|
4755
5105
|
let matches = true;
|
|
4756
5106
|
for (const clause of clauses) {
|
|
4757
|
-
if (!evaluateClause(r, clause)) {
|
|
5107
|
+
if (!evaluateClause(r, clause, fnView)) {
|
|
4758
5108
|
matches = false;
|
|
4759
5109
|
break;
|
|
4760
5110
|
}
|
|
@@ -4766,10 +5116,11 @@ function filterRecords(records, clauses) {
|
|
|
4766
5116
|
function executeClausePipeline(source, clauses, joinContext) {
|
|
4767
5117
|
let rel = [...source.snapshot()];
|
|
4768
5118
|
let filterBatch = [];
|
|
5119
|
+
const decodeForFns = fnViewDecoder(source);
|
|
4769
5120
|
for (const clause of clauses) {
|
|
4770
5121
|
if (clause.type === "crossJoin") {
|
|
4771
5122
|
if (filterBatch.length > 0) {
|
|
4772
|
-
rel = filterRecords(rel, filterBatch);
|
|
5123
|
+
rel = filterRecords(rel, filterBatch, decodeForFns);
|
|
4773
5124
|
filterBatch = [];
|
|
4774
5125
|
}
|
|
4775
5126
|
const rightSource = joinContext.resolveSource(clause.target);
|
|
@@ -4782,7 +5133,7 @@ function executeClausePipeline(source, clauses, joinContext) {
|
|
|
4782
5133
|
}
|
|
4783
5134
|
}
|
|
4784
5135
|
if (filterBatch.length > 0) {
|
|
4785
|
-
rel = filterRecords(rel, filterBatch);
|
|
5136
|
+
rel = filterRecords(rel, filterBatch, decodeForFns);
|
|
4786
5137
|
}
|
|
4787
5138
|
return rel;
|
|
4788
5139
|
}
|
|
@@ -4963,6 +5314,7 @@ var init_builder = __esm({
|
|
|
4963
5314
|
init_strategy4();
|
|
4964
5315
|
init_money_reducer();
|
|
4965
5316
|
init_normalize();
|
|
5317
|
+
init_where();
|
|
4966
5318
|
EMPTY_PLAN = {
|
|
4967
5319
|
clauses: [],
|
|
4968
5320
|
orderBy: [],
|
|
@@ -5054,9 +5406,18 @@ var init_builder = __esm({
|
|
|
5054
5406
|
this.predicates
|
|
5055
5407
|
);
|
|
5056
5408
|
}
|
|
5057
|
-
/**
|
|
5409
|
+
/**
|
|
5410
|
+
* Add a field comparison. Multiple where() calls are AND-combined.
|
|
5411
|
+
*
|
|
5412
|
+
* A declared money field compares in MAJOR units (#336): the operand
|
|
5413
|
+
* (`10000`, `'10000.00'`, or `{ amount, currency }` in multi mode) is
|
|
5414
|
+
* quantized into stored scaled-int space at build time and evaluated
|
|
5415
|
+
* BigInt-exact per record. A malformed operand or a string operator
|
|
5416
|
+
* (`contains`/`startsWith`) throws here, at the call site.
|
|
5417
|
+
*/
|
|
5058
5418
|
where(field, op, value) {
|
|
5059
|
-
const
|
|
5419
|
+
const desc = this.source.moneyFields?.[field];
|
|
5420
|
+
const clause = desc ? moneyFieldClause(field, op, value, desc) : { type: "field", field, op, value };
|
|
5060
5421
|
return new _Query(
|
|
5061
5422
|
this.source,
|
|
5062
5423
|
{ ...this.plan, clauses: [...this.plan.clauses, clause] },
|
|
@@ -5382,7 +5743,7 @@ var init_builder = __esm({
|
|
|
5382
5743
|
}
|
|
5383
5744
|
const { candidates, remainingClauses } = candidateRecords(this.source, this.plan.clauses);
|
|
5384
5745
|
if (remainingClauses.length === 0) return candidates.length;
|
|
5385
|
-
return filterRecords(candidates, remainingClauses).length;
|
|
5746
|
+
return filterRecords(candidates, remainingClauses, fnViewDecoder(this.source)).length;
|
|
5386
5747
|
}
|
|
5387
5748
|
/**
|
|
5388
5749
|
* Reduce the matching records through a named set of reducers.
|
|
@@ -5439,7 +5800,7 @@ var init_builder = __esm({
|
|
|
5439
5800
|
return executeClausePipeline(source, clauses, joinCtx);
|
|
5440
5801
|
}
|
|
5441
5802
|
const { candidates, remainingClauses } = candidateRecords(source, clauses);
|
|
5442
|
-
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses);
|
|
5803
|
+
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
|
|
5443
5804
|
};
|
|
5444
5805
|
const upstreams = [];
|
|
5445
5806
|
if (source.subscribe) {
|
|
@@ -5462,7 +5823,7 @@ var init_builder = __esm({
|
|
|
5462
5823
|
return executeClausePipeline(source, clauses, joinCtx);
|
|
5463
5824
|
}
|
|
5464
5825
|
const { candidates, remainingClauses } = candidateRecords(source, clauses);
|
|
5465
|
-
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses);
|
|
5826
|
+
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
|
|
5466
5827
|
};
|
|
5467
5828
|
const upstreams = [];
|
|
5468
5829
|
if (source.subscribe) {
|
|
@@ -5655,11 +6016,14 @@ function warnCardinalityApproaching(fields, observed) {
|
|
|
5655
6016
|
`[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.`
|
|
5656
6017
|
);
|
|
5657
6018
|
}
|
|
5658
|
-
function groupAndReduce(records, fieldOrFields, spec) {
|
|
6019
|
+
function groupAndReduce(records, fieldOrFields, spec, moneyFields) {
|
|
5659
6020
|
const fields = typeof fieldOrFields === "string" ? [fieldOrFields] : fieldOrFields;
|
|
5660
6021
|
if (fields.length === 0) {
|
|
5661
6022
|
throw new Error(".groupBy() requires at least one field");
|
|
5662
6023
|
}
|
|
6024
|
+
if (moneyFields) {
|
|
6025
|
+
spec = wrapMoneyReducers(spec, moneyFields);
|
|
6026
|
+
}
|
|
5663
6027
|
const buckets = /* @__PURE__ */ new Map();
|
|
5664
6028
|
const fieldLabel = fields.length === 1 ? fields[0] : `[${fields.join(", ")}]`;
|
|
5665
6029
|
for (const record of records) {
|
|
@@ -5715,6 +6079,7 @@ var init_groupby = __esm({
|
|
|
5715
6079
|
init_predicate();
|
|
5716
6080
|
init_canonical_key();
|
|
5717
6081
|
init_errors();
|
|
6082
|
+
init_money_reducer();
|
|
5718
6083
|
GROUPBY_WARN_CARDINALITY = 1e4;
|
|
5719
6084
|
GROUPBY_MAX_CARDINALITY = 1e5;
|
|
5720
6085
|
warnedCardinalityFields = /* @__PURE__ */ new Set();
|
|
@@ -5735,6 +6100,7 @@ var init_scan_builder = __esm({
|
|
|
5735
6100
|
init_predicate();
|
|
5736
6101
|
init_errors();
|
|
5737
6102
|
init_normalize();
|
|
6103
|
+
init_where();
|
|
5738
6104
|
DEFAULT_SCAN_PAGE_SIZE = 100;
|
|
5739
6105
|
ScanBuilder = class _ScanBuilder {
|
|
5740
6106
|
pageProvider;
|
|
@@ -5798,7 +6164,8 @@ var init_scan_builder = __esm({
|
|
|
5798
6164
|
* evaluates clauses per record in O(1) per clause.
|
|
5799
6165
|
*/
|
|
5800
6166
|
where(field, op, value) {
|
|
5801
|
-
const
|
|
6167
|
+
const desc = this.moneyFields?.[field];
|
|
6168
|
+
const clause = desc ? moneyFieldClause(field, op, value, desc) : { type: "field", field, op, value };
|
|
5802
6169
|
return new _ScanBuilder(
|
|
5803
6170
|
this.pageProvider,
|
|
5804
6171
|
this.pageSize,
|
|
@@ -6144,8 +6511,9 @@ var init_scan_builder = __esm({
|
|
|
6144
6511
|
*/
|
|
6145
6512
|
recordMatches(record) {
|
|
6146
6513
|
if (this.clauses.length === 0) return true;
|
|
6514
|
+
const fnView = this.moneyFields && Object.keys(this.moneyFields).length > 0 && hasFnClause(this.clauses) ? this.decodeMoney(record) : void 0;
|
|
6147
6515
|
for (const clause of this.clauses) {
|
|
6148
|
-
if (!evaluateClause(record, clause)) return false;
|
|
6516
|
+
if (!evaluateClause(record, clause, fnView)) return false;
|
|
6149
6517
|
}
|
|
6150
6518
|
return true;
|
|
6151
6519
|
}
|
|
@@ -7113,10 +7481,14 @@ function summarizeQueryPlan(query) {
|
|
|
7113
7481
|
});
|
|
7114
7482
|
}
|
|
7115
7483
|
function summarizeUnionPlan(spec) {
|
|
7116
|
-
const arms = (spec.unionSources ?? []).map((s) =>
|
|
7484
|
+
const arms = (spec.unionSources ?? []).map((s) => {
|
|
7485
|
+
const joins = s.join?.length ? `[${s.join.map((j) => `${j.field}\u2192${j.as}`).join(";")}]` : "";
|
|
7486
|
+
return `${s.collection}${joins}`;
|
|
7487
|
+
}).join(",");
|
|
7117
7488
|
const groupBy = Array.isArray(spec.groupBy) ? [...spec.groupBy].sort().join(",") : typeof spec.groupBy === "string" ? spec.groupBy : "";
|
|
7118
7489
|
const aggKeys = spec.aggregate ? Object.keys(spec.aggregate).sort().join(",") : "";
|
|
7119
|
-
|
|
7490
|
+
const moneyKeys = spec.moneyFields ? Object.keys(spec.moneyFields).sort().join(",") : "";
|
|
7491
|
+
return `union(${arms})|groupBy(${groupBy})|aggregate(${aggKeys})|money(${moneyKeys})`;
|
|
7120
7492
|
}
|
|
7121
7493
|
var init_dependency_analyzer = __esm({
|
|
7122
7494
|
"src/materialized-views/dependency-analyzer.ts"() {
|
|
@@ -7243,6 +7615,7 @@ var init_registry = __esm({
|
|
|
7243
7615
|
let isQuery = false;
|
|
7244
7616
|
if (spec.unionSources) {
|
|
7245
7617
|
dependencies = new Set(spec.unionSources.map((s) => s.collection));
|
|
7618
|
+
if (spec.sources) for (const s of spec.sources) dependencies.add(s);
|
|
7246
7619
|
queryPlanSummary = summarizeUnionPlan(spec);
|
|
7247
7620
|
} else {
|
|
7248
7621
|
const q = spec.query(dbForQuery);
|
|
@@ -7397,7 +7770,13 @@ async function materializeUnionResult(spec, db) {
|
|
|
7397
7770
|
const unified = [];
|
|
7398
7771
|
for (const arm of spec.unionSources) {
|
|
7399
7772
|
const coll = db.collection(arm.collection);
|
|
7400
|
-
|
|
7773
|
+
let q = coll.query();
|
|
7774
|
+
if (arm.join?.length) {
|
|
7775
|
+
for (const leg of arm.join) {
|
|
7776
|
+
q = q.join(leg.field, { as: leg.as, maxRows: leg.maxRows, strategy: leg.strategy });
|
|
7777
|
+
}
|
|
7778
|
+
}
|
|
7779
|
+
const sourceRows = q.toArray();
|
|
7401
7780
|
for (const r of sourceRows) {
|
|
7402
7781
|
const mapped = arm.map(r);
|
|
7403
7782
|
if (mapped == null) continue;
|
|
@@ -7414,7 +7793,7 @@ async function materializeUnionResult(spec, db) {
|
|
|
7414
7793
|
}
|
|
7415
7794
|
return [...seen.values()];
|
|
7416
7795
|
}
|
|
7417
|
-
return groupAndReduce(unified, groupFields, spec.aggregate);
|
|
7796
|
+
return groupAndReduce(unified, groupFields, spec.aggregate, spec.moneyFields);
|
|
7418
7797
|
}
|
|
7419
7798
|
async function listOutputIds(outputColl) {
|
|
7420
7799
|
const cAny = outputColl;
|
|
@@ -7686,6 +8065,7 @@ var init_collection = __esm({
|
|
|
7686
8065
|
init_strategy();
|
|
7687
8066
|
init_core();
|
|
7688
8067
|
init_normalize();
|
|
8068
|
+
init_paths();
|
|
7689
8069
|
init_computed();
|
|
7690
8070
|
init_strategy2();
|
|
7691
8071
|
init_policy();
|
|
@@ -8008,6 +8388,7 @@ var init_collection = __esm({
|
|
|
8008
8388
|
this.joinResolver = opts.joinResolver;
|
|
8009
8389
|
this.i18nFields = opts.i18nFields;
|
|
8010
8390
|
this.dictKeyFields = opts.dictKeyFields;
|
|
8391
|
+
if (opts.moneyFields) validateMoneyFieldPaths(opts.moneyFields);
|
|
8011
8392
|
this.moneyFields = opts.moneyFields;
|
|
8012
8393
|
this.computed = opts.computed;
|
|
8013
8394
|
this.dictLabelResolver = opts.dictLabelResolver;
|
|
@@ -8141,7 +8522,9 @@ var init_collection = __esm({
|
|
|
8141
8522
|
* declaration; this reconciles that ordering. First-wins. Not public.
|
|
8142
8523
|
*/
|
|
8143
8524
|
_applyMoneyFields(moneyFields) {
|
|
8144
|
-
if (this.moneyFields
|
|
8525
|
+
if (this.moneyFields !== void 0) return;
|
|
8526
|
+
validateMoneyFieldPaths(moneyFields);
|
|
8527
|
+
this.moneyFields = moneyFields;
|
|
8145
8528
|
}
|
|
8146
8529
|
/** @internal — attach computed fields post-construction. See {@link _applyMoneyFields}. */
|
|
8147
8530
|
_applyComputed(computed) {
|
|
@@ -8308,6 +8691,7 @@ var init_collection = __esm({
|
|
|
8308
8691
|
if (!hasWritePermission(this.keyring, this.name)) {
|
|
8309
8692
|
throw new ReadOnlyError();
|
|
8310
8693
|
}
|
|
8694
|
+
record = canonicalizeIncomingMoney(record, this.moneyFields);
|
|
8311
8695
|
if (this.subsystemBus?.hasGateHandlers("beforePut")) {
|
|
8312
8696
|
const existingEnv = await this.adapter.get(this.vault, this.name, id);
|
|
8313
8697
|
let existingRecord = null;
|
|
@@ -8324,7 +8708,7 @@ var init_collection = __esm({
|
|
|
8324
8708
|
collection: this.name,
|
|
8325
8709
|
docId: id,
|
|
8326
8710
|
incoming: record,
|
|
8327
|
-
existing: existingRecord,
|
|
8711
|
+
existing: canonicalizeStoredMoney(existingRecord, this.moneyFields),
|
|
8328
8712
|
existingVersion: existingEnv?._v ?? 0,
|
|
8329
8713
|
existingTs: existingEnv?._ts,
|
|
8330
8714
|
userId: this.keyring.userId,
|
|
@@ -8593,7 +8977,7 @@ var init_collection = __esm({
|
|
|
8593
8977
|
*/
|
|
8594
8978
|
async dispatchDerivations(id, record, version) {
|
|
8595
8979
|
if (this.derivationSource === void 0) return;
|
|
8596
|
-
const incoming = record;
|
|
8980
|
+
const incoming = canonicalizeStoredMoney(record, this.moneyFields);
|
|
8597
8981
|
if (incoming && typeof incoming === "object" && "_derivedFrom" in incoming) return;
|
|
8598
8982
|
const registry = this.derivationSource.registry();
|
|
8599
8983
|
const strategies = registry.strategiesForSource(this.name);
|
|
@@ -8605,9 +8989,18 @@ var init_collection = __esm({
|
|
|
8605
8989
|
if (DerivationExecutor2 === null) {
|
|
8606
8990
|
({ DerivationExecutor: DerivationExecutor2 } = await Promise.resolve().then(() => (init_executor(), executor_exports)));
|
|
8607
8991
|
}
|
|
8608
|
-
|
|
8992
|
+
let sourceWithId;
|
|
8993
|
+
let sourceVersion = version;
|
|
8994
|
+
if (spec.source === this.name) {
|
|
8995
|
+
sourceWithId = { ...incoming, id };
|
|
8996
|
+
} else {
|
|
8997
|
+
const primary = await this.derivationSource.getCollection(spec.source).get(id);
|
|
8998
|
+
if (primary === null || primary === void 0) continue;
|
|
8999
|
+
sourceWithId = { ...primary, id };
|
|
9000
|
+
sourceVersion = 0;
|
|
9001
|
+
}
|
|
8609
9002
|
const ctx = { vault: this.derivationSource.getReadOnlyFacade() };
|
|
8610
|
-
const result = await DerivationExecutor2.run(spec, sourceWithId,
|
|
9003
|
+
const result = await DerivationExecutor2.run(spec, sourceWithId, sourceVersion, strategyHash, ctx);
|
|
8611
9004
|
for (const key of Object.keys(spec.outputs)) {
|
|
8612
9005
|
const out = result.outputs[key];
|
|
8613
9006
|
if (!out) continue;
|
|
@@ -8826,7 +9219,7 @@ var init_collection = __esm({
|
|
|
8826
9219
|
vault: this.vault,
|
|
8827
9220
|
collection: this.name,
|
|
8828
9221
|
docId: id,
|
|
8829
|
-
existing: existingRecord,
|
|
9222
|
+
existing: canonicalizeStoredMoney(existingRecord, this.moneyFields),
|
|
8830
9223
|
existingVersion: existingEnv._v,
|
|
8831
9224
|
existingTs: existingEnv._ts,
|
|
8832
9225
|
internal,
|
|
@@ -10559,37 +10952,34 @@ var init_virtual_collection = __esm({
|
|
|
10559
10952
|
/** Get the merged row by id. */
|
|
10560
10953
|
async get(id) {
|
|
10561
10954
|
const overlayRow = await this.overlayCollection.get(id);
|
|
10562
|
-
if (overlayRow !== null && this.shadowPredicateApplies(overlayRow)) {
|
|
10563
|
-
return overlayRow;
|
|
10564
|
-
}
|
|
10565
10955
|
const baseRow = await this.baseCollection.get(id);
|
|
10566
|
-
|
|
10567
|
-
return null;
|
|
10956
|
+
return this.mergeRows(overlayRow, baseRow);
|
|
10568
10957
|
}
|
|
10569
10958
|
/** List union of base + overlay ids, applying the merge per row. */
|
|
10570
10959
|
async list() {
|
|
10571
10960
|
const baseRows = await this.baseCollection.list();
|
|
10572
10961
|
const overlayRows = await this.overlayCollection.list();
|
|
10573
|
-
const merged = /* @__PURE__ */ new Map();
|
|
10574
10962
|
const idOf = (row) => {
|
|
10575
10963
|
if (this.baseRowKey) return this.baseRowKey(row);
|
|
10576
10964
|
const idField = row.id;
|
|
10577
10965
|
return typeof idField === "string" ? idField : "";
|
|
10578
10966
|
};
|
|
10967
|
+
const baseById = /* @__PURE__ */ new Map();
|
|
10968
|
+
const overlayById = /* @__PURE__ */ new Map();
|
|
10579
10969
|
for (const row of baseRows) {
|
|
10580
10970
|
const id = idOf(row);
|
|
10581
|
-
if (id)
|
|
10971
|
+
if (id) baseById.set(id, row);
|
|
10582
10972
|
}
|
|
10583
10973
|
for (const row of overlayRows) {
|
|
10584
10974
|
const id = idOf(row);
|
|
10585
|
-
if (
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10975
|
+
if (id) overlayById.set(id, row);
|
|
10976
|
+
}
|
|
10977
|
+
const out = [];
|
|
10978
|
+
for (const id of /* @__PURE__ */ new Set([...baseById.keys(), ...overlayById.keys()])) {
|
|
10979
|
+
const merged = this.mergeRows(overlayById.get(id) ?? null, baseById.get(id) ?? null);
|
|
10980
|
+
if (merged !== null) out.push(merged);
|
|
10591
10981
|
}
|
|
10592
|
-
return
|
|
10982
|
+
return out;
|
|
10593
10983
|
}
|
|
10594
10984
|
/**
|
|
10595
10985
|
* Write to the overlay. Two forms:
|
|
@@ -10629,9 +11019,42 @@ var init_virtual_collection = __esm({
|
|
|
10629
11019
|
async delete(id) {
|
|
10630
11020
|
await this.overlayCollection.delete(id);
|
|
10631
11021
|
}
|
|
10632
|
-
/**
|
|
10633
|
-
|
|
10634
|
-
|
|
11022
|
+
/**
|
|
11023
|
+
* Merge a single id's overlay + base rows into the visible row.
|
|
11024
|
+
*
|
|
11025
|
+
* Priority (first match wins):
|
|
11026
|
+
* 1. Binary shadow win — overlay present AND
|
|
11027
|
+
* `overlay[shadowField] === shadowValue` → return the overlay row
|
|
11028
|
+
* entirely. This stays FIRST so the original binary behaviour is
|
|
11029
|
+
* unchanged whether or not `mergeMode` is configured.
|
|
11030
|
+
* 2. Field-level merge — overlay present, `mergeMode` configured,
|
|
11031
|
+
* and a rule whose `whenStatus` equals `overlay[shadowField]`.
|
|
11032
|
+
* The matched rule pulls its `overlayFields` (those present on
|
|
11033
|
+
* the overlay row) on top of the base row. With no base row, the
|
|
11034
|
+
* overlay row is returned as-is.
|
|
11035
|
+
* 3. Fallback — return the base row (possibly `null`). An
|
|
11036
|
+
* overlay-only row that qualifies under neither (1) nor (2) and
|
|
11037
|
+
* has no base is therefore NOT surfaced.
|
|
11038
|
+
*/
|
|
11039
|
+
mergeRows(overlayRow, baseRow) {
|
|
11040
|
+
const shadowField = this.spec.shadowField;
|
|
11041
|
+
if (overlayRow !== null && overlayRow[shadowField] === this.spec.shadowValue) {
|
|
11042
|
+
return overlayRow;
|
|
11043
|
+
}
|
|
11044
|
+
if (overlayRow !== null && this.spec.mergeMode) {
|
|
11045
|
+
const status = overlayRow[shadowField];
|
|
11046
|
+
const rule = this.spec.mergeMode.rules.find((r) => r.whenStatus === status);
|
|
11047
|
+
if (rule) {
|
|
11048
|
+
if (baseRow === null) return overlayRow;
|
|
11049
|
+
const overlaySrc = overlayRow;
|
|
11050
|
+
const picked = {};
|
|
11051
|
+
for (const field of rule.overlayFields) {
|
|
11052
|
+
if (field in overlaySrc) picked[field] = overlaySrc[field];
|
|
11053
|
+
}
|
|
11054
|
+
return { ...baseRow, ...picked };
|
|
11055
|
+
}
|
|
11056
|
+
}
|
|
11057
|
+
return baseRow;
|
|
10635
11058
|
}
|
|
10636
11059
|
// ─── Throw-stubs for the unimplemented Collection<T> surface ───────
|
|
10637
11060
|
//
|
|
@@ -10781,6 +11204,21 @@ var init_archive = __esm({
|
|
|
10781
11204
|
});
|
|
10782
11205
|
|
|
10783
11206
|
// src/sequence/index.ts
|
|
11207
|
+
function resolveSequenceKey(series, opts) {
|
|
11208
|
+
const partition = opts?.partition;
|
|
11209
|
+
if (!partition || partition.length === 0) return series;
|
|
11210
|
+
const parts = partition.map((p) => {
|
|
11211
|
+
if (typeof p === "number" && !Number.isFinite(p)) {
|
|
11212
|
+
throw new ValidationError(`sequence partition component must be a finite number, got ${p}`);
|
|
11213
|
+
}
|
|
11214
|
+
const s = String(p);
|
|
11215
|
+
if (s === "") {
|
|
11216
|
+
throw new ValidationError("sequence partition component must not be empty");
|
|
11217
|
+
}
|
|
11218
|
+
return encodeURIComponent(s);
|
|
11219
|
+
});
|
|
11220
|
+
return `${series}\0${parts.join("/")}`;
|
|
11221
|
+
}
|
|
10784
11222
|
async function sleepBackoff2(attempt) {
|
|
10785
11223
|
const ceil = Math.min(2 ** attempt, 32);
|
|
10786
11224
|
const ms = Math.floor(Math.random() * ceil);
|
|
@@ -10820,7 +11258,8 @@ var init_sequence = __esm({
|
|
|
10820
11258
|
handle(name) {
|
|
10821
11259
|
return {
|
|
10822
11260
|
next: () => this.next(name),
|
|
10823
|
-
peek: () => this.peek(name)
|
|
11261
|
+
peek: () => this.peek(name),
|
|
11262
|
+
seedTo: (n) => this.seedTo(name, n)
|
|
10824
11263
|
};
|
|
10825
11264
|
}
|
|
10826
11265
|
assertOnline() {
|
|
@@ -10873,6 +11312,30 @@ var init_sequence = __esm({
|
|
|
10873
11312
|
void lastConflict;
|
|
10874
11313
|
throw new SequenceContentionError(name, MAX_NEXT_ATTEMPTS);
|
|
10875
11314
|
}
|
|
11315
|
+
async seedTo(name, n) {
|
|
11316
|
+
this.assertOnline();
|
|
11317
|
+
if (n <= 0) return;
|
|
11318
|
+
let lastConflict;
|
|
11319
|
+
for (let attempt = 0; attempt < MAX_NEXT_ATTEMPTS; attempt++) {
|
|
11320
|
+
const { env, value } = await this.read(name);
|
|
11321
|
+
if (value >= n) return;
|
|
11322
|
+
const expectedVersion = env?._v ?? 0;
|
|
11323
|
+
const envelope = await this.encryptState({ value: n }, expectedVersion + 1);
|
|
11324
|
+
try {
|
|
11325
|
+
await this.adapter.put(this.vault, SEQUENCE_COLLECTION, name, envelope, expectedVersion);
|
|
11326
|
+
return;
|
|
11327
|
+
} catch (err) {
|
|
11328
|
+
if (err instanceof ConflictError) {
|
|
11329
|
+
lastConflict = err;
|
|
11330
|
+
if (attempt < MAX_NEXT_ATTEMPTS - 1) await sleepBackoff2(attempt);
|
|
11331
|
+
continue;
|
|
11332
|
+
}
|
|
11333
|
+
throw err;
|
|
11334
|
+
}
|
|
11335
|
+
}
|
|
11336
|
+
void lastConflict;
|
|
11337
|
+
throw new SequenceContentionError(name, MAX_NEXT_ATTEMPTS);
|
|
11338
|
+
}
|
|
10876
11339
|
};
|
|
10877
11340
|
}
|
|
10878
11341
|
});
|
|
@@ -12887,11 +13350,15 @@ var init_read_only_facade = __esm({
|
|
|
12887
13350
|
});
|
|
12888
13351
|
|
|
12889
13352
|
// src/derivations/strategy-hash.ts
|
|
12890
|
-
async function computeStrategyHash(source, outputKeys, derive) {
|
|
13353
|
+
async function computeStrategyHash(source, outputKeys, derive, sources) {
|
|
12891
13354
|
const canonical2 = JSON.stringify({
|
|
12892
13355
|
source,
|
|
12893
13356
|
outputs: [...outputKeys].sort(),
|
|
12894
|
-
derive: derive.toString()
|
|
13357
|
+
derive: derive.toString(),
|
|
13358
|
+
// Declared sibling sources (#344) — adding/removing a trigger
|
|
13359
|
+
// collection invalidates cached derived records. Omitted when empty
|
|
13360
|
+
// so strategies without siblings keep their existing hash.
|
|
13361
|
+
...sources?.length ? { sources: [...sources].sort() } : {}
|
|
12895
13362
|
});
|
|
12896
13363
|
const bytes = new TextEncoder().encode(canonical2);
|
|
12897
13364
|
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
@@ -12920,11 +13387,16 @@ var init_registry3 = __esm({
|
|
|
12920
13387
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12921
13388
|
async register(spec) {
|
|
12922
13389
|
const outputKeys = Object.keys(spec.outputs);
|
|
12923
|
-
const strategyHash = await computeStrategyHash(spec.source, outputKeys, spec.derive);
|
|
13390
|
+
const strategyHash = await computeStrategyHash(spec.source, outputKeys, spec.derive, spec.sources);
|
|
12924
13391
|
const reg = { spec, strategyHash };
|
|
12925
13392
|
const fromSource = this._bySource.get(spec.source);
|
|
12926
13393
|
if (fromSource) fromSource.push(reg);
|
|
12927
13394
|
else this._bySource.set(spec.source, [reg]);
|
|
13395
|
+
for (const extra of spec.sources ?? []) {
|
|
13396
|
+
const fromExtra = this._bySource.get(extra);
|
|
13397
|
+
if (fromExtra) fromExtra.push(reg);
|
|
13398
|
+
else this._bySource.set(extra, [reg]);
|
|
13399
|
+
}
|
|
12928
13400
|
for (const key of outputKeys) {
|
|
12929
13401
|
const output = spec.outputs[key];
|
|
12930
13402
|
if (!output) continue;
|
|
@@ -14100,17 +14572,23 @@ var init_vault = __esm({
|
|
|
14100
14572
|
* const cur = await vault.sequence('invoice-2026').peek() // current value, no allocation
|
|
14101
14573
|
* ```
|
|
14102
14574
|
*/
|
|
14103
|
-
sequence(
|
|
14104
|
-
if (
|
|
14575
|
+
sequence(series, opts) {
|
|
14576
|
+
if (series.includes("\0")) {
|
|
14577
|
+
throw new ValidationError(`sequence("${series}"): series name must not contain a null byte (\\x00).`);
|
|
14578
|
+
}
|
|
14579
|
+
if (this.numberingConfigs.has(series)) {
|
|
14105
14580
|
const eng = this.deferred();
|
|
14106
14581
|
return {
|
|
14107
|
-
next: async (
|
|
14108
|
-
if (!
|
|
14109
|
-
throw new ValidationError(`sequence("${
|
|
14582
|
+
next: async (nextOpts) => {
|
|
14583
|
+
if (!nextOpts?.for) {
|
|
14584
|
+
throw new ValidationError(`sequence("${series}") is a deferred-numbering series; call next({ for: recordId }).`);
|
|
14110
14585
|
}
|
|
14111
|
-
return (await eng.enqueue(
|
|
14586
|
+
return (await eng.enqueue(series, nextOpts.for)).assigned;
|
|
14112
14587
|
},
|
|
14113
|
-
peek: () => eng.peek(
|
|
14588
|
+
peek: () => eng.peek(series),
|
|
14589
|
+
seedTo: () => {
|
|
14590
|
+
throw new ValidationError(`sequence("${series}") is a deferred-numbering series; seedTo is CAS-only.`);
|
|
14591
|
+
}
|
|
14114
14592
|
};
|
|
14115
14593
|
}
|
|
14116
14594
|
if (!this.sequenceStore) {
|
|
@@ -14122,7 +14600,7 @@ var init_vault = __esm({
|
|
|
14122
14600
|
actor: this.keyring.userId
|
|
14123
14601
|
});
|
|
14124
14602
|
}
|
|
14125
|
-
return this.sequenceStore.handle(
|
|
14603
|
+
return this.sequenceStore.handle(resolveSequenceKey(series, opts));
|
|
14126
14604
|
}
|
|
14127
14605
|
/** @internal — lazily build the deferred-numbering engine with a cache-coherent stamp. */
|
|
14128
14606
|
deferred() {
|
|
@@ -14414,9 +14892,24 @@ var init_vault = __esm({
|
|
|
14414
14892
|
});
|
|
14415
14893
|
}
|
|
14416
14894
|
if (rule.mode === "cascade") {
|
|
14895
|
+
const txCtx = this.noydb._activeTxContextOrNull;
|
|
14417
14896
|
for (const match of matches) {
|
|
14418
14897
|
const matchId = match["id"] ?? null;
|
|
14419
14898
|
if (matchId === null) continue;
|
|
14899
|
+
if (txCtx !== null) {
|
|
14900
|
+
const prior = await this.adapter.get(this.name, rule.collection, matchId);
|
|
14901
|
+
if (prior !== null) {
|
|
14902
|
+
txCtx._executed.push({
|
|
14903
|
+
op: {
|
|
14904
|
+
type: "delete",
|
|
14905
|
+
vaultName: this.name,
|
|
14906
|
+
collectionName: rule.collection,
|
|
14907
|
+
id: matchId
|
|
14908
|
+
},
|
|
14909
|
+
priorEnvelope: prior
|
|
14910
|
+
});
|
|
14911
|
+
}
|
|
14912
|
+
}
|
|
14420
14913
|
await fromCollection.delete(matchId);
|
|
14421
14914
|
}
|
|
14422
14915
|
}
|
|
@@ -16805,7 +17298,7 @@ var init_strategy11 = __esm({
|
|
|
16805
17298
|
'Multi-record transactions require the tx strategy. Import `{ withTransactions }` from "@noy-db/hub/tx" and pass it to `createNoydb({ txStrategy: withTransactions() })`.'
|
|
16806
17299
|
);
|
|
16807
17300
|
NO_TX = {
|
|
16808
|
-
async runTransaction() {
|
|
17301
|
+
async runTransaction(_db, _fn, _options, _txInvariants) {
|
|
16809
17302
|
throw NOT_ENABLED6;
|
|
16810
17303
|
},
|
|
16811
17304
|
async runDryRun() {
|