@vielzeug/coins 26.11.5 → 26.11.6

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/_decimal.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CoinsError as e } from "./errors.js";
2
- //#region src/_decimal.ts
2
+ // src/_decimal.ts
3
3
  var t = /^(-?)(\d+)(?:\.(\d+))?$/, n = 1e3, r = 100, i = /* @__PURE__ */ new Set([
4
4
  "awayFromZero",
5
5
  "ceil",
@@ -58,7 +58,6 @@ function gcd(e, t) {
58
58
  function lcm(e, t) {
59
59
  return e / gcd(e, t) * t;
60
60
  }
61
- //#endregion
62
61
  export { assertRoundingMode, decimal, gcd, lcm, roundDivision, toDecimalString };
63
62
 
64
63
  //# sourceMappingURL=_decimal.js.map
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./_decimal.cjs"),n=require("./currency.cjs"),r=require("./money.cjs");var i=1e5;function sum(t,i){if(i&&!n.isCurrency(i.currency))throw new e.CoinsError(`INVALID_CURRENCY`,`sum() requires a canonical currency`);let a=0n,o=i?.currency;for(let n of t){if(r.assertMoney(n),o===void 0)o=n.currency;else if(n.currency!==o)throw new e.CurrencyMismatchError(o.code,n.currency.code);a+=n.amount}if(o===void 0)throw new e.CoinsError(`INVALID_MONEY`,`sum() of empty iterable requires { currency }`);return r.createMoney(a,o)}function allocate(n,a){if(r.assertMoney(n),typeof a==`number`)return allocateEvenly(n,a);if(!Array.isArray(a))throw new e.CoinsError(`INVALID_ALLOCATION`,`Allocation weights must be an array`);if(a.length===0)throw new e.CoinsError(`INVALID_ALLOCATION`,`Allocation weights cannot be empty`);if(a.length>i)throw new e.CoinsError(`INVALID_ALLOCATION`,`Allocation cannot exceed ${i} parts`);for(let t=0;t<a.length;t++)if(!Object.hasOwn(a,t))throw new e.CoinsError(`INVALID_ALLOCATION`,`Allocation weights cannot contain empty slots`);let o=a.map(t.decimal);if(o.some(e=>e.numerator<0n))throw new e.CoinsError(`INVALID_ALLOCATION`,`Allocation weights cannot be negative`);let s=o.reduce((e,n)=>t.lcm(e,n.denominator),1n),c=o.map(e=>e.numerator*(s/e.denominator)),l=c.reduce((e,t)=>e+t,0n);if(l===0n)throw new e.CoinsError(`INVALID_ALLOCATION`,`Allocation weights must include a positive value`);let u=n.amount<0n?-1n:1n,d=n.amount<0n?-n.amount:n.amount,f=c.map(e=>d*e/l),p=d-f.reduce((e,t)=>e+t,0n),m=c.map((e,t)=>({index:t,remainder:d*e%l})).sort((e,t)=>e.remainder===t.remainder?e.index-t.index:e.remainder>t.remainder?-1:1);for(let e of m){if(p===0n)break;f[e.index]+=1n,p-=1n}return f.map(e=>r.createMoney(e*u,n.currency))}function allocateEvenly(t,n){if(!Number.isSafeInteger(n)||n<1||n>i)throw new e.CoinsError(`INVALID_ALLOCATION`,`Allocation count must be a positive safe integer no greater than ${i}`);let a=BigInt(n),o=t.amount<0n?-1n:1n,s=t.amount<0n?-t.amount:t.amount,c=s/a,l=s%a;return Array.from({length:n},(e,n)=>r.createMoney((c+(BigInt(n)<l?1n:0n))*o,t.currency))}exports.allocate=allocate,exports.sum=sum;
1
+ const e=require("./_decimal.cjs"),t=require("./currency.cjs"),n=require("./errors.cjs"),r=require("./money.cjs");var i=1e5;function sum(e,i){if(i&&!t.isCurrency(i.currency))throw new n.CoinsError(`INVALID_CURRENCY`,`sum() requires a canonical currency`);let a=0n,o=i?.currency;for(let t of e){if(r.assertMoney(t),o===void 0)o=t.currency;else if(t.currency!==o)throw new n.CurrencyMismatchError(o.code,t.currency.code);a+=t.amount}if(o===void 0)throw new n.CoinsError(`INVALID_MONEY`,`sum() of empty iterable requires { currency }`);return r.createMoney(a,o)}function allocate(t,a){if(r.assertMoney(t),typeof a==`number`)return allocateEvenly(t,a);if(!Array.isArray(a))throw new n.CoinsError(`INVALID_ALLOCATION`,`Allocation weights must be an array`);if(a.length===0)throw new n.CoinsError(`INVALID_ALLOCATION`,`Allocation weights cannot be empty`);if(a.length>i)throw new n.CoinsError(`INVALID_ALLOCATION`,`Allocation cannot exceed ${i} parts`);for(let e=0;e<a.length;e++)if(!Object.hasOwn(a,e))throw new n.CoinsError(`INVALID_ALLOCATION`,`Allocation weights cannot contain empty slots`);let o=a.map(e.decimal);if(o.some(e=>e.numerator<0n))throw new n.CoinsError(`INVALID_ALLOCATION`,`Allocation weights cannot be negative`);let s=o.reduce((t,n)=>e.lcm(t,n.denominator),1n),c=o.map(e=>e.numerator*(s/e.denominator)),l=c.reduce((e,t)=>e+t,0n);if(l===0n)throw new n.CoinsError(`INVALID_ALLOCATION`,`Allocation weights must include a positive value`);let u=t.amount<0n?-1n:1n,d=t.amount<0n?-t.amount:t.amount,f=c.map(e=>d*e/l),p=d-f.reduce((e,t)=>e+t,0n),m=c.map((e,t)=>({index:t,remainder:d*e%l})).sort((e,t)=>e.remainder===t.remainder?e.index-t.index:e.remainder>t.remainder?-1:1);for(let e of m){if(p===0n)break;f[e.index]+=1n,p-=1n}return f.map(e=>r.createMoney(e*u,t.currency))}function allocateEvenly(e,t){if(!Number.isSafeInteger(t)||t<1||t>i)throw new n.CoinsError(`INVALID_ALLOCATION`,`Allocation count must be a positive safe integer no greater than ${i}`);let a=BigInt(t),o=e.amount<0n?-1n:1n,s=e.amount<0n?-e.amount:e.amount,c=s/a,l=s%a;return Array.from({length:t},(t,n)=>r.createMoney((c+(BigInt(n)<l?1n:0n))*o,e.currency))}exports.allocate=allocate,exports.sum=sum;
2
2
  //# sourceMappingURL=aggregate.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate.cjs","names":["isCurrency","CoinsError","CurrencyMismatchError","createMoney","decimal","lcm","require_errors","require__decimal","require_currency","require_money","MAX_ALLOCATION_PARTS","options","amount","currency","value","values","weightsOrCount","index","weights","weight","commonDenominator","result","scaledWeights","totalWeight","sign","absolute","shares","remainder","ranked","left","right","entry","countValue","count","base"],"sources":["../src/aggregate.ts"],"sourcesContent":["import { decimal, lcm } from './_decimal';\nimport { isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { assertMoney, createMoney } from './money';\nimport type { Currency, Money } from './types';\n\nconst MAX_ALLOCATION_PARTS = 100_000;\n\nexport function sum<C extends Currency>(values: readonly Money<C>[]): Money<C>;\nexport function sum<C extends Currency>(values: Iterable<Money<C>>, options: { currency: C }): Money<C>;\nexport function sum<C extends Currency>(values: Iterable<Money<C>>, options?: { currency: C }): Money<C> {\n if (options && !isCurrency(options.currency)) {\n throw new CoinsError('INVALID_CURRENCY', 'sum() requires a canonical currency');\n }\n\n let amount = 0n;\n let currency: C | undefined = options?.currency;\n\n for (const value of values) {\n assertMoney(value);\n\n if (currency === undefined) {\n currency = value.currency;\n } else if (value.currency !== currency) {\n throw new CurrencyMismatchError(currency.code, value.currency.code);\n }\n\n amount += value.amount;\n }\n\n if (currency === undefined) throw new CoinsError('INVALID_MONEY', 'sum() of empty iterable requires { currency }');\n\n return createMoney(amount, currency);\n}\n\nexport function allocate<C extends Currency>(value: Money<C>, count: number): Money<C>[];\nexport function allocate<C extends Currency>(value: Money<C>, weights: readonly string[]): Money<C>[];\nexport function allocate<C extends Currency>(value: Money<C>, weightsOrCount: number | readonly string[]): Money<C>[] {\n assertMoney(value);\n\n if (typeof weightsOrCount === 'number') return allocateEvenly(value, weightsOrCount);\n\n if (!Array.isArray(weightsOrCount)) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights must be an array');\n }\n if (weightsOrCount.length === 0) throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot be empty');\n if (weightsOrCount.length > MAX_ALLOCATION_PARTS) {\n throw new CoinsError('INVALID_ALLOCATION', `Allocation cannot exceed ${MAX_ALLOCATION_PARTS} parts`);\n }\n for (let index = 0; index < weightsOrCount.length; index++) {\n if (!Object.hasOwn(weightsOrCount, index)) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot contain empty slots');\n }\n }\n\n const weights = weightsOrCount.map(decimal);\n\n if (weights.some((weight) => weight.numerator < 0n)) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot be negative');\n }\n\n const commonDenominator = weights.reduce((result, weight) => lcm(result, weight.denominator), 1n);\n const scaledWeights = weights.map((weight) => weight.numerator * (commonDenominator / weight.denominator));\n const totalWeight = scaledWeights.reduce((result, weight) => result + weight, 0n);\n\n if (totalWeight === 0n)\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights must include a positive value');\n\n const sign = value.amount < 0n ? -1n : 1n;\n const absolute = value.amount < 0n ? -value.amount : value.amount;\n const shares = scaledWeights.map((weight) => (absolute * weight) / totalWeight);\n let remainder = absolute - shares.reduce((result, amount) => result + amount, 0n);\n const ranked = scaledWeights\n .map((weight, index) => ({ index, remainder: (absolute * weight) % totalWeight }))\n .sort((left, right) =>\n left.remainder === right.remainder ? left.index - right.index : left.remainder > right.remainder ? -1 : 1,\n );\n\n for (const entry of ranked) {\n if (remainder === 0n) break;\n\n shares[entry.index]! += 1n;\n remainder -= 1n;\n }\n\n return shares.map((amount) => createMoney(amount * sign, value.currency));\n}\n\nfunction allocateEvenly<C extends Currency>(value: Money<C>, countValue: number): Money<C>[] {\n if (!Number.isSafeInteger(countValue) || countValue < 1 || countValue > MAX_ALLOCATION_PARTS) {\n throw new CoinsError(\n 'INVALID_ALLOCATION',\n `Allocation count must be a positive safe integer no greater than ${MAX_ALLOCATION_PARTS}`,\n );\n }\n\n const count = BigInt(countValue);\n const sign = value.amount < 0n ? -1n : 1n;\n const absolute = value.amount < 0n ? -value.amount : value.amount;\n const base = absolute / count;\n const remainder = absolute % count;\n\n return Array.from({ length: countValue }, (_, index) =>\n createMoney((base + (BigInt(index) < remainder ? 1n : 0n)) * sign, value.currency),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"aggregate.cjs","names":["isCurrency","CoinsError","CurrencyMismatchError","createMoney","decimal","lcm","require__decimal","require_currency","require_errors","require_money","MAX_ALLOCATION_PARTS","options","amount","currency","value","values","weightsOrCount","index","weights","weight","commonDenominator","result","scaledWeights","totalWeight","sign","absolute","shares","remainder","ranked","left","right","entry","countValue","count","base"],"sources":["../src/aggregate.ts"],"sourcesContent":["import { decimal, lcm } from './_decimal';\nimport { isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { assertMoney, createMoney } from './money';\nimport type { Currency, Money } from './types';\n\nconst MAX_ALLOCATION_PARTS = 100_000;\n\nexport function sum<C extends Currency>(values: readonly Money<C>[]): Money<C>;\nexport function sum<C extends Currency>(values: Iterable<Money<C>>, options: { currency: C }): Money<C>;\nexport function sum<C extends Currency>(values: Iterable<Money<C>>, options?: { currency: C }): Money<C> {\n if (options && !isCurrency(options.currency)) {\n throw new CoinsError('INVALID_CURRENCY', 'sum() requires a canonical currency');\n }\n\n let amount = 0n;\n let currency: C | undefined = options?.currency;\n\n for (const value of values) {\n assertMoney(value);\n\n if (currency === undefined) {\n currency = value.currency;\n } else if (value.currency !== currency) {\n throw new CurrencyMismatchError(currency.code, value.currency.code);\n }\n\n amount += value.amount;\n }\n\n if (currency === undefined) throw new CoinsError('INVALID_MONEY', 'sum() of empty iterable requires { currency }');\n\n return createMoney(amount, currency);\n}\n\nexport function allocate<C extends Currency>(value: Money<C>, count: number): Money<C>[];\nexport function allocate<C extends Currency>(value: Money<C>, weights: readonly string[]): Money<C>[];\nexport function allocate<C extends Currency>(value: Money<C>, weightsOrCount: number | readonly string[]): Money<C>[] {\n assertMoney(value);\n\n if (typeof weightsOrCount === 'number') return allocateEvenly(value, weightsOrCount);\n\n if (!Array.isArray(weightsOrCount)) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights must be an array');\n }\n if (weightsOrCount.length === 0) throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot be empty');\n if (weightsOrCount.length > MAX_ALLOCATION_PARTS) {\n throw new CoinsError('INVALID_ALLOCATION', `Allocation cannot exceed ${MAX_ALLOCATION_PARTS} parts`);\n }\n for (let index = 0; index < weightsOrCount.length; index++) {\n if (!Object.hasOwn(weightsOrCount, index)) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot contain empty slots');\n }\n }\n\n const weights = weightsOrCount.map(decimal);\n\n if (weights.some((weight) => weight.numerator < 0n)) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot be negative');\n }\n\n const commonDenominator = weights.reduce((result, weight) => lcm(result, weight.denominator), 1n);\n const scaledWeights = weights.map((weight) => weight.numerator * (commonDenominator / weight.denominator));\n const totalWeight = scaledWeights.reduce((result, weight) => result + weight, 0n);\n\n if (totalWeight === 0n)\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights must include a positive value');\n\n const sign = value.amount < 0n ? -1n : 1n;\n const absolute = value.amount < 0n ? -value.amount : value.amount;\n const shares = scaledWeights.map((weight) => (absolute * weight) / totalWeight);\n let remainder = absolute - shares.reduce((result, amount) => result + amount, 0n);\n const ranked = scaledWeights\n .map((weight, index) => ({ index, remainder: (absolute * weight) % totalWeight }))\n .sort((left, right) =>\n left.remainder === right.remainder ? left.index - right.index : left.remainder > right.remainder ? -1 : 1,\n );\n\n for (const entry of ranked) {\n if (remainder === 0n) break;\n\n shares[entry.index]! += 1n;\n remainder -= 1n;\n }\n\n return shares.map((amount) => createMoney(amount * sign, value.currency));\n}\n\nfunction allocateEvenly<C extends Currency>(value: Money<C>, countValue: number): Money<C>[] {\n if (!Number.isSafeInteger(countValue) || countValue < 1 || countValue > MAX_ALLOCATION_PARTS) {\n throw new CoinsError(\n 'INVALID_ALLOCATION',\n `Allocation count must be a positive safe integer no greater than ${MAX_ALLOCATION_PARTS}`,\n );\n }\n\n const count = BigInt(countValue);\n const sign = value.amount < 0n ? -1n : 1n;\n const absolute = value.amount < 0n ? -value.amount : value.amount;\n const base = absolute / count;\n const remainder = absolute % count;\n\n return Array.from({ length: countValue }, (_, index) =>\n createMoney((base + (BigInt(index) < remainder ? 1n : 0n)) * sign, value.currency),\n );\n}\n"],"mappings":"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"}
package/dist/aggregate.js CHANGED
@@ -1,31 +1,31 @@
1
- import { CoinsError as e, CurrencyMismatchError as t } from "./errors.js";
2
- import { decimal as n, lcm as r } from "./_decimal.js";
3
- import { isCurrency as i } from "./currency.js";
1
+ import { decimal as e, lcm as t } from "./_decimal.js";
2
+ import { isCurrency as n } from "./currency.js";
3
+ import { CoinsError as r, CurrencyMismatchError as i } from "./errors.js";
4
4
  import { assertMoney as a, createMoney as o } from "./money.js";
5
- //#region src/aggregate.ts
5
+ // src/aggregate.ts
6
6
  var s = 1e5;
7
- function sum(n, r) {
8
- if (r && !i(r.currency)) throw new e("INVALID_CURRENCY", "sum() requires a canonical currency");
9
- let s = 0n, c = r?.currency;
10
- for (let e of n) {
11
- if (a(e), c === void 0) c = e.currency;
12
- else if (e.currency !== c) throw new t(c.code, e.currency.code);
13
- s += e.amount;
7
+ function sum(e, t) {
8
+ if (t && !n(t.currency)) throw new r("INVALID_CURRENCY", "sum() requires a canonical currency");
9
+ let s = 0n, c = t?.currency;
10
+ for (let t of e) {
11
+ if (a(t), c === void 0) c = t.currency;
12
+ else if (t.currency !== c) throw new i(c.code, t.currency.code);
13
+ s += t.amount;
14
14
  }
15
- if (c === void 0) throw new e("INVALID_MONEY", "sum() of empty iterable requires { currency }");
15
+ if (c === void 0) throw new r("INVALID_MONEY", "sum() of empty iterable requires { currency }");
16
16
  return o(s, c);
17
17
  }
18
- function allocate(t, i) {
19
- if (a(t), typeof i == "number") return allocateEvenly(t, i);
20
- if (!Array.isArray(i)) throw new e("INVALID_ALLOCATION", "Allocation weights must be an array");
21
- if (i.length === 0) throw new e("INVALID_ALLOCATION", "Allocation weights cannot be empty");
22
- if (i.length > s) throw new e("INVALID_ALLOCATION", `Allocation cannot exceed ${s} parts`);
23
- for (let t = 0; t < i.length; t++) if (!Object.hasOwn(i, t)) throw new e("INVALID_ALLOCATION", "Allocation weights cannot contain empty slots");
24
- let c = i.map(n);
25
- if (c.some((e) => e.numerator < 0n)) throw new e("INVALID_ALLOCATION", "Allocation weights cannot be negative");
26
- let l = c.reduce((e, t) => r(e, t.denominator), 1n), u = c.map((e) => e.numerator * (l / e.denominator)), d = u.reduce((e, t) => e + t, 0n);
27
- if (d === 0n) throw new e("INVALID_ALLOCATION", "Allocation weights must include a positive value");
28
- let f = t.amount < 0n ? -1n : 1n, p = t.amount < 0n ? -t.amount : t.amount, m = u.map((e) => p * e / d), h = p - m.reduce((e, t) => e + t, 0n), g = u.map((e, t) => ({
18
+ function allocate(n, i) {
19
+ if (a(n), typeof i == "number") return allocateEvenly(n, i);
20
+ if (!Array.isArray(i)) throw new r("INVALID_ALLOCATION", "Allocation weights must be an array");
21
+ if (i.length === 0) throw new r("INVALID_ALLOCATION", "Allocation weights cannot be empty");
22
+ if (i.length > s) throw new r("INVALID_ALLOCATION", `Allocation cannot exceed ${s} parts`);
23
+ for (let e = 0; e < i.length; e++) if (!Object.hasOwn(i, e)) throw new r("INVALID_ALLOCATION", "Allocation weights cannot contain empty slots");
24
+ let c = i.map(e);
25
+ if (c.some((e) => e.numerator < 0n)) throw new r("INVALID_ALLOCATION", "Allocation weights cannot be negative");
26
+ let l = c.reduce((e, n) => t(e, n.denominator), 1n), u = c.map((e) => e.numerator * (l / e.denominator)), d = u.reduce((e, t) => e + t, 0n);
27
+ if (d === 0n) throw new r("INVALID_ALLOCATION", "Allocation weights must include a positive value");
28
+ let f = n.amount < 0n ? -1n : 1n, p = n.amount < 0n ? -n.amount : n.amount, m = u.map((e) => p * e / d), h = p - m.reduce((e, t) => e + t, 0n), g = u.map((e, t) => ({
29
29
  index: t,
30
30
  remainder: p * e % d
31
31
  })).sort((e, t) => e.remainder === t.remainder ? e.index - t.index : e.remainder > t.remainder ? -1 : 1);
@@ -33,14 +33,13 @@ function allocate(t, i) {
33
33
  if (h === 0n) break;
34
34
  m[e.index] += 1n, h -= 1n;
35
35
  }
36
- return m.map((e) => o(e * f, t.currency));
36
+ return m.map((e) => o(e * f, n.currency));
37
37
  }
38
- function allocateEvenly(t, n) {
39
- if (!Number.isSafeInteger(n) || n < 1 || n > s) throw new e("INVALID_ALLOCATION", `Allocation count must be a positive safe integer no greater than ${s}`);
40
- let r = BigInt(n), i = t.amount < 0n ? -1n : 1n, a = t.amount < 0n ? -t.amount : t.amount, c = a / r, l = a % r;
41
- return Array.from({ length: n }, (e, n) => o((c + (BigInt(n) < l ? 1n : 0n)) * i, t.currency));
38
+ function allocateEvenly(e, t) {
39
+ if (!Number.isSafeInteger(t) || t < 1 || t > s) throw new r("INVALID_ALLOCATION", `Allocation count must be a positive safe integer no greater than ${s}`);
40
+ let n = BigInt(t), i = e.amount < 0n ? -1n : 1n, a = e.amount < 0n ? -e.amount : e.amount, c = a / n, l = a % n;
41
+ return Array.from({ length: t }, (t, n) => o((c + (BigInt(n) < l ? 1n : 0n)) * i, e.currency));
42
42
  }
43
- //#endregion
44
43
  export { allocate, sum };
45
44
 
46
45
  //# sourceMappingURL=aggregate.js.map
package/dist/coins.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/errors.ts
1
+ // src/errors.ts
2
2
  var CoinsError = class extends Error {
3
3
  code;
4
4
  constructor(e, t, n) {
@@ -81,8 +81,7 @@ function gcd(e, t) {
81
81
  function lcm(e, t) {
82
82
  return e / gcd(e, t) * t;
83
83
  }
84
- //#endregion
85
- //#region src/currency.ts
84
+ // src/currency.ts
86
85
  var i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new WeakSet();
87
86
  function register(e, t) {
88
87
  validateDefinition(e, t);
@@ -113,8 +112,7 @@ function validateDefinition(e, t) {
113
112
  if (!/^[A-Z]{3}$/.test(e)) throw new CoinsError("INVALID_CURRENCY", `Currency code must be three uppercase letters: "${e}"`);
114
113
  if (!Number.isInteger(t) || t < 0 || t > 6) throw new CoinsError("INVALID_CURRENCY", `Currency "${e}" must have 0–6 minor-unit digits`);
115
114
  }
116
- //#endregion
117
- //#region src/money.ts
115
+ // src/money.ts
118
116
  var p = /* @__PURE__ */ new WeakSet(), m = "halfAwayFromZero", h = /^(?:0|-[1-9]\d*|[1-9]\d*)$/, g = 1e3;
119
117
  function money(e, t, n) {
120
118
  if (assertCurrency(t), typeof e == "bigint") {
@@ -219,8 +217,7 @@ function isPlainDataObject(e) {
219
217
  function isDataProperty(e) {
220
218
  return e !== void 0 && "value" in e && e.get === void 0 && e.set === void 0;
221
219
  }
222
- //#endregion
223
- //#region src/aggregate.ts
220
+ // src/aggregate.ts
224
221
  var _ = 1e5;
225
222
  function sum(e, t) {
226
223
  if (t && !isCurrency(t.currency)) throw new CoinsError("INVALID_CURRENCY", "sum() requires a canonical currency");
@@ -258,8 +255,7 @@ function allocateEvenly(e, t) {
258
255
  let n = BigInt(t), r = e.amount < 0n ? -1n : 1n, i = e.amount < 0n ? -e.amount : e.amount, a = i / n, o = i % n;
259
256
  return Array.from({ length: t }, (t, n) => createMoney((a + (BigInt(n) < o ? 1n : 0n)) * r, e.currency));
260
257
  }
261
- //#endregion
262
- //#region src/exchange.ts
258
+ // src/exchange.ts
263
259
  var v = /* @__PURE__ */ new WeakSet();
264
260
  function exchangeRate({ from: e, to: t, value: n }) {
265
261
  if (!isCurrency(e) || !isCurrency(t)) throw new CoinsError("INVALID_CURRENCY", "Exchange rate requires registered currencies");
@@ -280,8 +276,7 @@ function exchange(e, t, n = {}) {
280
276
  if (e.currency !== t.from) throw new CurrencyMismatchError(e.currency.code, t.from.code);
281
277
  return createMoney(roundDivision(e.amount * t.value.numerator * 10n ** BigInt(t.to.minorUnit), t.value.denominator * 10n ** BigInt(t.from.minorUnit), n.rounding ?? "halfAwayFromZero"), t.to);
282
278
  }
283
- //#endregion
284
- //#region src/format.ts
279
+ // src/format.ts
285
280
  var y = 20, b = "halfAwayFromZero", x = {
286
281
  awayFromZero: "expand",
287
282
  ceil: "ceil",
@@ -343,8 +338,7 @@ function normalizeParts(e) {
343
338
  });
344
339
  return t;
345
340
  }
346
- //#endregion
347
- //#region src/serialization.ts
341
+ // src/serialization.ts
348
342
  function toJSON(e) {
349
343
  return assertMoney(e), {
350
344
  amount: e.amount.toString(),
@@ -352,7 +346,6 @@ function toJSON(e) {
352
346
  unit: "minor"
353
347
  };
354
348
  }
355
- //#endregion
356
349
  export { d as BHD, CoinsError, CurrencyMismatchError, s as EUR, c as GBP, InvalidCurrencyError, l as JPY, u as KRW, f as KWD, o as USD, abs, add, allocate, clamp, compare, currency, decodeMoney, divide, exchange, exchangeRate, format, formatParts, isCurrency, isExchangeRate, isMoney, money, multiply, negate, round, subtract, sum, toDecimal, toJSON };
357
350
 
358
351
  //# sourceMappingURL=coins.js.map
package/dist/coins.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"coins.js","names":["message","options","code","expected","received","value","DECIMAL","MAX_DECIMAL_DIGITS","MAX_DECIMAL_PLACES","ROUNDING_MODES","match","fraction","denominator","unsigned","numerator","divisor","mode","negative","absolute","quotient","remainder","result","doubled","amount","scale","minorUnit","whole","a","left","b","right","builtins","canonicalCurrencies","definition","USD","EUR","GBP","JPY","KRW","BHD","KWD","input","canonicalMoney","defaultRounding","INTEGER","MAX_INTEGER_DIGITS","currency","scaled","descriptors","amountDescriptor","currencyDescriptor","ownKeys","unitDescriptor","resolveCurrency","resolvedCurrency","error","scalar","factor","divisorMagnitude","dividend","fractionDigits","rounding","prototype","descriptor","MAX_ALLOCATION_PARTS","values","weightsOrCount","index","weights","weight","commonDenominator","scaledWeights","totalWeight","sign","shares","ranked","entry","countValue","count","base","canonicalRates","from","to","parsed","rate","MAX_FRACTION_DIGITS","defaultFormatRounding","intlRoundingModes","part","style","maximum","minimum","requestedMaximum","requestedMinimum","name","parts","raw","previous"],"sources":["../src/errors.ts","../src/_decimal.ts","../src/currency.ts","../src/money.ts","../src/aggregate.ts","../src/exchange.ts","../src/format.ts","../src/serialization.ts"],"sourcesContent":["export type CoinsErrorCode =\n | 'CURRENCY_MISMATCH'\n | 'DIVISION_BY_ZERO'\n | 'FORMAT_ERROR'\n | 'INVALID_ALLOCATION'\n | 'INVALID_CURRENCY'\n | 'INVALID_DECIMAL'\n | 'INVALID_EXCHANGE_RATE'\n | 'INVALID_MONEY'\n | 'INVALID_RANGE'\n | 'INVALID_ROUNDING';\n\nexport class CoinsError extends Error {\n readonly code: CoinsErrorCode;\n\n constructor(code: CoinsErrorCode, message: string, options?: ErrorOptions) {\n super(message, options);\n this.code = code;\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class CurrencyMismatchError extends CoinsError {\n readonly expected: string;\n readonly received: string;\n\n constructor(expected: string, received: string) {\n super('CURRENCY_MISMATCH', `Currency mismatch: canonical ${expected} and ${received} definitions differ`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nfunction describe(value: unknown): string {\n try {\n return String(value);\n } catch {\n return '<unprintable>';\n }\n}\n\nexport class InvalidCurrencyError extends CoinsError {\n readonly value: unknown;\n\n constructor(value: unknown) {\n super('INVALID_CURRENCY', `Unsupported currency: \"${describe(value)}\"`);\n this.value = value;\n }\n}\n","import { CoinsError } from './errors';\nimport type { Decimal, RoundingMode } from './types';\n\nconst DECIMAL = /^(-?)(\\d+)(?:\\.(\\d+))?$/;\nconst MAX_DECIMAL_DIGITS = 1000;\nconst MAX_DECIMAL_PLACES = 100;\nconst ROUNDING_MODES = new Set<RoundingMode>([\n 'awayFromZero',\n 'ceil',\n 'floor',\n 'halfAwayFromZero',\n 'halfEven',\n 'towardZero',\n]);\n\nexport function assertRoundingMode(value: unknown): asserts value is RoundingMode {\n if (!ROUNDING_MODES.has(value as RoundingMode)) {\n throw new CoinsError('INVALID_ROUNDING', `Unknown rounding mode: ${String(value)}`);\n }\n}\n\nexport function decimal(value: string): Decimal {\n if (typeof value !== 'string') throw new CoinsError('INVALID_DECIMAL', 'Decimal input must be a string');\n if (value.length > MAX_DECIMAL_DIGITS) throw new CoinsError('INVALID_DECIMAL', 'Decimal input is too long');\n\n const match = DECIMAL.exec(value);\n\n if (!match) throw new CoinsError('INVALID_DECIMAL', `Invalid decimal: \"${value}\"`);\n\n const fraction = (match[3] ?? '').replace(/0+$/, '');\n\n if (fraction.length > MAX_DECIMAL_PLACES) {\n throw new CoinsError('INVALID_DECIMAL', `Decimal precision cannot exceed ${MAX_DECIMAL_PLACES} places`);\n }\n\n const denominator = 10n ** BigInt(fraction.length);\n const unsigned = BigInt(match[2]!) * denominator + BigInt(fraction || '0');\n const numerator = match[1] === '-' && unsigned !== 0n ? -unsigned : unsigned;\n const divisor = gcd(numerator < 0n ? -numerator : numerator, denominator);\n\n return Object.freeze({ denominator: denominator / divisor, numerator: numerator / divisor }) as Decimal;\n}\n\nexport function roundDivision(numerator: bigint, denominator: bigint, mode: RoundingMode): bigint {\n assertRoundingMode(mode);\n if (denominator <= 0n) throw new CoinsError('INVALID_DECIMAL', 'Decimal denominator must be positive');\n\n const negative = numerator < 0n;\n const absolute = negative ? -numerator : numerator;\n const quotient = absolute / denominator;\n const remainder = absolute % denominator;\n\n if (remainder === 0n) return negative ? -quotient : quotient;\n\n const increment = (() => {\n switch (mode) {\n case 'awayFromZero':\n return true;\n case 'ceil':\n return !negative;\n case 'floor':\n return negative;\n case 'halfAwayFromZero':\n return remainder * 2n >= denominator;\n case 'halfEven': {\n const doubled = remainder * 2n;\n\n return doubled > denominator || (doubled === denominator && quotient % 2n !== 0n);\n }\n case 'towardZero':\n return false;\n default:\n throw new CoinsError('INVALID_ROUNDING', `Unknown rounding mode: ${mode satisfies never}`);\n }\n })();\n\n const result = increment ? quotient + 1n : quotient;\n\n return negative ? -result : result;\n}\n\nexport function toDecimalString(amount: bigint, minorUnit: number): string {\n const negative = amount < 0n;\n const absolute = negative ? -amount : amount;\n const scale = 10n ** BigInt(minorUnit);\n const whole = absolute / scale;\n\n if (minorUnit === 0) return `${negative ? '-' : ''}${whole}`;\n\n const fraction = (absolute % scale).toString().padStart(minorUnit, '0');\n\n return `${negative ? '-' : ''}${whole}.${fraction}`;\n}\n\nexport function gcd(left: bigint, right: bigint): bigint {\n let a = left;\n let b = right;\n\n while (b !== 0n) [a, b] = [b, a % b];\n\n return a === 0n ? 1n : a;\n}\n\nexport function lcm(left: bigint, right: bigint): bigint {\n return (left / gcd(left, right)) * right;\n}\n","import { CoinsError, InvalidCurrencyError } from './errors';\nimport type { Currency, CurrencyCode } from './types';\n\nconst builtins = new Map<string, Currency>();\nconst canonicalCurrencies = new WeakSet<object>();\n\nfunction register<C extends string>(code: C, minorUnit: number): Currency<C> {\n validateDefinition(code, minorUnit);\n\n const definition = Object.freeze({ code: code as CurrencyCode<C>, minorUnit }) as Currency<C>;\n\n canonicalCurrencies.add(definition);\n\n return definition;\n}\n\nfunction builtin<C extends string>(code: C, minorUnit: number): Currency<C> {\n const definition = register(code, minorUnit);\n\n builtins.set(code, definition);\n\n return definition;\n}\n\nexport const USD = builtin('USD', 2);\nexport const EUR = builtin('EUR', 2);\nexport const GBP = builtin('GBP', 2);\nexport const JPY = builtin('JPY', 0);\nexport const KRW = builtin('KRW', 0);\nexport const BHD = builtin('BHD', 3);\nexport const KWD = builtin('KWD', 3);\n\n/** Resolve a built-in currency by code, or construct an immutable custom currency from a definition. */\nexport function currency<C extends string>(code: C): Currency<C>;\nexport function currency<C extends string>(definition: { code: C; minorUnit: number }): Currency<C>;\nexport function currency<C extends string>(input: C | { code: C; minorUnit: number }): Currency<C> {\n if (typeof input === 'string') {\n const definition = builtins.get(input);\n\n if (!definition) throw new InvalidCurrencyError(input);\n\n return definition as Currency<C>;\n }\n\n if (typeof input !== 'object' || input === null) {\n throw new CoinsError('INVALID_CURRENCY', 'Currency definition must be an object');\n }\n\n return register(input.code, input.minorUnit);\n}\n\nexport function isCurrency(value: unknown): value is Currency {\n return typeof value === 'object' && value !== null && canonicalCurrencies.has(value);\n}\n\nfunction validateDefinition(code: string, minorUnit: number): void {\n if (!/^[A-Z]{3}$/.test(code)) {\n throw new CoinsError('INVALID_CURRENCY', `Currency code must be three uppercase letters: \"${code}\"`);\n }\n\n if (!Number.isInteger(minorUnit) || minorUnit < 0 || minorUnit > 6) {\n throw new CoinsError('INVALID_CURRENCY', `Currency \"${code}\" must have 0–6 minor-unit digits`);\n }\n}\n","import { decimal, roundDivision, toDecimalString } from './_decimal';\nimport { currency, isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport type { Currency, Money, RoundingMode } from './types';\n\nconst canonicalMoney = new WeakSet<object>();\n\nconst defaultRounding: RoundingMode = 'halfAwayFromZero';\n\nconst INTEGER = /^(?:0|-[1-9]\\d*|[1-9]\\d*)$/;\nconst MAX_INTEGER_DIGITS = 1000;\n\nexport function money<C extends Currency>(amount: string, currency: C, options?: { rounding?: RoundingMode }): Money<C>;\nexport function money<C extends Currency>(amount: bigint, currency: C, options: { unit: 'minor' }): Money<C>;\nexport function money<C extends Currency>(\n amount: bigint | string,\n currency: C,\n options?: { rounding?: RoundingMode } | { unit: 'minor' },\n): Money<C> {\n assertCurrency(currency);\n\n if (typeof amount === 'bigint') {\n if (!options || !('unit' in options) || options.unit !== 'minor') {\n throw new CoinsError('INVALID_MONEY', \"Bigint amounts require { unit: 'minor' }\");\n }\n\n return createMoney(amount, currency);\n }\n\n if (options && 'unit' in options)\n throw new CoinsError('INVALID_MONEY', 'Decimal strings do not accept a unit option');\n\n const value = decimal(amount);\n const scaled = value.numerator * 10n ** BigInt(currency.minorUnit);\n const remainder = scaled % value.denominator;\n\n if (remainder !== 0n && options?.rounding === undefined) {\n throw new CoinsError('INVALID_MONEY', `Amount \"${amount}\" exceeds ${currency.code} precision; provide rounding`);\n }\n\n return createMoney(roundDivision(scaled, value.denominator, options?.rounding ?? defaultRounding), currency);\n}\n\n/**\n * Decodes untrusted/persisted data into canonical `Money`.\n * Accepts either a plain `Money`-shaped object (`{ amount: bigint, currency: Currency }`)\n * or a `MoneyJSON`-shaped object (`{ amount: string, currency: string, unit: 'minor' }`).\n * Use `money()` for trusted construction; use `decodeMoney()` for network/storage data.\n */\nexport function decodeMoney(value: unknown, options?: { currency?: (code: string) => Currency }): Money {\n if (!isPlainDataObject(value)) throw new CoinsError('INVALID_MONEY', 'Money must be a plain data object');\n\n const descriptors = Object.getOwnPropertyDescriptors(value);\n const amountDescriptor = descriptors.amount;\n const currencyDescriptor = descriptors.currency;\n\n if (!isDataProperty(amountDescriptor) || !isDataProperty(currencyDescriptor)) {\n throw new CoinsError('INVALID_MONEY', 'Money properties must be plain data values');\n }\n\n const ownKeys = Reflect.ownKeys(value);\n\n // Plain Money: bigint amount + registered currency\n if (typeof amountDescriptor.value === 'bigint' && isCurrency(currencyDescriptor.value)) {\n if (ownKeys.length !== 2) {\n throw new CoinsError('INVALID_MONEY', 'Plain money must have exactly the keys amount and currency');\n }\n\n return createMoney(amountDescriptor.value, currencyDescriptor.value);\n }\n\n // MoneyJSON: string amount + string currency code + unit: 'minor'\n const unitDescriptor = descriptors.unit;\n if (\n typeof amountDescriptor.value === 'string' &&\n typeof currencyDescriptor.value === 'string' &&\n isDataProperty(unitDescriptor) &&\n unitDescriptor.value === 'minor'\n ) {\n if (ownKeys.length !== 3) {\n throw new CoinsError('INVALID_MONEY', 'MoneyJSON must have exactly the keys amount, currency, unit');\n }\n if (amountDescriptor.value.length > MAX_INTEGER_DIGITS) {\n throw new CoinsError('INVALID_MONEY', `Money JSON amount is too long; maximum ${MAX_INTEGER_DIGITS} characters`);\n }\n if (!INTEGER.test(amountDescriptor.value)) {\n throw new CoinsError('INVALID_MONEY', `Invalid Money JSON amount \"${amountDescriptor.value}\"`);\n }\n const resolveCurrency = options?.currency ?? currency;\n try {\n const resolvedCurrency = resolveCurrency(currencyDescriptor.value);\n\n if (!isCurrency(resolvedCurrency) || resolvedCurrency.code !== currencyDescriptor.value) {\n throw new CoinsError(\n 'INVALID_CURRENCY',\n `Currency resolver must return the canonical \"${currencyDescriptor.value}\" definition`,\n );\n }\n\n return money(BigInt(amountDescriptor.value), resolvedCurrency, { unit: 'minor' });\n } catch (error) {\n throw new CoinsError('INVALID_MONEY', `Invalid Money JSON for currency \"${currencyDescriptor.value}\"`, {\n cause: error,\n });\n }\n }\n\n throw new CoinsError(\n 'INVALID_MONEY',\n 'Money must contain bigint amount and a registered currency, or a valid MoneyJSON shape',\n );\n}\n\nexport function isMoney(value: unknown): value is Money {\n return typeof value === 'object' && value !== null && canonicalMoney.has(value);\n}\n\nexport function add<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount + right.amount, left.currency);\n}\n\nexport function subtract<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount - right.amount, left.currency);\n}\n\nexport function multiply<C extends Currency>(\n value: Money<C>,\n factor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(factor);\n\n return createMoney(\n roundDivision(value.amount * scalar.numerator, scalar.denominator, options.rounding ?? defaultRounding),\n value.currency,\n );\n}\n\nexport function divide<C extends Currency>(\n value: Money<C>,\n divisor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(divisor);\n\n if (scalar.numerator === 0n) throw new CoinsError('DIVISION_BY_ZERO', 'Cannot divide money by zero');\n\n const divisorMagnitude = scalar.numerator < 0n ? -scalar.numerator : scalar.numerator;\n const dividend = value.amount * scalar.denominator;\n const quotient = roundDivision(\n scalar.numerator < 0n ? -dividend : dividend,\n divisorMagnitude,\n options.rounding ?? defaultRounding,\n );\n\n return createMoney(quotient, value.currency);\n}\n\nexport function compare<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): -1 | 0 | 1 {\n assertSameCurrency(left, right);\n\n return left.amount === right.amount ? 0 : left.amount < right.amount ? -1 : 1;\n}\n\nexport function clamp<C extends Currency>(\n value: Money<C>,\n options: { max: Money<NoInfer<C>>; min: Money<NoInfer<C>> },\n): Money<C> {\n if (compare(options.min, options.max) === 1) {\n throw new CoinsError('INVALID_RANGE', 'Clamp minimum cannot exceed maximum');\n }\n\n return compare(value, options.min) === -1 ? options.min : compare(value, options.max) === 1 ? options.max : value;\n}\n\nexport function abs<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(value.amount < 0n ? -value.amount : value.amount, value.currency);\n}\n\nexport function negate<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(-value.amount, value.currency);\n}\n\nexport function round<C extends Currency>(\n value: Money<C>,\n options: { fractionDigits: number; rounding?: RoundingMode },\n): Money<C> {\n assertMoney(value);\n\n const { fractionDigits, rounding = defaultRounding } = options;\n\n if (!Number.isInteger(fractionDigits) || fractionDigits < 0 || fractionDigits > value.currency.minorUnit) {\n throw new CoinsError('INVALID_ROUNDING', `fractionDigits must be an integer from 0 to ${value.currency.minorUnit}`);\n }\n\n const factor = 10n ** BigInt(value.currency.minorUnit - fractionDigits);\n\n return createMoney(roundDivision(value.amount, factor, rounding) * factor, value.currency);\n}\n\nexport function toDecimal(value: Money): string {\n assertMoney(value);\n\n return toDecimalString(value.amount, value.currency.minorUnit);\n}\n\nexport function createMoney<C extends Currency>(amount: bigint, currency: C): Money<C> {\n const value = Object.freeze({ amount, currency }) as Money<C>;\n\n canonicalMoney.add(value);\n\n return value;\n}\n\nexport function assertMoney(value: unknown): asserts value is Money {\n if (!isMoney(value)) {\n throw new CoinsError('INVALID_MONEY', 'Money must be a canonical @vielzeug/coins value');\n }\n}\n\nfunction assertSameCurrency(left: Money, right: Money): void {\n assertMoney(left);\n assertMoney(right);\n\n if (left.currency !== right.currency) {\n throw new CurrencyMismatchError(left.currency.code, right.currency.code);\n }\n}\n\nfunction assertCurrency(value: unknown): asserts value is Currency {\n if (!isCurrency(value)) throw new CoinsError('INVALID_CURRENCY', 'Money requires a registered currency');\n}\n\nfunction isPlainDataObject(value: unknown): value is Record<PropertyKey, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n\n const prototype = Object.getPrototypeOf(value);\n\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction isDataProperty(\n descriptor: PropertyDescriptor | undefined,\n): descriptor is PropertyDescriptor & { value: unknown } {\n return (\n descriptor !== undefined && 'value' in descriptor && descriptor.get === undefined && descriptor.set === undefined\n );\n}\n","import { decimal, lcm } from './_decimal';\nimport { isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { assertMoney, createMoney } from './money';\nimport type { Currency, Money } from './types';\n\nconst MAX_ALLOCATION_PARTS = 100_000;\n\nexport function sum<C extends Currency>(values: readonly Money<C>[]): Money<C>;\nexport function sum<C extends Currency>(values: Iterable<Money<C>>, options: { currency: C }): Money<C>;\nexport function sum<C extends Currency>(values: Iterable<Money<C>>, options?: { currency: C }): Money<C> {\n if (options && !isCurrency(options.currency)) {\n throw new CoinsError('INVALID_CURRENCY', 'sum() requires a canonical currency');\n }\n\n let amount = 0n;\n let currency: C | undefined = options?.currency;\n\n for (const value of values) {\n assertMoney(value);\n\n if (currency === undefined) {\n currency = value.currency;\n } else if (value.currency !== currency) {\n throw new CurrencyMismatchError(currency.code, value.currency.code);\n }\n\n amount += value.amount;\n }\n\n if (currency === undefined) throw new CoinsError('INVALID_MONEY', 'sum() of empty iterable requires { currency }');\n\n return createMoney(amount, currency);\n}\n\nexport function allocate<C extends Currency>(value: Money<C>, count: number): Money<C>[];\nexport function allocate<C extends Currency>(value: Money<C>, weights: readonly string[]): Money<C>[];\nexport function allocate<C extends Currency>(value: Money<C>, weightsOrCount: number | readonly string[]): Money<C>[] {\n assertMoney(value);\n\n if (typeof weightsOrCount === 'number') return allocateEvenly(value, weightsOrCount);\n\n if (!Array.isArray(weightsOrCount)) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights must be an array');\n }\n if (weightsOrCount.length === 0) throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot be empty');\n if (weightsOrCount.length > MAX_ALLOCATION_PARTS) {\n throw new CoinsError('INVALID_ALLOCATION', `Allocation cannot exceed ${MAX_ALLOCATION_PARTS} parts`);\n }\n for (let index = 0; index < weightsOrCount.length; index++) {\n if (!Object.hasOwn(weightsOrCount, index)) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot contain empty slots');\n }\n }\n\n const weights = weightsOrCount.map(decimal);\n\n if (weights.some((weight) => weight.numerator < 0n)) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot be negative');\n }\n\n const commonDenominator = weights.reduce((result, weight) => lcm(result, weight.denominator), 1n);\n const scaledWeights = weights.map((weight) => weight.numerator * (commonDenominator / weight.denominator));\n const totalWeight = scaledWeights.reduce((result, weight) => result + weight, 0n);\n\n if (totalWeight === 0n)\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights must include a positive value');\n\n const sign = value.amount < 0n ? -1n : 1n;\n const absolute = value.amount < 0n ? -value.amount : value.amount;\n const shares = scaledWeights.map((weight) => (absolute * weight) / totalWeight);\n let remainder = absolute - shares.reduce((result, amount) => result + amount, 0n);\n const ranked = scaledWeights\n .map((weight, index) => ({ index, remainder: (absolute * weight) % totalWeight }))\n .sort((left, right) =>\n left.remainder === right.remainder ? left.index - right.index : left.remainder > right.remainder ? -1 : 1,\n );\n\n for (const entry of ranked) {\n if (remainder === 0n) break;\n\n shares[entry.index]! += 1n;\n remainder -= 1n;\n }\n\n return shares.map((amount) => createMoney(amount * sign, value.currency));\n}\n\nfunction allocateEvenly<C extends Currency>(value: Money<C>, countValue: number): Money<C>[] {\n if (!Number.isSafeInteger(countValue) || countValue < 1 || countValue > MAX_ALLOCATION_PARTS) {\n throw new CoinsError(\n 'INVALID_ALLOCATION',\n `Allocation count must be a positive safe integer no greater than ${MAX_ALLOCATION_PARTS}`,\n );\n }\n\n const count = BigInt(countValue);\n const sign = value.amount < 0n ? -1n : 1n;\n const absolute = value.amount < 0n ? -value.amount : value.amount;\n const base = absolute / count;\n const remainder = absolute % count;\n\n return Array.from({ length: countValue }, (_, index) =>\n createMoney((base + (BigInt(index) < remainder ? 1n : 0n)) * sign, value.currency),\n );\n}\n","import { decimal, roundDivision } from './_decimal';\nimport { isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { assertMoney, createMoney } from './money';\nimport type { Currency, ExchangeRate, Money, RoundingMode } from './types';\n\nconst canonicalRates = new WeakSet<object>();\n\nexport function exchangeRate<From extends Currency, To extends Currency>({\n from,\n to,\n value,\n}: {\n from: From;\n to: To;\n value: string;\n}): ExchangeRate<From, To> {\n if (!isCurrency(from) || !isCurrency(to))\n throw new CoinsError('INVALID_CURRENCY', 'Exchange rate requires registered currencies');\n\n const parsed = decimal(value);\n\n if (parsed.numerator <= 0n) throw new CoinsError('INVALID_DECIMAL', 'Exchange rates must be positive');\n\n const rate = Object.freeze({ from, to, value: parsed }) as ExchangeRate<From, To>;\n\n canonicalRates.add(rate);\n\n return rate;\n}\n\nexport function isExchangeRate(value: unknown): value is ExchangeRate {\n return typeof value === 'object' && value !== null && canonicalRates.has(value);\n}\n\nexport function exchange<From extends Currency, To extends Currency>(\n value: Money<From>,\n rate: ExchangeRate<From, To>,\n options: { rounding?: RoundingMode } = {},\n): Money<To> {\n assertMoney(value);\n\n if (!isExchangeRate(rate)) {\n throw new CoinsError('INVALID_EXCHANGE_RATE', 'Exchange requires a canonical exchange rate');\n }\n\n if (value.currency !== rate.from) throw new CurrencyMismatchError(value.currency.code, rate.from.code);\n\n const numerator = value.amount * rate.value.numerator * 10n ** BigInt(rate.to.minorUnit);\n const denominator = rate.value.denominator * 10n ** BigInt(rate.from.minorUnit);\n\n return createMoney(roundDivision(numerator, denominator, options.rounding ?? 'halfAwayFromZero'), rate.to);\n}\n","import { assertRoundingMode } from './_decimal';\nimport { CoinsError } from './errors';\nimport { assertMoney, toDecimal } from './money';\nimport type { FormatOptions, Money, MoneyFormatPart, RoundingMode } from './types';\n\nconst MAX_FRACTION_DIGITS = 20;\nconst defaultFormatRounding: RoundingMode = 'halfAwayFromZero';\nconst intlRoundingModes: Record<RoundingMode, Intl.NumberFormatOptions['roundingMode']> = {\n awayFromZero: 'expand',\n ceil: 'ceil',\n floor: 'floor',\n halfAwayFromZero: 'halfExpand',\n halfEven: 'halfEven',\n towardZero: 'trunc',\n};\n\nexport function format(value: Money, options: FormatOptions = {}): string {\n return formatParts(value, options)\n .map((part) => part.value)\n .join('');\n}\n\nexport function formatParts(value: Money, options: FormatOptions = {}): MoneyFormatPart[] {\n assertMoney(value);\n assertRoundingMode(options.rounding ?? defaultFormatRounding);\n\n const { maximum, minimum } = fractionDigits(value.currency.minorUnit, options);\n const style = options.style ?? 'symbol';\n\n try {\n const formatter = new Intl.NumberFormat(options.locale ?? 'en-US', {\n currency: value.currency.code,\n currencyDisplay: style,\n maximumFractionDigits: maximum,\n minimumFractionDigits: minimum,\n roundingMode: intlRoundingModes[options.rounding ?? defaultFormatRounding],\n style: 'currency',\n useGrouping: true,\n });\n\n return normalizeParts(formatter.formatToParts(toDecimal(value) as unknown as number));\n } catch (error) {\n if (error instanceof CoinsError) throw error;\n\n throw new CoinsError(\n 'FORMAT_ERROR',\n `Cannot format currency \"${value.currency.code}\" for locale \"${options.locale ?? 'en-US'}\"`,\n { cause: error },\n );\n }\n}\n\nfunction fractionDigits(scale: number, options: FormatOptions): { maximum: number; minimum: number } {\n const requestedMaximum = options.maximumFractionDigits;\n const requestedMinimum = options.minimumFractionDigits;\n\n validateFractionDigit('maximumFractionDigits', requestedMaximum);\n validateFractionDigit('minimumFractionDigits', requestedMinimum);\n\n const maximum = requestedMaximum ?? Math.max(scale, requestedMinimum ?? scale);\n const minimum = requestedMinimum ?? Math.min(scale, requestedMaximum ?? scale);\n\n if (minimum > maximum) {\n throw new CoinsError('FORMAT_ERROR', 'minimumFractionDigits cannot exceed maximumFractionDigits');\n }\n\n return { maximum, minimum };\n}\n\nfunction validateFractionDigit(name: string, value: number | undefined): void {\n if (value !== undefined && (!Number.isInteger(value) || value < 0 || value > MAX_FRACTION_DIGITS)) {\n throw new CoinsError(\n 'FORMAT_ERROR',\n `Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${MAX_FRACTION_DIGITS} (${name})`,\n );\n }\n}\n\nfunction normalizeParts(raw: Intl.NumberFormatPart[]): MoneyFormatPart[] {\n const parts: MoneyFormatPart[] = [];\n\n for (const part of raw) {\n if (part.type === 'integer' || part.type === 'group') {\n const previous = parts.at(-1);\n\n if (previous?.type === 'integer') {\n parts[parts.length - 1] = { type: 'integer', value: previous.value + part.value };\n } else {\n parts.push({ type: 'integer', value: part.value });\n }\n } else if (\n part.type === 'currency' ||\n part.type === 'decimal' ||\n part.type === 'fraction' ||\n part.type === 'literal' ||\n part.type === 'minusSign' ||\n part.type === 'plusSign'\n ) {\n parts.push({ type: part.type, value: part.value });\n } else {\n parts.push({ type: 'literal', value: part.value });\n }\n }\n\n return parts;\n}\n","import { assertMoney } from './money';\nimport type { Money, MoneyJSON } from './types';\n\nexport function toJSON(value: Money): MoneyJSON {\n assertMoney(value);\n\n return { amount: value.amount.toString(), currency: value.currency.code, unit: 'minor' };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"coins.js","names":["message","options","code","expected","received","value","DECIMAL","MAX_DECIMAL_DIGITS","MAX_DECIMAL_PLACES","ROUNDING_MODES","match","fraction","denominator","unsigned","numerator","divisor","mode","negative","absolute","quotient","remainder","result","doubled","amount","scale","minorUnit","whole","a","left","b","right","builtins","canonicalCurrencies","definition","USD","EUR","GBP","JPY","KRW","BHD","KWD","input","canonicalMoney","defaultRounding","INTEGER","MAX_INTEGER_DIGITS","currency","scaled","descriptors","amountDescriptor","currencyDescriptor","ownKeys","unitDescriptor","resolveCurrency","resolvedCurrency","error","scalar","factor","divisorMagnitude","dividend","fractionDigits","rounding","prototype","descriptor","MAX_ALLOCATION_PARTS","values","weightsOrCount","index","weights","weight","commonDenominator","scaledWeights","totalWeight","sign","shares","ranked","entry","countValue","count","base","canonicalRates","from","to","parsed","rate","MAX_FRACTION_DIGITS","defaultFormatRounding","intlRoundingModes","part","style","maximum","minimum","requestedMaximum","requestedMinimum","name","parts","raw","previous"],"sources":["../src/errors.ts","../src/_decimal.ts","../src/currency.ts","../src/money.ts","../src/aggregate.ts","../src/exchange.ts","../src/format.ts","../src/serialization.ts"],"sourcesContent":["export type CoinsErrorCode =\n | 'CURRENCY_MISMATCH'\n | 'DIVISION_BY_ZERO'\n | 'FORMAT_ERROR'\n | 'INVALID_ALLOCATION'\n | 'INVALID_CURRENCY'\n | 'INVALID_DECIMAL'\n | 'INVALID_EXCHANGE_RATE'\n | 'INVALID_MONEY'\n | 'INVALID_RANGE'\n | 'INVALID_ROUNDING';\n\nexport class CoinsError extends Error {\n readonly code: CoinsErrorCode;\n\n constructor(code: CoinsErrorCode, message: string, options?: ErrorOptions) {\n super(message, options);\n this.code = code;\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class CurrencyMismatchError extends CoinsError {\n readonly expected: string;\n readonly received: string;\n\n constructor(expected: string, received: string) {\n super('CURRENCY_MISMATCH', `Currency mismatch: canonical ${expected} and ${received} definitions differ`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nfunction describe(value: unknown): string {\n try {\n return String(value);\n } catch {\n return '<unprintable>';\n }\n}\n\nexport class InvalidCurrencyError extends CoinsError {\n readonly value: unknown;\n\n constructor(value: unknown) {\n super('INVALID_CURRENCY', `Unsupported currency: \"${describe(value)}\"`);\n this.value = value;\n }\n}\n","import { CoinsError } from './errors';\nimport type { Decimal, RoundingMode } from './types';\n\nconst DECIMAL = /^(-?)(\\d+)(?:\\.(\\d+))?$/;\nconst MAX_DECIMAL_DIGITS = 1000;\nconst MAX_DECIMAL_PLACES = 100;\nconst ROUNDING_MODES = new Set<RoundingMode>([\n 'awayFromZero',\n 'ceil',\n 'floor',\n 'halfAwayFromZero',\n 'halfEven',\n 'towardZero',\n]);\n\nexport function assertRoundingMode(value: unknown): asserts value is RoundingMode {\n if (!ROUNDING_MODES.has(value as RoundingMode)) {\n throw new CoinsError('INVALID_ROUNDING', `Unknown rounding mode: ${String(value)}`);\n }\n}\n\nexport function decimal(value: string): Decimal {\n if (typeof value !== 'string') throw new CoinsError('INVALID_DECIMAL', 'Decimal input must be a string');\n if (value.length > MAX_DECIMAL_DIGITS) throw new CoinsError('INVALID_DECIMAL', 'Decimal input is too long');\n\n const match = DECIMAL.exec(value);\n\n if (!match) throw new CoinsError('INVALID_DECIMAL', `Invalid decimal: \"${value}\"`);\n\n const fraction = (match[3] ?? '').replace(/0+$/, '');\n\n if (fraction.length > MAX_DECIMAL_PLACES) {\n throw new CoinsError('INVALID_DECIMAL', `Decimal precision cannot exceed ${MAX_DECIMAL_PLACES} places`);\n }\n\n const denominator = 10n ** BigInt(fraction.length);\n const unsigned = BigInt(match[2]!) * denominator + BigInt(fraction || '0');\n const numerator = match[1] === '-' && unsigned !== 0n ? -unsigned : unsigned;\n const divisor = gcd(numerator < 0n ? -numerator : numerator, denominator);\n\n return Object.freeze({ denominator: denominator / divisor, numerator: numerator / divisor }) as Decimal;\n}\n\nexport function roundDivision(numerator: bigint, denominator: bigint, mode: RoundingMode): bigint {\n assertRoundingMode(mode);\n if (denominator <= 0n) throw new CoinsError('INVALID_DECIMAL', 'Decimal denominator must be positive');\n\n const negative = numerator < 0n;\n const absolute = negative ? -numerator : numerator;\n const quotient = absolute / denominator;\n const remainder = absolute % denominator;\n\n if (remainder === 0n) return negative ? -quotient : quotient;\n\n const increment = (() => {\n switch (mode) {\n case 'awayFromZero':\n return true;\n case 'ceil':\n return !negative;\n case 'floor':\n return negative;\n case 'halfAwayFromZero':\n return remainder * 2n >= denominator;\n case 'halfEven': {\n const doubled = remainder * 2n;\n\n return doubled > denominator || (doubled === denominator && quotient % 2n !== 0n);\n }\n case 'towardZero':\n return false;\n default:\n throw new CoinsError('INVALID_ROUNDING', `Unknown rounding mode: ${mode satisfies never}`);\n }\n })();\n\n const result = increment ? quotient + 1n : quotient;\n\n return negative ? -result : result;\n}\n\nexport function toDecimalString(amount: bigint, minorUnit: number): string {\n const negative = amount < 0n;\n const absolute = negative ? -amount : amount;\n const scale = 10n ** BigInt(minorUnit);\n const whole = absolute / scale;\n\n if (minorUnit === 0) return `${negative ? '-' : ''}${whole}`;\n\n const fraction = (absolute % scale).toString().padStart(minorUnit, '0');\n\n return `${negative ? '-' : ''}${whole}.${fraction}`;\n}\n\nexport function gcd(left: bigint, right: bigint): bigint {\n let a = left;\n let b = right;\n\n while (b !== 0n) [a, b] = [b, a % b];\n\n return a === 0n ? 1n : a;\n}\n\nexport function lcm(left: bigint, right: bigint): bigint {\n return (left / gcd(left, right)) * right;\n}\n","import { CoinsError, InvalidCurrencyError } from './errors';\nimport type { Currency, CurrencyCode } from './types';\n\nconst builtins = new Map<string, Currency>();\nconst canonicalCurrencies = new WeakSet<object>();\n\nfunction register<C extends string>(code: C, minorUnit: number): Currency<C> {\n validateDefinition(code, minorUnit);\n\n const definition = Object.freeze({ code: code as CurrencyCode<C>, minorUnit }) as Currency<C>;\n\n canonicalCurrencies.add(definition);\n\n return definition;\n}\n\nfunction builtin<C extends string>(code: C, minorUnit: number): Currency<C> {\n const definition = register(code, minorUnit);\n\n builtins.set(code, definition);\n\n return definition;\n}\n\nexport const USD = builtin('USD', 2);\nexport const EUR = builtin('EUR', 2);\nexport const GBP = builtin('GBP', 2);\nexport const JPY = builtin('JPY', 0);\nexport const KRW = builtin('KRW', 0);\nexport const BHD = builtin('BHD', 3);\nexport const KWD = builtin('KWD', 3);\n\n/** Resolve a built-in currency by code, or construct an immutable custom currency from a definition. */\nexport function currency<C extends string>(code: C): Currency<C>;\nexport function currency<C extends string>(definition: { code: C; minorUnit: number }): Currency<C>;\nexport function currency<C extends string>(input: C | { code: C; minorUnit: number }): Currency<C> {\n if (typeof input === 'string') {\n const definition = builtins.get(input);\n\n if (!definition) throw new InvalidCurrencyError(input);\n\n return definition as Currency<C>;\n }\n\n if (typeof input !== 'object' || input === null) {\n throw new CoinsError('INVALID_CURRENCY', 'Currency definition must be an object');\n }\n\n return register(input.code, input.minorUnit);\n}\n\nexport function isCurrency(value: unknown): value is Currency {\n return typeof value === 'object' && value !== null && canonicalCurrencies.has(value);\n}\n\nfunction validateDefinition(code: string, minorUnit: number): void {\n if (!/^[A-Z]{3}$/.test(code)) {\n throw new CoinsError('INVALID_CURRENCY', `Currency code must be three uppercase letters: \"${code}\"`);\n }\n\n if (!Number.isInteger(minorUnit) || minorUnit < 0 || minorUnit > 6) {\n throw new CoinsError('INVALID_CURRENCY', `Currency \"${code}\" must have 0–6 minor-unit digits`);\n }\n}\n","import { decimal, roundDivision, toDecimalString } from './_decimal';\nimport { currency, isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport type { Currency, Money, RoundingMode } from './types';\n\nconst canonicalMoney = new WeakSet<object>();\n\nconst defaultRounding: RoundingMode = 'halfAwayFromZero';\n\nconst INTEGER = /^(?:0|-[1-9]\\d*|[1-9]\\d*)$/;\nconst MAX_INTEGER_DIGITS = 1000;\n\nexport function money<C extends Currency>(amount: string, currency: C, options?: { rounding?: RoundingMode }): Money<C>;\nexport function money<C extends Currency>(amount: bigint, currency: C, options: { unit: 'minor' }): Money<C>;\nexport function money<C extends Currency>(\n amount: bigint | string,\n currency: C,\n options?: { rounding?: RoundingMode } | { unit: 'minor' },\n): Money<C> {\n assertCurrency(currency);\n\n if (typeof amount === 'bigint') {\n if (!options || !('unit' in options) || options.unit !== 'minor') {\n throw new CoinsError('INVALID_MONEY', \"Bigint amounts require { unit: 'minor' }\");\n }\n\n return createMoney(amount, currency);\n }\n\n if (options && 'unit' in options)\n throw new CoinsError('INVALID_MONEY', 'Decimal strings do not accept a unit option');\n\n const value = decimal(amount);\n const scaled = value.numerator * 10n ** BigInt(currency.minorUnit);\n const remainder = scaled % value.denominator;\n\n if (remainder !== 0n && options?.rounding === undefined) {\n throw new CoinsError('INVALID_MONEY', `Amount \"${amount}\" exceeds ${currency.code} precision; provide rounding`);\n }\n\n return createMoney(roundDivision(scaled, value.denominator, options?.rounding ?? defaultRounding), currency);\n}\n\n/**\n * Decodes untrusted/persisted data into canonical `Money`.\n * Accepts either a plain `Money`-shaped object (`{ amount: bigint, currency: Currency }`)\n * or a `MoneyJSON`-shaped object (`{ amount: string, currency: string, unit: 'minor' }`).\n * Use `money()` for trusted construction; use `decodeMoney()` for network/storage data.\n */\nexport function decodeMoney(value: unknown, options?: { currency?: (code: string) => Currency }): Money {\n if (!isPlainDataObject(value)) throw new CoinsError('INVALID_MONEY', 'Money must be a plain data object');\n\n const descriptors = Object.getOwnPropertyDescriptors(value);\n const amountDescriptor = descriptors.amount;\n const currencyDescriptor = descriptors.currency;\n\n if (!isDataProperty(amountDescriptor) || !isDataProperty(currencyDescriptor)) {\n throw new CoinsError('INVALID_MONEY', 'Money properties must be plain data values');\n }\n\n const ownKeys = Reflect.ownKeys(value);\n\n // Plain Money: bigint amount + registered currency\n if (typeof amountDescriptor.value === 'bigint' && isCurrency(currencyDescriptor.value)) {\n if (ownKeys.length !== 2) {\n throw new CoinsError('INVALID_MONEY', 'Plain money must have exactly the keys amount and currency');\n }\n\n return createMoney(amountDescriptor.value, currencyDescriptor.value);\n }\n\n // MoneyJSON: string amount + string currency code + unit: 'minor'\n const unitDescriptor = descriptors.unit;\n if (\n typeof amountDescriptor.value === 'string' &&\n typeof currencyDescriptor.value === 'string' &&\n isDataProperty(unitDescriptor) &&\n unitDescriptor.value === 'minor'\n ) {\n if (ownKeys.length !== 3) {\n throw new CoinsError('INVALID_MONEY', 'MoneyJSON must have exactly the keys amount, currency, unit');\n }\n if (amountDescriptor.value.length > MAX_INTEGER_DIGITS) {\n throw new CoinsError('INVALID_MONEY', `Money JSON amount is too long; maximum ${MAX_INTEGER_DIGITS} characters`);\n }\n if (!INTEGER.test(amountDescriptor.value)) {\n throw new CoinsError('INVALID_MONEY', `Invalid Money JSON amount \"${amountDescriptor.value}\"`);\n }\n const resolveCurrency = options?.currency ?? currency;\n try {\n const resolvedCurrency = resolveCurrency(currencyDescriptor.value);\n\n if (!isCurrency(resolvedCurrency) || resolvedCurrency.code !== currencyDescriptor.value) {\n throw new CoinsError(\n 'INVALID_CURRENCY',\n `Currency resolver must return the canonical \"${currencyDescriptor.value}\" definition`,\n );\n }\n\n return money(BigInt(amountDescriptor.value), resolvedCurrency, { unit: 'minor' });\n } catch (error) {\n throw new CoinsError('INVALID_MONEY', `Invalid Money JSON for currency \"${currencyDescriptor.value}\"`, {\n cause: error,\n });\n }\n }\n\n throw new CoinsError(\n 'INVALID_MONEY',\n 'Money must contain bigint amount and a registered currency, or a valid MoneyJSON shape',\n );\n}\n\nexport function isMoney(value: unknown): value is Money {\n return typeof value === 'object' && value !== null && canonicalMoney.has(value);\n}\n\nexport function add<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount + right.amount, left.currency);\n}\n\nexport function subtract<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount - right.amount, left.currency);\n}\n\nexport function multiply<C extends Currency>(\n value: Money<C>,\n factor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(factor);\n\n return createMoney(\n roundDivision(value.amount * scalar.numerator, scalar.denominator, options.rounding ?? defaultRounding),\n value.currency,\n );\n}\n\nexport function divide<C extends Currency>(\n value: Money<C>,\n divisor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(divisor);\n\n if (scalar.numerator === 0n) throw new CoinsError('DIVISION_BY_ZERO', 'Cannot divide money by zero');\n\n const divisorMagnitude = scalar.numerator < 0n ? -scalar.numerator : scalar.numerator;\n const dividend = value.amount * scalar.denominator;\n const quotient = roundDivision(\n scalar.numerator < 0n ? -dividend : dividend,\n divisorMagnitude,\n options.rounding ?? defaultRounding,\n );\n\n return createMoney(quotient, value.currency);\n}\n\nexport function compare<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): -1 | 0 | 1 {\n assertSameCurrency(left, right);\n\n return left.amount === right.amount ? 0 : left.amount < right.amount ? -1 : 1;\n}\n\nexport function clamp<C extends Currency>(\n value: Money<C>,\n options: { max: Money<NoInfer<C>>; min: Money<NoInfer<C>> },\n): Money<C> {\n if (compare(options.min, options.max) === 1) {\n throw new CoinsError('INVALID_RANGE', 'Clamp minimum cannot exceed maximum');\n }\n\n return compare(value, options.min) === -1 ? options.min : compare(value, options.max) === 1 ? options.max : value;\n}\n\nexport function abs<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(value.amount < 0n ? -value.amount : value.amount, value.currency);\n}\n\nexport function negate<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(-value.amount, value.currency);\n}\n\nexport function round<C extends Currency>(\n value: Money<C>,\n options: { fractionDigits: number; rounding?: RoundingMode },\n): Money<C> {\n assertMoney(value);\n\n const { fractionDigits, rounding = defaultRounding } = options;\n\n if (!Number.isInteger(fractionDigits) || fractionDigits < 0 || fractionDigits > value.currency.minorUnit) {\n throw new CoinsError('INVALID_ROUNDING', `fractionDigits must be an integer from 0 to ${value.currency.minorUnit}`);\n }\n\n const factor = 10n ** BigInt(value.currency.minorUnit - fractionDigits);\n\n return createMoney(roundDivision(value.amount, factor, rounding) * factor, value.currency);\n}\n\nexport function toDecimal(value: Money): string {\n assertMoney(value);\n\n return toDecimalString(value.amount, value.currency.minorUnit);\n}\n\nexport function createMoney<C extends Currency>(amount: bigint, currency: C): Money<C> {\n const value = Object.freeze({ amount, currency }) as Money<C>;\n\n canonicalMoney.add(value);\n\n return value;\n}\n\nexport function assertMoney(value: unknown): asserts value is Money {\n if (!isMoney(value)) {\n throw new CoinsError('INVALID_MONEY', 'Money must be a canonical @vielzeug/coins value');\n }\n}\n\nfunction assertSameCurrency(left: Money, right: Money): void {\n assertMoney(left);\n assertMoney(right);\n\n if (left.currency !== right.currency) {\n throw new CurrencyMismatchError(left.currency.code, right.currency.code);\n }\n}\n\nfunction assertCurrency(value: unknown): asserts value is Currency {\n if (!isCurrency(value)) throw new CoinsError('INVALID_CURRENCY', 'Money requires a registered currency');\n}\n\nfunction isPlainDataObject(value: unknown): value is Record<PropertyKey, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n\n const prototype = Object.getPrototypeOf(value);\n\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction isDataProperty(\n descriptor: PropertyDescriptor | undefined,\n): descriptor is PropertyDescriptor & { value: unknown } {\n return (\n descriptor !== undefined && 'value' in descriptor && descriptor.get === undefined && descriptor.set === undefined\n );\n}\n","import { decimal, lcm } from './_decimal';\nimport { isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { assertMoney, createMoney } from './money';\nimport type { Currency, Money } from './types';\n\nconst MAX_ALLOCATION_PARTS = 100_000;\n\nexport function sum<C extends Currency>(values: readonly Money<C>[]): Money<C>;\nexport function sum<C extends Currency>(values: Iterable<Money<C>>, options: { currency: C }): Money<C>;\nexport function sum<C extends Currency>(values: Iterable<Money<C>>, options?: { currency: C }): Money<C> {\n if (options && !isCurrency(options.currency)) {\n throw new CoinsError('INVALID_CURRENCY', 'sum() requires a canonical currency');\n }\n\n let amount = 0n;\n let currency: C | undefined = options?.currency;\n\n for (const value of values) {\n assertMoney(value);\n\n if (currency === undefined) {\n currency = value.currency;\n } else if (value.currency !== currency) {\n throw new CurrencyMismatchError(currency.code, value.currency.code);\n }\n\n amount += value.amount;\n }\n\n if (currency === undefined) throw new CoinsError('INVALID_MONEY', 'sum() of empty iterable requires { currency }');\n\n return createMoney(amount, currency);\n}\n\nexport function allocate<C extends Currency>(value: Money<C>, count: number): Money<C>[];\nexport function allocate<C extends Currency>(value: Money<C>, weights: readonly string[]): Money<C>[];\nexport function allocate<C extends Currency>(value: Money<C>, weightsOrCount: number | readonly string[]): Money<C>[] {\n assertMoney(value);\n\n if (typeof weightsOrCount === 'number') return allocateEvenly(value, weightsOrCount);\n\n if (!Array.isArray(weightsOrCount)) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights must be an array');\n }\n if (weightsOrCount.length === 0) throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot be empty');\n if (weightsOrCount.length > MAX_ALLOCATION_PARTS) {\n throw new CoinsError('INVALID_ALLOCATION', `Allocation cannot exceed ${MAX_ALLOCATION_PARTS} parts`);\n }\n for (let index = 0; index < weightsOrCount.length; index++) {\n if (!Object.hasOwn(weightsOrCount, index)) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot contain empty slots');\n }\n }\n\n const weights = weightsOrCount.map(decimal);\n\n if (weights.some((weight) => weight.numerator < 0n)) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot be negative');\n }\n\n const commonDenominator = weights.reduce((result, weight) => lcm(result, weight.denominator), 1n);\n const scaledWeights = weights.map((weight) => weight.numerator * (commonDenominator / weight.denominator));\n const totalWeight = scaledWeights.reduce((result, weight) => result + weight, 0n);\n\n if (totalWeight === 0n)\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights must include a positive value');\n\n const sign = value.amount < 0n ? -1n : 1n;\n const absolute = value.amount < 0n ? -value.amount : value.amount;\n const shares = scaledWeights.map((weight) => (absolute * weight) / totalWeight);\n let remainder = absolute - shares.reduce((result, amount) => result + amount, 0n);\n const ranked = scaledWeights\n .map((weight, index) => ({ index, remainder: (absolute * weight) % totalWeight }))\n .sort((left, right) =>\n left.remainder === right.remainder ? left.index - right.index : left.remainder > right.remainder ? -1 : 1,\n );\n\n for (const entry of ranked) {\n if (remainder === 0n) break;\n\n shares[entry.index]! += 1n;\n remainder -= 1n;\n }\n\n return shares.map((amount) => createMoney(amount * sign, value.currency));\n}\n\nfunction allocateEvenly<C extends Currency>(value: Money<C>, countValue: number): Money<C>[] {\n if (!Number.isSafeInteger(countValue) || countValue < 1 || countValue > MAX_ALLOCATION_PARTS) {\n throw new CoinsError(\n 'INVALID_ALLOCATION',\n `Allocation count must be a positive safe integer no greater than ${MAX_ALLOCATION_PARTS}`,\n );\n }\n\n const count = BigInt(countValue);\n const sign = value.amount < 0n ? -1n : 1n;\n const absolute = value.amount < 0n ? -value.amount : value.amount;\n const base = absolute / count;\n const remainder = absolute % count;\n\n return Array.from({ length: countValue }, (_, index) =>\n createMoney((base + (BigInt(index) < remainder ? 1n : 0n)) * sign, value.currency),\n );\n}\n","import { decimal, roundDivision } from './_decimal';\nimport { isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { assertMoney, createMoney } from './money';\nimport type { Currency, ExchangeRate, Money, RoundingMode } from './types';\n\nconst canonicalRates = new WeakSet<object>();\n\nexport function exchangeRate<From extends Currency, To extends Currency>({\n from,\n to,\n value,\n}: {\n from: From;\n to: To;\n value: string;\n}): ExchangeRate<From, To> {\n if (!isCurrency(from) || !isCurrency(to))\n throw new CoinsError('INVALID_CURRENCY', 'Exchange rate requires registered currencies');\n\n const parsed = decimal(value);\n\n if (parsed.numerator <= 0n) throw new CoinsError('INVALID_DECIMAL', 'Exchange rates must be positive');\n\n const rate = Object.freeze({ from, to, value: parsed }) as ExchangeRate<From, To>;\n\n canonicalRates.add(rate);\n\n return rate;\n}\n\nexport function isExchangeRate(value: unknown): value is ExchangeRate {\n return typeof value === 'object' && value !== null && canonicalRates.has(value);\n}\n\nexport function exchange<From extends Currency, To extends Currency>(\n value: Money<From>,\n rate: ExchangeRate<From, To>,\n options: { rounding?: RoundingMode } = {},\n): Money<To> {\n assertMoney(value);\n\n if (!isExchangeRate(rate)) {\n throw new CoinsError('INVALID_EXCHANGE_RATE', 'Exchange requires a canonical exchange rate');\n }\n\n if (value.currency !== rate.from) throw new CurrencyMismatchError(value.currency.code, rate.from.code);\n\n const numerator = value.amount * rate.value.numerator * 10n ** BigInt(rate.to.minorUnit);\n const denominator = rate.value.denominator * 10n ** BigInt(rate.from.minorUnit);\n\n return createMoney(roundDivision(numerator, denominator, options.rounding ?? 'halfAwayFromZero'), rate.to);\n}\n","import { assertRoundingMode } from './_decimal';\nimport { CoinsError } from './errors';\nimport { assertMoney, toDecimal } from './money';\nimport type { FormatOptions, Money, MoneyFormatPart, RoundingMode } from './types';\n\nconst MAX_FRACTION_DIGITS = 20;\nconst defaultFormatRounding: RoundingMode = 'halfAwayFromZero';\nconst intlRoundingModes: Record<RoundingMode, Intl.NumberFormatOptions['roundingMode']> = {\n awayFromZero: 'expand',\n ceil: 'ceil',\n floor: 'floor',\n halfAwayFromZero: 'halfExpand',\n halfEven: 'halfEven',\n towardZero: 'trunc',\n};\n\nexport function format(value: Money, options: FormatOptions = {}): string {\n return formatParts(value, options)\n .map((part) => part.value)\n .join('');\n}\n\nexport function formatParts(value: Money, options: FormatOptions = {}): MoneyFormatPart[] {\n assertMoney(value);\n assertRoundingMode(options.rounding ?? defaultFormatRounding);\n\n const { maximum, minimum } = fractionDigits(value.currency.minorUnit, options);\n const style = options.style ?? 'symbol';\n\n try {\n const formatter = new Intl.NumberFormat(options.locale ?? 'en-US', {\n currency: value.currency.code,\n currencyDisplay: style,\n maximumFractionDigits: maximum,\n minimumFractionDigits: minimum,\n roundingMode: intlRoundingModes[options.rounding ?? defaultFormatRounding],\n style: 'currency',\n useGrouping: true,\n });\n\n return normalizeParts(formatter.formatToParts(toDecimal(value) as unknown as number));\n } catch (error) {\n if (error instanceof CoinsError) throw error;\n\n throw new CoinsError(\n 'FORMAT_ERROR',\n `Cannot format currency \"${value.currency.code}\" for locale \"${options.locale ?? 'en-US'}\"`,\n { cause: error },\n );\n }\n}\n\nfunction fractionDigits(scale: number, options: FormatOptions): { maximum: number; minimum: number } {\n const requestedMaximum = options.maximumFractionDigits;\n const requestedMinimum = options.minimumFractionDigits;\n\n validateFractionDigit('maximumFractionDigits', requestedMaximum);\n validateFractionDigit('minimumFractionDigits', requestedMinimum);\n\n const maximum = requestedMaximum ?? Math.max(scale, requestedMinimum ?? scale);\n const minimum = requestedMinimum ?? Math.min(scale, requestedMaximum ?? scale);\n\n if (minimum > maximum) {\n throw new CoinsError('FORMAT_ERROR', 'minimumFractionDigits cannot exceed maximumFractionDigits');\n }\n\n return { maximum, minimum };\n}\n\nfunction validateFractionDigit(name: string, value: number | undefined): void {\n if (value !== undefined && (!Number.isInteger(value) || value < 0 || value > MAX_FRACTION_DIGITS)) {\n throw new CoinsError(\n 'FORMAT_ERROR',\n `Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${MAX_FRACTION_DIGITS} (${name})`,\n );\n }\n}\n\nfunction normalizeParts(raw: Intl.NumberFormatPart[]): MoneyFormatPart[] {\n const parts: MoneyFormatPart[] = [];\n\n for (const part of raw) {\n if (part.type === 'integer' || part.type === 'group') {\n const previous = parts.at(-1);\n\n if (previous?.type === 'integer') {\n parts[parts.length - 1] = { type: 'integer', value: previous.value + part.value };\n } else {\n parts.push({ type: 'integer', value: part.value });\n }\n } else if (\n part.type === 'currency' ||\n part.type === 'decimal' ||\n part.type === 'fraction' ||\n part.type === 'literal' ||\n part.type === 'minusSign' ||\n part.type === 'plusSign'\n ) {\n parts.push({ type: part.type, value: part.value });\n } else {\n parts.push({ type: 'literal', value: part.value });\n }\n }\n\n return parts;\n}\n","import { assertMoney } from './money';\nimport type { Money, MoneyJSON } from './types';\n\nexport function toJSON(value: Money): MoneyJSON {\n assertMoney(value);\n\n return { amount: value.amount.toString(), currency: value.currency.code, unit: 'minor' };\n}\n"],"mappings":"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"}
package/dist/currency.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CoinsError as e, InvalidCurrencyError as t } from "./errors.js";
2
- //#region src/currency.ts
2
+ // src/currency.ts
3
3
  var n = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new WeakSet();
4
4
  function register(e, t) {
5
5
  validateDefinition(e, t);
@@ -30,7 +30,6 @@ function validateDefinition(t, n) {
30
30
  if (!/^[A-Z]{3}$/.test(t)) throw new e("INVALID_CURRENCY", `Currency code must be three uppercase letters: "${t}"`);
31
31
  if (!Number.isInteger(n) || n < 0 || n > 6) throw new e("INVALID_CURRENCY", `Currency "${t}" must have 0–6 minor-unit digits`);
32
32
  }
33
- //#endregion
34
33
  export { l as BHD, a as EUR, o as GBP, s as JPY, c as KRW, u as KWD, i as USD, currency, isCurrency };
35
34
 
36
35
  //# sourceMappingURL=currency.js.map
package/dist/errors.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/errors.ts
1
+ // src/errors.ts
2
2
  var CoinsError = class extends Error {
3
3
  code;
4
4
  constructor(e, t, n) {
@@ -24,7 +24,6 @@ var InvalidCurrencyError = class extends CoinsError {
24
24
  super("INVALID_CURRENCY", `Unsupported currency: "${describe(e)}"`), this.value = e;
25
25
  }
26
26
  };
27
- //#endregion
28
27
  export { CoinsError, CurrencyMismatchError, InvalidCurrencyError };
29
28
 
30
29
  //# sourceMappingURL=errors.js.map
package/dist/exchange.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./_decimal.cjs"),n=require("./currency.cjs"),r=require("./money.cjs");var i=new WeakSet;function exchangeRate({from:r,to:a,value:o}){if(!n.isCurrency(r)||!n.isCurrency(a))throw new e.CoinsError(`INVALID_CURRENCY`,`Exchange rate requires registered currencies`);let s=t.decimal(o);if(s.numerator<=0n)throw new e.CoinsError(`INVALID_DECIMAL`,`Exchange rates must be positive`);let c=Object.freeze({from:r,to:a,value:s});return i.add(c),c}function isExchangeRate(e){return typeof e==`object`&&!!e&&i.has(e)}function exchange(n,i,a={}){if(r.assertMoney(n),!isExchangeRate(i))throw new e.CoinsError(`INVALID_EXCHANGE_RATE`,`Exchange requires a canonical exchange rate`);if(n.currency!==i.from)throw new e.CurrencyMismatchError(n.currency.code,i.from.code);let o=n.amount*i.value.numerator*10n**BigInt(i.to.minorUnit),s=i.value.denominator*10n**BigInt(i.from.minorUnit);return r.createMoney(t.roundDivision(o,s,a.rounding??`halfAwayFromZero`),i.to)}exports.exchange=exchange,exports.exchangeRate=exchangeRate,exports.isExchangeRate=isExchangeRate;
1
+ const e=require("./_decimal.cjs"),t=require("./currency.cjs"),n=require("./errors.cjs"),r=require("./money.cjs");var i=new WeakSet;function exchangeRate({from:r,to:a,value:o}){if(!t.isCurrency(r)||!t.isCurrency(a))throw new n.CoinsError(`INVALID_CURRENCY`,`Exchange rate requires registered currencies`);let s=e.decimal(o);if(s.numerator<=0n)throw new n.CoinsError(`INVALID_DECIMAL`,`Exchange rates must be positive`);let c=Object.freeze({from:r,to:a,value:s});return i.add(c),c}function isExchangeRate(e){return typeof e==`object`&&!!e&&i.has(e)}function exchange(t,i,a={}){if(r.assertMoney(t),!isExchangeRate(i))throw new n.CoinsError(`INVALID_EXCHANGE_RATE`,`Exchange requires a canonical exchange rate`);if(t.currency!==i.from)throw new n.CurrencyMismatchError(t.currency.code,i.from.code);let o=t.amount*i.value.numerator*10n**BigInt(i.to.minorUnit),s=i.value.denominator*10n**BigInt(i.from.minorUnit);return r.createMoney(e.roundDivision(o,s,a.rounding??`halfAwayFromZero`),i.to)}exports.exchange=exchange,exports.exchangeRate=exchangeRate,exports.isExchangeRate=isExchangeRate;
2
2
  //# sourceMappingURL=exchange.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"exchange.cjs","names":["isCurrency","CoinsError","decimal","CurrencyMismatchError","createMoney","roundDivision","require_errors","require__decimal","require_currency","require_money","canonicalRates","from","to","parsed","value","rate","numerator","denominator","options"],"sources":["../src/exchange.ts"],"sourcesContent":["import { decimal, roundDivision } from './_decimal';\nimport { isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { assertMoney, createMoney } from './money';\nimport type { Currency, ExchangeRate, Money, RoundingMode } from './types';\n\nconst canonicalRates = new WeakSet<object>();\n\nexport function exchangeRate<From extends Currency, To extends Currency>({\n from,\n to,\n value,\n}: {\n from: From;\n to: To;\n value: string;\n}): ExchangeRate<From, To> {\n if (!isCurrency(from) || !isCurrency(to))\n throw new CoinsError('INVALID_CURRENCY', 'Exchange rate requires registered currencies');\n\n const parsed = decimal(value);\n\n if (parsed.numerator <= 0n) throw new CoinsError('INVALID_DECIMAL', 'Exchange rates must be positive');\n\n const rate = Object.freeze({ from, to, value: parsed }) as ExchangeRate<From, To>;\n\n canonicalRates.add(rate);\n\n return rate;\n}\n\nexport function isExchangeRate(value: unknown): value is ExchangeRate {\n return typeof value === 'object' && value !== null && canonicalRates.has(value);\n}\n\nexport function exchange<From extends Currency, To extends Currency>(\n value: Money<From>,\n rate: ExchangeRate<From, To>,\n options: { rounding?: RoundingMode } = {},\n): Money<To> {\n assertMoney(value);\n\n if (!isExchangeRate(rate)) {\n throw new CoinsError('INVALID_EXCHANGE_RATE', 'Exchange requires a canonical exchange rate');\n }\n\n if (value.currency !== rate.from) throw new CurrencyMismatchError(value.currency.code, rate.from.code);\n\n const numerator = value.amount * rate.value.numerator * 10n ** BigInt(rate.to.minorUnit);\n const denominator = rate.value.denominator * 10n ** BigInt(rate.from.minorUnit);\n\n return createMoney(roundDivision(numerator, denominator, options.rounding ?? 'halfAwayFromZero'), rate.to);\n}\n"],"mappings":"A,iHAMA,IAAMU,EAAiB,IAAI,QAE3B,SAAgB,aAAyD,CACvE,OACA,KACA,SAKyB,CACzB,GAAI,CAACV,EAAAA,WAAWW,CAAI,GAAK,CAACX,EAAAA,WAAWY,CAAE,EACrC,MAAM,IAAIX,EAAAA,WAAW,mBAAoB,8CAA8C,EAEzF,IAAMY,EAASX,EAAAA,QAAQY,CAAK,EAE5B,GAAID,EAAO,WAAa,GAAI,MAAM,IAAIZ,EAAAA,WAAW,kBAAmB,iCAAiC,EAErG,IAAMc,EAAO,OAAO,OAAO,CAAE,OAAM,KAAI,MAAOF,CAAO,CAAC,EAItD,OAFAH,EAAe,IAAIK,CAAI,EAEhBA,CACT,CAEA,SAAgB,eAAe,EAAuC,CACpE,OAAO,OAAOD,GAAU,YAAYA,GAAkBJ,EAAe,IAAII,CAAK,CAChF,CAEA,SAAgB,SACd,EACA,EACA,EAAuC,CAAC,EAC7B,CAGX,GAFAL,EAAA,YAAYK,CAAK,EAEb,CAAC,eAAeC,CAAI,EACtB,MAAM,IAAId,EAAAA,WAAW,wBAAyB,6CAA6C,EAG7F,GAAIa,EAAM,WAAaC,EAAK,KAAM,MAAM,IAAIZ,EAAAA,sBAAsBW,EAAM,SAAS,KAAMC,EAAK,KAAK,IAAI,EAErG,IAAMC,EAAYF,EAAM,OAASC,EAAK,MAAM,UAAY,KAAO,OAAOA,EAAK,GAAG,SAAS,EACjFE,EAAcF,EAAK,MAAM,YAAc,KAAO,OAAOA,EAAK,KAAK,SAAS,EAE9E,OAAOX,EAAAA,YAAYC,EAAAA,cAAcW,EAAWC,EAAaC,EAAQ,UAAY,kBAAkB,EAAGH,EAAK,EAAE,CAC3G,C"}
1
+ {"version":3,"file":"exchange.cjs","names":["isCurrency","CoinsError","decimal","CurrencyMismatchError","createMoney","roundDivision","require__decimal","require_currency","require_errors","require_money","canonicalRates","from","to","parsed","value","rate","numerator","denominator","options"],"sources":["../src/exchange.ts"],"sourcesContent":["import { decimal, roundDivision } from './_decimal';\nimport { isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { assertMoney, createMoney } from './money';\nimport type { Currency, ExchangeRate, Money, RoundingMode } from './types';\n\nconst canonicalRates = new WeakSet<object>();\n\nexport function exchangeRate<From extends Currency, To extends Currency>({\n from,\n to,\n value,\n}: {\n from: From;\n to: To;\n value: string;\n}): ExchangeRate<From, To> {\n if (!isCurrency(from) || !isCurrency(to))\n throw new CoinsError('INVALID_CURRENCY', 'Exchange rate requires registered currencies');\n\n const parsed = decimal(value);\n\n if (parsed.numerator <= 0n) throw new CoinsError('INVALID_DECIMAL', 'Exchange rates must be positive');\n\n const rate = Object.freeze({ from, to, value: parsed }) as ExchangeRate<From, To>;\n\n canonicalRates.add(rate);\n\n return rate;\n}\n\nexport function isExchangeRate(value: unknown): value is ExchangeRate {\n return typeof value === 'object' && value !== null && canonicalRates.has(value);\n}\n\nexport function exchange<From extends Currency, To extends Currency>(\n value: Money<From>,\n rate: ExchangeRate<From, To>,\n options: { rounding?: RoundingMode } = {},\n): Money<To> {\n assertMoney(value);\n\n if (!isExchangeRate(rate)) {\n throw new CoinsError('INVALID_EXCHANGE_RATE', 'Exchange requires a canonical exchange rate');\n }\n\n if (value.currency !== rate.from) throw new CurrencyMismatchError(value.currency.code, rate.from.code);\n\n const numerator = value.amount * rate.value.numerator * 10n ** BigInt(rate.to.minorUnit);\n const denominator = rate.value.denominator * 10n ** BigInt(rate.from.minorUnit);\n\n return createMoney(roundDivision(numerator, denominator, options.rounding ?? 'halfAwayFromZero'), rate.to);\n}\n"],"mappings":"A,iHAMA,IAAMU,EAAiB,IAAI,QAE3B,SAAgB,aAAyD,CACvE,OACA,KACA,SAKyB,CACzB,GAAI,CAACV,EAAAA,WAAWW,CAAI,GAAK,CAACX,EAAAA,WAAWY,CAAE,EACrC,MAAM,IAAIX,EAAAA,WAAW,mBAAoB,8CAA8C,EAEzF,IAAMY,EAASX,EAAAA,QAAQY,CAAK,EAE5B,GAAID,EAAO,WAAa,GAAI,MAAM,IAAIZ,EAAAA,WAAW,kBAAmB,iCAAiC,EAErG,IAAMc,EAAO,OAAO,OAAO,CAAE,OAAM,KAAI,MAAOF,CAAO,CAAC,EAItD,OAFAH,EAAe,IAAIK,CAAI,EAEhBA,CACT,CAEA,SAAgB,eAAe,EAAuC,CACpE,OAAO,OAAOD,GAAU,YAAYA,GAAkBJ,EAAe,IAAII,CAAK,CAChF,CAEA,SAAgB,SACd,EACA,EACA,EAAuC,CAAC,EAC7B,CAGX,GAFAL,EAAA,YAAYK,CAAK,EAEb,CAAC,eAAeC,CAAI,EACtB,MAAM,IAAId,EAAAA,WAAW,wBAAyB,6CAA6C,EAG7F,GAAIa,EAAM,WAAaC,EAAK,KAAM,MAAM,IAAIZ,EAAAA,sBAAsBW,EAAM,SAAS,KAAMC,EAAK,KAAK,IAAI,EAErG,IAAMC,EAAYF,EAAM,OAASC,EAAK,MAAM,UAAY,KAAO,OAAOA,EAAK,GAAG,SAAS,EACjFE,EAAcF,EAAK,MAAM,YAAc,KAAO,OAAOA,EAAK,KAAK,SAAS,EAE9E,OAAOX,EAAAA,YAAYC,EAAAA,cAAcW,EAAWC,EAAaC,EAAQ,UAAY,kBAAkB,EAAGH,EAAK,EAAE,CAC3G,C"}
package/dist/exchange.js CHANGED
@@ -1,16 +1,16 @@
1
- import { CoinsError as e, CurrencyMismatchError as t } from "./errors.js";
2
- import { decimal as n, roundDivision as r } from "./_decimal.js";
3
- import { isCurrency as i } from "./currency.js";
1
+ import { decimal as e, roundDivision as t } from "./_decimal.js";
2
+ import { isCurrency as n } from "./currency.js";
3
+ import { CoinsError as r, CurrencyMismatchError as i } from "./errors.js";
4
4
  import { assertMoney as a, createMoney as o } from "./money.js";
5
- //#region src/exchange.ts
5
+ // src/exchange.ts
6
6
  var s = /* @__PURE__ */ new WeakSet();
7
- function exchangeRate({ from: t, to: r, value: a }) {
8
- if (!i(t) || !i(r)) throw new e("INVALID_CURRENCY", "Exchange rate requires registered currencies");
9
- let o = n(a);
10
- if (o.numerator <= 0n) throw new e("INVALID_DECIMAL", "Exchange rates must be positive");
7
+ function exchangeRate({ from: t, to: i, value: a }) {
8
+ if (!n(t) || !n(i)) throw new r("INVALID_CURRENCY", "Exchange rate requires registered currencies");
9
+ let o = e(a);
10
+ if (o.numerator <= 0n) throw new r("INVALID_DECIMAL", "Exchange rates must be positive");
11
11
  let c = Object.freeze({
12
12
  from: t,
13
- to: r,
13
+ to: i,
14
14
  value: o
15
15
  });
16
16
  return s.add(c), c;
@@ -18,13 +18,12 @@ function exchangeRate({ from: t, to: r, value: a }) {
18
18
  function isExchangeRate(e) {
19
19
  return typeof e == "object" && !!e && s.has(e);
20
20
  }
21
- function exchange(n, i, s = {}) {
22
- if (a(n), !isExchangeRate(i)) throw new e("INVALID_EXCHANGE_RATE", "Exchange requires a canonical exchange rate");
23
- if (n.currency !== i.from) throw new t(n.currency.code, i.from.code);
24
- let c = n.amount * i.value.numerator * 10n ** BigInt(i.to.minorUnit), l = i.value.denominator * 10n ** BigInt(i.from.minorUnit);
25
- return o(r(c, l, s.rounding ?? "halfAwayFromZero"), i.to);
21
+ function exchange(e, n, s = {}) {
22
+ if (a(e), !isExchangeRate(n)) throw new r("INVALID_EXCHANGE_RATE", "Exchange requires a canonical exchange rate");
23
+ if (e.currency !== n.from) throw new i(e.currency.code, n.from.code);
24
+ let c = e.amount * n.value.numerator * 10n ** BigInt(n.to.minorUnit), l = n.value.denominator * 10n ** BigInt(n.from.minorUnit);
25
+ return o(t(c, l, s.rounding ?? "halfAwayFromZero"), n.to);
26
26
  }
27
- //#endregion
28
27
  export { exchange, exchangeRate, isExchangeRate };
29
28
 
30
29
  //# sourceMappingURL=exchange.js.map
package/dist/format.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./_decimal.cjs"),n=require("./money.cjs");var r=20,i=`halfAwayFromZero`,a={awayFromZero:`expand`,ceil:`ceil`,floor:`floor`,halfAwayFromZero:`halfExpand`,halfEven:`halfEven`,towardZero:`trunc`};function format(e,t={}){return formatParts(e,t).map(e=>e.value).join(``)}function formatParts(r,o={}){n.assertMoney(r),t.assertRoundingMode(o.rounding??i);let{maximum:s,minimum:c}=fractionDigits(r.currency.minorUnit,o),l=o.style??`symbol`;try{return normalizeParts(new Intl.NumberFormat(o.locale??`en-US`,{currency:r.currency.code,currencyDisplay:l,maximumFractionDigits:s,minimumFractionDigits:c,roundingMode:a[o.rounding??i],style:`currency`,useGrouping:!0}).formatToParts(n.toDecimal(r)))}catch(t){throw t instanceof e.CoinsError?t:new e.CoinsError(`FORMAT_ERROR`,`Cannot format currency "${r.currency.code}" for locale "${o.locale??`en-US`}"`,{cause:t})}}function fractionDigits(t,n){let r=n.maximumFractionDigits,i=n.minimumFractionDigits;validateFractionDigit(`maximumFractionDigits`,r),validateFractionDigit(`minimumFractionDigits`,i);let a=r??Math.max(t,i??t),o=i??Math.min(t,r??t);if(o>a)throw new e.CoinsError(`FORMAT_ERROR`,`minimumFractionDigits cannot exceed maximumFractionDigits`);return{maximum:a,minimum:o}}function validateFractionDigit(t,n){if(n!==void 0&&(!Number.isInteger(n)||n<0||n>r))throw new e.CoinsError(`FORMAT_ERROR`,`Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${r} (${t})`)}function normalizeParts(e){let t=[];for(let n of e)if(n.type===`integer`||n.type===`group`){let e=t.at(-1);e?.type===`integer`?t[t.length-1]={type:`integer`,value:e.value+n.value}:t.push({type:`integer`,value:n.value})}else n.type===`currency`||n.type===`decimal`||n.type===`fraction`||n.type===`literal`||n.type===`minusSign`||n.type===`plusSign`?t.push({type:n.type,value:n.value}):t.push({type:`literal`,value:n.value});return t}exports.format=format,exports.formatParts=formatParts;
1
+ const e=require("./_decimal.cjs"),t=require("./errors.cjs"),n=require("./money.cjs");var r=20,i=`halfAwayFromZero`,a={awayFromZero:`expand`,ceil:`ceil`,floor:`floor`,halfAwayFromZero:`halfExpand`,halfEven:`halfEven`,towardZero:`trunc`};function format(e,t={}){return formatParts(e,t).map(e=>e.value).join(``)}function formatParts(r,o={}){n.assertMoney(r),e.assertRoundingMode(o.rounding??i);let{maximum:s,minimum:c}=fractionDigits(r.currency.minorUnit,o),l=o.style??`symbol`;try{return normalizeParts(new Intl.NumberFormat(o.locale??`en-US`,{currency:r.currency.code,currencyDisplay:l,maximumFractionDigits:s,minimumFractionDigits:c,roundingMode:a[o.rounding??i],style:`currency`,useGrouping:!0}).formatToParts(n.toDecimal(r)))}catch(e){throw e instanceof t.CoinsError?e:new t.CoinsError(`FORMAT_ERROR`,`Cannot format currency "${r.currency.code}" for locale "${o.locale??`en-US`}"`,{cause:e})}}function fractionDigits(e,n){let r=n.maximumFractionDigits,i=n.minimumFractionDigits;validateFractionDigit(`maximumFractionDigits`,r),validateFractionDigit(`minimumFractionDigits`,i);let a=r??Math.max(e,i??e),o=i??Math.min(e,r??e);if(o>a)throw new t.CoinsError(`FORMAT_ERROR`,`minimumFractionDigits cannot exceed maximumFractionDigits`);return{maximum:a,minimum:o}}function validateFractionDigit(e,n){if(n!==void 0&&(!Number.isInteger(n)||n<0||n>r))throw new t.CoinsError(`FORMAT_ERROR`,`Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${r} (${e})`)}function normalizeParts(e){let t=[];for(let n of e)if(n.type===`integer`||n.type===`group`){let e=t.at(-1);e?.type===`integer`?t[t.length-1]={type:`integer`,value:e.value+n.value}:t.push({type:`integer`,value:n.value})}else n.type===`currency`||n.type===`decimal`||n.type===`fraction`||n.type===`literal`||n.type===`minusSign`||n.type===`plusSign`?t.push({type:n.type,value:n.value}):t.push({type:`literal`,value:n.value});return t}exports.format=format,exports.formatParts=formatParts;
2
2
  //# sourceMappingURL=format.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.cjs","names":["toDecimal","CoinsError","require_errors","require__decimal","require_money","MAX_FRACTION_DIGITS","defaultFormatRounding","intlRoundingModes","value","options","part","style","maximum","minimum","error","requestedMaximum","requestedMinimum","scale","name","parts","raw","previous"],"sources":["../src/format.ts"],"sourcesContent":["import { assertRoundingMode } from './_decimal';\nimport { CoinsError } from './errors';\nimport { assertMoney, toDecimal } from './money';\nimport type { FormatOptions, Money, MoneyFormatPart, RoundingMode } from './types';\n\nconst MAX_FRACTION_DIGITS = 20;\nconst defaultFormatRounding: RoundingMode = 'halfAwayFromZero';\nconst intlRoundingModes: Record<RoundingMode, Intl.NumberFormatOptions['roundingMode']> = {\n awayFromZero: 'expand',\n ceil: 'ceil',\n floor: 'floor',\n halfAwayFromZero: 'halfExpand',\n halfEven: 'halfEven',\n towardZero: 'trunc',\n};\n\nexport function format(value: Money, options: FormatOptions = {}): string {\n return formatParts(value, options)\n .map((part) => part.value)\n .join('');\n}\n\nexport function formatParts(value: Money, options: FormatOptions = {}): MoneyFormatPart[] {\n assertMoney(value);\n assertRoundingMode(options.rounding ?? defaultFormatRounding);\n\n const { maximum, minimum } = fractionDigits(value.currency.minorUnit, options);\n const style = options.style ?? 'symbol';\n\n try {\n const formatter = new Intl.NumberFormat(options.locale ?? 'en-US', {\n currency: value.currency.code,\n currencyDisplay: style,\n maximumFractionDigits: maximum,\n minimumFractionDigits: minimum,\n roundingMode: intlRoundingModes[options.rounding ?? defaultFormatRounding],\n style: 'currency',\n useGrouping: true,\n });\n\n return normalizeParts(formatter.formatToParts(toDecimal(value) as unknown as number));\n } catch (error) {\n if (error instanceof CoinsError) throw error;\n\n throw new CoinsError(\n 'FORMAT_ERROR',\n `Cannot format currency \"${value.currency.code}\" for locale \"${options.locale ?? 'en-US'}\"`,\n { cause: error },\n );\n }\n}\n\nfunction fractionDigits(scale: number, options: FormatOptions): { maximum: number; minimum: number } {\n const requestedMaximum = options.maximumFractionDigits;\n const requestedMinimum = options.minimumFractionDigits;\n\n validateFractionDigit('maximumFractionDigits', requestedMaximum);\n validateFractionDigit('minimumFractionDigits', requestedMinimum);\n\n const maximum = requestedMaximum ?? Math.max(scale, requestedMinimum ?? scale);\n const minimum = requestedMinimum ?? Math.min(scale, requestedMaximum ?? scale);\n\n if (minimum > maximum) {\n throw new CoinsError('FORMAT_ERROR', 'minimumFractionDigits cannot exceed maximumFractionDigits');\n }\n\n return { maximum, minimum };\n}\n\nfunction validateFractionDigit(name: string, value: number | undefined): void {\n if (value !== undefined && (!Number.isInteger(value) || value < 0 || value > MAX_FRACTION_DIGITS)) {\n throw new CoinsError(\n 'FORMAT_ERROR',\n `Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${MAX_FRACTION_DIGITS} (${name})`,\n );\n }\n}\n\nfunction normalizeParts(raw: Intl.NumberFormatPart[]): MoneyFormatPart[] {\n const parts: MoneyFormatPart[] = [];\n\n for (const part of raw) {\n if (part.type === 'integer' || part.type === 'group') {\n const previous = parts.at(-1);\n\n if (previous?.type === 'integer') {\n parts[parts.length - 1] = { type: 'integer', value: previous.value + part.value };\n } else {\n parts.push({ type: 'integer', value: part.value });\n }\n } else if (\n part.type === 'currency' ||\n part.type === 'decimal' ||\n part.type === 'fraction' ||\n part.type === 'literal' ||\n part.type === 'minusSign' ||\n part.type === 'plusSign'\n ) {\n parts.push({ type: part.type, value: part.value });\n } else {\n parts.push({ type: 'literal', value: part.value });\n }\n }\n\n return parts;\n}\n"],"mappings":"A,qFAKA,IAAMK,EAAsB,GACtBC,EAAsC,mBACtCC,EAAoF,CACxF,aAAc,SACd,KAAM,OACN,MAAO,QACP,iBAAkB,aAClB,SAAU,WACV,WAAY,OACd,EAEA,SAAgB,OAAO,EAAc,EAAyB,CAAC,EAAW,CACxE,OAAO,YAAYC,EAAOC,CAAO,CAAC,CAC/B,IAAK,GAASC,EAAK,KAAK,CAAC,CACzB,KAAK,EAAE,CACZ,CAEA,SAAgB,YAAY,EAAc,EAAyB,CAAC,EAAsB,CACxFN,EAAA,YAAYI,CAAK,EACjBL,EAAA,mBAAmBM,EAAQ,UAAYH,CAAqB,EAE5D,GAAM,CAAE,UAAS,WAAY,eAAeE,EAAM,SAAS,UAAWC,CAAO,EACvEE,EAAQF,EAAQ,OAAS,SAE/B,GAAI,CAWF,OAAO,eAAe,IAVA,KAAK,aAAaA,EAAQ,QAAU,QAAS,CACjE,SAAUD,EAAM,SAAS,KACzB,gBAAiBG,EACjB,sBAAuBC,EACvB,sBAAuBC,EACvB,aAAcN,EAAkBE,EAAQ,UAAYH,GACpD,MAAO,WACP,YAAa,EACf,CAEsB,CAAA,CAAU,cAAcN,EAAAA,UAAUQ,CAAK,CAAsB,CAAC,CACtF,OAASM,EAAO,CAGd,MAFIA,aAAiBb,EAAAA,WAAkBa,EAEjC,IAAIb,EAAAA,WACR,eACA,2BAA2BO,EAAM,SAAS,KAAK,gBAAgBC,EAAQ,QAAU,QAAQ,GACzF,CAAE,MAAOK,CAAM,CACjB,CACF,CACF,CAEA,SAAS,eAAe,EAAe,EAA8D,CACnG,IAAMC,EAAmBN,EAAQ,sBAC3BO,EAAmBP,EAAQ,sBAEjC,sBAAsB,wBAAyBM,CAAgB,EAC/D,sBAAsB,wBAAyBC,CAAgB,EAE/D,IAAMJ,EAAUG,GAAoB,KAAK,IAAIE,EAAOD,GAAoBC,CAAK,EACvEJ,EAAUG,GAAoB,KAAK,IAAIC,EAAOF,GAAoBE,CAAK,EAE7E,GAAIJ,EAAUD,EACZ,MAAM,IAAIX,EAAAA,WAAW,eAAgB,2DAA2D,EAGlG,MAAO,CAAE,UAAS,SAAQ,CAC5B,CAEA,SAAS,sBAAsB,EAAc,EAAiC,CAC5E,GAAIO,IAAU,IAAA,KAAc,CAAC,OAAO,UAAUA,CAAK,GAAKA,EAAQ,GAAKA,EAAQH,GAC3E,MAAM,IAAIJ,EAAAA,WACR,eACA,uEAAuEI,EAAoB,IAAIa,EAAK,EACtG,CAEJ,CAEA,SAAS,eAAe,EAAiD,CACvE,IAAMC,EAA2B,CAAC,EAElC,IAAK,IAAMT,KAAQU,EACjB,GAAIV,EAAK,OAAS,WAAaA,EAAK,OAAS,QAAS,CACpD,IAAMW,EAAWF,EAAM,GAAG,EAAE,EAExBE,GAAU,OAAS,UACrB,EAAMF,EAAM,OAAS,GAAK,CAAE,KAAM,UAAW,MAAOE,EAAS,MAAQX,EAAK,KAAM,EAEhFS,EAAM,KAAK,CAAE,KAAM,UAAW,MAAOT,EAAK,KAAM,CAAC,CAErD,MACEA,EAAK,OAAS,YACdA,EAAK,OAAS,WACdA,EAAK,OAAS,YACdA,EAAK,OAAS,WACdA,EAAK,OAAS,aACdA,EAAK,OAAS,WAEdS,EAAM,KAAK,CAAE,KAAMT,EAAK,KAAM,MAAOA,EAAK,KAAM,CAAC,EAEjDS,EAAM,KAAK,CAAE,KAAM,UAAW,MAAOT,EAAK,KAAM,CAAC,EAIrD,OAAOS,CACT,C"}
1
+ {"version":3,"file":"format.cjs","names":["toDecimal","CoinsError","require__decimal","require_errors","require_money","MAX_FRACTION_DIGITS","defaultFormatRounding","intlRoundingModes","value","options","part","style","maximum","minimum","error","requestedMaximum","requestedMinimum","scale","name","parts","raw","previous"],"sources":["../src/format.ts"],"sourcesContent":["import { assertRoundingMode } from './_decimal';\nimport { CoinsError } from './errors';\nimport { assertMoney, toDecimal } from './money';\nimport type { FormatOptions, Money, MoneyFormatPart, RoundingMode } from './types';\n\nconst MAX_FRACTION_DIGITS = 20;\nconst defaultFormatRounding: RoundingMode = 'halfAwayFromZero';\nconst intlRoundingModes: Record<RoundingMode, Intl.NumberFormatOptions['roundingMode']> = {\n awayFromZero: 'expand',\n ceil: 'ceil',\n floor: 'floor',\n halfAwayFromZero: 'halfExpand',\n halfEven: 'halfEven',\n towardZero: 'trunc',\n};\n\nexport function format(value: Money, options: FormatOptions = {}): string {\n return formatParts(value, options)\n .map((part) => part.value)\n .join('');\n}\n\nexport function formatParts(value: Money, options: FormatOptions = {}): MoneyFormatPart[] {\n assertMoney(value);\n assertRoundingMode(options.rounding ?? defaultFormatRounding);\n\n const { maximum, minimum } = fractionDigits(value.currency.minorUnit, options);\n const style = options.style ?? 'symbol';\n\n try {\n const formatter = new Intl.NumberFormat(options.locale ?? 'en-US', {\n currency: value.currency.code,\n currencyDisplay: style,\n maximumFractionDigits: maximum,\n minimumFractionDigits: minimum,\n roundingMode: intlRoundingModes[options.rounding ?? defaultFormatRounding],\n style: 'currency',\n useGrouping: true,\n });\n\n return normalizeParts(formatter.formatToParts(toDecimal(value) as unknown as number));\n } catch (error) {\n if (error instanceof CoinsError) throw error;\n\n throw new CoinsError(\n 'FORMAT_ERROR',\n `Cannot format currency \"${value.currency.code}\" for locale \"${options.locale ?? 'en-US'}\"`,\n { cause: error },\n );\n }\n}\n\nfunction fractionDigits(scale: number, options: FormatOptions): { maximum: number; minimum: number } {\n const requestedMaximum = options.maximumFractionDigits;\n const requestedMinimum = options.minimumFractionDigits;\n\n validateFractionDigit('maximumFractionDigits', requestedMaximum);\n validateFractionDigit('minimumFractionDigits', requestedMinimum);\n\n const maximum = requestedMaximum ?? Math.max(scale, requestedMinimum ?? scale);\n const minimum = requestedMinimum ?? Math.min(scale, requestedMaximum ?? scale);\n\n if (minimum > maximum) {\n throw new CoinsError('FORMAT_ERROR', 'minimumFractionDigits cannot exceed maximumFractionDigits');\n }\n\n return { maximum, minimum };\n}\n\nfunction validateFractionDigit(name: string, value: number | undefined): void {\n if (value !== undefined && (!Number.isInteger(value) || value < 0 || value > MAX_FRACTION_DIGITS)) {\n throw new CoinsError(\n 'FORMAT_ERROR',\n `Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${MAX_FRACTION_DIGITS} (${name})`,\n );\n }\n}\n\nfunction normalizeParts(raw: Intl.NumberFormatPart[]): MoneyFormatPart[] {\n const parts: MoneyFormatPart[] = [];\n\n for (const part of raw) {\n if (part.type === 'integer' || part.type === 'group') {\n const previous = parts.at(-1);\n\n if (previous?.type === 'integer') {\n parts[parts.length - 1] = { type: 'integer', value: previous.value + part.value };\n } else {\n parts.push({ type: 'integer', value: part.value });\n }\n } else if (\n part.type === 'currency' ||\n part.type === 'decimal' ||\n part.type === 'fraction' ||\n part.type === 'literal' ||\n part.type === 'minusSign' ||\n part.type === 'plusSign'\n ) {\n parts.push({ type: part.type, value: part.value });\n } else {\n parts.push({ type: 'literal', value: part.value });\n }\n }\n\n return parts;\n}\n"],"mappings":"A,qFAKA,IAAMK,EAAsB,GACtBC,EAAsC,mBACtCC,EAAoF,CACxF,aAAc,SACd,KAAM,OACN,MAAO,QACP,iBAAkB,aAClB,SAAU,WACV,WAAY,OACd,EAEA,SAAgB,OAAO,EAAc,EAAyB,CAAC,EAAW,CACxE,OAAO,YAAYC,EAAOC,CAAO,CAAC,CAC/B,IAAK,GAASC,EAAK,KAAK,CAAC,CACzB,KAAK,EAAE,CACZ,CAEA,SAAgB,YAAY,EAAc,EAAyB,CAAC,EAAsB,CACxFN,EAAA,YAAYI,CAAK,EACjBN,EAAA,mBAAmBO,EAAQ,UAAYH,CAAqB,EAE5D,GAAM,CAAE,UAAS,WAAY,eAAeE,EAAM,SAAS,UAAWC,CAAO,EACvEE,EAAQF,EAAQ,OAAS,SAE/B,GAAI,CAWF,OAAO,eAAe,IAVA,KAAK,aAAaA,EAAQ,QAAU,QAAS,CACjE,SAAUD,EAAM,SAAS,KACzB,gBAAiBG,EACjB,sBAAuBC,EACvB,sBAAuBC,EACvB,aAAcN,EAAkBE,EAAQ,UAAYH,GACpD,MAAO,WACP,YAAa,EACf,CAEsB,CAAA,CAAU,cAAcN,EAAAA,UAAUQ,CAAK,CAAsB,CAAC,CACtF,OAASM,EAAO,CAGd,MAFIA,aAAiBb,EAAAA,WAAkBa,EAEjC,IAAIb,EAAAA,WACR,eACA,2BAA2BO,EAAM,SAAS,KAAK,gBAAgBC,EAAQ,QAAU,QAAQ,GACzF,CAAE,MAAOK,CAAM,CACjB,CACF,CACF,CAEA,SAAS,eAAe,EAAe,EAA8D,CACnG,IAAMC,EAAmBN,EAAQ,sBAC3BO,EAAmBP,EAAQ,sBAEjC,sBAAsB,wBAAyBM,CAAgB,EAC/D,sBAAsB,wBAAyBC,CAAgB,EAE/D,IAAMJ,EAAUG,GAAoB,KAAK,IAAIE,EAAOD,GAAoBC,CAAK,EACvEJ,EAAUG,GAAoB,KAAK,IAAIC,EAAOF,GAAoBE,CAAK,EAE7E,GAAIJ,EAAUD,EACZ,MAAM,IAAIX,EAAAA,WAAW,eAAgB,2DAA2D,EAGlG,MAAO,CAAE,UAAS,SAAQ,CAC5B,CAEA,SAAS,sBAAsB,EAAc,EAAiC,CAC5E,GAAIO,IAAU,IAAA,KAAc,CAAC,OAAO,UAAUA,CAAK,GAAKA,EAAQ,GAAKA,EAAQH,GAC3E,MAAM,IAAIJ,EAAAA,WACR,eACA,uEAAuEI,EAAoB,IAAIa,EAAK,EACtG,CAEJ,CAEA,SAAS,eAAe,EAAiD,CACvE,IAAMC,EAA2B,CAAC,EAElC,IAAK,IAAMT,KAAQU,EACjB,GAAIV,EAAK,OAAS,WAAaA,EAAK,OAAS,QAAS,CACpD,IAAMW,EAAWF,EAAM,GAAG,EAAE,EAExBE,GAAU,OAAS,UACrB,EAAMF,EAAM,OAAS,GAAK,CAAE,KAAM,UAAW,MAAOE,EAAS,MAAQX,EAAK,KAAM,EAEhFS,EAAM,KAAK,CAAE,KAAM,UAAW,MAAOT,EAAK,KAAM,CAAC,CAErD,MACEA,EAAK,OAAS,YACdA,EAAK,OAAS,WACdA,EAAK,OAAS,YACdA,EAAK,OAAS,WACdA,EAAK,OAAS,aACdA,EAAK,OAAS,WAEdS,EAAM,KAAK,CAAE,KAAMT,EAAK,KAAM,MAAOA,EAAK,KAAM,CAAC,EAEjDS,EAAM,KAAK,CAAE,KAAM,UAAW,MAAOT,EAAK,KAAM,CAAC,EAIrD,OAAOS,CACT,C"}
package/dist/format.js CHANGED
@@ -1,7 +1,7 @@
1
- import { CoinsError as e } from "./errors.js";
2
- import { assertRoundingMode as t } from "./_decimal.js";
1
+ import { assertRoundingMode as e } from "./_decimal.js";
2
+ import { CoinsError as t } from "./errors.js";
3
3
  import { assertMoney as n, toDecimal as r } from "./money.js";
4
- //#region src/format.ts
4
+ // src/format.ts
5
5
  var i = 20, a = "halfAwayFromZero", o = {
6
6
  awayFromZero: "expand",
7
7
  ceil: "ceil",
@@ -14,7 +14,7 @@ function format(e, t = {}) {
14
14
  return formatParts(e, t).map((e) => e.value).join("");
15
15
  }
16
16
  function formatParts(i, s = {}) {
17
- n(i), t(s.rounding ?? a);
17
+ n(i), e(s.rounding ?? a);
18
18
  let { maximum: c, minimum: l } = fractionDigits(i.currency.minorUnit, s), u = s.style ?? "symbol";
19
19
  try {
20
20
  return normalizeParts(new Intl.NumberFormat(s.locale ?? "en-US", {
@@ -26,22 +26,22 @@ function formatParts(i, s = {}) {
26
26
  style: "currency",
27
27
  useGrouping: !0
28
28
  }).formatToParts(r(i)));
29
- } catch (t) {
30
- throw t instanceof e ? t : new e("FORMAT_ERROR", `Cannot format currency "${i.currency.code}" for locale "${s.locale ?? "en-US"}"`, { cause: t });
29
+ } catch (e) {
30
+ throw e instanceof t ? e : new t("FORMAT_ERROR", `Cannot format currency "${i.currency.code}" for locale "${s.locale ?? "en-US"}"`, { cause: e });
31
31
  }
32
32
  }
33
- function fractionDigits(t, n) {
33
+ function fractionDigits(e, n) {
34
34
  let r = n.maximumFractionDigits, i = n.minimumFractionDigits;
35
35
  validateFractionDigit("maximumFractionDigits", r), validateFractionDigit("minimumFractionDigits", i);
36
- let a = r ?? Math.max(t, i ?? t), o = i ?? Math.min(t, r ?? t);
37
- if (o > a) throw new e("FORMAT_ERROR", "minimumFractionDigits cannot exceed maximumFractionDigits");
36
+ let a = r ?? Math.max(e, i ?? e), o = i ?? Math.min(e, r ?? e);
37
+ if (o > a) throw new t("FORMAT_ERROR", "minimumFractionDigits cannot exceed maximumFractionDigits");
38
38
  return {
39
39
  maximum: a,
40
40
  minimum: o
41
41
  };
42
42
  }
43
- function validateFractionDigit(t, n) {
44
- if (n !== void 0 && (!Number.isInteger(n) || n < 0 || n > i)) throw new e("FORMAT_ERROR", `Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${i} (${t})`);
43
+ function validateFractionDigit(e, n) {
44
+ if (n !== void 0 && (!Number.isInteger(n) || n < 0 || n > i)) throw new t("FORMAT_ERROR", `Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${i} (${e})`);
45
45
  }
46
46
  function normalizeParts(e) {
47
47
  let t = [];
@@ -63,7 +63,6 @@ function normalizeParts(e) {
63
63
  });
64
64
  return t;
65
65
  }
66
- //#endregion
67
66
  export { format, formatParts };
68
67
 
69
68
  //# sourceMappingURL=format.js.map
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./currency.cjs"),n=require("./money.cjs"),r=require("./aggregate.cjs"),i=require("./exchange.cjs"),a=require("./format.cjs"),o=require("./serialization.cjs");exports.BHD=t.BHD,exports.CoinsError=e.CoinsError,exports.CurrencyMismatchError=e.CurrencyMismatchError,exports.EUR=t.EUR,exports.GBP=t.GBP,exports.InvalidCurrencyError=e.InvalidCurrencyError,exports.JPY=t.JPY,exports.KRW=t.KRW,exports.KWD=t.KWD,exports.USD=t.USD,exports.abs=n.abs,exports.add=n.add,exports.allocate=r.allocate,exports.clamp=n.clamp,exports.compare=n.compare,exports.currency=t.currency,exports.decodeMoney=n.decodeMoney,exports.divide=n.divide,exports.exchange=i.exchange,exports.exchangeRate=i.exchangeRate,exports.format=a.format,exports.formatParts=a.formatParts,exports.isCurrency=t.isCurrency,exports.isExchangeRate=i.isExchangeRate,exports.isMoney=n.isMoney,exports.money=n.money,exports.multiply=n.multiply,exports.negate=n.negate,exports.round=n.round,exports.subtract=n.subtract,exports.sum=r.sum,exports.toDecimal=n.toDecimal,exports.toJSON=o.toJSON;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./aggregate.cjs"),t=require("./currency.cjs"),n=require("./errors.cjs"),r=require("./exchange.cjs"),i=require("./format.cjs"),a=require("./money.cjs"),o=require("./serialization.cjs");exports.BHD=t.BHD,exports.CoinsError=n.CoinsError,exports.CurrencyMismatchError=n.CurrencyMismatchError,exports.EUR=t.EUR,exports.GBP=t.GBP,exports.InvalidCurrencyError=n.InvalidCurrencyError,exports.JPY=t.JPY,exports.KRW=t.KRW,exports.KWD=t.KWD,exports.USD=t.USD,exports.abs=a.abs,exports.add=a.add,exports.allocate=e.allocate,exports.clamp=a.clamp,exports.compare=a.compare,exports.currency=t.currency,exports.decodeMoney=a.decodeMoney,exports.divide=a.divide,exports.exchange=r.exchange,exports.exchangeRate=r.exchangeRate,exports.format=i.format,exports.formatParts=i.formatParts,exports.isCurrency=t.isCurrency,exports.isExchangeRate=r.isExchangeRate,exports.isMoney=a.isMoney,exports.money=a.money,exports.multiply=a.multiply,exports.negate=a.negate,exports.round=a.round,exports.subtract=a.subtract,exports.sum=e.sum,exports.toDecimal=a.toDecimal,exports.toJSON=o.toJSON;
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { CoinsError as e, CurrencyMismatchError as t, InvalidCurrencyError as n } from "./errors.js";
2
- import { BHD as r, EUR as i, GBP as a, JPY as o, KRW as s, KWD as c, USD as l, currency as u, isCurrency as d } from "./currency.js";
3
- import { abs as f, add as p, clamp as m, compare as h, decodeMoney as g, divide as _, isMoney as v, money as y, multiply as b, negate as x, round as S, subtract as C, toDecimal as w } from "./money.js";
4
- import { allocate as T, sum as E } from "./aggregate.js";
5
- import { exchange as D, exchangeRate as O, isExchangeRate as k } from "./exchange.js";
6
- import { format as A, formatParts as j } from "./format.js";
1
+ import { allocate as e, sum as t } from "./aggregate.js";
2
+ import { BHD as n, EUR as r, GBP as i, JPY as a, KRW as o, KWD as s, USD as c, currency as l, isCurrency as u } from "./currency.js";
3
+ import { CoinsError as d, CurrencyMismatchError as f, InvalidCurrencyError as p } from "./errors.js";
4
+ import { exchange as m, exchangeRate as h, isExchangeRate as g } from "./exchange.js";
5
+ import { format as _, formatParts as v } from "./format.js";
6
+ import { abs as y, add as b, clamp as x, compare as S, decodeMoney as C, divide as w, isMoney as T, money as E, multiply as D, negate as O, round as k, subtract as A, toDecimal as j } from "./money.js";
7
7
  import { toJSON as M } from "./serialization.js";
8
- export { r as BHD, e as CoinsError, t as CurrencyMismatchError, i as EUR, a as GBP, n as InvalidCurrencyError, o as JPY, s as KRW, c as KWD, l as USD, f as abs, p as add, T as allocate, m as clamp, h as compare, u as currency, g as decodeMoney, _ as divide, D as exchange, O as exchangeRate, A as format, j as formatParts, d as isCurrency, k as isExchangeRate, v as isMoney, y as money, b as multiply, x as negate, S as round, C as subtract, E as sum, w as toDecimal, M as toJSON };
8
+ export { n as BHD, d as CoinsError, f as CurrencyMismatchError, r as EUR, i as GBP, p as InvalidCurrencyError, a as JPY, o as KRW, s as KWD, c as USD, y as abs, b as add, e as allocate, x as clamp, S as compare, l as currency, C as decodeMoney, w as divide, m as exchange, h as exchangeRate, _ as format, v as formatParts, u as isCurrency, g as isExchangeRate, T as isMoney, E as money, D as multiply, O as negate, k as round, A as subtract, t as sum, j as toDecimal, M as toJSON };
package/dist/money.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./_decimal.cjs"),n=require("./currency.cjs");var r=new WeakSet,i=`halfAwayFromZero`,a=/^(?:0|-[1-9]\d*|[1-9]\d*)$/,o=1e3;function money(n,r,a){if(assertCurrency(r),typeof n==`bigint`){if(!a||!(`unit`in a)||a.unit!==`minor`)throw new e.CoinsError(`INVALID_MONEY`,`Bigint amounts require { unit: 'minor' }`);return createMoney(n,r)}if(a&&`unit`in a)throw new e.CoinsError(`INVALID_MONEY`,`Decimal strings do not accept a unit option`);let o=t.decimal(n),s=o.numerator*10n**BigInt(r.minorUnit);if(s%o.denominator!==0n&&a?.rounding===void 0)throw new e.CoinsError(`INVALID_MONEY`,`Amount "${n}" exceeds ${r.code} precision; provide rounding`);return createMoney(t.roundDivision(s,o.denominator,a?.rounding??i),r)}function decodeMoney(t,r){if(!isPlainDataObject(t))throw new e.CoinsError(`INVALID_MONEY`,`Money must be a plain data object`);let i=Object.getOwnPropertyDescriptors(t),s=i.amount,c=i.currency;if(!isDataProperty(s)||!isDataProperty(c))throw new e.CoinsError(`INVALID_MONEY`,`Money properties must be plain data values`);let l=Reflect.ownKeys(t);if(typeof s.value==`bigint`&&n.isCurrency(c.value)){if(l.length!==2)throw new e.CoinsError(`INVALID_MONEY`,`Plain money must have exactly the keys amount and currency`);return createMoney(s.value,c.value)}let u=i.unit;if(typeof s.value==`string`&&typeof c.value==`string`&&isDataProperty(u)&&u.value===`minor`){if(l.length!==3)throw new e.CoinsError(`INVALID_MONEY`,`MoneyJSON must have exactly the keys amount, currency, unit`);if(s.value.length>o)throw new e.CoinsError(`INVALID_MONEY`,`Money JSON amount is too long; maximum ${o} characters`);if(!a.test(s.value))throw new e.CoinsError(`INVALID_MONEY`,`Invalid Money JSON amount "${s.value}"`);let t=r?.currency??n.currency;try{let r=t(c.value);if(!n.isCurrency(r)||r.code!==c.value)throw new e.CoinsError(`INVALID_CURRENCY`,`Currency resolver must return the canonical "${c.value}" definition`);return money(BigInt(s.value),r,{unit:`minor`})}catch(t){throw new e.CoinsError(`INVALID_MONEY`,`Invalid Money JSON for currency "${c.value}"`,{cause:t})}}throw new e.CoinsError(`INVALID_MONEY`,`Money must contain bigint amount and a registered currency, or a valid MoneyJSON shape`)}function isMoney(e){return typeof e==`object`&&!!e&&r.has(e)}function add(e,t){return assertSameCurrency(e,t),createMoney(e.amount+t.amount,e.currency)}function subtract(e,t){return assertSameCurrency(e,t),createMoney(e.amount-t.amount,e.currency)}function multiply(e,n,r={}){assertMoney(e);let a=t.decimal(n);return createMoney(t.roundDivision(e.amount*a.numerator,a.denominator,r.rounding??i),e.currency)}function divide(n,r,a={}){assertMoney(n);let o=t.decimal(r);if(o.numerator===0n)throw new e.CoinsError(`DIVISION_BY_ZERO`,`Cannot divide money by zero`);let s=o.numerator<0n?-o.numerator:o.numerator,c=n.amount*o.denominator;return createMoney(t.roundDivision(o.numerator<0n?-c:c,s,a.rounding??i),n.currency)}function compare(e,t){return assertSameCurrency(e,t),e.amount===t.amount?0:e.amount<t.amount?-1:1}function clamp(t,n){if(compare(n.min,n.max)===1)throw new e.CoinsError(`INVALID_RANGE`,`Clamp minimum cannot exceed maximum`);return compare(t,n.min)===-1?n.min:compare(t,n.max)===1?n.max:t}function abs(e){return assertMoney(e),createMoney(e.amount<0n?-e.amount:e.amount,e.currency)}function negate(e){return assertMoney(e),createMoney(-e.amount,e.currency)}function round(n,r){assertMoney(n);let{fractionDigits:a,rounding:o=i}=r;if(!Number.isInteger(a)||a<0||a>n.currency.minorUnit)throw new e.CoinsError(`INVALID_ROUNDING`,`fractionDigits must be an integer from 0 to ${n.currency.minorUnit}`);let s=10n**BigInt(n.currency.minorUnit-a);return createMoney(t.roundDivision(n.amount,s,o)*s,n.currency)}function toDecimal(e){return assertMoney(e),t.toDecimalString(e.amount,e.currency.minorUnit)}function createMoney(e,t){let n=Object.freeze({amount:e,currency:t});return r.add(n),n}function assertMoney(t){if(!isMoney(t))throw new e.CoinsError(`INVALID_MONEY`,`Money must be a canonical @vielzeug/coins value`)}function assertSameCurrency(t,n){if(assertMoney(t),assertMoney(n),t.currency!==n.currency)throw new e.CurrencyMismatchError(t.currency.code,n.currency.code)}function assertCurrency(t){if(!n.isCurrency(t))throw new e.CoinsError(`INVALID_CURRENCY`,`Money requires a registered currency`)}function isPlainDataObject(e){if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function isDataProperty(e){return e!==void 0&&`value`in e&&e.get===void 0&&e.set===void 0}exports.abs=abs,exports.add=add,exports.assertMoney=assertMoney,exports.clamp=clamp,exports.compare=compare,exports.createMoney=createMoney,exports.decodeMoney=decodeMoney,exports.divide=divide,exports.isMoney=isMoney,exports.money=money,exports.multiply=multiply,exports.negate=negate,exports.round=round,exports.subtract=subtract,exports.toDecimal=toDecimal;
1
+ const e=require("./_decimal.cjs"),t=require("./currency.cjs"),n=require("./errors.cjs");var r=new WeakSet,i=`halfAwayFromZero`,a=/^(?:0|-[1-9]\d*|[1-9]\d*)$/,o=1e3;function money(t,r,a){if(assertCurrency(r),typeof t==`bigint`){if(!a||!(`unit`in a)||a.unit!==`minor`)throw new n.CoinsError(`INVALID_MONEY`,`Bigint amounts require { unit: 'minor' }`);return createMoney(t,r)}if(a&&`unit`in a)throw new n.CoinsError(`INVALID_MONEY`,`Decimal strings do not accept a unit option`);let o=e.decimal(t),s=o.numerator*10n**BigInt(r.minorUnit);if(s%o.denominator!==0n&&a?.rounding===void 0)throw new n.CoinsError(`INVALID_MONEY`,`Amount "${t}" exceeds ${r.code} precision; provide rounding`);return createMoney(e.roundDivision(s,o.denominator,a?.rounding??i),r)}function decodeMoney(e,r){if(!isPlainDataObject(e))throw new n.CoinsError(`INVALID_MONEY`,`Money must be a plain data object`);let i=Object.getOwnPropertyDescriptors(e),s=i.amount,c=i.currency;if(!isDataProperty(s)||!isDataProperty(c))throw new n.CoinsError(`INVALID_MONEY`,`Money properties must be plain data values`);let l=Reflect.ownKeys(e);if(typeof s.value==`bigint`&&t.isCurrency(c.value)){if(l.length!==2)throw new n.CoinsError(`INVALID_MONEY`,`Plain money must have exactly the keys amount and currency`);return createMoney(s.value,c.value)}let u=i.unit;if(typeof s.value==`string`&&typeof c.value==`string`&&isDataProperty(u)&&u.value===`minor`){if(l.length!==3)throw new n.CoinsError(`INVALID_MONEY`,`MoneyJSON must have exactly the keys amount, currency, unit`);if(s.value.length>o)throw new n.CoinsError(`INVALID_MONEY`,`Money JSON amount is too long; maximum ${o} characters`);if(!a.test(s.value))throw new n.CoinsError(`INVALID_MONEY`,`Invalid Money JSON amount "${s.value}"`);let e=r?.currency??t.currency;try{let r=e(c.value);if(!t.isCurrency(r)||r.code!==c.value)throw new n.CoinsError(`INVALID_CURRENCY`,`Currency resolver must return the canonical "${c.value}" definition`);return money(BigInt(s.value),r,{unit:`minor`})}catch(e){throw new n.CoinsError(`INVALID_MONEY`,`Invalid Money JSON for currency "${c.value}"`,{cause:e})}}throw new n.CoinsError(`INVALID_MONEY`,`Money must contain bigint amount and a registered currency, or a valid MoneyJSON shape`)}function isMoney(e){return typeof e==`object`&&!!e&&r.has(e)}function add(e,t){return assertSameCurrency(e,t),createMoney(e.amount+t.amount,e.currency)}function subtract(e,t){return assertSameCurrency(e,t),createMoney(e.amount-t.amount,e.currency)}function multiply(t,n,r={}){assertMoney(t);let a=e.decimal(n);return createMoney(e.roundDivision(t.amount*a.numerator,a.denominator,r.rounding??i),t.currency)}function divide(t,r,a={}){assertMoney(t);let o=e.decimal(r);if(o.numerator===0n)throw new n.CoinsError(`DIVISION_BY_ZERO`,`Cannot divide money by zero`);let s=o.numerator<0n?-o.numerator:o.numerator,c=t.amount*o.denominator;return createMoney(e.roundDivision(o.numerator<0n?-c:c,s,a.rounding??i),t.currency)}function compare(e,t){return assertSameCurrency(e,t),e.amount===t.amount?0:e.amount<t.amount?-1:1}function clamp(e,t){if(compare(t.min,t.max)===1)throw new n.CoinsError(`INVALID_RANGE`,`Clamp minimum cannot exceed maximum`);return compare(e,t.min)===-1?t.min:compare(e,t.max)===1?t.max:e}function abs(e){return assertMoney(e),createMoney(e.amount<0n?-e.amount:e.amount,e.currency)}function negate(e){return assertMoney(e),createMoney(-e.amount,e.currency)}function round(t,r){assertMoney(t);let{fractionDigits:a,rounding:o=i}=r;if(!Number.isInteger(a)||a<0||a>t.currency.minorUnit)throw new n.CoinsError(`INVALID_ROUNDING`,`fractionDigits must be an integer from 0 to ${t.currency.minorUnit}`);let s=10n**BigInt(t.currency.minorUnit-a);return createMoney(e.roundDivision(t.amount,s,o)*s,t.currency)}function toDecimal(t){return assertMoney(t),e.toDecimalString(t.amount,t.currency.minorUnit)}function createMoney(e,t){let n=Object.freeze({amount:e,currency:t});return r.add(n),n}function assertMoney(e){if(!isMoney(e))throw new n.CoinsError(`INVALID_MONEY`,`Money must be a canonical @vielzeug/coins value`)}function assertSameCurrency(e,t){if(assertMoney(e),assertMoney(t),e.currency!==t.currency)throw new n.CurrencyMismatchError(e.currency.code,t.currency.code)}function assertCurrency(e){if(!t.isCurrency(e))throw new n.CoinsError(`INVALID_CURRENCY`,`Money requires a registered currency`)}function isPlainDataObject(e){if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function isDataProperty(e){return e!==void 0&&`value`in e&&e.get===void 0&&e.set===void 0}exports.abs=abs,exports.add=add,exports.assertMoney=assertMoney,exports.clamp=clamp,exports.compare=compare,exports.createMoney=createMoney,exports.decodeMoney=decodeMoney,exports.divide=divide,exports.isMoney=isMoney,exports.money=money,exports.multiply=multiply,exports.negate=negate,exports.round=round,exports.subtract=subtract,exports.toDecimal=toDecimal;
2
2
  //# sourceMappingURL=money.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"money.cjs","names":["CoinsError","decimal","roundDivision","isCurrency","currency","toDecimalString","CurrencyMismatchError","require_errors","require__decimal","require_currency","canonicalMoney","defaultRounding","INTEGER","MAX_INTEGER_DIGITS","currency","amount","options","value","scaled","descriptors","amountDescriptor","currencyDescriptor","ownKeys","unitDescriptor","resolveCurrency","resolvedCurrency","error","left","right","scalar","factor","divisor","divisorMagnitude","dividend","fractionDigits","rounding","prototype","descriptor"],"sources":["../src/money.ts"],"sourcesContent":["import { decimal, roundDivision, toDecimalString } from './_decimal';\nimport { currency, isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport type { Currency, Money, RoundingMode } from './types';\n\nconst canonicalMoney = new WeakSet<object>();\n\nconst defaultRounding: RoundingMode = 'halfAwayFromZero';\n\nconst INTEGER = /^(?:0|-[1-9]\\d*|[1-9]\\d*)$/;\nconst MAX_INTEGER_DIGITS = 1000;\n\nexport function money<C extends Currency>(amount: string, currency: C, options?: { rounding?: RoundingMode }): Money<C>;\nexport function money<C extends Currency>(amount: bigint, currency: C, options: { unit: 'minor' }): Money<C>;\nexport function money<C extends Currency>(\n amount: bigint | string,\n currency: C,\n options?: { rounding?: RoundingMode } | { unit: 'minor' },\n): Money<C> {\n assertCurrency(currency);\n\n if (typeof amount === 'bigint') {\n if (!options || !('unit' in options) || options.unit !== 'minor') {\n throw new CoinsError('INVALID_MONEY', \"Bigint amounts require { unit: 'minor' }\");\n }\n\n return createMoney(amount, currency);\n }\n\n if (options && 'unit' in options)\n throw new CoinsError('INVALID_MONEY', 'Decimal strings do not accept a unit option');\n\n const value = decimal(amount);\n const scaled = value.numerator * 10n ** BigInt(currency.minorUnit);\n const remainder = scaled % value.denominator;\n\n if (remainder !== 0n && options?.rounding === undefined) {\n throw new CoinsError('INVALID_MONEY', `Amount \"${amount}\" exceeds ${currency.code} precision; provide rounding`);\n }\n\n return createMoney(roundDivision(scaled, value.denominator, options?.rounding ?? defaultRounding), currency);\n}\n\n/**\n * Decodes untrusted/persisted data into canonical `Money`.\n * Accepts either a plain `Money`-shaped object (`{ amount: bigint, currency: Currency }`)\n * or a `MoneyJSON`-shaped object (`{ amount: string, currency: string, unit: 'minor' }`).\n * Use `money()` for trusted construction; use `decodeMoney()` for network/storage data.\n */\nexport function decodeMoney(value: unknown, options?: { currency?: (code: string) => Currency }): Money {\n if (!isPlainDataObject(value)) throw new CoinsError('INVALID_MONEY', 'Money must be a plain data object');\n\n const descriptors = Object.getOwnPropertyDescriptors(value);\n const amountDescriptor = descriptors.amount;\n const currencyDescriptor = descriptors.currency;\n\n if (!isDataProperty(amountDescriptor) || !isDataProperty(currencyDescriptor)) {\n throw new CoinsError('INVALID_MONEY', 'Money properties must be plain data values');\n }\n\n const ownKeys = Reflect.ownKeys(value);\n\n // Plain Money: bigint amount + registered currency\n if (typeof amountDescriptor.value === 'bigint' && isCurrency(currencyDescriptor.value)) {\n if (ownKeys.length !== 2) {\n throw new CoinsError('INVALID_MONEY', 'Plain money must have exactly the keys amount and currency');\n }\n\n return createMoney(amountDescriptor.value, currencyDescriptor.value);\n }\n\n // MoneyJSON: string amount + string currency code + unit: 'minor'\n const unitDescriptor = descriptors.unit;\n if (\n typeof amountDescriptor.value === 'string' &&\n typeof currencyDescriptor.value === 'string' &&\n isDataProperty(unitDescriptor) &&\n unitDescriptor.value === 'minor'\n ) {\n if (ownKeys.length !== 3) {\n throw new CoinsError('INVALID_MONEY', 'MoneyJSON must have exactly the keys amount, currency, unit');\n }\n if (amountDescriptor.value.length > MAX_INTEGER_DIGITS) {\n throw new CoinsError('INVALID_MONEY', `Money JSON amount is too long; maximum ${MAX_INTEGER_DIGITS} characters`);\n }\n if (!INTEGER.test(amountDescriptor.value)) {\n throw new CoinsError('INVALID_MONEY', `Invalid Money JSON amount \"${amountDescriptor.value}\"`);\n }\n const resolveCurrency = options?.currency ?? currency;\n try {\n const resolvedCurrency = resolveCurrency(currencyDescriptor.value);\n\n if (!isCurrency(resolvedCurrency) || resolvedCurrency.code !== currencyDescriptor.value) {\n throw new CoinsError(\n 'INVALID_CURRENCY',\n `Currency resolver must return the canonical \"${currencyDescriptor.value}\" definition`,\n );\n }\n\n return money(BigInt(amountDescriptor.value), resolvedCurrency, { unit: 'minor' });\n } catch (error) {\n throw new CoinsError('INVALID_MONEY', `Invalid Money JSON for currency \"${currencyDescriptor.value}\"`, {\n cause: error,\n });\n }\n }\n\n throw new CoinsError(\n 'INVALID_MONEY',\n 'Money must contain bigint amount and a registered currency, or a valid MoneyJSON shape',\n );\n}\n\nexport function isMoney(value: unknown): value is Money {\n return typeof value === 'object' && value !== null && canonicalMoney.has(value);\n}\n\nexport function add<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount + right.amount, left.currency);\n}\n\nexport function subtract<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount - right.amount, left.currency);\n}\n\nexport function multiply<C extends Currency>(\n value: Money<C>,\n factor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(factor);\n\n return createMoney(\n roundDivision(value.amount * scalar.numerator, scalar.denominator, options.rounding ?? defaultRounding),\n value.currency,\n );\n}\n\nexport function divide<C extends Currency>(\n value: Money<C>,\n divisor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(divisor);\n\n if (scalar.numerator === 0n) throw new CoinsError('DIVISION_BY_ZERO', 'Cannot divide money by zero');\n\n const divisorMagnitude = scalar.numerator < 0n ? -scalar.numerator : scalar.numerator;\n const dividend = value.amount * scalar.denominator;\n const quotient = roundDivision(\n scalar.numerator < 0n ? -dividend : dividend,\n divisorMagnitude,\n options.rounding ?? defaultRounding,\n );\n\n return createMoney(quotient, value.currency);\n}\n\nexport function compare<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): -1 | 0 | 1 {\n assertSameCurrency(left, right);\n\n return left.amount === right.amount ? 0 : left.amount < right.amount ? -1 : 1;\n}\n\nexport function clamp<C extends Currency>(\n value: Money<C>,\n options: { max: Money<NoInfer<C>>; min: Money<NoInfer<C>> },\n): Money<C> {\n if (compare(options.min, options.max) === 1) {\n throw new CoinsError('INVALID_RANGE', 'Clamp minimum cannot exceed maximum');\n }\n\n return compare(value, options.min) === -1 ? options.min : compare(value, options.max) === 1 ? options.max : value;\n}\n\nexport function abs<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(value.amount < 0n ? -value.amount : value.amount, value.currency);\n}\n\nexport function negate<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(-value.amount, value.currency);\n}\n\nexport function round<C extends Currency>(\n value: Money<C>,\n options: { fractionDigits: number; rounding?: RoundingMode },\n): Money<C> {\n assertMoney(value);\n\n const { fractionDigits, rounding = defaultRounding } = options;\n\n if (!Number.isInteger(fractionDigits) || fractionDigits < 0 || fractionDigits > value.currency.minorUnit) {\n throw new CoinsError('INVALID_ROUNDING', `fractionDigits must be an integer from 0 to ${value.currency.minorUnit}`);\n }\n\n const factor = 10n ** BigInt(value.currency.minorUnit - fractionDigits);\n\n return createMoney(roundDivision(value.amount, factor, rounding) * factor, value.currency);\n}\n\nexport function toDecimal(value: Money): string {\n assertMoney(value);\n\n return toDecimalString(value.amount, value.currency.minorUnit);\n}\n\nexport function createMoney<C extends Currency>(amount: bigint, currency: C): Money<C> {\n const value = Object.freeze({ amount, currency }) as Money<C>;\n\n canonicalMoney.add(value);\n\n return value;\n}\n\nexport function assertMoney(value: unknown): asserts value is Money {\n if (!isMoney(value)) {\n throw new CoinsError('INVALID_MONEY', 'Money must be a canonical @vielzeug/coins value');\n }\n}\n\nfunction assertSameCurrency(left: Money, right: Money): void {\n assertMoney(left);\n assertMoney(right);\n\n if (left.currency !== right.currency) {\n throw new CurrencyMismatchError(left.currency.code, right.currency.code);\n }\n}\n\nfunction assertCurrency(value: unknown): asserts value is Currency {\n if (!isCurrency(value)) throw new CoinsError('INVALID_CURRENCY', 'Money requires a registered currency');\n}\n\nfunction isPlainDataObject(value: unknown): value is Record<PropertyKey, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n\n const prototype = Object.getPrototypeOf(value);\n\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction isDataProperty(\n descriptor: PropertyDescriptor | undefined,\n): descriptor is PropertyDescriptor & { value: unknown } {\n return (\n descriptor !== undefined && 'value' in descriptor && descriptor.get === undefined && descriptor.set === undefined\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"money.cjs","names":["CoinsError","decimal","roundDivision","isCurrency","currency","toDecimalString","CurrencyMismatchError","require__decimal","require_currency","require_errors","canonicalMoney","defaultRounding","INTEGER","MAX_INTEGER_DIGITS","currency","amount","options","value","scaled","descriptors","amountDescriptor","currencyDescriptor","ownKeys","unitDescriptor","resolveCurrency","resolvedCurrency","error","left","right","scalar","factor","divisor","divisorMagnitude","dividend","fractionDigits","rounding","prototype","descriptor"],"sources":["../src/money.ts"],"sourcesContent":["import { decimal, roundDivision, toDecimalString } from './_decimal';\nimport { currency, isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport type { Currency, Money, RoundingMode } from './types';\n\nconst canonicalMoney = new WeakSet<object>();\n\nconst defaultRounding: RoundingMode = 'halfAwayFromZero';\n\nconst INTEGER = /^(?:0|-[1-9]\\d*|[1-9]\\d*)$/;\nconst MAX_INTEGER_DIGITS = 1000;\n\nexport function money<C extends Currency>(amount: string, currency: C, options?: { rounding?: RoundingMode }): Money<C>;\nexport function money<C extends Currency>(amount: bigint, currency: C, options: { unit: 'minor' }): Money<C>;\nexport function money<C extends Currency>(\n amount: bigint | string,\n currency: C,\n options?: { rounding?: RoundingMode } | { unit: 'minor' },\n): Money<C> {\n assertCurrency(currency);\n\n if (typeof amount === 'bigint') {\n if (!options || !('unit' in options) || options.unit !== 'minor') {\n throw new CoinsError('INVALID_MONEY', \"Bigint amounts require { unit: 'minor' }\");\n }\n\n return createMoney(amount, currency);\n }\n\n if (options && 'unit' in options)\n throw new CoinsError('INVALID_MONEY', 'Decimal strings do not accept a unit option');\n\n const value = decimal(amount);\n const scaled = value.numerator * 10n ** BigInt(currency.minorUnit);\n const remainder = scaled % value.denominator;\n\n if (remainder !== 0n && options?.rounding === undefined) {\n throw new CoinsError('INVALID_MONEY', `Amount \"${amount}\" exceeds ${currency.code} precision; provide rounding`);\n }\n\n return createMoney(roundDivision(scaled, value.denominator, options?.rounding ?? defaultRounding), currency);\n}\n\n/**\n * Decodes untrusted/persisted data into canonical `Money`.\n * Accepts either a plain `Money`-shaped object (`{ amount: bigint, currency: Currency }`)\n * or a `MoneyJSON`-shaped object (`{ amount: string, currency: string, unit: 'minor' }`).\n * Use `money()` for trusted construction; use `decodeMoney()` for network/storage data.\n */\nexport function decodeMoney(value: unknown, options?: { currency?: (code: string) => Currency }): Money {\n if (!isPlainDataObject(value)) throw new CoinsError('INVALID_MONEY', 'Money must be a plain data object');\n\n const descriptors = Object.getOwnPropertyDescriptors(value);\n const amountDescriptor = descriptors.amount;\n const currencyDescriptor = descriptors.currency;\n\n if (!isDataProperty(amountDescriptor) || !isDataProperty(currencyDescriptor)) {\n throw new CoinsError('INVALID_MONEY', 'Money properties must be plain data values');\n }\n\n const ownKeys = Reflect.ownKeys(value);\n\n // Plain Money: bigint amount + registered currency\n if (typeof amountDescriptor.value === 'bigint' && isCurrency(currencyDescriptor.value)) {\n if (ownKeys.length !== 2) {\n throw new CoinsError('INVALID_MONEY', 'Plain money must have exactly the keys amount and currency');\n }\n\n return createMoney(amountDescriptor.value, currencyDescriptor.value);\n }\n\n // MoneyJSON: string amount + string currency code + unit: 'minor'\n const unitDescriptor = descriptors.unit;\n if (\n typeof amountDescriptor.value === 'string' &&\n typeof currencyDescriptor.value === 'string' &&\n isDataProperty(unitDescriptor) &&\n unitDescriptor.value === 'minor'\n ) {\n if (ownKeys.length !== 3) {\n throw new CoinsError('INVALID_MONEY', 'MoneyJSON must have exactly the keys amount, currency, unit');\n }\n if (amountDescriptor.value.length > MAX_INTEGER_DIGITS) {\n throw new CoinsError('INVALID_MONEY', `Money JSON amount is too long; maximum ${MAX_INTEGER_DIGITS} characters`);\n }\n if (!INTEGER.test(amountDescriptor.value)) {\n throw new CoinsError('INVALID_MONEY', `Invalid Money JSON amount \"${amountDescriptor.value}\"`);\n }\n const resolveCurrency = options?.currency ?? currency;\n try {\n const resolvedCurrency = resolveCurrency(currencyDescriptor.value);\n\n if (!isCurrency(resolvedCurrency) || resolvedCurrency.code !== currencyDescriptor.value) {\n throw new CoinsError(\n 'INVALID_CURRENCY',\n `Currency resolver must return the canonical \"${currencyDescriptor.value}\" definition`,\n );\n }\n\n return money(BigInt(amountDescriptor.value), resolvedCurrency, { unit: 'minor' });\n } catch (error) {\n throw new CoinsError('INVALID_MONEY', `Invalid Money JSON for currency \"${currencyDescriptor.value}\"`, {\n cause: error,\n });\n }\n }\n\n throw new CoinsError(\n 'INVALID_MONEY',\n 'Money must contain bigint amount and a registered currency, or a valid MoneyJSON shape',\n );\n}\n\nexport function isMoney(value: unknown): value is Money {\n return typeof value === 'object' && value !== null && canonicalMoney.has(value);\n}\n\nexport function add<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount + right.amount, left.currency);\n}\n\nexport function subtract<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount - right.amount, left.currency);\n}\n\nexport function multiply<C extends Currency>(\n value: Money<C>,\n factor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(factor);\n\n return createMoney(\n roundDivision(value.amount * scalar.numerator, scalar.denominator, options.rounding ?? defaultRounding),\n value.currency,\n );\n}\n\nexport function divide<C extends Currency>(\n value: Money<C>,\n divisor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(divisor);\n\n if (scalar.numerator === 0n) throw new CoinsError('DIVISION_BY_ZERO', 'Cannot divide money by zero');\n\n const divisorMagnitude = scalar.numerator < 0n ? -scalar.numerator : scalar.numerator;\n const dividend = value.amount * scalar.denominator;\n const quotient = roundDivision(\n scalar.numerator < 0n ? -dividend : dividend,\n divisorMagnitude,\n options.rounding ?? defaultRounding,\n );\n\n return createMoney(quotient, value.currency);\n}\n\nexport function compare<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): -1 | 0 | 1 {\n assertSameCurrency(left, right);\n\n return left.amount === right.amount ? 0 : left.amount < right.amount ? -1 : 1;\n}\n\nexport function clamp<C extends Currency>(\n value: Money<C>,\n options: { max: Money<NoInfer<C>>; min: Money<NoInfer<C>> },\n): Money<C> {\n if (compare(options.min, options.max) === 1) {\n throw new CoinsError('INVALID_RANGE', 'Clamp minimum cannot exceed maximum');\n }\n\n return compare(value, options.min) === -1 ? options.min : compare(value, options.max) === 1 ? options.max : value;\n}\n\nexport function abs<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(value.amount < 0n ? -value.amount : value.amount, value.currency);\n}\n\nexport function negate<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(-value.amount, value.currency);\n}\n\nexport function round<C extends Currency>(\n value: Money<C>,\n options: { fractionDigits: number; rounding?: RoundingMode },\n): Money<C> {\n assertMoney(value);\n\n const { fractionDigits, rounding = defaultRounding } = options;\n\n if (!Number.isInteger(fractionDigits) || fractionDigits < 0 || fractionDigits > value.currency.minorUnit) {\n throw new CoinsError('INVALID_ROUNDING', `fractionDigits must be an integer from 0 to ${value.currency.minorUnit}`);\n }\n\n const factor = 10n ** BigInt(value.currency.minorUnit - fractionDigits);\n\n return createMoney(roundDivision(value.amount, factor, rounding) * factor, value.currency);\n}\n\nexport function toDecimal(value: Money): string {\n assertMoney(value);\n\n return toDecimalString(value.amount, value.currency.minorUnit);\n}\n\nexport function createMoney<C extends Currency>(amount: bigint, currency: C): Money<C> {\n const value = Object.freeze({ amount, currency }) as Money<C>;\n\n canonicalMoney.add(value);\n\n return value;\n}\n\nexport function assertMoney(value: unknown): asserts value is Money {\n if (!isMoney(value)) {\n throw new CoinsError('INVALID_MONEY', 'Money must be a canonical @vielzeug/coins value');\n }\n}\n\nfunction assertSameCurrency(left: Money, right: Money): void {\n assertMoney(left);\n assertMoney(right);\n\n if (left.currency !== right.currency) {\n throw new CurrencyMismatchError(left.currency.code, right.currency.code);\n }\n}\n\nfunction assertCurrency(value: unknown): asserts value is Currency {\n if (!isCurrency(value)) throw new CoinsError('INVALID_CURRENCY', 'Money requires a registered currency');\n}\n\nfunction isPlainDataObject(value: unknown): value is Record<PropertyKey, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n\n const prototype = Object.getPrototypeOf(value);\n\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction isDataProperty(\n descriptor: PropertyDescriptor | undefined,\n): descriptor is PropertyDescriptor & { value: unknown } {\n return (\n descriptor !== undefined && 'value' in descriptor && descriptor.get === undefined && descriptor.set === undefined\n );\n}\n"],"mappings":"A,wFAKA,IAAMU,EAAiB,IAAI,QAErBC,EAAgC,mBAEhCC,EAAU,6BACVC,EAAqB,IAI3B,SAAgB,MACd,EACA,EACA,EACU,CAGV,GAFA,eAAeC,CAAQ,EAEnB,OAAOC,GAAW,SAAU,CAC9B,GAAI,CAACC,GAAW,EAAE,SAAUA,IAAYA,EAAQ,OAAS,QACvD,MAAM,IAAIhB,EAAAA,WAAW,gBAAiB,0CAA0C,EAGlF,OAAO,YAAYe,EAAQD,CAAQ,CACrC,CAEA,GAAIE,GAAW,SAAUA,EACvB,MAAM,IAAIhB,EAAAA,WAAW,gBAAiB,6CAA6C,EAErF,IAAMiB,EAAQhB,EAAAA,QAAQc,CAAM,EACtBG,EAASD,EAAM,UAAY,KAAO,OAAOH,EAAS,SAAS,EAGjE,GAFkBI,EAASD,EAAM,cAEf,IAAMD,GAAS,WAAa,IAAA,GAC5C,MAAM,IAAIhB,EAAAA,WAAW,gBAAiB,WAAWe,EAAO,YAAYD,EAAS,KAAK,6BAA6B,EAGjH,OAAO,YAAYZ,EAAAA,cAAcgB,EAAQD,EAAM,YAAaD,GAAS,UAAYL,CAAe,EAAGG,CAAQ,CAC7G,CAQA,SAAgB,YAAY,EAAgB,EAA4D,CACtG,GAAI,CAAC,kBAAkBG,CAAK,EAAG,MAAM,IAAIjB,EAAAA,WAAW,gBAAiB,mCAAmC,EAExG,IAAMmB,EAAc,OAAO,0BAA0BF,CAAK,EACpDG,EAAmBD,EAAY,OAC/BE,EAAqBF,EAAY,SAEvC,GAAI,CAAC,eAAeC,CAAgB,GAAK,CAAC,eAAeC,CAAkB,EACzE,MAAM,IAAIrB,EAAAA,WAAW,gBAAiB,4CAA4C,EAGpF,IAAMsB,EAAU,QAAQ,QAAQL,CAAK,EAGrC,GAAI,OAAOG,EAAiB,OAAU,UAAYjB,EAAAA,WAAWkB,EAAmB,KAAK,EAAG,CACtF,GAAIC,EAAQ,SAAW,EACrB,MAAM,IAAItB,EAAAA,WAAW,gBAAiB,4DAA4D,EAGpG,OAAO,YAAYoB,EAAiB,MAAOC,EAAmB,KAAK,CACrE,CAGA,IAAME,EAAiBJ,EAAY,KACnC,GACE,OAAOC,EAAiB,OAAU,UAClC,OAAOC,EAAmB,OAAU,UACpC,eAAeE,CAAc,GAC7BA,EAAe,QAAU,QACzB,CACA,GAAID,EAAQ,SAAW,EACrB,MAAM,IAAItB,EAAAA,WAAW,gBAAiB,6DAA6D,EAErG,GAAIoB,EAAiB,MAAM,OAASP,EAClC,MAAM,IAAIb,EAAAA,WAAW,gBAAiB,0CAA0Ca,EAAmB,YAAY,EAEjH,GAAI,CAACD,EAAQ,KAAKQ,EAAiB,KAAK,EACtC,MAAM,IAAIpB,EAAAA,WAAW,gBAAiB,8BAA8BoB,EAAiB,MAAM,EAAE,EAE/F,IAAMI,EAAkBR,GAAS,UAAYZ,EAAAA,SAC7C,GAAI,CACF,IAAMqB,EAAmBD,EAAgBH,EAAmB,KAAK,EAEjE,GAAI,CAAClB,EAAAA,WAAWsB,CAAgB,GAAKA,EAAiB,OAASJ,EAAmB,MAChF,MAAM,IAAIrB,EAAAA,WACR,mBACA,gDAAgDqB,EAAmB,MAAM,aAC3E,EAGF,OAAO,MAAM,OAAOD,EAAiB,KAAK,EAAGK,EAAkB,CAAE,KAAM,OAAQ,CAAC,CAClF,OAASC,EAAO,CACd,MAAM,IAAI1B,EAAAA,WAAW,gBAAiB,oCAAoCqB,EAAmB,MAAM,GAAI,CACrG,MAAOK,CACT,CAAC,CACH,CACF,CAEA,MAAM,IAAI1B,EAAAA,WACR,gBACA,wFACF,CACF,CAEA,SAAgB,QAAQ,EAAgC,CACtD,OAAO,OAAOiB,GAAU,YAAYA,GAAkBP,EAAe,IAAIO,CAAK,CAChF,CAEA,SAAgB,IAAwB,EAAgB,EAAoC,CAG1F,OAFA,mBAAmBU,EAAMC,CAAK,EAEvB,YAAYD,EAAK,OAASC,EAAM,OAAQD,EAAK,QAAQ,CAC9D,CAEA,SAAgB,SAA6B,EAAgB,EAAoC,CAG/F,OAFA,mBAAmBA,EAAMC,CAAK,EAEvB,YAAYD,EAAK,OAASC,EAAM,OAAQD,EAAK,QAAQ,CAC9D,CAEA,SAAgB,SACd,EACA,EACA,EAAuC,CAAC,EAC9B,CACV,YAAYV,CAAK,EAEjB,IAAMY,EAAS5B,EAAAA,QAAQ6B,CAAM,EAE7B,OAAO,YACL5B,EAAAA,cAAce,EAAM,OAASY,EAAO,UAAWA,EAAO,YAAab,EAAQ,UAAYL,CAAe,EACtGM,EAAM,QACR,CACF,CAEA,SAAgB,OACd,EACA,EACA,EAAuC,CAAC,EAC9B,CACV,YAAYA,CAAK,EAEjB,IAAMY,EAAS5B,EAAAA,QAAQ8B,CAAO,EAE9B,GAAIF,EAAO,YAAc,GAAI,MAAM,IAAI7B,EAAAA,WAAW,mBAAoB,6BAA6B,EAEnG,IAAMgC,EAAmBH,EAAO,UAAY,GAAK,CAACA,EAAO,UAAYA,EAAO,UACtEI,EAAWhB,EAAM,OAASY,EAAO,YAOvC,OAAO,YANU3B,EAAAA,cACf2B,EAAO,UAAY,GAAK,CAACI,EAAWA,EACpCD,EACAhB,EAAQ,UAAYL,CAGH,EAAUM,EAAM,QAAQ,CAC7C,CAEA,SAAgB,QAA4B,EAAgB,EAAsC,CAGhG,OAFA,mBAAmBU,EAAMC,CAAK,EAEvBD,EAAK,SAAWC,EAAM,OAAS,EAAID,EAAK,OAASC,EAAM,OAAS,GAAK,CAC9E,CAEA,SAAgB,MACd,EACA,EACU,CACV,GAAI,QAAQZ,EAAQ,IAAKA,EAAQ,GAAG,IAAM,EACxC,MAAM,IAAIhB,EAAAA,WAAW,gBAAiB,qCAAqC,EAG7E,OAAO,QAAQiB,EAAOD,EAAQ,GAAG,IAAM,GAAKA,EAAQ,IAAM,QAAQC,EAAOD,EAAQ,GAAG,IAAM,EAAIA,EAAQ,IAAMC,CAC9G,CAEA,SAAgB,IAAwB,EAA2B,CAGjE,OAFA,YAAYA,CAAK,EAEV,YAAYA,EAAM,OAAS,GAAK,CAACA,EAAM,OAASA,EAAM,OAAQA,EAAM,QAAQ,CACrF,CAEA,SAAgB,OAA2B,EAA2B,CAGpE,OAFA,YAAYA,CAAK,EAEV,YAAY,CAACA,EAAM,OAAQA,EAAM,QAAQ,CAClD,CAEA,SAAgB,MACd,EACA,EACU,CACV,YAAYA,CAAK,EAEjB,GAAM,CAAE,iBAAgB,WAAWN,GAAoBK,EAEvD,GAAI,CAAC,OAAO,UAAUkB,CAAc,GAAKA,EAAiB,GAAKA,EAAiBjB,EAAM,SAAS,UAC7F,MAAM,IAAIjB,EAAAA,WAAW,mBAAoB,+CAA+CiB,EAAM,SAAS,WAAW,EAGpH,IAAMa,EAAS,KAAO,OAAOb,EAAM,SAAS,UAAYiB,CAAc,EAEtE,OAAO,YAAYhC,EAAAA,cAAce,EAAM,OAAQa,EAAQK,CAAQ,EAAIL,EAAQb,EAAM,QAAQ,CAC3F,CAEA,SAAgB,UAAU,EAAsB,CAG9C,OAFA,YAAYA,CAAK,EAEVZ,EAAAA,gBAAgBY,EAAM,OAAQA,EAAM,SAAS,SAAS,CAC/D,CAEA,SAAgB,YAAgC,EAAgB,EAAuB,CACrF,IAAMA,EAAQ,OAAO,OAAO,CAAE,SAAQ,UAAS,CAAC,EAIhD,OAFAP,EAAe,IAAIO,CAAK,EAEjBA,CACT,CAEA,SAAgB,YAAY,EAAwC,CAClE,GAAI,CAAC,QAAQA,CAAK,EAChB,MAAM,IAAIjB,EAAAA,WAAW,gBAAiB,iDAAiD,CAE3F,CAEA,SAAS,mBAAmB,EAAa,EAAoB,CAI3D,GAHA,YAAY2B,CAAI,EAChB,YAAYC,CAAK,EAEbD,EAAK,WAAaC,EAAM,SAC1B,MAAM,IAAItB,EAAAA,sBAAsBqB,EAAK,SAAS,KAAMC,EAAM,SAAS,IAAI,CAE3E,CAEA,SAAS,eAAe,EAA2C,CACjE,GAAI,CAACzB,EAAAA,WAAWc,CAAK,EAAG,MAAM,IAAIjB,EAAAA,WAAW,mBAAoB,sCAAsC,CACzG,CAEA,SAAS,kBAAkB,EAAuD,CAChF,GAAI,OAAOiB,GAAU,WAAYA,EAAgB,MAAO,GAExD,IAAMmB,EAAY,OAAO,eAAenB,CAAK,EAE7C,OAAOmB,IAAc,OAAO,WAAaA,IAAc,IACzD,CAEA,SAAS,eACP,EACuD,CACvD,OACEC,IAAe,IAAA,IAAa,UAAWA,GAAcA,EAAW,MAAQ,IAAA,IAAaA,EAAW,MAAQ,IAAA,EAE5G,C"}
package/dist/money.js CHANGED
@@ -1,42 +1,42 @@
1
- import { CoinsError as e, CurrencyMismatchError as t } from "./errors.js";
2
- import { decimal as n, roundDivision as r, toDecimalString as i } from "./_decimal.js";
3
- import { currency as a, isCurrency as o } from "./currency.js";
4
- //#region src/money.ts
1
+ import { decimal as e, roundDivision as t, toDecimalString as n } from "./_decimal.js";
2
+ import { currency as r, isCurrency as i } from "./currency.js";
3
+ import { CoinsError as a, CurrencyMismatchError as o } from "./errors.js";
4
+ // src/money.ts
5
5
  var s = /* @__PURE__ */ new WeakSet(), c = "halfAwayFromZero", l = /^(?:0|-[1-9]\d*|[1-9]\d*)$/, u = 1e3;
6
- function money(t, i, a) {
7
- if (assertCurrency(i), typeof t == "bigint") {
8
- if (!a || !("unit" in a) || a.unit !== "minor") throw new e("INVALID_MONEY", "Bigint amounts require { unit: 'minor' }");
9
- return createMoney(t, i);
6
+ function money(n, r, i) {
7
+ if (assertCurrency(r), typeof n == "bigint") {
8
+ if (!i || !("unit" in i) || i.unit !== "minor") throw new a("INVALID_MONEY", "Bigint amounts require { unit: 'minor' }");
9
+ return createMoney(n, r);
10
10
  }
11
- if (a && "unit" in a) throw new e("INVALID_MONEY", "Decimal strings do not accept a unit option");
12
- let o = n(t), s = o.numerator * 10n ** BigInt(i.minorUnit);
13
- if (s % o.denominator !== 0n && a?.rounding === void 0) throw new e("INVALID_MONEY", `Amount "${t}" exceeds ${i.code} precision; provide rounding`);
14
- return createMoney(r(s, o.denominator, a?.rounding ?? c), i);
15
- }
16
- function decodeMoney(t, n) {
17
- if (!isPlainDataObject(t)) throw new e("INVALID_MONEY", "Money must be a plain data object");
18
- let r = Object.getOwnPropertyDescriptors(t), i = r.amount, s = r.currency;
19
- if (!isDataProperty(i) || !isDataProperty(s)) throw new e("INVALID_MONEY", "Money properties must be plain data values");
20
- let c = Reflect.ownKeys(t);
21
- if (typeof i.value == "bigint" && o(s.value)) {
22
- if (c.length !== 2) throw new e("INVALID_MONEY", "Plain money must have exactly the keys amount and currency");
23
- return createMoney(i.value, s.value);
11
+ if (i && "unit" in i) throw new a("INVALID_MONEY", "Decimal strings do not accept a unit option");
12
+ let o = e(n), s = o.numerator * 10n ** BigInt(r.minorUnit);
13
+ if (s % o.denominator !== 0n && i?.rounding === void 0) throw new a("INVALID_MONEY", `Amount "${n}" exceeds ${r.code} precision; provide rounding`);
14
+ return createMoney(t(s, o.denominator, i?.rounding ?? c), r);
15
+ }
16
+ function decodeMoney(e, t) {
17
+ if (!isPlainDataObject(e)) throw new a("INVALID_MONEY", "Money must be a plain data object");
18
+ let n = Object.getOwnPropertyDescriptors(e), o = n.amount, s = n.currency;
19
+ if (!isDataProperty(o) || !isDataProperty(s)) throw new a("INVALID_MONEY", "Money properties must be plain data values");
20
+ let c = Reflect.ownKeys(e);
21
+ if (typeof o.value == "bigint" && i(s.value)) {
22
+ if (c.length !== 2) throw new a("INVALID_MONEY", "Plain money must have exactly the keys amount and currency");
23
+ return createMoney(o.value, s.value);
24
24
  }
25
- let d = r.unit;
26
- if (typeof i.value == "string" && typeof s.value == "string" && isDataProperty(d) && d.value === "minor") {
27
- if (c.length !== 3) throw new e("INVALID_MONEY", "MoneyJSON must have exactly the keys amount, currency, unit");
28
- if (i.value.length > u) throw new e("INVALID_MONEY", `Money JSON amount is too long; maximum ${u} characters`);
29
- if (!l.test(i.value)) throw new e("INVALID_MONEY", `Invalid Money JSON amount "${i.value}"`);
30
- let t = n?.currency ?? a;
25
+ let d = n.unit;
26
+ if (typeof o.value == "string" && typeof s.value == "string" && isDataProperty(d) && d.value === "minor") {
27
+ if (c.length !== 3) throw new a("INVALID_MONEY", "MoneyJSON must have exactly the keys amount, currency, unit");
28
+ if (o.value.length > u) throw new a("INVALID_MONEY", `Money JSON amount is too long; maximum ${u} characters`);
29
+ if (!l.test(o.value)) throw new a("INVALID_MONEY", `Invalid Money JSON amount "${o.value}"`);
30
+ let e = t?.currency ?? r;
31
31
  try {
32
- let n = t(s.value);
33
- if (!o(n) || n.code !== s.value) throw new e("INVALID_CURRENCY", `Currency resolver must return the canonical "${s.value}" definition`);
34
- return money(BigInt(i.value), n, { unit: "minor" });
35
- } catch (t) {
36
- throw new e("INVALID_MONEY", `Invalid Money JSON for currency "${s.value}"`, { cause: t });
32
+ let t = e(s.value);
33
+ if (!i(t) || t.code !== s.value) throw new a("INVALID_CURRENCY", `Currency resolver must return the canonical "${s.value}" definition`);
34
+ return money(BigInt(o.value), t, { unit: "minor" });
35
+ } catch (e) {
36
+ throw new a("INVALID_MONEY", `Invalid Money JSON for currency "${s.value}"`, { cause: e });
37
37
  }
38
38
  }
39
- throw new e("INVALID_MONEY", "Money must contain bigint amount and a registered currency, or a valid MoneyJSON shape");
39
+ throw new a("INVALID_MONEY", "Money must contain bigint amount and a registered currency, or a valid MoneyJSON shape");
40
40
  }
41
41
  function isMoney(e) {
42
42
  return typeof e == "object" && !!e && s.has(e);
@@ -47,24 +47,24 @@ function add(e, t) {
47
47
  function subtract(e, t) {
48
48
  return assertSameCurrency(e, t), createMoney(e.amount - t.amount, e.currency);
49
49
  }
50
- function multiply(e, t, i = {}) {
51
- assertMoney(e);
52
- let a = n(t);
53
- return createMoney(r(e.amount * a.numerator, a.denominator, i.rounding ?? c), e.currency);
50
+ function multiply(n, r, i = {}) {
51
+ assertMoney(n);
52
+ let a = e(r);
53
+ return createMoney(t(n.amount * a.numerator, a.denominator, i.rounding ?? c), n.currency);
54
54
  }
55
- function divide(t, i, a = {}) {
56
- assertMoney(t);
57
- let o = n(i);
58
- if (o.numerator === 0n) throw new e("DIVISION_BY_ZERO", "Cannot divide money by zero");
59
- let s = o.numerator < 0n ? -o.numerator : o.numerator, l = t.amount * o.denominator;
60
- return createMoney(r(o.numerator < 0n ? -l : l, s, a.rounding ?? c), t.currency);
55
+ function divide(n, r, i = {}) {
56
+ assertMoney(n);
57
+ let o = e(r);
58
+ if (o.numerator === 0n) throw new a("DIVISION_BY_ZERO", "Cannot divide money by zero");
59
+ let s = o.numerator < 0n ? -o.numerator : o.numerator, l = n.amount * o.denominator;
60
+ return createMoney(t(o.numerator < 0n ? -l : l, s, i.rounding ?? c), n.currency);
61
61
  }
62
62
  function compare(e, t) {
63
63
  return assertSameCurrency(e, t), e.amount === t.amount ? 0 : e.amount < t.amount ? -1 : 1;
64
64
  }
65
- function clamp(t, n) {
66
- if (compare(n.min, n.max) === 1) throw new e("INVALID_RANGE", "Clamp minimum cannot exceed maximum");
67
- return compare(t, n.min) === -1 ? n.min : compare(t, n.max) === 1 ? n.max : t;
65
+ function clamp(e, t) {
66
+ if (compare(t.min, t.max) === 1) throw new a("INVALID_RANGE", "Clamp minimum cannot exceed maximum");
67
+ return compare(e, t.min) === -1 ? t.min : compare(e, t.max) === 1 ? t.max : e;
68
68
  }
69
69
  function abs(e) {
70
70
  return assertMoney(e), createMoney(e.amount < 0n ? -e.amount : e.amount, e.currency);
@@ -72,15 +72,15 @@ function abs(e) {
72
72
  function negate(e) {
73
73
  return assertMoney(e), createMoney(-e.amount, e.currency);
74
74
  }
75
- function round(t, n) {
76
- assertMoney(t);
77
- let { fractionDigits: i, rounding: a = c } = n;
78
- if (!Number.isInteger(i) || i < 0 || i > t.currency.minorUnit) throw new e("INVALID_ROUNDING", `fractionDigits must be an integer from 0 to ${t.currency.minorUnit}`);
79
- let o = 10n ** BigInt(t.currency.minorUnit - i);
80
- return createMoney(r(t.amount, o, a) * o, t.currency);
75
+ function round(e, n) {
76
+ assertMoney(e);
77
+ let { fractionDigits: r, rounding: i = c } = n;
78
+ if (!Number.isInteger(r) || r < 0 || r > e.currency.minorUnit) throw new a("INVALID_ROUNDING", `fractionDigits must be an integer from 0 to ${e.currency.minorUnit}`);
79
+ let o = 10n ** BigInt(e.currency.minorUnit - r);
80
+ return createMoney(t(e.amount, o, i) * o, e.currency);
81
81
  }
82
82
  function toDecimal(e) {
83
- return assertMoney(e), i(e.amount, e.currency.minorUnit);
83
+ return assertMoney(e), n(e.amount, e.currency.minorUnit);
84
84
  }
85
85
  function createMoney(e, t) {
86
86
  let n = Object.freeze({
@@ -89,14 +89,14 @@ function createMoney(e, t) {
89
89
  });
90
90
  return s.add(n), n;
91
91
  }
92
- function assertMoney(t) {
93
- if (!isMoney(t)) throw new e("INVALID_MONEY", "Money must be a canonical @vielzeug/coins value");
92
+ function assertMoney(e) {
93
+ if (!isMoney(e)) throw new a("INVALID_MONEY", "Money must be a canonical @vielzeug/coins value");
94
94
  }
95
- function assertSameCurrency(e, n) {
96
- if (assertMoney(e), assertMoney(n), e.currency !== n.currency) throw new t(e.currency.code, n.currency.code);
95
+ function assertSameCurrency(e, t) {
96
+ if (assertMoney(e), assertMoney(t), e.currency !== t.currency) throw new o(e.currency.code, t.currency.code);
97
97
  }
98
- function assertCurrency(t) {
99
- if (!o(t)) throw new e("INVALID_CURRENCY", "Money requires a registered currency");
98
+ function assertCurrency(e) {
99
+ if (!i(e)) throw new a("INVALID_CURRENCY", "Money requires a registered currency");
100
100
  }
101
101
  function isPlainDataObject(e) {
102
102
  if (typeof e != "object" || !e) return !1;
@@ -106,7 +106,6 @@ function isPlainDataObject(e) {
106
106
  function isDataProperty(e) {
107
107
  return e !== void 0 && "value" in e && e.get === void 0 && e.set === void 0;
108
108
  }
109
- //#endregion
110
109
  export { abs, add, assertMoney, clamp, compare, createMoney, decodeMoney, divide, isMoney, money, multiply, negate, round, subtract, toDecimal };
111
110
 
112
111
  //# sourceMappingURL=money.js.map
@@ -1,5 +1,5 @@
1
1
  import { assertMoney as e } from "./money.js";
2
- //#region src/serialization.ts
2
+ // src/serialization.ts
3
3
  function toJSON(t) {
4
4
  return e(t), {
5
5
  amount: t.amount.toString(),
@@ -7,7 +7,6 @@ function toJSON(t) {
7
7
  unit: "minor"
8
8
  };
9
9
  }
10
- //#endregion
11
10
  export { toJSON };
12
11
 
13
12
  //# sourceMappingURL=serialization.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/coins",
3
- "version": "26.11.5",
3
+ "version": "26.11.6",
4
4
  "description": "Currency formatting and exchange utilities for monetary arithmetic",
5
5
  "repository": {
6
6
  "type": "git",
@@ -31,19 +31,19 @@
31
31
  "registry": "https://registry.npmjs.org/"
32
32
  },
33
33
  "scripts": {
34
- "build": "vite build && pnpm --silent run build:bundle && pnpm --silent run build:types && pnpm --silent run verify:exports",
34
+ "build": "vite build && bun run --silent build:bundle && bun run --silent build:types && bun run --silent verify:exports",
35
35
  "build:bundle": "vite build --config vite.bundle.config.ts",
36
36
  "build:types": "tsc -p tsconfig.declarations.json",
37
37
  "fix": "biome check --write src",
38
38
  "lint": "biome ci src",
39
- "prepublishOnly": "pnpm --silent run build",
39
+ "prepublishOnly": "bun run --silent build",
40
40
  "test": "vitest",
41
- "verify:exports": "node verify-exports.mjs"
41
+ "verify:exports": "bun verify-exports.mjs"
42
42
  },
43
43
  "devDependencies": {
44
- "@types/node": "^26.6.5",
44
+ "@types/node": "^26.6.6",
45
45
  "typescript": "^6.0.3",
46
- "vite": "^8.3.4",
47
- "vitest": "^5.0.5"
46
+ "vite": "^8.3.6",
47
+ "vitest": "^5.1.0"
48
48
  }
49
49
  }