@ducatjs/money 0.0.0 → 0.2.0
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/LICENSE +21 -0
- package/NOTICE +51 -0
- package/README.md +1034 -3
- package/assets/CREDITS.md +12 -0
- package/data/adyen-fx.json +19 -0
- package/data/adyen.json +171 -0
- package/data/braintree.json +223 -0
- package/data/cash-rounding.json +520 -0
- package/data/checkout.json +230 -0
- package/data/cldr-cash.json +101 -0
- package/data/historic.json +78 -0
- package/data/iso4217-list-one.xml +1956 -0
- package/data/iso4217-list-three.xml +1185 -0
- package/data/klarna.json +95 -0
- package/data/paypal.json +65 -0
- package/data/stripe-fx.json +17 -0
- package/data/stripe.json +285 -0
- package/dist/adyen-fx.d.ts +25 -0
- package/dist/adyen-fx.d.ts.map +1 -0
- package/dist/adyen-fx.js +140 -0
- package/dist/adyen-fx.js.map +1 -0
- package/dist/adyen.d.ts +25 -0
- package/dist/adyen.d.ts.map +1 -0
- package/dist/adyen.js +34 -0
- package/dist/adyen.js.map +1 -0
- package/dist/allocate.d.ts +72 -0
- package/dist/allocate.d.ts.map +1 -0
- package/dist/allocate.js +314 -0
- package/dist/allocate.js.map +1 -0
- package/dist/bag.d.ts +58 -0
- package/dist/bag.d.ts.map +1 -0
- package/dist/bag.js +122 -0
- package/dist/bag.js.map +1 -0
- package/dist/boc.d.ts +31 -0
- package/dist/boc.d.ts.map +1 -0
- package/dist/boc.js +114 -0
- package/dist/boc.js.map +1 -0
- package/dist/boe.d.ts +33 -0
- package/dist/boe.d.ts.map +1 -0
- package/dist/boe.js +114 -0
- package/dist/boe.js.map +1 -0
- package/dist/braintree.d.ts +34 -0
- package/dist/braintree.d.ts.map +1 -0
- package/dist/braintree.js +45 -0
- package/dist/braintree.js.map +1 -0
- package/dist/cash.d.ts +64 -0
- package/dist/cash.d.ts.map +1 -0
- package/dist/cash.js +134 -0
- package/dist/cash.js.map +1 -0
- package/dist/checkout.d.ts +42 -0
- package/dist/checkout.d.ts.map +1 -0
- package/dist/checkout.js +52 -0
- package/dist/checkout.js.map +1 -0
- package/dist/convert.d.ts +52 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/convert.js +65 -0
- package/dist/convert.js.map +1 -0
- package/dist/currency.d.ts +22 -0
- package/dist/currency.d.ts.map +1 -0
- package/dist/currency.js +38 -0
- package/dist/currency.js.map +1 -0
- package/dist/decimal.d.ts +52 -0
- package/dist/decimal.d.ts.map +1 -0
- package/dist/decimal.js +251 -0
- package/dist/decimal.js.map +1 -0
- package/dist/drizzle/mysql.d.ts +105 -0
- package/dist/drizzle/mysql.d.ts.map +1 -0
- package/dist/drizzle/mysql.js +60 -0
- package/dist/drizzle/mysql.js.map +1 -0
- package/dist/drizzle/pg.d.ts +103 -0
- package/dist/drizzle/pg.d.ts.map +1 -0
- package/dist/drizzle/pg.js +60 -0
- package/dist/drizzle/pg.js.map +1 -0
- package/dist/drizzle/sqlite.d.ts +102 -0
- package/dist/drizzle/sqlite.d.ts.map +1 -0
- package/dist/drizzle/sqlite.js +59 -0
- package/dist/drizzle/sqlite.js.map +1 -0
- package/dist/ecb.d.ts +52 -0
- package/dist/ecb.d.ts.map +1 -0
- package/dist/ecb.js +285 -0
- package/dist/ecb.js.map +1 -0
- package/dist/exact.d.ts +62 -0
- package/dist/exact.d.ts.map +1 -0
- package/dist/exact.js +147 -0
- package/dist/exact.js.map +1 -0
- package/dist/fed.d.ts +35 -0
- package/dist/fed.d.ts.map +1 -0
- package/dist/fed.js +134 -0
- package/dist/fed.js.map +1 -0
- package/dist/fetch.d.ts +103 -0
- package/dist/fetch.d.ts.map +1 -0
- package/dist/fetch.js +222 -0
- package/dist/fetch.js.map +1 -0
- package/dist/fixed.d.ts +121 -0
- package/dist/fixed.d.ts.map +1 -0
- package/dist/fixed.js +420 -0
- package/dist/fixed.js.map +1 -0
- package/dist/fn.d.ts +34 -0
- package/dist/fn.d.ts.map +1 -0
- package/dist/fn.js +37 -0
- package/dist/fn.js.map +1 -0
- package/dist/format.d.ts +13 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +90 -0
- package/dist/format.js.map +1 -0
- package/dist/generated/adyen.d.ts +3 -0
- package/dist/generated/adyen.d.ts.map +1 -0
- package/dist/generated/adyen.js +144 -0
- package/dist/generated/adyen.js.map +1 -0
- package/dist/generated/braintree.d.ts +3 -0
- package/dist/generated/braintree.d.ts.map +1 -0
- package/dist/generated/braintree.js +140 -0
- package/dist/generated/braintree.js.map +1 -0
- package/dist/generated/cash.d.ts +14 -0
- package/dist/generated/cash.d.ts.map +1 -0
- package/dist/generated/cash.js +465 -0
- package/dist/generated/cash.js.map +1 -0
- package/dist/generated/checkout.d.ts +3 -0
- package/dist/generated/checkout.d.ts.map +1 -0
- package/dist/generated/checkout.js +151 -0
- package/dist/generated/checkout.js.map +1 -0
- package/dist/generated/historic.d.ts +337 -0
- package/dist/generated/historic.d.ts.map +1 -0
- package/dist/generated/historic.js +66 -0
- package/dist/generated/historic.js.map +1 -0
- package/dist/generated/iso4217.d.ts +174 -0
- package/dist/generated/iso4217.d.ts.map +1 -0
- package/dist/generated/iso4217.js +175 -0
- package/dist/generated/iso4217.js.map +1 -0
- package/dist/generated/kernel.d.ts +17 -0
- package/dist/generated/kernel.d.ts.map +1 -0
- package/dist/generated/kernel.js +208 -0
- package/dist/generated/kernel.js.map +1 -0
- package/dist/generated/klarna.d.ts +3 -0
- package/dist/generated/klarna.d.ts.map +1 -0
- package/dist/generated/klarna.js +21 -0
- package/dist/generated/klarna.js.map +1 -0
- package/dist/generated/paypal.d.ts +3 -0
- package/dist/generated/paypal.d.ts.map +1 -0
- package/dist/generated/paypal.js +31 -0
- package/dist/generated/paypal.js.map +1 -0
- package/dist/generated/stripe.d.ts +8 -0
- package/dist/generated/stripe.d.ts.map +1 -0
- package/dist/generated/stripe.js +150 -0
- package/dist/generated/stripe.js.map +1 -0
- package/dist/historic.d.ts +40 -0
- package/dist/historic.d.ts.map +1 -0
- package/dist/historic.js +43 -0
- package/dist/historic.js.map +1 -0
- package/dist/history.d.ts +96 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +213 -0
- package/dist/history.js.map +1 -0
- package/dist/hmrc.d.ts +28 -0
- package/dist/hmrc.d.ts.map +1 -0
- package/dist/hmrc.js +108 -0
- package/dist/hmrc.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/klarna.d.ts +31 -0
- package/dist/klarna.d.ts.map +1 -0
- package/dist/klarna.js +38 -0
- package/dist/klarna.js.map +1 -0
- package/dist/kysely.d.ts +120 -0
- package/dist/kysely.d.ts.map +1 -0
- package/dist/kysely.js +309 -0
- package/dist/kysely.js.map +1 -0
- package/dist/money.d.ts +302 -0
- package/dist/money.d.ts.map +1 -0
- package/dist/money.js +703 -0
- package/dist/money.js.map +1 -0
- package/dist/parse.d.ts +55 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +419 -0
- package/dist/parse.js.map +1 -0
- package/dist/paypal.d.ts +41 -0
- package/dist/paypal.d.ts.map +1 -0
- package/dist/paypal.js +58 -0
- package/dist/paypal.js.map +1 -0
- package/dist/pg.d.ts +100 -0
- package/dist/pg.d.ts.map +1 -0
- package/dist/pg.js +343 -0
- package/dist/pg.js.map +1 -0
- package/dist/plugins/adyen.d.ts +15 -0
- package/dist/plugins/adyen.d.ts.map +1 -0
- package/dist/plugins/adyen.js +10 -0
- package/dist/plugins/adyen.js.map +1 -0
- package/dist/plugins/all.d.ts +29 -0
- package/dist/plugins/all.d.ts.map +1 -0
- package/dist/plugins/all.js +30 -0
- package/dist/plugins/all.js.map +1 -0
- package/dist/plugins/allocate.d.ts +20 -0
- package/dist/plugins/allocate.d.ts.map +1 -0
- package/dist/plugins/allocate.js +26 -0
- package/dist/plugins/allocate.js.map +1 -0
- package/dist/plugins/braintree.d.ts +12 -0
- package/dist/plugins/braintree.d.ts.map +1 -0
- package/dist/plugins/braintree.js +12 -0
- package/dist/plugins/braintree.js.map +1 -0
- package/dist/plugins/cash.d.ts +19 -0
- package/dist/plugins/cash.d.ts.map +1 -0
- package/dist/plugins/cash.js +16 -0
- package/dist/plugins/cash.js.map +1 -0
- package/dist/plugins/checkout.d.ts +21 -0
- package/dist/plugins/checkout.d.ts.map +1 -0
- package/dist/plugins/checkout.js +16 -0
- package/dist/plugins/checkout.js.map +1 -0
- package/dist/plugins/convert.d.ts +19 -0
- package/dist/plugins/convert.d.ts.map +1 -0
- package/dist/plugins/convert.js +13 -0
- package/dist/plugins/convert.js.map +1 -0
- package/dist/plugins/exact.d.ts +15 -0
- package/dist/plugins/exact.d.ts.map +1 -0
- package/dist/plugins/exact.js +13 -0
- package/dist/plugins/exact.js.map +1 -0
- package/dist/plugins/fixed.d.ts +17 -0
- package/dist/plugins/fixed.d.ts.map +1 -0
- package/dist/plugins/fixed.js +13 -0
- package/dist/plugins/fixed.js.map +1 -0
- package/dist/plugins/format.d.ts +9 -0
- package/dist/plugins/format.d.ts.map +1 -0
- package/dist/plugins/format.js +16 -0
- package/dist/plugins/format.js.map +1 -0
- package/dist/plugins/klarna.d.ts +10 -0
- package/dist/plugins/klarna.d.ts.map +1 -0
- package/dist/plugins/klarna.js +10 -0
- package/dist/plugins/klarna.js.map +1 -0
- package/dist/plugins/paypal.d.ts +14 -0
- package/dist/plugins/paypal.d.ts.map +1 -0
- package/dist/plugins/paypal.js +16 -0
- package/dist/plugins/paypal.js.map +1 -0
- package/dist/plugins/prisma.d.ts +8 -0
- package/dist/plugins/prisma.d.ts.map +1 -0
- package/dist/plugins/prisma.js +13 -0
- package/dist/plugins/prisma.js.map +1 -0
- package/dist/plugins/stripe.d.ts +14 -0
- package/dist/plugins/stripe.d.ts.map +1 -0
- package/dist/plugins/stripe.js +16 -0
- package/dist/plugins/stripe.js.map +1 -0
- package/dist/plugins/tax.d.ts +20 -0
- package/dist/plugins/tax.d.ts.map +1 -0
- package/dist/plugins/tax.js +18 -0
- package/dist/plugins/tax.js.map +1 -0
- package/dist/policy.d.ts +34 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +90 -0
- package/dist/policy.js.map +1 -0
- package/dist/prisma.d.ts +77 -0
- package/dist/prisma.d.ts.map +1 -0
- package/dist/prisma.js +76 -0
- package/dist/prisma.js.map +1 -0
- package/dist/processor.d.ts +28 -0
- package/dist/processor.d.ts.map +1 -0
- package/dist/processor.js +118 -0
- package/dist/processor.js.map +1 -0
- package/dist/provider.d.ts +103 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +280 -0
- package/dist/provider.js.map +1 -0
- package/dist/rate-text.d.ts +38 -0
- package/dist/rate-text.d.ts.map +1 -0
- package/dist/rate-text.js +143 -0
- package/dist/rate-text.js.map +1 -0
- package/dist/rate.d.ts +175 -0
- package/dist/rate.d.ts.map +1 -0
- package/dist/rate.js +371 -0
- package/dist/rate.js.map +1 -0
- package/dist/rounding-error.d.ts +9 -0
- package/dist/rounding-error.d.ts.map +1 -0
- package/dist/rounding-error.js +9 -0
- package/dist/rounding-error.js.map +1 -0
- package/dist/rounding.d.ts +50 -0
- package/dist/rounding.d.ts.map +1 -0
- package/dist/rounding.js +87 -0
- package/dist/rounding.js.map +1 -0
- package/dist/schema.d.ts +43 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +77 -0
- package/dist/schema.js.map +1 -0
- package/dist/sql.d.ts +54 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +131 -0
- package/dist/sql.js.map +1 -0
- package/dist/stripe-fx.d.ts +22 -0
- package/dist/stripe-fx.d.ts.map +1 -0
- package/dist/stripe-fx.js +30 -0
- package/dist/stripe-fx.js.map +1 -0
- package/dist/stripe.d.ts +36 -0
- package/dist/stripe.d.ts.map +1 -0
- package/dist/stripe.js +39 -0
- package/dist/stripe.js.map +1 -0
- package/dist/tax.d.ts +33 -0
- package/dist/tax.d.ts.map +1 -0
- package/dist/tax.js +55 -0
- package/dist/tax.js.map +1 -0
- package/dist/testing/arbitraries.d.ts +43 -0
- package/dist/testing/arbitraries.d.ts.map +1 -0
- package/dist/testing/arbitraries.js +98 -0
- package/dist/testing/arbitraries.js.map +1 -0
- package/dist/testing/index.d.ts +67 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +159 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/jest.d.ts +2 -0
- package/dist/testing/jest.d.ts.map +1 -0
- package/dist/testing/jest.js +15 -0
- package/dist/testing/jest.js.map +1 -0
- package/dist/testing/vitest.d.ts +7 -0
- package/dist/testing/vitest.d.ts.map +1 -0
- package/dist/testing/vitest.js +12 -0
- package/dist/testing/vitest.js.map +1 -0
- package/dist/valibot.d.ts +33 -0
- package/dist/valibot.d.ts.map +1 -0
- package/dist/valibot.js +61 -0
- package/dist/valibot.js.map +1 -0
- package/dist/zod.d.ts +36 -0
- package/dist/zod.d.ts.map +1 -0
- package/dist/zod.js +58 -0
- package/dist/zod.js.map +1 -0
- package/package.json +280 -5
- package/src/adyen-fx.ts +175 -0
- package/src/adyen.ts +46 -0
- package/src/allocate.ts +393 -0
- package/src/bag.ts +170 -0
- package/src/boc.ts +136 -0
- package/src/boe.ts +131 -0
- package/src/braintree.ts +71 -0
- package/src/cash.ts +183 -0
- package/src/checkout.ts +77 -0
- package/src/convert.ts +177 -0
- package/src/currency.ts +61 -0
- package/src/decimal.ts +283 -0
- package/src/drizzle/mysql.ts +78 -0
- package/src/drizzle/pg.ts +78 -0
- package/src/drizzle/sqlite.ts +70 -0
- package/src/ecb.ts +328 -0
- package/src/exact.ts +182 -0
- package/src/fed.ts +146 -0
- package/src/fetch.ts +306 -0
- package/src/fixed.ts +558 -0
- package/src/fn.ts +94 -0
- package/src/format.ts +106 -0
- package/src/generated/adyen.ts +144 -0
- package/src/generated/braintree.ts +140 -0
- package/src/generated/cash.ts +478 -0
- package/src/generated/checkout.ts +151 -0
- package/src/generated/historic.ts +67 -0
- package/src/generated/iso4217.ts +179 -0
- package/src/generated/kernel.ts +230 -0
- package/src/generated/klarna.ts +21 -0
- package/src/generated/paypal.ts +31 -0
- package/src/generated/stripe.ts +150 -0
- package/src/historic.ts +77 -0
- package/src/history.ts +333 -0
- package/src/hmrc.ts +125 -0
- package/src/index.ts +41 -0
- package/src/klarna.ts +54 -0
- package/src/kysely.ts +588 -0
- package/src/money.ts +979 -0
- package/src/parse.ts +511 -0
- package/src/paypal.ts +93 -0
- package/src/pg.ts +410 -0
- package/src/plugins/adyen.ts +25 -0
- package/src/plugins/all.ts +57 -0
- package/src/plugins/allocate.ts +53 -0
- package/src/plugins/braintree.ts +23 -0
- package/src/plugins/cash.ts +33 -0
- package/src/plugins/checkout.ts +32 -0
- package/src/plugins/convert.ts +49 -0
- package/src/plugins/exact.ts +23 -0
- package/src/plugins/fixed.ts +28 -0
- package/src/plugins/format.ts +28 -0
- package/src/plugins/klarna.ts +21 -0
- package/src/plugins/paypal.ts +29 -0
- package/src/plugins/prisma.ts +24 -0
- package/src/plugins/stripe.ts +29 -0
- package/src/plugins/tax.ts +40 -0
- package/src/policy.ts +124 -0
- package/src/prisma.ts +199 -0
- package/src/processor.ts +181 -0
- package/src/provider.ts +389 -0
- package/src/rate-text.ts +151 -0
- package/src/rate.ts +546 -0
- package/src/rounding-error.ts +8 -0
- package/src/rounding.ts +122 -0
- package/src/schema.ts +119 -0
- package/src/sql.ts +178 -0
- package/src/stripe-fx.ts +49 -0
- package/src/stripe.ts +69 -0
- package/src/tax.ts +102 -0
- package/src/testing/arbitraries.ts +134 -0
- package/src/testing/index.ts +247 -0
- package/src/testing/jest.ts +18 -0
- package/src/testing/vitest.ts +18 -0
- package/src/valibot.ts +87 -0
- package/src/zod.ts +78 -0
package/dist/policy.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@ducatjs/money/policy`: rounding policies, reusable values for a scale, an increment and a
|
|
3
|
+
* rounding mode ("CHF cash", "4 decimals, half even"), accepted wherever a rounding mode is. The
|
|
4
|
+
* core only hands the work to the policy, so none of this code is in a bundle that never imports
|
|
5
|
+
* this entry (or `cashPolicy` from `@ducatjs/money/cash`).
|
|
6
|
+
*/
|
|
7
|
+
import { currencyExponent, isCurrency } from './currency.js';
|
|
8
|
+
import { MAX_DECIMAL_SCALE } from './decimal.js';
|
|
9
|
+
import { CurrencyMismatchError, multiply, multiplyByRatio, ratio, ROUND_PRODUCT, } from './money.js';
|
|
10
|
+
import { DEFAULT_ROUNDING, Rounding } from './rounding.js';
|
|
11
|
+
const ONE = ratio(1);
|
|
12
|
+
const MODES = new Set(Object.values(Rounding));
|
|
13
|
+
/**
|
|
14
|
+
* Builds a reusable rounding policy (see `RoundingPolicy`). The options are checked here, once, so
|
|
15
|
+
* applying the policy needs no checks.
|
|
16
|
+
*
|
|
17
|
+
* const unitPrice = roundingPolicy({ scale: 4, rounding: 'halfEven' })
|
|
18
|
+
* const fiveCents = roundingPolicy({ increment: 5 }) // 0.05 in a 2-decimal currency
|
|
19
|
+
*/
|
|
20
|
+
export function roundingPolicy(options = {}) {
|
|
21
|
+
const scale = options.scale;
|
|
22
|
+
const increment = options.increment ?? 1;
|
|
23
|
+
const rounding = options.rounding ?? DEFAULT_ROUNDING;
|
|
24
|
+
const currency = options.currency;
|
|
25
|
+
if (scale !== undefined &&
|
|
26
|
+
!(Number.isInteger(scale) && scale >= 0 && scale <= MAX_DECIMAL_SCALE)) {
|
|
27
|
+
throw new RangeError(`Invalid scale ${scale}`);
|
|
28
|
+
}
|
|
29
|
+
if (!(Number.isSafeInteger(increment) && increment >= 1)) {
|
|
30
|
+
throw new RangeError(`Invalid increment ${increment}; it must be a positive integer`);
|
|
31
|
+
}
|
|
32
|
+
if (!MODES.has(rounding)) {
|
|
33
|
+
throw new RangeError(`Invalid rounding mode ${String(rounding)}`);
|
|
34
|
+
}
|
|
35
|
+
// A typo such as 'chf' would otherwise make a policy that rejects every amount.
|
|
36
|
+
if (currency !== undefined && !isCurrency(currency)) {
|
|
37
|
+
throw new RangeError(`Unknown currency ${currency}`);
|
|
38
|
+
}
|
|
39
|
+
const incrementAsBigint = BigInt(increment);
|
|
40
|
+
// money × factor rounded once to the policy: with an increment, count whole increments
|
|
41
|
+
// (money × factor / increment, rounded at the policy's scale) and multiply back, which is
|
|
42
|
+
// rounding to the nearest multiple of the increment. The count's ratio is left unreduced: the
|
|
43
|
+
// division rounds the same either way, and a gcd per call would cost more than it saves.
|
|
44
|
+
const roundProduct = (money, factor) => {
|
|
45
|
+
if (currency !== undefined && currency !== money.currency) {
|
|
46
|
+
throw new CurrencyMismatchError(currency, money.currency);
|
|
47
|
+
}
|
|
48
|
+
const resultScale = scale ?? currencyExponent(money.currency);
|
|
49
|
+
if (increment === 1) {
|
|
50
|
+
return multiplyByRatio(money, factor, rounding, resultScale);
|
|
51
|
+
}
|
|
52
|
+
const stepDenominator = factor.d * increment;
|
|
53
|
+
const perStep = {
|
|
54
|
+
num: factor.num,
|
|
55
|
+
den: factor.den * incrementAsBigint,
|
|
56
|
+
n: factor.n,
|
|
57
|
+
// NaN (not a safe integer) sends multiplyByRatio down its bigint path
|
|
58
|
+
d: Number.isSafeInteger(stepDenominator) ? stepDenominator : NaN,
|
|
59
|
+
};
|
|
60
|
+
return multiply(multiplyByRatio(money, perStep, rounding, resultScale), increment);
|
|
61
|
+
};
|
|
62
|
+
const policy = (money) => roundProduct(money, ONE);
|
|
63
|
+
return Object.freeze(Object.assign(policy, { scale, increment, rounding, currency, [ROUND_PRODUCT]: roundProduct }));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Rounding to the currency's ISO exponent, half up: the policy most amounts should end with.
|
|
67
|
+
* `assertScale(m, isoPolicy)` checks that an amount is already there.
|
|
68
|
+
*/
|
|
69
|
+
export const isoPolicy = /* @__PURE__ */ roundingPolicy();
|
|
70
|
+
/**
|
|
71
|
+
* Throws a RangeError unless `money` is exactly what `policy` produces: at the policy's scale and a
|
|
72
|
+
* multiple of its increment (and in its currency, if it has one). Use it where an amount must
|
|
73
|
+
* already be rounded, such as before booking it or sending it to a processor.
|
|
74
|
+
*/
|
|
75
|
+
export function assertScale(money, policy) {
|
|
76
|
+
if (policy.currency !== undefined && policy.currency !== money.currency) {
|
|
77
|
+
throw new CurrencyMismatchError(policy.currency, money.currency);
|
|
78
|
+
}
|
|
79
|
+
const scale = policy.scale ?? currencyExponent(money.currency);
|
|
80
|
+
if (money.scale !== scale) {
|
|
81
|
+
throw new RangeError(`${money} has scale ${money.scale}, expected ${scale}`);
|
|
82
|
+
}
|
|
83
|
+
const minor = money.minor;
|
|
84
|
+
const increment = policy.increment;
|
|
85
|
+
const isMultiple = typeof minor === 'number' ? minor % increment === 0 : minor % BigInt(increment) === 0n;
|
|
86
|
+
if (!isMultiple) {
|
|
87
|
+
throw new RangeError(`${money} is not a multiple of ${increment} at scale ${scale}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
|
package/dist/prisma.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { type CurrencyInput, type Money, type MoneyOptions } from './money.js';
|
|
2
|
+
/** Structural type of Prisma.Decimal / decimal.js / big.js values. */
|
|
3
|
+
export interface DecimalLike {
|
|
4
|
+
toFixed(): string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Money from a Decimal (or decimal string) column. Exact; throws if it has more decimals than the
|
|
8
|
+
* currency.
|
|
9
|
+
*/
|
|
10
|
+
export declare function fromDecimal<C extends CurrencyInput>(value: DecimalLike | string, currency: C, options?: MoneyOptions): Money<C>;
|
|
11
|
+
/** A Decimal for a NUMERIC column: toDecimalValue(m, Prisma.Decimal). */
|
|
12
|
+
export declare function toDecimalValue<D>(money: Money, Decimal: new (value: string) => D): D;
|
|
13
|
+
type Row<A extends string, V> = {
|
|
14
|
+
[P in A]: V;
|
|
15
|
+
};
|
|
16
|
+
/** Options for `minorMoneyField` and `toMinorColumns`: a column that records each amount's scale. */
|
|
17
|
+
export interface ScaleColumnOption<S extends string> {
|
|
18
|
+
/**
|
|
19
|
+
* The integer column holding the number of decimals the minor units are counted in (2 for
|
|
20
|
+
* cents). With it, any scale round-trips, and stored amounts keep their meaning even if a
|
|
21
|
+
* currency's exponent changes.
|
|
22
|
+
*/
|
|
23
|
+
readonly scale: S;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A Prisma result-extension field that exposes a Money built from `amount_minor` + `currency`
|
|
27
|
+
* columns, and optionally a scale column:
|
|
28
|
+
*
|
|
29
|
+
* const db = new PrismaClient().$extends({
|
|
30
|
+
* result: { order: { total: minorMoneyField('totalMinor', 'currency') } },
|
|
31
|
+
* });
|
|
32
|
+
* (await db.order.findFirstOrThrow()).total // Money
|
|
33
|
+
*
|
|
34
|
+
* Without a scale column the minor units are read at the currency's current exponent.
|
|
35
|
+
*/
|
|
36
|
+
export declare function minorMoneyField<const A extends string, const K extends string>(amount: A, currency: K): {
|
|
37
|
+
needs: {
|
|
38
|
+
[P in A | K]: true;
|
|
39
|
+
};
|
|
40
|
+
compute: (row: Row<A, bigint | number> & Row<K, string>) => Money;
|
|
41
|
+
};
|
|
42
|
+
export declare function minorMoneyField<const A extends string, const K extends string, const S extends string>(amount: A, currency: K, options: ScaleColumnOption<S>): {
|
|
43
|
+
needs: {
|
|
44
|
+
[P in A | K | S]: true;
|
|
45
|
+
};
|
|
46
|
+
compute: (row: Row<A, bigint | number> & Row<K, string> & Row<S, bigint | number>) => Money;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* A Prisma result-extension field for a Decimal column, with the currency taken from another column
|
|
50
|
+
* (`{ currencyField: 'currency' }`) or fixed (`{ currency: 'EUR' }`).
|
|
51
|
+
*/
|
|
52
|
+
export declare function decimalMoneyField<const A extends string, const K extends string>(amount: A, currency: {
|
|
53
|
+
currencyField: K;
|
|
54
|
+
}): {
|
|
55
|
+
needs: {
|
|
56
|
+
[P in A | K]: true;
|
|
57
|
+
};
|
|
58
|
+
compute: (row: Row<A, DecimalLike> & Row<K, string>) => Money;
|
|
59
|
+
};
|
|
60
|
+
export declare function decimalMoneyField<const A extends string, const C extends CurrencyInput>(amount: A, currency: {
|
|
61
|
+
currency: C;
|
|
62
|
+
}): {
|
|
63
|
+
needs: {
|
|
64
|
+
[P in A]: true;
|
|
65
|
+
};
|
|
66
|
+
compute: (row: Row<A, DecimalLike>) => Money<C>;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Column values for writes: `data: { ...toMinorColumns(total, 'totalMinor', 'currency') }`.
|
|
70
|
+
* Minor units are a bigint (Prisma BigInt). Without a scale column, throws if the value has a
|
|
71
|
+
* non-standard scale, because the columns cannot record it. With `{ scale: 'amountScale' }`, any
|
|
72
|
+
* scale is stored, next to the minor units it counts.
|
|
73
|
+
*/
|
|
74
|
+
export declare function toMinorColumns<const A extends string, const K extends string>(money: Money, amount: A, currency: K): Row<A, bigint> & Row<K, string>;
|
|
75
|
+
export declare function toMinorColumns<const A extends string, const K extends string, const S extends string>(money: Money, amount: A, currency: K, options: ScaleColumnOption<S>): Row<A, bigint> & Row<K, string> & Row<S, number>;
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=prisma.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../src/prisma.ts"],"names":[],"mappings":"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"}
|
package/dist/prisma.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prisma / SQL interop with no dependency on @prisma/client (works with any Decimal class that has
|
|
3
|
+
* toFixed(): Prisma.Decimal, decimal.js, big.js).
|
|
4
|
+
*
|
|
5
|
+
* Storage options (see README "Databases"):
|
|
6
|
+
* 1. two columns: amount_minor BIGINT + currency CHAR(3)
|
|
7
|
+
* -> minorMoneyField / toMinorColumns
|
|
8
|
+
* 2. a DECIMAL/NUMERIC column (+ a currency column or constant)
|
|
9
|
+
* -> decimalMoneyField / toDecimalValue
|
|
10
|
+
*/
|
|
11
|
+
import { MAX_DECIMAL_SCALE } from './decimal.js';
|
|
12
|
+
import { fromMinor, money, toDecimal, toMinor, } from './money.js';
|
|
13
|
+
/**
|
|
14
|
+
* Money from a Decimal (or decimal string) column. Exact; throws if it has more decimals than the
|
|
15
|
+
* currency.
|
|
16
|
+
*/
|
|
17
|
+
export function fromDecimal(value, currency, options) {
|
|
18
|
+
return money(typeof value === 'string' ? value : value.toFixed(), currency, options);
|
|
19
|
+
}
|
|
20
|
+
/** A Decimal for a NUMERIC column: toDecimalValue(m, Prisma.Decimal). */
|
|
21
|
+
export function toDecimalValue(money, Decimal) {
|
|
22
|
+
return new Decimal(toDecimal(money));
|
|
23
|
+
}
|
|
24
|
+
/** A scale read from a row: a safe, non-negative integer (a SMALLINT or INT column). */
|
|
25
|
+
function readScale(value, column) {
|
|
26
|
+
const scale = typeof value === 'bigint' ? Number(value) : value;
|
|
27
|
+
// capped like parsed decimals: a corrupt row must not make toDecimal build a huge power of ten
|
|
28
|
+
const isValid = typeof scale === 'number' &&
|
|
29
|
+
Number.isInteger(scale) &&
|
|
30
|
+
scale >= 0 &&
|
|
31
|
+
scale <= MAX_DECIMAL_SCALE;
|
|
32
|
+
if (!isValid) {
|
|
33
|
+
throw new RangeError(`Expected an integer scale from 0 to ${MAX_DECIMAL_SCALE} in "${column}", got ${String(value)}`);
|
|
34
|
+
}
|
|
35
|
+
return scale;
|
|
36
|
+
}
|
|
37
|
+
export function minorMoneyField(amount, currency, options) {
|
|
38
|
+
if (options === undefined) {
|
|
39
|
+
return {
|
|
40
|
+
needs: { [amount]: true, [currency]: true },
|
|
41
|
+
compute: (row) => fromMinor(row[amount], row[currency]),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const scaleColumn = options.scale;
|
|
45
|
+
return {
|
|
46
|
+
needs: { [amount]: true, [currency]: true, [scaleColumn]: true },
|
|
47
|
+
compute: (row) => fromMinor(row[amount], row[currency], readScale(row[scaleColumn], scaleColumn)),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
51
|
+
export function decimalMoneyField(amount, currencySource) {
|
|
52
|
+
if ('currencyField' in currencySource) {
|
|
53
|
+
return {
|
|
54
|
+
needs: { [amount]: true, [currencySource.currencyField]: true },
|
|
55
|
+
compute: (row) => fromDecimal(row[amount], row[currencySource.currencyField]),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
needs: { [amount]: true },
|
|
60
|
+
compute: (row) => fromDecimal(row[amount], currencySource.currency),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export function toMinorColumns(money, amount, currency, options) {
|
|
64
|
+
if (options === undefined) {
|
|
65
|
+
return { [amount]: toMinorAtIsoScale(money), [currency]: money.currency };
|
|
66
|
+
}
|
|
67
|
+
return { [amount]: toMinor(money), [currency]: money.currency, [options.scale]: money.scale };
|
|
68
|
+
}
|
|
69
|
+
function toMinorAtIsoScale(money) {
|
|
70
|
+
const iso = fromMinor(0, money.currency).scale;
|
|
71
|
+
if (money.scale !== iso) {
|
|
72
|
+
throw new RangeError(`${money} has scale ${money.scale}; round() it to ${iso} decimals before storing minor units`);
|
|
73
|
+
}
|
|
74
|
+
return toMinor(money);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=prisma.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.js","sourceRoot":"","sources":["../src/prisma.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type CurrencyInput, type Money } from './money.js';
|
|
2
|
+
import { type RoundingMode } from './rounding.js';
|
|
3
|
+
/**
|
|
4
|
+
* Which signs a processor field accepts, checked after rounding:
|
|
5
|
+
* - 'any': negative amounts are documented (Klarna's discount order lines);
|
|
6
|
+
* - 'nonNegative': zero is documented but negatives aren't (refunds are sent as positive amounts);
|
|
7
|
+
* - 'positive': the processor rejects zero too (Braintree's transaction amount).
|
|
8
|
+
*/
|
|
9
|
+
export type SignRule = 'any' | 'nonNegative' | 'positive';
|
|
10
|
+
/**
|
|
11
|
+
* @internal Money -> a processor's integer amount at the processor's exponent.
|
|
12
|
+
* Throws a RangeError when the amount doesn't fit in a safe integer or has a sign `sign` rules out.
|
|
13
|
+
*/
|
|
14
|
+
export declare function toProcessorAmount(processor: string, money: Money, exponent: number, multiple: number, rounding: RoundingMode, sign?: SignRule): number;
|
|
15
|
+
/**
|
|
16
|
+
* @internal Money -> a processor's decimal string with exactly `exponent` decimals
|
|
17
|
+
* ("10.00", or "1000" for JPY), built from the integer so it never goes through a float.
|
|
18
|
+
*/
|
|
19
|
+
export declare function toProcessorDecimal(processor: string, money: Money, exponent: number, rounding: RoundingMode, sign?: SignRule): string;
|
|
20
|
+
/** @internal A processor's integer amount -> Money (see `toIsoScaleWhenExact` for the scale). */
|
|
21
|
+
export declare function fromProcessorAmount<C extends CurrencyInput>(processor: string, amount: number | bigint, currency: C, exponent: number): Money<C>;
|
|
22
|
+
/**
|
|
23
|
+
* @internal A processor's decimal string -> Money, exactly (never via a float). `pattern` is the
|
|
24
|
+
* processor's documented format and `maxLength` its length limit, if any. The scale follows
|
|
25
|
+
* `toIsoScaleWhenExact`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function fromProcessorDecimal<C extends CurrencyInput>(processor: string, value: string, currency: C, pattern: RegExp, maxLength?: number): Money<C>;
|
|
28
|
+
//# sourceMappingURL=processor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processor.d.ts","sourceRoot":"","sources":["../src/processor.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0C,KAAK,aAAa,EAAE,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AACpG,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAEvB;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,aAAa,GAAG,UAAU,CAAC;AAuE1D;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,YAAY,EACtB,IAAI,GAAE,QAAgB,GACrB,MAAM,CASR;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,YAAY,EACtB,IAAI,GAAE,QAAgB,GACrB,MAAM,CAMR;AAmBD,iGAAiG;AACjG,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,aAAa,EACzD,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,QAAQ,EAAE,CAAC,EACX,QAAQ,EAAE,MAAM,GACf,KAAK,CAAC,CAAC,CAAC,CAMV;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,aAAa,EAC1D,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,MAAM,EACf,SAAS,SAAW,GACnB,KAAK,CAAC,CAAC,CAAC,CAWV"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { currencyExponent } from './currency.js';
|
|
2
|
+
import { formatScaled, parseDecimal, parsePlainDecimal, normalizeMinor } from './decimal.js';
|
|
3
|
+
import { fromMinor, scaleDown, scaleUp, toScale } from './money.js';
|
|
4
|
+
import { divideAndRound, divideAndRoundBigint, RoundingError, } from './rounding.js';
|
|
5
|
+
/**
|
|
6
|
+
* Rescales Money's minor units to a processor's exponent, then to a multiple of `multiple` minor
|
|
7
|
+
* units (Checkout.com's CLP, Stripe's ISK). Throws a RoundingError naming the processor when
|
|
8
|
+
* precision would be lost and `rounding` is 'unnecessary'.
|
|
9
|
+
*/
|
|
10
|
+
function rescaleForProcessor(processor, money, exponent, multiple, rounding) {
|
|
11
|
+
// Going up in scale is always exact; going down (e.g. IDR on Adyen) may need rounding.
|
|
12
|
+
let minor;
|
|
13
|
+
try {
|
|
14
|
+
if (exponent >= money.scale) {
|
|
15
|
+
minor = scaleUp(money.minor, exponent - money.scale);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
minor = scaleDown(money.minor, money.scale - exponent, rounding);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error instanceof RoundingError) {
|
|
23
|
+
throw new RoundingError(`${money} has more precision than ${processor} accepts for ${money.currency} ` +
|
|
24
|
+
`(${exponent} decimals); pass a rounding mode`);
|
|
25
|
+
}
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
if (multiple === 1) {
|
|
29
|
+
return minor;
|
|
30
|
+
}
|
|
31
|
+
const isMultiple = typeof minor === 'number' ? minor % multiple === 0 : minor % BigInt(multiple) === 0n;
|
|
32
|
+
if (isMultiple) {
|
|
33
|
+
return minor;
|
|
34
|
+
}
|
|
35
|
+
if (rounding === 'unnecessary') {
|
|
36
|
+
throw new RoundingError(`${processor} requires ${money.currency} amounts to be a multiple of ${multiple} minor units; ` +
|
|
37
|
+
`${money} is not (pass a rounding mode)`);
|
|
38
|
+
}
|
|
39
|
+
if (typeof minor === 'number') {
|
|
40
|
+
return divideAndRound(minor, multiple, rounding) * multiple;
|
|
41
|
+
}
|
|
42
|
+
const bigMultiple = BigInt(multiple);
|
|
43
|
+
return normalizeMinor(divideAndRoundBigint(minor, bigMultiple, rounding) * bigMultiple);
|
|
44
|
+
}
|
|
45
|
+
/** Throws a RangeError when the rounded amount has a sign the processor doesn't accept. */
|
|
46
|
+
function checkSign(processor, money, minor, sign) {
|
|
47
|
+
// Checked on the rounded amount, so -0.004 USD rounded to 0 counts as zero, not as negative.
|
|
48
|
+
if (sign === 'any') {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (minor < 0) {
|
|
52
|
+
throw new RangeError(`${processor} amounts can't be negative (${money}); send refunds as positive amounts`);
|
|
53
|
+
}
|
|
54
|
+
if (sign === 'positive' && (minor === 0 || minor === 0n)) {
|
|
55
|
+
throw new RangeError(`${processor} amounts must be greater than zero (${money})`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* @internal Money -> a processor's integer amount at the processor's exponent.
|
|
60
|
+
* Throws a RangeError when the amount doesn't fit in a safe integer or has a sign `sign` rules out.
|
|
61
|
+
*/
|
|
62
|
+
export function toProcessorAmount(processor, money, exponent, multiple, rounding, sign = 'any') {
|
|
63
|
+
const minor = rescaleForProcessor(processor, money, exponent, multiple, rounding);
|
|
64
|
+
if (typeof minor !== 'number') {
|
|
65
|
+
throw new RangeError(`${money} is too large for ${processor}`);
|
|
66
|
+
}
|
|
67
|
+
checkSign(processor, money, minor, sign);
|
|
68
|
+
// `+ 0` turns -0 into 0, so a zero amount never serialises as "-0".
|
|
69
|
+
return minor + 0;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @internal Money -> a processor's decimal string with exactly `exponent` decimals
|
|
73
|
+
* ("10.00", or "1000" for JPY), built from the integer so it never goes through a float.
|
|
74
|
+
*/
|
|
75
|
+
export function toProcessorDecimal(processor, money, exponent, rounding, sign = 'any') {
|
|
76
|
+
const minor = rescaleForProcessor(processor, money, exponent, 1, rounding);
|
|
77
|
+
checkSign(processor, money, minor, sign);
|
|
78
|
+
// `+ 0` turns -0 into 0, so a zero amount never serialises as "-0".
|
|
79
|
+
return formatScaled(typeof minor === 'number' ? minor + 0 : minor, exponent);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Returns Money at the currency's ISO scale when that is exact, and otherwise at the scale it
|
|
83
|
+
* came with, so a processor's extra precision (Stripe's ISK 12.34) is kept rather than lost.
|
|
84
|
+
*/
|
|
85
|
+
function toIsoScaleWhenExact(money, currency) {
|
|
86
|
+
const isoExponent = currencyExponent(currency);
|
|
87
|
+
if (isoExponent >= money.scale) {
|
|
88
|
+
return toScale(money, isoExponent);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
return toScale(money, isoExponent, 'unnecessary');
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return money;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** @internal A processor's integer amount -> Money (see `toIsoScaleWhenExact` for the scale). */
|
|
98
|
+
export function fromProcessorAmount(processor, amount, currency, exponent) {
|
|
99
|
+
if (typeof amount === 'number' && !Number.isSafeInteger(amount)) {
|
|
100
|
+
throw new RangeError(`${processor} amount must be an integer, got ${amount}`);
|
|
101
|
+
}
|
|
102
|
+
return toIsoScaleWhenExact(fromMinor(amount, currency, exponent), currency);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* @internal A processor's decimal string -> Money, exactly (never via a float). `pattern` is the
|
|
106
|
+
* processor's documented format and `maxLength` its length limit, if any. The scale follows
|
|
107
|
+
* `toIsoScaleWhenExact`.
|
|
108
|
+
*/
|
|
109
|
+
export function fromProcessorDecimal(processor, value, currency, pattern, maxLength = Infinity) {
|
|
110
|
+
if (typeof value !== 'string' || value.length > maxLength || !pattern.test(value)) {
|
|
111
|
+
throw new SyntaxError(`${processor} amount must be a decimal string, got ${JSON.stringify(value)}`);
|
|
112
|
+
}
|
|
113
|
+
// PayPal's pattern allows a bare fraction (".5"), which the decimal parsers don't.
|
|
114
|
+
const withLeadingZero = value.replace(/^(-?)\./, '$10.');
|
|
115
|
+
const parsed = parsePlainDecimal(withLeadingZero) ?? parseDecimal(withLeadingZero);
|
|
116
|
+
return toIsoScaleWhenExact(fromMinor(parsed.digits, currency, parsed.scale), currency);
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processor.js","sourceRoot":"","sources":["../src/processor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { Factor } from './decimal.js';
|
|
2
|
+
import { type Rate, type RateInfo } from './rate.js';
|
|
3
|
+
/** When a rate is wanted for. */
|
|
4
|
+
export interface RateQuery {
|
|
5
|
+
/** An ISO-8601 date or instant, or a Date. */
|
|
6
|
+
readonly asOf?: string | Date;
|
|
7
|
+
}
|
|
8
|
+
/** Looks up a rate synchronously. `undefined` when it has none for the pair (or for `asOf`). */
|
|
9
|
+
export interface RateProvider {
|
|
10
|
+
rate<From extends string, To extends string>(from: From, to: To, query?: RateQuery): Rate<From, To> | undefined;
|
|
11
|
+
}
|
|
12
|
+
/** A fixed set of rates, as built by rateTable() and returned by the ECB parsers and fetchers. */
|
|
13
|
+
export interface RateTable extends RateProvider {
|
|
14
|
+
readonly rates: readonly Rate[];
|
|
15
|
+
/** The quoted currency, for a one-base table. */
|
|
16
|
+
readonly base?: string;
|
|
17
|
+
/** The table's date, when it has one. */
|
|
18
|
+
readonly asOf?: string;
|
|
19
|
+
/** When the table was published, when that is known. */
|
|
20
|
+
readonly availableAt?: string;
|
|
21
|
+
/** The last day (or instant) the table applies to, for a table valid for a period. */
|
|
22
|
+
readonly validUntil?: string;
|
|
23
|
+
/** Currencies the source listed without a value (holidays, `ND`, `N/A`). */
|
|
24
|
+
readonly missing?: readonly string[];
|
|
25
|
+
}
|
|
26
|
+
/** Thrown when no provider has a rate for the pair. */
|
|
27
|
+
export declare class RateUnavailableError extends Error {
|
|
28
|
+
readonly from: string;
|
|
29
|
+
readonly to: string;
|
|
30
|
+
readonly asOf: string | undefined;
|
|
31
|
+
constructor(from: string, to: string, asOf?: string | Date);
|
|
32
|
+
}
|
|
33
|
+
/** The rate for the pair, or RateUnavailableError: a lookup that must succeed. */
|
|
34
|
+
export declare function rateOf<From extends string, To extends string>(provider: RateProvider, from: From, to: To, query?: RateQuery): Rate<From, To>;
|
|
35
|
+
/** 1:1 for a currency to itself; nothing otherwise. */
|
|
36
|
+
export declare function identity(): RateProvider;
|
|
37
|
+
/**
|
|
38
|
+
* A one-base table: `rates.USD` is the number of USD per 1 `base` (or a base → USD Rate), or null
|
|
39
|
+
* when the source has no value for the day. For rates published the other way round or per 100
|
|
40
|
+
* units, see `quotedTable` in `@ducatjs/money/convert/history`.
|
|
41
|
+
*/
|
|
42
|
+
export interface RateTableSpec extends Omit<RateInfo, 'quote'> {
|
|
43
|
+
readonly base: string;
|
|
44
|
+
readonly rates: Readonly<Record<string, Factor | Rate | null>>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A fixed table of rates: one base currency against many (ECB style), or a list of Rates. Only the
|
|
48
|
+
* pairs given are answered; wrap it in `reversed()` or `via()` for the others.
|
|
49
|
+
*
|
|
50
|
+
* A query with an `asOf` gets nothing from a rate that doesn't apply then: one dated after it, one
|
|
51
|
+
* published (`availableAt`) after it, or one whose `validUntil` has passed. Days are UTC days, and
|
|
52
|
+
* a date query covers the whole day: without an `availableAt`, a query at any time on the rate's
|
|
53
|
+
* own day gets the rate, even before the source published it (the ECB publishes at about 15:00
|
|
54
|
+
* UTC). Give `availableAt` to rule that out.
|
|
55
|
+
*
|
|
56
|
+
* const ecb = rateTable({ base: 'EUR', source: 'ECB', asOf: '2026-11-06', rates: { USD: '1.1165' } });
|
|
57
|
+
*/
|
|
58
|
+
export declare function rateTable(spec: RateTableSpec | readonly Rate[]): RateTable;
|
|
59
|
+
/** Asks each provider in turn; the first with an answer wins. */
|
|
60
|
+
export declare function chain(...providers: readonly RateProvider[]): RateProvider;
|
|
61
|
+
/** Answers to→from lookups with the exact inverse of the provider's from→to rate. */
|
|
62
|
+
export declare function reversed(provider: RateProvider): RateProvider;
|
|
63
|
+
/**
|
|
64
|
+
* Triangulates through `base`: from→base, then base→to, composed exactly (converting rounds once).
|
|
65
|
+
* Each leg is looked up directly or as an exact inverse, so an ECB table (EUR→X only) answers X→Y.
|
|
66
|
+
*/
|
|
67
|
+
export declare function via(base: string, provider: RateProvider): RateProvider;
|
|
68
|
+
/** Options for snapshots(). */
|
|
69
|
+
export interface SnapshotOptions {
|
|
70
|
+
/**
|
|
71
|
+
* How many earlier days to try when a day has no table (weekends, holidays). Default 4, which
|
|
72
|
+
* covers Good Friday to Easter Monday.
|
|
73
|
+
*/
|
|
74
|
+
readonly lookback?: number;
|
|
75
|
+
/** How many loaded days to keep; the least recently used are dropped. Default 32. */
|
|
76
|
+
readonly max?: number;
|
|
77
|
+
}
|
|
78
|
+
/** Dated tables, loaded on demand. */
|
|
79
|
+
export interface Snapshots {
|
|
80
|
+
/** The latest table at or before `asOf` (a date, an instant or a Date), within the lookback. */
|
|
81
|
+
at(asOf: string | Date): Promise<RateProvider>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Dated tables from a loader you supply: `load('2026-11-06')` resolves to that day's table, or to
|
|
85
|
+
* undefined when there is none. Days are UTC days. Loaded tables are cached by day. Misses and
|
|
86
|
+
* failures are not cached, so a table that isn't published yet is tried again next time. A loader
|
|
87
|
+
* that throws fails the lookup rather than falling back to an older day, so an outage doesn't
|
|
88
|
+
* silently become a stale rate. The rates carry the day they're from. A table with an
|
|
89
|
+
* `availableAt` after an instant asked for is passed over, so a lookup at 09:00 on a publication
|
|
90
|
+
* day gets the day before.
|
|
91
|
+
*
|
|
92
|
+
* const history = snapshots(async (day) => parseEcbXml(await readFile(`ecb/${day}.xml`, 'utf8'))[0]);
|
|
93
|
+
* const fx = await history.at('2026-11-08'); // a Sunday: Friday 2026-11-06's table
|
|
94
|
+
*/
|
|
95
|
+
export declare function snapshots(load: (day: string) => Promise<RateProvider | undefined>, options?: SnapshotOptions): Snapshots;
|
|
96
|
+
/**
|
|
97
|
+
* Snapshots over tables you already hold (e.g. an ECB 90-day history), keyed by their asOf day.
|
|
98
|
+
*
|
|
99
|
+
* const history = snapshotsFrom(parseEcbXml(xml90d));
|
|
100
|
+
* const fx = await history.at('2026-11-08');
|
|
101
|
+
*/
|
|
102
|
+
export declare function snapshotsFrom(tables: readonly RateTable[], options?: SnapshotOptions): Snapshots;
|
|
103
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,EAML,KAAK,IAAI,EACT,KAAK,QAAQ,EACd,MAAM,WAAW,CAAC;AAInB,iCAAiC;AACjC,MAAM,WAAW,SAAS;IACxB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,gGAAgG;AAChG,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,IAAI,SAAS,MAAM,EAAE,EAAE,SAAS,MAAM,EACzC,IAAI,EAAE,IAAI,EACV,EAAE,EAAE,EAAE,EACN,KAAK,CAAC,EAAE,SAAS,GAChB,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;CAC/B;AAED,kGAAkG;AAClG,MAAM,WAAW,SAAU,SAAQ,YAAY;IAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;IAChC,iDAAiD;IACjD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,sFAAsF;IACtF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED,uDAAuD;AACvD,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;gBAEtB,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;CAe3D;AAOD,kFAAkF;AAClF,wBAAgB,MAAM,CAAC,IAAI,SAAS,MAAM,EAAE,EAAE,SAAS,MAAM,EAC3D,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE,IAAI,EACV,EAAE,EAAE,EAAE,EACN,KAAK,CAAC,EAAE,SAAS,GAChB,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAehB;AAED,uDAAuD;AACvD,wBAAgB,QAAQ,IAAI,YAAY,CAUvC;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC;CAChE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,IAAI,EAAE,GAAG,SAAS,CAgE1E;AAoBD,iEAAiE;AACjE,wBAAgB,KAAK,CAAC,GAAG,SAAS,EAAE,SAAS,YAAY,EAAE,GAAG,YAAY,CAYzE;AAED,qFAAqF;AACrF,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,YAAY,CAM7D;AAED;;;GAGG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,YAAY,CA6BtE;AAED,+BAA+B;AAC/B,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,qFAAqF;IACrF,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,sCAAsC;AACtC,MAAM,WAAW,SAAS;IACxB,gGAAgG;IAChG,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAChD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,EACxD,OAAO,CAAC,EAAE,eAAe,GACxB,SAAS,CAgEX;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAQhG"}
|