@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/money/iso4217.ts","../src/money/descriptor.ts","../src/money/fixed-point.ts","../src/money/where.ts","../src/query/predicate.ts"],"sourcesContent":["/**\n * ISO 4217 minor-unit (scale) lookup.\n *\n * Maps a currency code to the number of decimal places its minor unit\n * uses — `EUR` → 2 (cents), `JPY` → 0 (no minor unit), `BHD` → 3 (fils).\n * Only listed codes are \"known\"; an unlisted code returns `null` so the\n * descriptor layer can demand an explicit `scale` / `scaleOverrides`\n * rather than silently assuming 2. This avoids the classic bug where a\n * 0-decimal currency (JPY) is stored as if it had cents.\n *\n * Coverage is the common ISO-4217 set; exotic or non-listed codes must\n * declare scale explicitly. This table is the single source of truth for\n * default scale resolution — see {@link scaleForCurrency}.\n */\nconst MINOR_UNITS: Readonly<Record<string, number>> = {\n // 2-decimal majors\n EUR: 2, USD: 2, GBP: 2, CHF: 2, CAD: 2, AUD: 2, NZD: 2, SGD: 2,\n HKD: 2, CNY: 2, INR: 2, BRL: 2, MXN: 2, ZAR: 2, RUB: 2, TRY: 2,\n PLN: 2, SEK: 2, NOK: 2, DKK: 2, CZK: 2, HUF: 2, RON: 2, ILS: 2,\n THB: 2, PHP: 2, MYR: 2, IDR: 2, AED: 2, SAR: 2, QAR: 2, EGP: 2,\n // 0-decimal\n JPY: 0, KRW: 0, ISK: 0, CLP: 0, VND: 0, XOF: 0, XAF: 0, PYG: 0,\n // 3-decimal\n BHD: 3, KWD: 3, OMR: 3, TND: 3, JOD: 3, IQD: 3, LYD: 3,\n}\n\n/**\n * Return the ISO-4217 minor-unit scale for a currency code, or `null`\n * when the code is not in the known set (caller must supply an explicit\n * scale).\n */\nexport function scaleForCurrency(code: string): number | null {\n const v = MINOR_UNITS[code]\n return v === undefined ? null : v\n}\n","/**\n * The `money()` field descriptor — a branded schema-layer descriptor, a\n * sibling of `i18nText()` / `dictKey()`. It owns currency, scale, and\n * rounding policy for a field; the pure arithmetic lives in\n * {@link ./fixed-point} and default scale resolution in\n * {@link ./iso4217}.\n *\n * Two modes:\n * - **fixed** `money({ currency: 'EUR' })` — one currency for the\n * field; the value stores a bare scaled-integer string.\n * - **multi** `money({ currencies: 'any' | [...] })` — currency travels\n * per record; the value stores `{ amount, currency }`.\n *\n * `currency` and `currencies` are mutually exclusive.\n */\n\nimport type { RoundingMode } from './fixed-point.js'\nimport { scaleForCurrency } from './iso4217.js'\nimport { NoydbError } from '../errors.js'\n\nexport interface MoneyOptionsFixed {\n currency: string\n /** Override the ISO-4217 default scale (required for unlisted codes). */\n scale?: number\n rounding?: RoundingMode\n}\n\nexport interface MoneyOptionsMulti {\n currencies: 'any' | readonly string[]\n /** Per-currency scale overrides (required for unlisted codes). */\n scaleOverrides?: Record<string, number>\n rounding?: RoundingMode\n}\n\nexport type MoneyOptions = MoneyOptionsFixed | MoneyOptionsMulti\n\nexport interface MoneyDescriptor {\n readonly _noydbMoney: true\n readonly mode: 'fixed' | 'multi'\n readonly options: MoneyOptions\n readonly rounding: RoundingMode | undefined\n /** The currency for fixed mode; `undefined` in multi mode. */\n readonly fixedCurrency: string | undefined\n /** Resolve the scale for a currency, throwing if not allowed / unknown. */\n scaleFor(currency: string): number\n /** Whether this descriptor permits the given currency. */\n allows(currency: string): boolean\n /**\n * The single currency this descriptor implies, if any — fixed mode, or\n * multi mode with exactly one allow-listed currency. Lets a multi field\n * accept a bare amount unambiguously. `undefined` otherwise.\n */\n soleCurrency(): string | undefined\n}\n\n/** Raised when a written value carries more precision than `scale` allows. */\nexport class MoneyPrecisionError extends NoydbError {\n constructor(\n public readonly field: string,\n public readonly value: unknown,\n public readonly scale: number,\n ) {\n super(\n 'MONEY_PRECISION',\n `money: value ${JSON.stringify(value)} for field \"${field}\" exceeds scale ${scale} ` +\n `and no rounding mode is configured`,\n )\n this.name = 'MoneyPrecisionError'\n }\n}\n\n/** Raised when a currency is disallowed or has no resolvable scale. */\nexport class MoneyCurrencyError extends NoydbError {\n constructor(\n public readonly currency: string,\n public readonly reason: 'not-allowed' | 'unknown-scale',\n public readonly field?: string,\n ) {\n super(\n 'MONEY_CURRENCY',\n reason === 'not-allowed'\n ? `money: currency \"${currency}\" is not allowed${field ? ` for field \"${field}\"` : ''}`\n : `money: no scale known for currency \"${currency}\"${field ? ` (field \"${field}\")` : ''} — ` +\n `pass an explicit scale / scaleOverrides`,\n )\n this.name = 'MoneyCurrencyError'\n }\n}\n\n/** Raised when an aggregate operation is not supported on a money field. */\nexport class MoneyUnsupportedError extends NoydbError {\n constructor(\n public readonly field: string,\n message?: string,\n ) {\n super(\n 'MONEY_UNSUPPORTED',\n message ??\n `money: operation is not supported on field \"${field}\" — use sum() and count() and divide at the boundary`,\n )\n this.name = 'MoneyUnsupportedError'\n }\n}\n\nfunction isMultiOptions(o: MoneyOptions): o is MoneyOptionsMulti {\n return 'currencies' in o\n}\n\n/** Create a {@link MoneyDescriptor}. */\nexport function money(options: MoneyOptions): MoneyDescriptor {\n const hasFixed = 'currency' in options\n const hasMulti = 'currencies' in options\n if (hasFixed && hasMulti) {\n throw new TypeError('money: `currency` and `currencies` are mutually exclusive')\n }\n if (!hasFixed && !hasMulti) {\n throw new TypeError('money: one of `currency` or `currencies` is required')\n }\n\n const rounding = options.rounding\n\n if (isMultiOptions(options)) {\n const overrides = options.scaleOverrides ?? {}\n const allowList = options.currencies\n const allows = (c: string): boolean =>\n allowList === 'any' ? true : allowList.includes(c)\n const scaleFor = (c: string): number => {\n if (!allows(c)) throw new MoneyCurrencyError(c, 'not-allowed')\n const s = overrides[c] ?? scaleForCurrency(c)\n if (s === null || s === undefined) throw new MoneyCurrencyError(c, 'unknown-scale')\n return s\n }\n // Eagerly validate the allow-list resolves (catch typos at construction).\n if (allowList !== 'any') for (const c of allowList) scaleFor(c)\n const soleCurrency = (): string | undefined =>\n allowList !== 'any' && allowList.length === 1 ? allowList[0] : undefined\n return {\n _noydbMoney: true,\n mode: 'multi',\n options,\n rounding,\n fixedCurrency: undefined,\n scaleFor,\n allows,\n soleCurrency,\n }\n }\n\n // fixed\n const currency = options.currency\n const resolvedScale = options.scale ?? scaleForCurrency(currency)\n if (resolvedScale === null || resolvedScale === undefined) {\n throw new MoneyCurrencyError(currency, 'unknown-scale')\n }\n return {\n _noydbMoney: true,\n mode: 'fixed',\n options,\n rounding,\n fixedCurrency: currency,\n scaleFor(c: string): number {\n if (c !== currency) throw new MoneyCurrencyError(c, 'not-allowed')\n return resolvedScale\n },\n allows: (c: string): boolean => c === currency,\n soleCurrency: (): string | undefined => currency,\n }\n}\n\n/** Runtime predicate for detecting a {@link MoneyDescriptor}. */\nexport function isMoneyDescriptor(x: unknown): x is MoneyDescriptor {\n return (\n typeof x === 'object' &&\n x !== null &&\n (x as { _noydbMoney?: unknown })._noydbMoney === true\n )\n}\n","/**\n * Pure fixed-point decimal core for the money descriptor.\n *\n * All conversion goes decimal-string ⇄ scaled `BigInt`. There is no\n * floating-point arithmetic anywhere: a value like `123.45` becomes\n * `12345n` purely by string manipulation, never by `value * 100` (which\n * would reintroduce the very drift money() exists to eliminate). BigInt\n * has no magnitude ceiling, so values past `Number.MAX_SAFE_INTEGER`\n * stay exact end-to-end.\n *\n * This module knows nothing about currencies, descriptors, or storage —\n * it is the isolated, exhaustively-tested arithmetic kernel.\n */\n\nexport type RoundingMode =\n | 'half-up'\n | 'half-even'\n | 'half-down'\n | 'up'\n | 'down'\n | 'ceil'\n | 'floor'\n\nexport type ParseResult =\n | { ok: true; value: bigint }\n | { ok: false; reason: 'precision' | 'nonfinite' }\n\n/**\n * Expand exponent notation (`1e-7`, `1.5e3`) into a plain decimal\n * string. Returns the input unchanged when it carries no exponent.\n */\nfunction expandExponent(s: string): string {\n const m = /^([+-]?)(\\d+)(?:\\.(\\d+))?[eE]([+-]?\\d+)$/.exec(s)\n if (!m) return s\n const sign = m[1] === '-' ? '-' : ''\n const intp = m[2]!\n const frac = m[3] ?? ''\n const exp = Number(m[4]!)\n const digits = intp + frac\n const pointPos = intp.length + exp\n let body: string\n if (pointPos <= 0) {\n body = '0.' + '0'.repeat(-pointPos) + digits\n } else if (pointPos >= digits.length) {\n body = digits + '0'.repeat(pointPos - digits.length)\n } else {\n body = digits.slice(0, pointPos) + '.' + digits.slice(pointPos)\n }\n return sign + body\n}\n\n/**\n * Normalize an input to a canonical decimal string, or `null` if it is\n * non-finite / not a valid decimal.\n */\nfunction toCanonicalDecimalString(input: number | string): string | null {\n let s: string\n if (typeof input === 'number') {\n if (!Number.isFinite(input)) return null\n s = String(input)\n } else {\n s = input.trim()\n }\n s = expandExponent(s)\n if (s.startsWith('+')) s = s.slice(1)\n // optional sign, then digits with at most one dot, at least one digit\n if (!/^-?(\\d+(\\.\\d*)?|\\.\\d+)$/.test(s)) return null\n return s\n}\n\n/**\n * Decide whether the truncated magnitude should be incremented by one\n * minor unit, given the discarded fractional tail. Only called when the\n * tail is known to contain a non-zero digit.\n */\nfunction shouldRoundUp(\n negative: boolean,\n lastKeptDigit: number,\n firstDiscarded: number,\n hasMoreNonZeroAfterFirst: boolean,\n mode: RoundingMode,\n): boolean {\n switch (mode) {\n case 'up':\n return true\n case 'down':\n return false\n case 'ceil':\n return !negative\n case 'floor':\n return negative\n case 'half-up':\n return firstDiscarded >= 5\n case 'half-down':\n return firstDiscarded > 5 || (firstDiscarded === 5 && hasMoreNonZeroAfterFirst)\n case 'half-even':\n if (firstDiscarded > 5) return true\n if (firstDiscarded < 5) return false\n // exactly 5 leading the tail\n return hasMoreNonZeroAfterFirst || lastKeptDigit % 2 === 1\n }\n}\n\n/**\n * Parse a decimal (`number | string`) into a scaled `BigInt` at the\n * given scale. When the input carries more fractional precision than\n * `scale`:\n * - `rounding` omitted ⇒ `{ ok: false, reason: 'precision' }`\n * - `rounding` set ⇒ the tail is rounded per the mode.\n * Non-finite / unparseable input ⇒ `{ ok: false, reason: 'nonfinite' }`.\n */\nexport function parseToScaledInt(\n input: number | string,\n scale: number,\n rounding?: RoundingMode,\n): ParseResult {\n const canonical = toCanonicalDecimalString(input)\n if (canonical === null) return { ok: false, reason: 'nonfinite' }\n\n const negative = canonical.startsWith('-')\n const unsigned = negative ? canonical.slice(1) : canonical\n const dot = unsigned.indexOf('.')\n const intPart = dot === -1 ? unsigned : unsigned.slice(0, dot)\n const fracPart = dot === -1 ? '' : unsigned.slice(dot + 1)\n\n const intDigits = intPart === '' ? '0' : intPart\n\n if (fracPart.length <= scale) {\n const keep = fracPart.padEnd(scale, '0')\n const magnitude = BigInt(intDigits + keep)\n return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude }\n }\n\n // More precision than scale — inspect the discarded tail.\n const keep = fracPart.slice(0, scale)\n const tail = fracPart.slice(scale)\n const magnitudeDigits = intDigits + keep\n let magnitude = BigInt(magnitudeDigits)\n\n if (/^0+$/.test(tail)) {\n // tail is all zeros — exact, no rounding required.\n return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude }\n }\n\n if (rounding === undefined) return { ok: false, reason: 'precision' }\n\n const lastKeptDigit = Number(magnitudeDigits[magnitudeDigits.length - 1])\n const firstDiscarded = Number(tail[0])\n const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1))\n if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {\n magnitude += 1n\n }\n return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude }\n}\n\n/**\n * Number of fractional digits in the canonical decimal rendering of\n * `input` (`'123.45'` → 2, `100` → 0, `'1e-7'` → 7), or `null` when the\n * input is non-finite / not a decimal. Used to infer the working scale\n * of arithmetic helpers from their canonical-string inputs.\n */\nexport function decimalScaleOf(input: number | string): number | null {\n const s = toCanonicalDecimalString(input)\n if (s === null) return null\n const dot = s.indexOf('.')\n return dot === -1 ? 0 : s.length - dot - 1\n}\n\n/**\n * Re-scale a scaled `BigInt` from `fromScale` to `toScale`. Scaling up\n * is exact (append zeros); scaling down rounds the discarded tail per\n * `rounding`. The rounding decision reuses the same kernel as\n * {@link parseToScaledInt}, so `rescaleScaledInt(parse('1.005', 3), 3, 2)`\n * and `parse('1.005', 2, mode)` agree for every mode.\n */\nexport function rescaleScaledInt(\n value: bigint,\n fromScale: number,\n toScale: number,\n rounding: RoundingMode = 'half-up',\n): bigint {\n if (toScale >= fromScale) return value * 10n ** BigInt(toScale - fromScale)\n const drop = fromScale - toScale\n const negative = value < 0n\n const absStr = (negative ? -value : value).toString().padStart(drop + 1, '0')\n const keptStr = absStr.slice(0, absStr.length - drop)\n const tail = absStr.slice(absStr.length - drop)\n let magnitude = BigInt(keptStr)\n if (!/^0+$/.test(tail)) {\n const lastKeptDigit = Number(keptStr[keptStr.length - 1])\n const firstDiscarded = Number(tail[0])\n const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1))\n if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {\n magnitude += 1n\n }\n }\n return negative && magnitude !== 0n ? -magnitude : magnitude\n}\n\n/**\n * Render a scaled `BigInt` back to its canonical decimal string at the\n * given scale. `(12345n, 2)` → `'123.45'`; `(5n, 0)` → `'5'`;\n * `(-1n, 2)` → `'-0.01'`. Exact for any magnitude.\n */\nexport function formatScaledInt(value: bigint, scale: number): string {\n const negative = value < 0n\n const abs = (negative ? -value : value).toString()\n if (scale === 0) return (negative ? '-' : '') + abs\n const padded = abs.padStart(scale + 1, '0')\n const cut = padded.length - scale\n const intPart = padded.slice(0, cut)\n const fracPart = padded.slice(cut)\n return (negative ? '-' : '') + intPart + '.' + fracPart\n}\n","/**\n * Money-aware `where()` comparison (#336).\n *\n * Query clauses evaluate against RAW stored records — money decode\n * happens on output only (#322) — so a money field's stored form is a\n * scaled-integer digit string (`'1000000'`) while the caller naturally\n * writes the operand in major units (`10000`, `'10000.00'`). Without a\n * rewrite the comparison is silently wrong by the scale factor, and a\n * string-vs-number comparison is excluded by `isComparable` anyway.\n *\n * Two halves:\n *\n * - {@link moneyFieldClause} runs at QUERY BUILD time: it quantizes the\n * caller's major-unit operand into stored scaled-int space via the\n * same `parseToScaledInt` path as writes, so a malformed operand\n * throws at `.where()` — not silently filters everything out.\n * - {@link evaluateMoneyClause} runs per record and compares\n * `BigInt`-exact in scaled space (exact past 2^53, like the rest of\n * the money subsystem).\n *\n * Currency semantics (multi mode): an operand carries one currency —\n * explicit via `{ amount, currency }`, or the descriptor's sole allowed\n * currency for a bare amount. A record in a DIFFERENT currency has no\n * defined order against the operand: it matches `!=` and nothing else.\n */\n\nimport { parseToScaledInt } from './fixed-point.js'\nimport { MoneyUnsupportedError, type MoneyDescriptor } from './descriptor.js'\nimport type { FieldClause, Operator } from '../query/predicate.js'\n\n/** One quantized operand value: scaled digit string + its currency. */\ninterface MoneyOperandEntry {\n readonly scaled: string\n readonly currency: string\n}\n\n/**\n * The money payload attached to a {@link FieldClause} over a declared\n * money field. `entries` holds one element for comparison ops, two for\n * `between` (lo, hi — same currency), N for `in`.\n */\nexport interface MoneyWhereOperand {\n readonly mode: 'fixed' | 'multi'\n readonly entries: ReadonlyArray<MoneyOperandEntry>\n}\n\ninterface MoneyValueObject {\n amount: unknown\n currency: unknown\n}\n\nfunction isMoneyValueObject(v: unknown): v is MoneyValueObject {\n return typeof v === 'object' && v !== null && 'currency' in v\n}\n\n/** Quantize ONE operand value to scaled space, resolving its currency. */\nfunction parseOperand(field: string, raw: unknown, desc: MoneyDescriptor): MoneyOperandEntry {\n let amount: unknown\n let currency: string\n if (desc.mode === 'fixed') {\n currency = desc.fixedCurrency!\n amount = raw\n } else if (isMoneyValueObject(raw)) {\n currency = String(raw.currency)\n amount = raw.amount\n } else {\n const sole = desc.soleCurrency()\n if (sole === undefined) {\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): field is multi-currency — compare against { amount, currency }, not a bare amount`,\n )\n }\n currency = sole\n amount = raw\n }\n if (typeof amount !== 'number' && typeof amount !== 'string') {\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): operand ${JSON.stringify(raw)} is not a money amount`,\n )\n }\n const r = parseToScaledInt(amount, desc.scaleFor(currency), desc.rounding)\n if (!r.ok) {\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): operand ${JSON.stringify(amount)} is not a finite decimal`,\n )\n }\n return { scaled: r.value.toString(), currency }\n}\n\n/**\n * Build the {@link FieldClause} for a `where()` over a declared money\n * field. The operand is quantized into stored scaled-int space NOW —\n * build time — so typos throw at the call site.\n *\n * For FIXED mode the clause's `value` is rewritten to the scaled digit\n * string(s): that is exactly the stored form, so the secondary-index\n * fast path (`lookupEqual`/`lookupIn`, which probes with `clause.value`)\n * keeps working. MULTI mode keeps the caller's `value` untouched — the\n * index stringifies object keys to a no-match sentinel, so the executor\n * must skip the index for these clauses (it checks `money.mode`).\n */\nexport function moneyFieldClause(\n field: string,\n op: Operator,\n value: unknown,\n desc: MoneyDescriptor,\n): FieldClause {\n switch (op) {\n case '==': case '!=': case '<': case '<=': case '>': case '>=': {\n const e = parseOperand(field, value, desc)\n return withMoney(field, op, value, desc, [e])\n }\n case 'between': {\n if (!Array.isArray(value) || value.length !== 2) {\n throw new MoneyUnsupportedError(`where(\"${field}\"): 'between' needs a [lo, hi] tuple`)\n }\n const lo = parseOperand(field, value[0], desc)\n const hi = parseOperand(field, value[1], desc)\n if (lo.currency !== hi.currency) {\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): 'between' bounds mix currencies (${lo.currency} vs ${hi.currency})`,\n )\n }\n return withMoney(field, op, value, desc, [lo, hi])\n }\n case 'in': {\n if (!Array.isArray(value)) {\n throw new MoneyUnsupportedError(`where(\"${field}\"): 'in' needs an array of amounts`)\n }\n return withMoney(field, op, value, desc, value.map(v => parseOperand(field, v, desc)))\n }\n default:\n // contains / startsWith — string ops have no meaning in scaled space.\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): operator '${op}' is not supported on a money field`,\n )\n }\n}\n\nfunction withMoney(\n field: string,\n op: Operator,\n originalValue: unknown,\n desc: MoneyDescriptor,\n entries: ReadonlyArray<MoneyOperandEntry>,\n): FieldClause {\n const money: MoneyWhereOperand = { mode: desc.mode, entries }\n // Fixed mode: surface the stored-form operand as `value` for the\n // index fast path; arrays (in/between) map element-wise.\n const value = desc.mode !== 'fixed'\n ? originalValue\n : entries.length === 1 && op !== 'in' && op !== 'between'\n ? entries[0]!.scaled\n : entries.map(e => e.scaled)\n return { type: 'field', field, op, value, money }\n}\n\n/** Read a raw STORED money value into scaled space, or null if absent/malformed. */\nfunction readStored(actual: unknown, operand: MoneyWhereOperand): MoneyOperandEntry | null {\n let amount: unknown\n let currency: string\n if (operand.mode === 'fixed') {\n if (typeof actual !== 'string' && typeof actual !== 'number') return null\n amount = actual\n currency = operand.entries[0]?.currency ?? ''\n } else {\n if (!isMoneyValueObject(actual)) return null\n if (typeof actual.currency !== 'string') return null\n amount = actual.amount\n currency = actual.currency\n }\n if (typeof amount !== 'string' && typeof amount !== 'number') return null\n try {\n return { scaled: BigInt(amount).toString(), currency }\n } catch {\n return null\n }\n}\n\n/**\n * Per-record evaluation of a money clause: BigInt-exact comparison in\n * scaled-integer space. `actual` is the RAW stored field value.\n *\n * Missing/malformed stored values and cross-currency comparisons match\n * `!=` only — consistent with the generic clause semantics where an\n * absent field is \"not equal\" and has no defined order.\n */\nexport function evaluateMoneyClause(\n actual: unknown,\n op: Operator,\n operand: MoneyWhereOperand,\n): boolean {\n const stored = readStored(actual, operand)\n if (stored === null) return op === '!='\n const a = BigInt(stored.scaled)\n\n if (op === 'in') {\n return operand.entries.some(\n e => e.currency === stored.currency && BigInt(e.scaled) === a,\n )\n }\n if (op === 'between') {\n const [lo, hi] = operand.entries\n if (!lo || !hi || lo.currency !== stored.currency) return false\n return a >= BigInt(lo.scaled) && a <= BigInt(hi.scaled)\n }\n\n const e = operand.entries[0]\n if (!e) return false\n if (e.currency !== stored.currency) return op === '!='\n const b = BigInt(e.scaled)\n switch (op) {\n case '==': return a === b\n case '!=': return a !== b\n case '<': return a < b\n case '<=': return a <= b\n case '>': return a > b\n case '>=': return a >= b\n default: return false\n }\n}\n","/**\n * Operator implementations for the query DSL.\n *\n * All predicates run client-side, AFTER decryption — they never see ciphertext.\n * The only dependency is the money clause evaluator (#336) — still\n * tree-shakeable through it.\n */\n\nimport { evaluateMoneyClause, type MoneyWhereOperand } from '../money/where.js'\n\n/** Comparison operators supported by the where() builder. */\nexport type Operator =\n | '=='\n | '!='\n | '<'\n | '<='\n | '>'\n | '>='\n | 'in'\n | 'contains'\n | 'startsWith'\n | 'between'\n\n/**\n * A single field comparison clause inside a query plan.\n * Plans are JSON-serializable, so this type uses primitives only.\n */\nexport interface FieldClause {\n readonly type: 'field'\n readonly field: string\n readonly op: Operator\n readonly value: unknown\n /**\n * Present when `field` is a declared money field (#336): the operand\n * quantized into stored scaled-int space at query BUILD time, so the\n * per-record comparison is BigInt-exact against the raw stored value.\n * Built by `moneyFieldClause` — `Query.where()` attaches it when the\n * source declares the field in `moneyFields`.\n */\n readonly money?: MoneyWhereOperand\n}\n\n/**\n * A user-supplied predicate function escape hatch. Not serializable.\n *\n * The predicate accepts `unknown` at the type level so the surrounding\n * Clause type can stay non-parametric — this keeps Collection<T> covariant\n * in T at the public API surface. Builder methods cast user predicates\n * (typed `(record: T) => boolean`) into this shape on the way in.\n */\nexport interface FilterClause {\n readonly type: 'filter'\n readonly fn: (record: unknown) => boolean\n}\n\n/**\n * A declared deterministic predicate reference. The query\n * builder produces this via `.wherePredicate(name, ctx?)` when a\n * Query has been augmented with a predicates map (typically by the\n * materialized-view registry — see MV v2 spec § Function-based\n * source-row predicates).\n *\n * `predicateHash` is the consumer-supplied stable hash for the\n * function body; `ctxHash` is the canonical-JSON SHA-256 of `ctx`.\n * Both fold into the MV's `queryHash` so a function or ctx change\n * forces refresh on next visit.\n *\n * `fn` is resolved at builder time from the predicates map and\n * embedded directly — so `evaluateClause` can fire it without a\n * runtime lookup.\n */\nexport interface WherePredicateClause {\n readonly type: 'wherePredicate'\n readonly name: string\n readonly ctx: unknown\n readonly predicateHash: string\n readonly ctxHash: string\n readonly fn: (record: unknown, ctx?: unknown) => boolean\n}\n\n/** A logical group of clauses combined by AND or OR. */\nexport interface GroupClause {\n readonly type: 'group'\n readonly op: 'and' | 'or'\n readonly clauses: readonly Clause[]\n}\n\n/**\n * Cartesian-product expansion clause. Appended to `QueryPlan.clauses`\n * by `Query.crossJoin()`. Processed in declaration order by\n * `executeClausePipeline` — NOT by `evaluateClause` (which is a\n * per-record predicate and throws on this type).\n */\nexport interface CrossJoinClause {\n readonly type: 'crossJoin'\n /** Target collection name to cross-join against. */\n readonly target: string\n /** Alias under which the right-side record is exposed on each result row. */\n readonly as: string\n /**\n * Lateral filter callback. `undefined` → full cartesian product.\n * Two call shapes:\n * - Subset: `(left) => TTarget[]` — returns the right rows for this left row\n * - Predicate: `(left) => (right) => boolean` — executor materializes then filters\n */\n readonly on?: (left: unknown) => unknown[] | ((right: unknown) => boolean)\n /** When `on:` was supplied as `{ predicate: name }`, the name is stored here for queryHash. */\n readonly onPredicateName?: string\n /** Per-clause row ceiling override. `undefined` → `DEFAULT_CROSS_JOIN_MAX_ROWS`. */\n readonly maxRows?: number\n}\n\nexport type Clause = FieldClause | FilterClause | WherePredicateClause | GroupClause | CrossJoinClause\n\n/**\n * Read a possibly nested field path like \"address.city\" from a record.\n * Returns undefined if any segment is missing.\n */\nexport function readPath(record: unknown, path: string): unknown {\n if (record === null || record === undefined) return undefined\n if (!path.includes('.')) {\n return (record as Record<string, unknown>)[path]\n }\n const segments = path.split('.')\n let cursor: unknown = record\n for (const segment of segments) {\n if (cursor === null || cursor === undefined) return undefined\n cursor = (cursor as Record<string, unknown>)[segment]\n }\n return cursor\n}\n\n/**\n * Evaluate a single field clause against a record.\n * Returns false on type mismatches rather than throwing — query results\n * exclude non-matching records by definition.\n */\nexport function evaluateFieldClause(record: unknown, clause: FieldClause): boolean {\n const actual = readPath(record, clause.field)\n const { op, value } = clause\n\n // Money fields compare BigInt-exact in scaled-integer space (#336) —\n // the stored form is a digit string, so the generic paths below would\n // either reject (string vs number is not comparable) or compare\n // lexicographically. The operand was quantized at build time.\n if (clause.money) return evaluateMoneyClause(actual, op, clause.money)\n\n switch (op) {\n case '==':\n return actual === value\n case '!=':\n return actual !== value\n case '<':\n return isComparable(actual, value) && (actual as number) < (value as number)\n case '<=':\n return isComparable(actual, value) && (actual as number) <= (value as number)\n case '>':\n return isComparable(actual, value) && (actual as number) > (value as number)\n case '>=':\n return isComparable(actual, value) && (actual as number) >= (value as number)\n case 'in':\n return Array.isArray(value) && value.includes(actual)\n case 'contains':\n if (typeof actual === 'string') return typeof value === 'string' && actual.includes(value)\n if (Array.isArray(actual)) return actual.includes(value)\n return false\n case 'startsWith':\n return typeof actual === 'string' && typeof value === 'string' && actual.startsWith(value)\n case 'between': {\n if (!Array.isArray(value) || value.length !== 2) return false\n const [lo, hi] = value\n if (!isComparable(actual, lo) || !isComparable(actual, hi)) return false\n return (actual as number) >= (lo as number) && (actual as number) <= (hi as number)\n }\n default: {\n // Exhaustiveness — TS will error if a new operator is added without a case.\n const _exhaustive: never = op\n void _exhaustive\n return false\n }\n }\n}\n\n/**\n * Two values are \"comparable\" if they share an order-defined runtime type.\n * Strings compare lexicographically; numbers and Dates numerically; otherwise false.\n */\nfunction isComparable(a: unknown, b: unknown): boolean {\n if (typeof a === 'number' && typeof b === 'number') return true\n if (typeof a === 'string' && typeof b === 'string') return true\n if (a instanceof Date && b instanceof Date) return true\n return false\n}\n\n/**\n * Evaluate any clause (field / filter / group) against a record.\n * The recursion depth is bounded by the user's query expression — no risk of\n * blowing the stack on a 50K-record collection.\n *\n * `fnRecord`, when provided, is the view handed to USER CALLBACK clauses\n * (`filter` / `wherePredicate`) instead of `record` — the executor passes\n * the money-decoded view there (#335) so user code never sees the stored\n * scaled-int form, while field clauses keep evaluating against the raw\n * record (their money operands are pre-quantized to that space, #336).\n */\nexport function evaluateClause(record: unknown, clause: Clause, fnRecord?: unknown): boolean {\n switch (clause.type) {\n case 'field':\n return evaluateFieldClause(record, clause)\n case 'filter':\n return clause.fn(fnRecord !== undefined ? fnRecord : record)\n case 'wherePredicate':\n return clause.fn(fnRecord !== undefined ? fnRecord : record, clause.ctx)\n case 'crossJoin':\n throw new Error(\n `evaluateClause: 'crossJoin' clauses are expansion primitives and are not ` +\n `evaluated per-record. This is a query planner routing error — ` +\n `crossJoin clauses must be extracted from the clause list before calling ` +\n `evaluateClause or filterRecords.`,\n )\n case 'group':\n if (clause.op === 'and') {\n for (const child of clause.clauses) {\n if (!evaluateClause(record, child, fnRecord)) return false\n }\n return true\n } else {\n for (const child of clause.clauses) {\n if (evaluateClause(record, child, fnRecord)) return true\n }\n return false\n }\n }\n}\n\n/**\n * Does the clause list contain any user-callback clause (filter /\n * wherePredicate), at any group nesting depth? Used by executors to\n * decide whether the per-record decoded view needs materializing.\n */\nexport function hasFnClause(clauses: readonly Clause[]): boolean {\n for (const c of clauses) {\n if (c.type === 'filter' || c.type === 'wherePredicate') return true\n if (c.type === 'group' && hasFnClause(c.clauses)) return true\n }\n return false\n}\n"],"mappings":";;;;;AAcA,IAAM,cAAgD;AAAA;AAAA,EAEpD,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAC7D,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAC7D,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAC7D,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA;AAAA,EAE7D,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA;AAAA,EAE7D,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AAAA,EAAG,KAAK;AACvD;AAOO,SAAS,iBAAiB,MAA6B;AAC5D,QAAM,IAAI,YAAY,IAAI;AAC1B,SAAO,MAAM,SAAY,OAAO;AAClC;;;ACsBO,IAAM,sBAAN,cAAkC,WAAW;AAAA,EAClD,YACkB,OACA,OACA,OAChB;AACA;AAAA,MACE;AAAA,MACA,gBAAgB,KAAK,UAAU,KAAK,CAAC,eAAe,KAAK,mBAAmB,KAAK;AAAA,IAEnF;AARgB;AACA;AACA;AAOhB,SAAK,OAAO;AAAA,EACd;AAAA,EAVkB;AAAA,EACA;AAAA,EACA;AASpB;AAGO,IAAM,qBAAN,cAAiC,WAAW;AAAA,EACjD,YACkB,UACA,QACA,OAChB;AACA;AAAA,MACE;AAAA,MACA,WAAW,gBACP,oBAAoB,QAAQ,mBAAmB,QAAQ,eAAe,KAAK,MAAM,EAAE,KACnF,uCAAuC,QAAQ,IAAI,QAAQ,YAAY,KAAK,OAAO,EAAE;AAAA,IAE3F;AAVgB;AACA;AACA;AAShB,SAAK,OAAO;AAAA,EACd;AAAA,EAZkB;AAAA,EACA;AAAA,EACA;AAWpB;AAGO,IAAM,wBAAN,cAAoC,WAAW;AAAA,EACpD,YACkB,OAChB,SACA;AACA;AAAA,MACE;AAAA,MACA,WACE,+CAA+C,KAAK;AAAA,IACxD;AAPgB;AAQhB,SAAK,OAAO;AAAA,EACd;AAAA,EATkB;AAUpB;AAEA,SAAS,eAAe,GAAyC;AAC/D,SAAO,gBAAgB;AACzB;AAGO,SAAS,MAAM,SAAwC;AAC5D,QAAM,WAAW,cAAc;AAC/B,QAAM,WAAW,gBAAgB;AACjC,MAAI,YAAY,UAAU;AACxB,UAAM,IAAI,UAAU,2DAA2D;AAAA,EACjF;AACA,MAAI,CAAC,YAAY,CAAC,UAAU;AAC1B,UAAM,IAAI,UAAU,sDAAsD;AAAA,EAC5E;AAEA,QAAM,WAAW,QAAQ;AAEzB,MAAI,eAAe,OAAO,GAAG;AAC3B,UAAM,YAAY,QAAQ,kBAAkB,CAAC;AAC7C,UAAM,YAAY,QAAQ;AAC1B,UAAM,SAAS,CAAC,MACd,cAAc,QAAQ,OAAO,UAAU,SAAS,CAAC;AACnD,UAAM,WAAW,CAAC,MAAsB;AACtC,UAAI,CAAC,OAAO,CAAC,EAAG,OAAM,IAAI,mBAAmB,GAAG,aAAa;AAC7D,YAAM,IAAI,UAAU,CAAC,KAAK,iBAAiB,CAAC;AAC5C,UAAI,MAAM,QAAQ,MAAM,OAAW,OAAM,IAAI,mBAAmB,GAAG,eAAe;AAClF,aAAO;AAAA,IACT;AAEA,QAAI,cAAc,MAAO,YAAW,KAAK,UAAW,UAAS,CAAC;AAC9D,UAAM,eAAe,MACnB,cAAc,SAAS,UAAU,WAAW,IAAI,UAAU,CAAC,IAAI;AACjE,WAAO;AAAA,MACL,aAAa;AAAA,MACb,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,eAAe;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,QAAM,WAAW,QAAQ;AACzB,QAAM,gBAAgB,QAAQ,SAAS,iBAAiB,QAAQ;AAChE,MAAI,kBAAkB,QAAQ,kBAAkB,QAAW;AACzD,UAAM,IAAI,mBAAmB,UAAU,eAAe;AAAA,EACxD;AACA,SAAO;AAAA,IACL,aAAa;AAAA,IACb,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf,SAAS,GAAmB;AAC1B,UAAI,MAAM,SAAU,OAAM,IAAI,mBAAmB,GAAG,aAAa;AACjE,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,CAAC,MAAuB,MAAM;AAAA,IACtC,cAAc,MAA0B;AAAA,EAC1C;AACF;AAGO,SAAS,kBAAkB,GAAkC;AAClE,SACE,OAAO,MAAM,YACb,MAAM,QACL,EAAgC,gBAAgB;AAErD;;;ACjJA,SAAS,eAAe,GAAmB;AACzC,QAAM,IAAI,2CAA2C,KAAK,CAAC;AAC3D,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,OAAO,EAAE,CAAC,MAAM,MAAM,MAAM;AAClC,QAAM,OAAO,EAAE,CAAC;AAChB,QAAM,OAAO,EAAE,CAAC,KAAK;AACrB,QAAM,MAAM,OAAO,EAAE,CAAC,CAAE;AACxB,QAAM,SAAS,OAAO;AACtB,QAAM,WAAW,KAAK,SAAS;AAC/B,MAAI;AACJ,MAAI,YAAY,GAAG;AACjB,WAAO,OAAO,IAAI,OAAO,CAAC,QAAQ,IAAI;AAAA,EACxC,WAAW,YAAY,OAAO,QAAQ;AACpC,WAAO,SAAS,IAAI,OAAO,WAAW,OAAO,MAAM;AAAA,EACrD,OAAO;AACL,WAAO,OAAO,MAAM,GAAG,QAAQ,IAAI,MAAM,OAAO,MAAM,QAAQ;AAAA,EAChE;AACA,SAAO,OAAO;AAChB;AAMA,SAAS,yBAAyB,OAAuC;AACvE,MAAI;AACJ,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,QAAI,OAAO,KAAK;AAAA,EAClB,OAAO;AACL,QAAI,MAAM,KAAK;AAAA,EACjB;AACA,MAAI,eAAe,CAAC;AACpB,MAAI,EAAE,WAAW,GAAG,EAAG,KAAI,EAAE,MAAM,CAAC;AAEpC,MAAI,CAAC,0BAA0B,KAAK,CAAC,EAAG,QAAO;AAC/C,SAAO;AACT;AAOA,SAAS,cACP,UACA,eACA,gBACA,0BACA,MACS;AACT,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,CAAC;AAAA,IACV,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,kBAAkB;AAAA,IAC3B,KAAK;AACH,aAAO,iBAAiB,KAAM,mBAAmB,KAAK;AAAA,IACxD,KAAK;AACH,UAAI,iBAAiB,EAAG,QAAO;AAC/B,UAAI,iBAAiB,EAAG,QAAO;AAE/B,aAAO,4BAA4B,gBAAgB,MAAM;AAAA,EAC7D;AACF;AAUO,SAAS,iBACd,OACA,OACA,UACa;AACb,QAAM,YAAY,yBAAyB,KAAK;AAChD,MAAI,cAAc,KAAM,QAAO,EAAE,IAAI,OAAO,QAAQ,YAAY;AAEhE,QAAM,WAAW,UAAU,WAAW,GAAG;AACzC,QAAM,WAAW,WAAW,UAAU,MAAM,CAAC,IAAI;AACjD,QAAM,MAAM,SAAS,QAAQ,GAAG;AAChC,QAAM,UAAU,QAAQ,KAAK,WAAW,SAAS,MAAM,GAAG,GAAG;AAC7D,QAAM,WAAW,QAAQ,KAAK,KAAK,SAAS,MAAM,MAAM,CAAC;AAEzD,QAAM,YAAY,YAAY,KAAK,MAAM;AAEzC,MAAI,SAAS,UAAU,OAAO;AAC5B,UAAMA,QAAO,SAAS,OAAO,OAAO,GAAG;AACvC,UAAMC,aAAY,OAAO,YAAYD,KAAI;AACzC,WAAO,EAAE,IAAI,MAAM,OAAO,YAAYC,eAAc,KAAK,CAACA,aAAYA,WAAU;AAAA,EAClF;AAGA,QAAM,OAAO,SAAS,MAAM,GAAG,KAAK;AACpC,QAAM,OAAO,SAAS,MAAM,KAAK;AACjC,QAAM,kBAAkB,YAAY;AACpC,MAAI,YAAY,OAAO,eAAe;AAEtC,MAAI,OAAO,KAAK,IAAI,GAAG;AAErB,WAAO,EAAE,IAAI,MAAM,OAAO,YAAY,cAAc,KAAK,CAAC,YAAY,UAAU;AAAA,EAClF;AAEA,MAAI,aAAa,OAAW,QAAO,EAAE,IAAI,OAAO,QAAQ,YAAY;AAEpE,QAAM,gBAAgB,OAAO,gBAAgB,gBAAgB,SAAS,CAAC,CAAC;AACxE,QAAM,iBAAiB,OAAO,KAAK,CAAC,CAAC;AACrC,QAAM,2BAA2B,QAAQ,KAAK,KAAK,MAAM,CAAC,CAAC;AAC3D,MAAI,cAAc,UAAU,eAAe,gBAAgB,0BAA0B,QAAQ,GAAG;AAC9F,iBAAa;AAAA,EACf;AACA,SAAO,EAAE,IAAI,MAAM,OAAO,YAAY,cAAc,KAAK,CAAC,YAAY,UAAU;AAClF;AAQO,SAAS,eAAe,OAAuC;AACpE,QAAM,IAAI,yBAAyB,KAAK;AACxC,MAAI,MAAM,KAAM,QAAO;AACvB,QAAM,MAAM,EAAE,QAAQ,GAAG;AACzB,SAAO,QAAQ,KAAK,IAAI,EAAE,SAAS,MAAM;AAC3C;AASO,SAAS,iBACd,OACA,WACA,SACA,WAAyB,WACjB;AACR,MAAI,WAAW,UAAW,QAAO,QAAQ,OAAO,OAAO,UAAU,SAAS;AAC1E,QAAM,OAAO,YAAY;AACzB,QAAM,WAAW,QAAQ;AACzB,QAAM,UAAU,WAAW,CAAC,QAAQ,OAAO,SAAS,EAAE,SAAS,OAAO,GAAG,GAAG;AAC5E,QAAM,UAAU,OAAO,MAAM,GAAG,OAAO,SAAS,IAAI;AACpD,QAAM,OAAO,OAAO,MAAM,OAAO,SAAS,IAAI;AAC9C,MAAI,YAAY,OAAO,OAAO;AAC9B,MAAI,CAAC,OAAO,KAAK,IAAI,GAAG;AACtB,UAAM,gBAAgB,OAAO,QAAQ,QAAQ,SAAS,CAAC,CAAC;AACxD,UAAM,iBAAiB,OAAO,KAAK,CAAC,CAAC;AACrC,UAAM,2BAA2B,QAAQ,KAAK,KAAK,MAAM,CAAC,CAAC;AAC3D,QAAI,cAAc,UAAU,eAAe,gBAAgB,0BAA0B,QAAQ,GAAG;AAC9F,mBAAa;AAAA,IACf;AAAA,EACF;AACA,SAAO,YAAY,cAAc,KAAK,CAAC,YAAY;AACrD;AAOO,SAAS,gBAAgB,OAAe,OAAuB;AACpE,QAAM,WAAW,QAAQ;AACzB,QAAM,OAAO,WAAW,CAAC,QAAQ,OAAO,SAAS;AACjD,MAAI,UAAU,EAAG,SAAQ,WAAW,MAAM,MAAM;AAChD,QAAM,SAAS,IAAI,SAAS,QAAQ,GAAG,GAAG;AAC1C,QAAM,MAAM,OAAO,SAAS;AAC5B,QAAM,UAAU,OAAO,MAAM,GAAG,GAAG;AACnC,QAAM,WAAW,OAAO,MAAM,GAAG;AACjC,UAAQ,WAAW,MAAM,MAAM,UAAU,MAAM;AACjD;;;AClKA,SAAS,mBAAmB,GAAmC;AAC7D,SAAO,OAAO,MAAM,YAAY,MAAM,QAAQ,cAAc;AAC9D;AAGA,SAAS,aAAa,OAAe,KAAc,MAA0C;AAC3F,MAAI;AACJ,MAAI;AACJ,MAAI,KAAK,SAAS,SAAS;AACzB,eAAW,KAAK;AAChB,aAAS;AAAA,EACX,WAAW,mBAAmB,GAAG,GAAG;AAClC,eAAW,OAAO,IAAI,QAAQ;AAC9B,aAAS,IAAI;AAAA,EACf,OAAO;AACL,UAAM,OAAO,KAAK,aAAa;AAC/B,QAAI,SAAS,QAAW;AACtB,YAAM,IAAI;AAAA,QACR,UAAU,KAAK;AAAA,MACjB;AAAA,IACF;AACA,eAAW;AACX,aAAS;AAAA,EACX;AACA,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,UAAU;AAC5D,UAAM,IAAI;AAAA,MACR,UAAU,KAAK,eAAe,KAAK,UAAU,GAAG,CAAC;AAAA,IACnD;AAAA,EACF;AACA,QAAM,IAAI,iBAAiB,QAAQ,KAAK,SAAS,QAAQ,GAAG,KAAK,QAAQ;AACzE,MAAI,CAAC,EAAE,IAAI;AACT,UAAM,IAAI;AAAA,MACR,UAAU,KAAK,eAAe,KAAK,UAAU,MAAM,CAAC;AAAA,IACtD;AAAA,EACF;AACA,SAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,GAAG,SAAS;AAChD;AAcO,SAAS,iBACd,OACA,IACA,OACA,MACa;AACb,UAAQ,IAAI;AAAA,IACV,KAAK;AAAA,IAAM,KAAK;AAAA,IAAM,KAAK;AAAA,IAAK,KAAK;AAAA,IAAM,KAAK;AAAA,IAAK,KAAK,MAAM;AAC9D,YAAM,IAAI,aAAa,OAAO,OAAO,IAAI;AACzC,aAAO,UAAU,OAAO,IAAI,OAAO,MAAM,CAAC,CAAC,CAAC;AAAA,IAC9C;AAAA,IACA,KAAK,WAAW;AACd,UAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AAC/C,cAAM,IAAI,sBAAsB,UAAU,KAAK,sCAAsC;AAAA,MACvF;AACA,YAAM,KAAK,aAAa,OAAO,MAAM,CAAC,GAAG,IAAI;AAC7C,YAAM,KAAK,aAAa,OAAO,MAAM,CAAC,GAAG,IAAI;AAC7C,UAAI,GAAG,aAAa,GAAG,UAAU;AAC/B,cAAM,IAAI;AAAA,UACR,UAAU,KAAK,wCAAwC,GAAG,QAAQ,OAAO,GAAG,QAAQ;AAAA,QACtF;AAAA,MACF;AACA,aAAO,UAAU,OAAO,IAAI,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AAAA,IACnD;AAAA,IACA,KAAK,MAAM;AACT,UAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;AACzB,cAAM,IAAI,sBAAsB,UAAU,KAAK,oCAAoC;AAAA,MACrF;AACA,aAAO,UAAU,OAAO,IAAI,OAAO,MAAM,MAAM,IAAI,OAAK,aAAa,OAAO,GAAG,IAAI,CAAC,CAAC;AAAA,IACvF;AAAA,IACA;AAEE,YAAM,IAAI;AAAA,QACR,UAAU,KAAK,iBAAiB,EAAE;AAAA,MACpC;AAAA,EACJ;AACF;AAEA,SAAS,UACP,OACA,IACA,eACA,MACA,SACa;AACb,QAAMC,SAA2B,EAAE,MAAM,KAAK,MAAM,QAAQ;AAG5D,QAAM,QAAQ,KAAK,SAAS,UACxB,gBACA,QAAQ,WAAW,KAAK,OAAO,QAAQ,OAAO,YAC5C,QAAQ,CAAC,EAAG,SACZ,QAAQ,IAAI,OAAK,EAAE,MAAM;AAC/B,SAAO,EAAE,MAAM,SAAS,OAAO,IAAI,OAAO,OAAAA,OAAM;AAClD;AAGA,SAAS,WAAW,QAAiB,SAAsD;AACzF,MAAI;AACJ,MAAI;AACJ,MAAI,QAAQ,SAAS,SAAS;AAC5B,QAAI,OAAO,WAAW,YAAY,OAAO,WAAW,SAAU,QAAO;AACrE,aAAS;AACT,eAAW,QAAQ,QAAQ,CAAC,GAAG,YAAY;AAAA,EAC7C,OAAO;AACL,QAAI,CAAC,mBAAmB,MAAM,EAAG,QAAO;AACxC,QAAI,OAAO,OAAO,aAAa,SAAU,QAAO;AAChD,aAAS,OAAO;AAChB,eAAW,OAAO;AAAA,EACpB;AACA,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,SAAU,QAAO;AACrE,MAAI;AACF,WAAO,EAAE,QAAQ,OAAO,MAAM,EAAE,SAAS,GAAG,SAAS;AAAA,EACvD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUO,SAAS,oBACd,QACA,IACA,SACS;AACT,QAAM,SAAS,WAAW,QAAQ,OAAO;AACzC,MAAI,WAAW,KAAM,QAAO,OAAO;AACnC,QAAM,IAAI,OAAO,OAAO,MAAM;AAE9B,MAAI,OAAO,MAAM;AACf,WAAO,QAAQ,QAAQ;AAAA,MACrB,CAAAC,OAAKA,GAAE,aAAa,OAAO,YAAY,OAAOA,GAAE,MAAM,MAAM;AAAA,IAC9D;AAAA,EACF;AACA,MAAI,OAAO,WAAW;AACpB,UAAM,CAAC,IAAI,EAAE,IAAI,QAAQ;AACzB,QAAI,CAAC,MAAM,CAAC,MAAM,GAAG,aAAa,OAAO,SAAU,QAAO;AAC1D,WAAO,KAAK,OAAO,GAAG,MAAM,KAAK,KAAK,OAAO,GAAG,MAAM;AAAA,EACxD;AAEA,QAAM,IAAI,QAAQ,QAAQ,CAAC;AAC3B,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,EAAE,aAAa,OAAO,SAAU,QAAO,OAAO;AAClD,QAAM,IAAI,OAAO,EAAE,MAAM;AACzB,UAAQ,IAAI;AAAA,IACV,KAAK;AAAM,aAAO,MAAM;AAAA,IACxB,KAAK;AAAM,aAAO,MAAM;AAAA,IACxB,KAAK;AAAK,aAAO,IAAI;AAAA,IACrB,KAAK;AAAM,aAAO,KAAK;AAAA,IACvB,KAAK;AAAK,aAAO,IAAI;AAAA,IACrB,KAAK;AAAM,aAAO,KAAK;AAAA,IACvB;AAAS,aAAO;AAAA,EAClB;AACF;;;ACtGO,SAAS,SAAS,QAAiB,MAAuB;AAC/D,MAAI,WAAW,QAAQ,WAAW,OAAW,QAAO;AACpD,MAAI,CAAC,KAAK,SAAS,GAAG,GAAG;AACvB,WAAQ,OAAmC,IAAI;AAAA,EACjD;AACA,QAAM,WAAW,KAAK,MAAM,GAAG;AAC/B,MAAI,SAAkB;AACtB,aAAW,WAAW,UAAU;AAC9B,QAAI,WAAW,QAAQ,WAAW,OAAW,QAAO;AACpD,aAAU,OAAmC,OAAO;AAAA,EACtD;AACA,SAAO;AACT;AAOO,SAAS,oBAAoB,QAAiB,QAA8B;AACjF,QAAM,SAAS,SAAS,QAAQ,OAAO,KAAK;AAC5C,QAAM,EAAE,IAAI,MAAM,IAAI;AAMtB,MAAI,OAAO,MAAO,QAAO,oBAAoB,QAAQ,IAAI,OAAO,KAAK;AAErE,UAAQ,IAAI;AAAA,IACV,KAAK;AACH,aAAO,WAAW;AAAA,IACpB,KAAK;AACH,aAAO,WAAW;AAAA,IACpB,KAAK;AACH,aAAO,aAAa,QAAQ,KAAK,KAAM,SAAqB;AAAA,IAC9D,KAAK;AACH,aAAO,aAAa,QAAQ,KAAK,KAAM,UAAsB;AAAA,IAC/D,KAAK;AACH,aAAO,aAAa,QAAQ,KAAK,KAAM,SAAqB;AAAA,IAC9D,KAAK;AACH,aAAO,aAAa,QAAQ,KAAK,KAAM,UAAsB;AAAA,IAC/D,KAAK;AACH,aAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,MAAM;AAAA,IACtD,KAAK;AACH,UAAI,OAAO,WAAW,SAAU,QAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AACzF,UAAI,MAAM,QAAQ,MAAM,EAAG,QAAO,OAAO,SAAS,KAAK;AACvD,aAAO;AAAA,IACT,KAAK;AACH,aAAO,OAAO,WAAW,YAAY,OAAO,UAAU,YAAY,OAAO,WAAW,KAAK;AAAA,IAC3F,KAAK,WAAW;AACd,UAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,EAAG,QAAO;AACxD,YAAM,CAAC,IAAI,EAAE,IAAI;AACjB,UAAI,CAAC,aAAa,QAAQ,EAAE,KAAK,CAAC,aAAa,QAAQ,EAAE,EAAG,QAAO;AACnE,aAAQ,UAAsB,MAAkB,UAAsB;AAAA,IACxE;AAAA,IACA,SAAS;AAEP,YAAM,cAAqB;AAC3B,WAAK;AACL,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAMA,SAAS,aAAa,GAAY,GAAqB;AACrD,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SAAU,QAAO;AAC3D,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SAAU,QAAO;AAC3D,MAAI,aAAa,QAAQ,aAAa,KAAM,QAAO;AACnD,SAAO;AACT;AAaO,SAAS,eAAe,QAAiB,QAAgB,UAA6B;AAC3F,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK;AACH,aAAO,oBAAoB,QAAQ,MAAM;AAAA,IAC3C,KAAK;AACH,aAAO,OAAO,GAAG,aAAa,SAAY,WAAW,MAAM;AAAA,IAC7D,KAAK;AACH,aAAO,OAAO,GAAG,aAAa,SAAY,WAAW,QAAQ,OAAO,GAAG;AAAA,IACzE,KAAK;AACH,YAAM,IAAI;AAAA,QACR;AAAA,MAIF;AAAA,IACF,KAAK;AACH,UAAI,OAAO,OAAO,OAAO;AACvB,mBAAW,SAAS,OAAO,SAAS;AAClC,cAAI,CAAC,eAAe,QAAQ,OAAO,QAAQ,EAAG,QAAO;AAAA,QACvD;AACA,eAAO;AAAA,MACT,OAAO;AACL,mBAAW,SAAS,OAAO,SAAS;AAClC,cAAI,eAAe,QAAQ,OAAO,QAAQ,EAAG,QAAO;AAAA,QACtD;AACA,eAAO;AAAA,MACT;AAAA,EACJ;AACF;AAOO,SAAS,YAAY,SAAqC;AAC/D,aAAW,KAAK,SAAS;AACvB,QAAI,EAAE,SAAS,YAAY,EAAE,SAAS,iBAAkB,QAAO;AAC/D,QAAI,EAAE,SAAS,WAAW,YAAY,EAAE,OAAO,EAAG,QAAO;AAAA,EAC3D;AACA,SAAO;AACT;","names":["keep","magnitude","money","e"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
evaluateClause,
|
|
3
3
|
readPath
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-CJORTUJ2.js";
|
|
5
5
|
import {
|
|
6
6
|
IndexRequiredError
|
|
7
7
|
} from "./chunk-535SSHBS.js";
|
|
@@ -427,4 +427,4 @@ export {
|
|
|
427
427
|
PersistedCollectionIndex,
|
|
428
428
|
LazyQuery
|
|
429
429
|
};
|
|
430
|
-
//# sourceMappingURL=chunk-
|
|
430
|
+
//# sourceMappingURL=chunk-CZI2A4MQ.js.map
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
assertStrongPassphrase,
|
|
6
6
|
mintKeyringCanary,
|
|
7
7
|
persistKeyring
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-6H2ZUNR7.js";
|
|
9
9
|
import {
|
|
10
10
|
NOYDB_FORMAT_VERSION,
|
|
11
11
|
NOYDB_KEYRING_VERSION
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-F3BPIPLS.js";
|
|
13
13
|
import {
|
|
14
14
|
base64ToBuffer,
|
|
15
15
|
bufferToBase64,
|
|
@@ -827,4 +827,4 @@ export {
|
|
|
827
827
|
magicLinkGrantRecordId,
|
|
828
828
|
isMagicLinkGrantExpired
|
|
829
829
|
};
|
|
830
|
-
//# sourceMappingURL=chunk-
|
|
830
|
+
//# sourceMappingURL=chunk-DLZ2ONOD.js.map
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
-
MoneyPrecisionError,
|
|
3
|
-
formatScaledInt,
|
|
4
|
-
parseToScaledInt,
|
|
5
2
|
wrapMoneyReducers
|
|
6
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-L2BNJ6HM.js";
|
|
7
4
|
import {
|
|
5
|
+
MoneyPrecisionError,
|
|
8
6
|
evaluateClause,
|
|
7
|
+
formatScaledInt,
|
|
8
|
+
hasFnClause,
|
|
9
|
+
moneyFieldClause,
|
|
10
|
+
parseToScaledInt,
|
|
9
11
|
readPath
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-CJORTUJ2.js";
|
|
11
13
|
import {
|
|
12
14
|
CrossJoinSourceUnknownError,
|
|
13
15
|
CrossJoinTooLargeError,
|
|
14
16
|
DanglingReferenceError,
|
|
15
|
-
JoinTooLargeError
|
|
17
|
+
JoinTooLargeError,
|
|
18
|
+
ValidationError
|
|
16
19
|
} from "./chunk-535SSHBS.js";
|
|
17
20
|
|
|
18
21
|
// src/query/join.ts
|
|
@@ -260,6 +263,108 @@ var NO_AGGREGATE = {
|
|
|
260
263
|
}
|
|
261
264
|
};
|
|
262
265
|
|
|
266
|
+
// src/money/paths.ts
|
|
267
|
+
var SEGMENT_RE = /^(\*|[^.[\]*]+)(\[\])?$/;
|
|
268
|
+
var parseCache = /* @__PURE__ */ new Map();
|
|
269
|
+
function parseMoneyPath(path) {
|
|
270
|
+
const cached = parseCache.get(path);
|
|
271
|
+
if (cached) return cached;
|
|
272
|
+
if (typeof path !== "string" || path.length === 0) {
|
|
273
|
+
throw new ValidationError("moneyFields: path must be a non-empty string");
|
|
274
|
+
}
|
|
275
|
+
const segments = [];
|
|
276
|
+
for (const part of path.split(".")) {
|
|
277
|
+
const m = SEGMENT_RE.exec(part);
|
|
278
|
+
if (!m) {
|
|
279
|
+
throw new ValidationError(
|
|
280
|
+
`moneyFields: invalid path "${path}" \u2014 segment "${part}" must be a key, "key[]", "*", or "*[]"`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
const array = m[2] === "[]";
|
|
284
|
+
segments.push(
|
|
285
|
+
m[1] === "*" ? { kind: "wildcard", array } : { kind: "key", key: m[1], array }
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
parseCache.set(path, segments);
|
|
289
|
+
return segments;
|
|
290
|
+
}
|
|
291
|
+
function isSimpleMoneyPath(path) {
|
|
292
|
+
return !path.includes(".") && !path.includes("[") && !path.includes("*");
|
|
293
|
+
}
|
|
294
|
+
function validateMoneyFieldPaths(moneyFields) {
|
|
295
|
+
for (const path of Object.keys(moneyFields)) parseMoneyPath(path);
|
|
296
|
+
}
|
|
297
|
+
function transformAtMoneyPath(node, path, segments, index, visit, lenient) {
|
|
298
|
+
if (node === null || node === void 0) return node;
|
|
299
|
+
const seg = segments[index];
|
|
300
|
+
const last = index === segments.length - 1;
|
|
301
|
+
if (seg.kind === "key") {
|
|
302
|
+
if (typeof node !== "object" || Array.isArray(node)) {
|
|
303
|
+
if (lenient) return node;
|
|
304
|
+
throw new ValidationError(
|
|
305
|
+
`moneyFields: path "${path}" expected an object at segment "${seg.key}", got ${Array.isArray(node) ? "an array" : typeof node}`
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
const obj2 = node;
|
|
309
|
+
if (!(seg.key in obj2) || obj2[seg.key] === null || obj2[seg.key] === void 0) return node;
|
|
310
|
+
if (seg.array) {
|
|
311
|
+
const arr = obj2[seg.key];
|
|
312
|
+
if (!Array.isArray(arr)) {
|
|
313
|
+
if (lenient) return node;
|
|
314
|
+
throw new ValidationError(
|
|
315
|
+
`moneyFields: path "${path}" declares "${seg.key}[]" but the value is not an array`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
const cloned = [...arr];
|
|
319
|
+
if (last) {
|
|
320
|
+
for (let i = 0; i < cloned.length; i++) visit(cloned, i);
|
|
321
|
+
} else {
|
|
322
|
+
for (let i = 0; i < cloned.length; i++) {
|
|
323
|
+
cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return { ...obj2, [seg.key]: cloned };
|
|
327
|
+
}
|
|
328
|
+
const clone2 = { ...obj2 };
|
|
329
|
+
if (last) {
|
|
330
|
+
visit(clone2, seg.key);
|
|
331
|
+
} else {
|
|
332
|
+
clone2[seg.key] = transformAtMoneyPath(clone2[seg.key], path, segments, index + 1, visit, lenient);
|
|
333
|
+
}
|
|
334
|
+
return clone2;
|
|
335
|
+
}
|
|
336
|
+
if (seg.array) {
|
|
337
|
+
if (!Array.isArray(node)) {
|
|
338
|
+
if (lenient) return node;
|
|
339
|
+
throw new ValidationError(`moneyFields: path "${path}" declares "*[]" but the value is not an array`);
|
|
340
|
+
}
|
|
341
|
+
const cloned = [...node];
|
|
342
|
+
if (last) {
|
|
343
|
+
for (let i = 0; i < cloned.length; i++) visit(cloned, i);
|
|
344
|
+
} else {
|
|
345
|
+
for (let i = 0; i < cloned.length; i++) {
|
|
346
|
+
cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return cloned;
|
|
350
|
+
}
|
|
351
|
+
if (typeof node !== "object" || Array.isArray(node)) {
|
|
352
|
+
if (lenient) return node;
|
|
353
|
+
throw new ValidationError(
|
|
354
|
+
`moneyFields: path "${path}" applies "*" to a non-object (${Array.isArray(node) ? 'array \u2014 use "*[]"' : typeof node})`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
const obj = node;
|
|
358
|
+
const clone = { ...obj };
|
|
359
|
+
for (const key of Object.keys(obj)) {
|
|
360
|
+
const v = clone[key];
|
|
361
|
+
if (v === null || v === void 0) continue;
|
|
362
|
+
if (last) visit(clone, key);
|
|
363
|
+
else clone[key] = transformAtMoneyPath(v, path, segments, index + 1, visit, lenient);
|
|
364
|
+
}
|
|
365
|
+
return clone;
|
|
366
|
+
}
|
|
367
|
+
|
|
263
368
|
// src/money/normalize.ts
|
|
264
369
|
function isMoneyValueObject(v) {
|
|
265
370
|
return typeof v === "object" && v !== null && "currency" in v;
|
|
@@ -272,33 +377,68 @@ function quantizeAmount(field, input, scale, rounding) {
|
|
|
272
377
|
}
|
|
273
378
|
return r.value.toString();
|
|
274
379
|
}
|
|
380
|
+
function canonicalizeStoredMoney(record, moneyFields) {
|
|
381
|
+
if (record === null || record === void 0) return record;
|
|
382
|
+
if (!moneyFields || Object.keys(moneyFields).length === 0) return record;
|
|
383
|
+
return decodeMoneyFields(record, moneyFields, "raw");
|
|
384
|
+
}
|
|
385
|
+
function canonicalizeIncomingMoney(record, moneyFields) {
|
|
386
|
+
if (!moneyFields || Object.keys(moneyFields).length === 0) return record;
|
|
387
|
+
try {
|
|
388
|
+
return decodeMoneyFields(
|
|
389
|
+
quantizeMoneyFields(record, moneyFields),
|
|
390
|
+
moneyFields,
|
|
391
|
+
"raw"
|
|
392
|
+
);
|
|
393
|
+
} catch {
|
|
394
|
+
return record;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
function quantizeValue(field, raw, desc) {
|
|
398
|
+
if (desc.mode === "fixed") {
|
|
399
|
+
const currency2 = desc.fixedCurrency;
|
|
400
|
+
return quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
|
|
401
|
+
}
|
|
402
|
+
let amount;
|
|
403
|
+
let currency;
|
|
404
|
+
if (isMoneyValueObject(raw)) {
|
|
405
|
+
currency = String(raw.currency);
|
|
406
|
+
amount = raw.amount;
|
|
407
|
+
} else {
|
|
408
|
+
const sole = desc.soleCurrency();
|
|
409
|
+
if (sole === void 0) {
|
|
410
|
+
throw new TypeError(
|
|
411
|
+
`money: field "${field}" is multi-currency \u2014 write { amount, currency }, not a bare amount`
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
currency = sole;
|
|
415
|
+
amount = raw;
|
|
416
|
+
}
|
|
417
|
+
const scale = desc.scaleFor(currency);
|
|
418
|
+
return { amount: quantizeAmount(field, amount, scale, desc.rounding), currency };
|
|
419
|
+
}
|
|
275
420
|
function quantizeMoneyFields(record, moneyFields) {
|
|
276
|
-
|
|
277
|
-
for (const [
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
out[field] = quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
|
|
421
|
+
let out = { ...record };
|
|
422
|
+
for (const [path, desc] of Object.entries(moneyFields)) {
|
|
423
|
+
if (isSimpleMoneyPath(path)) {
|
|
424
|
+
const raw = out[path];
|
|
425
|
+
if (raw === null || raw === void 0) continue;
|
|
426
|
+
out[path] = quantizeValue(path, raw, desc);
|
|
283
427
|
continue;
|
|
284
428
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
amount = raw;
|
|
299
|
-
}
|
|
300
|
-
const scale = desc.scaleFor(currency);
|
|
301
|
-
out[field] = { amount: quantizeAmount(field, amount, scale, desc.rounding), currency };
|
|
429
|
+
out = transformAtMoneyPath(
|
|
430
|
+
out,
|
|
431
|
+
path,
|
|
432
|
+
parseMoneyPath(path),
|
|
433
|
+
0,
|
|
434
|
+
(container, key) => {
|
|
435
|
+
const raw = container[key];
|
|
436
|
+
if (raw === null || raw === void 0) return;
|
|
437
|
+
container[key] = quantizeValue(path, raw, desc);
|
|
438
|
+
},
|
|
439
|
+
/* lenient */
|
|
440
|
+
false
|
|
441
|
+
);
|
|
302
442
|
}
|
|
303
443
|
return out;
|
|
304
444
|
}
|
|
@@ -311,33 +451,70 @@ function formatCurrency(decimal, currency, scale, locale) {
|
|
|
311
451
|
});
|
|
312
452
|
return fmt.format(decimal);
|
|
313
453
|
}
|
|
454
|
+
function decodeValue(stored, desc) {
|
|
455
|
+
let currency;
|
|
456
|
+
let scaledIntString;
|
|
457
|
+
if (desc.mode === "fixed") {
|
|
458
|
+
if (typeof stored !== "string" && typeof stored !== "number") return null;
|
|
459
|
+
currency = desc.fixedCurrency;
|
|
460
|
+
scaledIntString = String(stored);
|
|
461
|
+
} else {
|
|
462
|
+
if (!isMoneyValueObject(stored)) return null;
|
|
463
|
+
const amount = stored.amount;
|
|
464
|
+
if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") return null;
|
|
465
|
+
currency = stored.currency;
|
|
466
|
+
scaledIntString = String(amount);
|
|
467
|
+
}
|
|
468
|
+
const scale = desc.scaleFor(currency);
|
|
469
|
+
let decimal;
|
|
470
|
+
try {
|
|
471
|
+
decimal = formatScaledInt(BigInt(scaledIntString), scale);
|
|
472
|
+
} catch {
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
return {
|
|
476
|
+
decoded: desc.mode === "fixed" ? decimal : { amount: decimal, currency },
|
|
477
|
+
decimal,
|
|
478
|
+
currency,
|
|
479
|
+
scale
|
|
480
|
+
};
|
|
481
|
+
}
|
|
314
482
|
function decodeMoneyFields(record, moneyFields, locale) {
|
|
315
|
-
|
|
483
|
+
let out = { ...record };
|
|
316
484
|
const format = locale !== "raw";
|
|
317
485
|
const fmtLocale = typeof locale === "string" && locale !== "raw" ? locale : "en-US";
|
|
318
|
-
for (const [
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") continue;
|
|
331
|
-
currency = stored.currency;
|
|
332
|
-
scaledIntString = String(amount);
|
|
333
|
-
}
|
|
334
|
-
const scale = desc.scaleFor(currency);
|
|
335
|
-
const decimal = formatScaledInt(BigInt(scaledIntString), scale);
|
|
336
|
-
out[field] = desc.mode === "fixed" ? decimal : { amount: decimal, currency };
|
|
337
|
-
if (format) {
|
|
338
|
-
out[`${field}Formatted`] = formatCurrency(decimal, currency, scale, fmtLocale);
|
|
339
|
-
out[`${field}Number`] = Number(decimal);
|
|
486
|
+
for (const [path, desc] of Object.entries(moneyFields)) {
|
|
487
|
+
if (isSimpleMoneyPath(path)) {
|
|
488
|
+
const stored = out[path];
|
|
489
|
+
if (stored === null || stored === void 0) continue;
|
|
490
|
+
const r = decodeValue(stored, desc);
|
|
491
|
+
if (r === null) continue;
|
|
492
|
+
out[path] = r.decoded;
|
|
493
|
+
if (format) {
|
|
494
|
+
out[`${path}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale);
|
|
495
|
+
out[`${path}Number`] = Number(r.decimal);
|
|
496
|
+
}
|
|
497
|
+
continue;
|
|
340
498
|
}
|
|
499
|
+
out = transformAtMoneyPath(
|
|
500
|
+
out,
|
|
501
|
+
path,
|
|
502
|
+
parseMoneyPath(path),
|
|
503
|
+
0,
|
|
504
|
+
(container, key) => {
|
|
505
|
+
const stored = container[key];
|
|
506
|
+
if (stored === null || stored === void 0) return;
|
|
507
|
+
const r = decodeValue(stored, desc);
|
|
508
|
+
if (r === null) return;
|
|
509
|
+
container[key] = r.decoded;
|
|
510
|
+
if (format && typeof key === "string" && !Array.isArray(container)) {
|
|
511
|
+
container[`${key}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale);
|
|
512
|
+
container[`${key}Number`] = Number(r.decimal);
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
/* lenient */
|
|
516
|
+
true
|
|
517
|
+
);
|
|
341
518
|
}
|
|
342
519
|
return out;
|
|
343
520
|
}
|
|
@@ -434,9 +611,18 @@ var Query = class _Query {
|
|
|
434
611
|
this.predicates
|
|
435
612
|
);
|
|
436
613
|
}
|
|
437
|
-
/**
|
|
614
|
+
/**
|
|
615
|
+
* Add a field comparison. Multiple where() calls are AND-combined.
|
|
616
|
+
*
|
|
617
|
+
* A declared money field compares in MAJOR units (#336): the operand
|
|
618
|
+
* (`10000`, `'10000.00'`, or `{ amount, currency }` in multi mode) is
|
|
619
|
+
* quantized into stored scaled-int space at build time and evaluated
|
|
620
|
+
* BigInt-exact per record. A malformed operand or a string operator
|
|
621
|
+
* (`contains`/`startsWith`) throws here, at the call site.
|
|
622
|
+
*/
|
|
438
623
|
where(field, op, value) {
|
|
439
|
-
const
|
|
624
|
+
const desc = this.source.moneyFields?.[field];
|
|
625
|
+
const clause = desc ? moneyFieldClause(field, op, value, desc) : { type: "field", field, op, value };
|
|
440
626
|
return new _Query(
|
|
441
627
|
this.source,
|
|
442
628
|
{ ...this.plan, clauses: [...this.plan.clauses, clause] },
|
|
@@ -762,7 +948,7 @@ var Query = class _Query {
|
|
|
762
948
|
}
|
|
763
949
|
const { candidates, remainingClauses } = candidateRecords(this.source, this.plan.clauses);
|
|
764
950
|
if (remainingClauses.length === 0) return candidates.length;
|
|
765
|
-
return filterRecords(candidates, remainingClauses).length;
|
|
951
|
+
return filterRecords(candidates, remainingClauses, fnViewDecoder(this.source)).length;
|
|
766
952
|
}
|
|
767
953
|
/**
|
|
768
954
|
* Reduce the matching records through a named set of reducers.
|
|
@@ -819,7 +1005,7 @@ var Query = class _Query {
|
|
|
819
1005
|
return executeClausePipeline(source, clauses, joinCtx);
|
|
820
1006
|
}
|
|
821
1007
|
const { candidates, remainingClauses } = candidateRecords(source, clauses);
|
|
822
|
-
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses);
|
|
1008
|
+
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
|
|
823
1009
|
};
|
|
824
1010
|
const upstreams = [];
|
|
825
1011
|
if (source.subscribe) {
|
|
@@ -842,7 +1028,7 @@ var Query = class _Query {
|
|
|
842
1028
|
return executeClausePipeline(source, clauses, joinCtx);
|
|
843
1029
|
}
|
|
844
1030
|
const { candidates, remainingClauses } = candidateRecords(source, clauses);
|
|
845
|
-
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses);
|
|
1031
|
+
return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
|
|
846
1032
|
};
|
|
847
1033
|
const upstreams = [];
|
|
848
1034
|
if (source.subscribe) {
|
|
@@ -994,7 +1180,7 @@ function executePlanWithSource(source, plan, joinContext) {
|
|
|
994
1180
|
result = executeClausePipeline(source, plan.clauses, joinContext);
|
|
995
1181
|
} else {
|
|
996
1182
|
const { candidates, remainingClauses } = candidateRecords(source, plan.clauses);
|
|
997
|
-
result = remainingClauses.length === 0 ? [...candidates] : filterRecords(candidates, remainingClauses);
|
|
1183
|
+
result = remainingClauses.length === 0 ? [...candidates] : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
|
|
998
1184
|
}
|
|
999
1185
|
if (plan.orderBy.length > 0) {
|
|
1000
1186
|
result = sortRecords(result, plan.orderBy);
|
|
@@ -1017,6 +1203,7 @@ function candidateRecords(source, clauses) {
|
|
|
1017
1203
|
const clause = clauses[i];
|
|
1018
1204
|
if (clause.type !== "field") continue;
|
|
1019
1205
|
if (!indexes.has(clause.field)) continue;
|
|
1206
|
+
if (clause.money?.mode === "multi") continue;
|
|
1020
1207
|
let ids = null;
|
|
1021
1208
|
if (clause.op === "==") {
|
|
1022
1209
|
ids = indexes.lookupEqual(clause.field, clause.value);
|
|
@@ -1062,13 +1249,20 @@ function executePlan(records, plan) {
|
|
|
1062
1249
|
}
|
|
1063
1250
|
return result;
|
|
1064
1251
|
}
|
|
1065
|
-
function
|
|
1252
|
+
function fnViewDecoder(source) {
|
|
1253
|
+
const mf = source.moneyFields;
|
|
1254
|
+
if (!mf || Object.keys(mf).length === 0) return void 0;
|
|
1255
|
+
return (r) => decodeMoneyFields(r, mf, "raw");
|
|
1256
|
+
}
|
|
1257
|
+
function filterRecords(records, clauses, decodeForFns) {
|
|
1066
1258
|
if (clauses.length === 0) return [...records];
|
|
1259
|
+
const needsFnView = decodeForFns !== void 0 && hasFnClause(clauses);
|
|
1067
1260
|
const out = [];
|
|
1068
1261
|
for (const r of records) {
|
|
1262
|
+
const fnView = needsFnView ? decodeForFns(r) : void 0;
|
|
1069
1263
|
let matches = true;
|
|
1070
1264
|
for (const clause of clauses) {
|
|
1071
|
-
if (!evaluateClause(r, clause)) {
|
|
1265
|
+
if (!evaluateClause(r, clause, fnView)) {
|
|
1072
1266
|
matches = false;
|
|
1073
1267
|
break;
|
|
1074
1268
|
}
|
|
@@ -1080,10 +1274,11 @@ function filterRecords(records, clauses) {
|
|
|
1080
1274
|
function executeClausePipeline(source, clauses, joinContext) {
|
|
1081
1275
|
let rel = [...source.snapshot()];
|
|
1082
1276
|
let filterBatch = [];
|
|
1277
|
+
const decodeForFns = fnViewDecoder(source);
|
|
1083
1278
|
for (const clause of clauses) {
|
|
1084
1279
|
if (clause.type === "crossJoin") {
|
|
1085
1280
|
if (filterBatch.length > 0) {
|
|
1086
|
-
rel = filterRecords(rel, filterBatch);
|
|
1281
|
+
rel = filterRecords(rel, filterBatch, decodeForFns);
|
|
1087
1282
|
filterBatch = [];
|
|
1088
1283
|
}
|
|
1089
1284
|
const rightSource = joinContext.resolveSource(clause.target);
|
|
@@ -1096,7 +1291,7 @@ function executeClausePipeline(source, clauses, joinContext) {
|
|
|
1096
1291
|
}
|
|
1097
1292
|
}
|
|
1098
1293
|
if (filterBatch.length > 0) {
|
|
1099
|
-
rel = filterRecords(rel, filterBatch);
|
|
1294
|
+
rel = filterRecords(rel, filterBatch, decodeForFns);
|
|
1100
1295
|
}
|
|
1101
1296
|
return rel;
|
|
1102
1297
|
}
|
|
@@ -1331,7 +1526,8 @@ var ScanBuilder = class _ScanBuilder {
|
|
|
1331
1526
|
* evaluates clauses per record in O(1) per clause.
|
|
1332
1527
|
*/
|
|
1333
1528
|
where(field, op, value) {
|
|
1334
|
-
const
|
|
1529
|
+
const desc = this.moneyFields?.[field];
|
|
1530
|
+
const clause = desc ? moneyFieldClause(field, op, value, desc) : { type: "field", field, op, value };
|
|
1335
1531
|
return new _ScanBuilder(
|
|
1336
1532
|
this.pageProvider,
|
|
1337
1533
|
this.pageSize,
|
|
@@ -1677,8 +1873,9 @@ var ScanBuilder = class _ScanBuilder {
|
|
|
1677
1873
|
*/
|
|
1678
1874
|
recordMatches(record) {
|
|
1679
1875
|
if (this.clauses.length === 0) return true;
|
|
1876
|
+
const fnView = this.moneyFields && Object.keys(this.moneyFields).length > 0 && hasFnClause(this.clauses) ? this.decodeMoney(record) : void 0;
|
|
1680
1877
|
for (const clause of this.clauses) {
|
|
1681
|
-
if (!evaluateClause(record, clause)) return false;
|
|
1878
|
+
if (!evaluateClause(record, clause, fnView)) return false;
|
|
1682
1879
|
}
|
|
1683
1880
|
return true;
|
|
1684
1881
|
}
|
|
@@ -1691,6 +1888,9 @@ function coerceRefKey2(value) {
|
|
|
1691
1888
|
}
|
|
1692
1889
|
|
|
1693
1890
|
export {
|
|
1891
|
+
validateMoneyFieldPaths,
|
|
1892
|
+
canonicalizeStoredMoney,
|
|
1893
|
+
canonicalizeIncomingMoney,
|
|
1694
1894
|
quantizeMoneyFields,
|
|
1695
1895
|
decodeMoneyFields,
|
|
1696
1896
|
DEFAULT_JOIN_MAX_ROWS,
|
|
@@ -1703,4 +1903,4 @@ export {
|
|
|
1703
1903
|
executePlan,
|
|
1704
1904
|
ScanBuilder
|
|
1705
1905
|
};
|
|
1706
|
-
//# sourceMappingURL=chunk-
|
|
1906
|
+
//# sourceMappingURL=chunk-DUREQF5W.js.map
|