@vielzeug/coins 2.2.0 → 26.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +12 -67
  2. package/dist/_decimal.cjs +1 -1
  3. package/dist/_decimal.cjs.map +1 -1
  4. package/dist/_decimal.d.ts +1 -0
  5. package/dist/_decimal.d.ts.map +1 -1
  6. package/dist/_decimal.js +1 -52
  7. package/dist/_decimal.js.map +1 -1
  8. package/dist/aggregate.cjs +1 -1
  9. package/dist/aggregate.cjs.map +1 -1
  10. package/dist/aggregate.d.ts.map +1 -1
  11. package/dist/aggregate.js +1 -39
  12. package/dist/aggregate.js.map +1 -1
  13. package/dist/coins.cjs +1 -1
  14. package/dist/coins.cjs.map +1 -1
  15. package/dist/coins.iife.js +1 -1
  16. package/dist/coins.iife.js.map +1 -1
  17. package/dist/coins.js +1 -1
  18. package/dist/coins.js.map +1 -1
  19. package/dist/currency.cjs +1 -1
  20. package/dist/currency.cjs.map +1 -1
  21. package/dist/currency.d.ts.map +1 -1
  22. package/dist/currency.js +1 -34
  23. package/dist/currency.js.map +1 -1
  24. package/dist/errors.cjs +1 -1
  25. package/dist/errors.cjs.map +1 -1
  26. package/dist/errors.d.ts.map +1 -1
  27. package/dist/errors.js +1 -21
  28. package/dist/errors.js.map +1 -1
  29. package/dist/exchange.cjs +1 -1
  30. package/dist/exchange.cjs.map +1 -1
  31. package/dist/exchange.js +1 -29
  32. package/dist/exchange.js.map +1 -1
  33. package/dist/format.cjs +1 -1
  34. package/dist/format.cjs.map +1 -1
  35. package/dist/format.d.ts.map +1 -1
  36. package/dist/format.js +1 -66
  37. package/dist/format.js.map +1 -1
  38. package/dist/index.cjs +1 -1
  39. package/dist/index.d.ts +2 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1 -8
  42. package/dist/money.cjs +1 -1
  43. package/dist/money.cjs.map +1 -1
  44. package/dist/money.d.ts +9 -2
  45. package/dist/money.d.ts.map +1 -1
  46. package/dist/money.js +1 -93
  47. package/dist/money.js.map +1 -1
  48. package/dist/serialization.cjs +1 -1
  49. package/dist/serialization.cjs.map +1 -1
  50. package/dist/serialization.d.ts +1 -4
  51. package/dist/serialization.d.ts.map +1 -1
  52. package/dist/serialization.js +1 -44
  53. package/dist/serialization.js.map +1 -1
  54. package/package.json +9 -8
package/README.md CHANGED
@@ -1,14 +1,16 @@
1
1
  # @vielzeug/coins
2
2
 
3
- Exact monetary arithmetic for TypeScript. Coins stores bigint minor units, uses explicit currency definitions, and accepts decimal strings at every exact-input boundary.
3
+ > Currency formatting and exchange utilities for monetary arithmetic
4
4
 
5
- ## Install
5
+ ## Installation
6
6
 
7
7
  ```sh
8
8
  pnpm add @vielzeug/coins
9
+ npm install @vielzeug/coins
10
+ yarn add @vielzeug/coins
9
11
  ```
10
12
 
11
- ## Start Here
13
+ ## Quick Start
12
14
 
13
15
  ```ts
14
16
  import { USD, add, money, toDecimal } from '@vielzeug/coins';
@@ -18,71 +20,14 @@ const subtotal = add(money('12.50', USD), money('7.25', USD));
18
20
  console.log(toDecimal(subtotal)); // '19.75'
19
21
  ```
20
22
 
21
- `money()` has one common path and one explicit low-level path:
23
+ ## Documentation
22
24
 
23
- ```ts
24
- money('19.99', USD); // exact decimal major units
25
- money('19.999', USD, { rounding: 'halfEven' });
26
- money(1999n, USD, { unit: 'minor' }); // stored minor units
27
- ```
28
-
29
- ## Core API
30
-
31
- ```ts
32
- import {
33
- EUR,
34
- USD,
35
- allocate,
36
- exchange,
37
- exchangeRate,
38
- format,
39
- money,
40
- multiply,
41
- sum,
42
- toDecimal,
43
- } from '@vielzeug/coins';
44
-
45
- const items = [money('12.50', USD), money('7.25', USD)];
46
- const subtotal = sum(items);
47
- const total = multiply(subtotal, '1.08', { rounding: 'halfEven' });
48
- const shares = allocate(total, 3);
49
-
50
- const usdToEur = exchangeRate({ from: USD, to: EUR, value: '0.9234' });
51
- const eurTotal = exchange(total, usdToEur, { rounding: 'halfEven' });
52
-
53
- console.log(format(eurTotal, { locale: 'de-DE' }));
54
- console.log(shares.map(toDecimal));
55
- ```
56
-
57
- ## Currency Definitions
58
-
59
- Built-in definitions: `USD`, `EUR`, `GBP`, `JPY`, `KRW`, `BHD`, `KWD`.
60
-
61
- Define a business currency explicitly when required:
62
-
63
- ```ts
64
- import { currency, money } from '@vielzeug/coins';
65
-
66
- const POINTS = currency({ code: 'PTS', minorUnit: 0 });
67
- const balance = money('250', POINTS);
68
- ```
69
-
70
- ## Errors
71
-
72
- All failures extend `CoinsError` and expose `code`.
73
-
74
- ```ts
75
- import { CoinsError, money, USD } from '@vielzeug/coins';
76
-
77
- try {
78
- money('19.999', USD);
79
- } catch (error) {
80
- if (error instanceof CoinsError && error.code === 'INVALID_MONEY') {
81
- // Over-precise decimal requires a rounding mode.
82
- }
83
- }
84
- ```
25
+ - [Overview](https://vielzeug.dev/coins/)
26
+ - [Usage Guide](https://vielzeug.dev/coins/usage)
27
+ - [API Reference](https://vielzeug.dev/coins/api)
28
+ - [Examples](https://vielzeug.dev/coins/examples)
29
+ - [Migration Guide](https://vielzeug.dev/coins/migration)
85
30
 
86
31
  ## License
87
32
 
88
- MIT © Helmuth Saatkamp
33
+ MIT © [Helmuth Saatkamp](https://github.com/helmuthdu) — part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
package/dist/_decimal.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs");var t=/^(-?)(\d+)(?:\.(\d+))?$/,n=1e3,r=100;function i(i){if(i.length>n)throw new e.CoinsError(`INVALID_DECIMAL`,`Decimal input is too long`);let a=t.exec(i);if(!a)throw new e.CoinsError(`INVALID_DECIMAL`,`Invalid decimal: "${i}"`);let o=(a[3]??``).replace(/0+$/,``);if(o.length>r)throw new e.CoinsError(`INVALID_DECIMAL`,`Decimal precision cannot exceed ${r} places`);let c=10n**BigInt(o.length),l=BigInt(a[2])*c+BigInt(o||`0`),u=a[1]===`-`&&l!==0n?-l:l,d=s(u<0n?-u:u,c);return Object.freeze({denominator:c/d,numerator:u/d})}function a(t,n,r){if(n<=0n)throw new e.CoinsError(`INVALID_DECIMAL`,`Decimal denominator must be positive`);let i=t<0n,a=i?-t:t,o=a/n,s=a%n;if(s===0n)return i?-o:o;let c=(()=>{switch(r){case`awayFromZero`:return!0;case`ceil`:return!i;case`floor`:return i;case`halfAwayFromZero`:return s*2n>=n;case`halfEven`:{let e=s*2n;return e>n||e===n&&o%2n!=0n}case`towardZero`:return!1;default:throw new e.CoinsError(`INVALID_ROUNDING`,`Unknown rounding mode: ${r}`)}})()?o+1n:o;return i?-c:c}function o(e,t){let n=e<0n,r=n?-e:e,i=10n**BigInt(t),a=r/i;if(t===0)return`${n?`-`:``}${a}`;let o=(r%i).toString().padStart(t,`0`);return`${n?`-`:``}${a}.${o}`}function s(e,t){let n=e,r=t;for(;r!==0n;)[n,r]=[r,n%r];return n===0n?1n:n}function c(e,t){return e/s(e,t)*t}exports.decimal=i,exports.gcd=s,exports.lcm=c,exports.roundDivision=a,exports.toDecimalString=o;
1
+ const e=require("./errors.cjs");var t=/^(-?)(\d+)(?:\.(\d+))?$/,n=1e3,r=100,i=new Set([`awayFromZero`,`ceil`,`floor`,`halfAwayFromZero`,`halfEven`,`towardZero`]);function assertRoundingMode(t){if(!i.has(t))throw new e.CoinsError(`INVALID_ROUNDING`,`Unknown rounding mode: ${String(t)}`)}function decimal(i){if(typeof i!=`string`)throw new e.CoinsError(`INVALID_DECIMAL`,`Decimal input must be a string`);if(i.length>n)throw new e.CoinsError(`INVALID_DECIMAL`,`Decimal input is too long`);let a=t.exec(i);if(!a)throw new e.CoinsError(`INVALID_DECIMAL`,`Invalid decimal: "${i}"`);let o=(a[3]??``).replace(/0+$/,``);if(o.length>r)throw new e.CoinsError(`INVALID_DECIMAL`,`Decimal precision cannot exceed ${r} places`);let s=10n**BigInt(o.length),c=BigInt(a[2])*s+BigInt(o||`0`),l=a[1]===`-`&&c!==0n?-c:c,u=gcd(l<0n?-l:l,s);return Object.freeze({denominator:s/u,numerator:l/u})}function roundDivision(t,n,r){if(assertRoundingMode(r),n<=0n)throw new e.CoinsError(`INVALID_DECIMAL`,`Decimal denominator must be positive`);let i=t<0n,a=i?-t:t,o=a/n,s=a%n;if(s===0n)return i?-o:o;let c=(()=>{switch(r){case`awayFromZero`:return!0;case`ceil`:return!i;case`floor`:return i;case`halfAwayFromZero`:return s*2n>=n;case`halfEven`:{let e=s*2n;return e>n||e===n&&o%2n!=0n}case`towardZero`:return!1;default:throw new e.CoinsError(`INVALID_ROUNDING`,`Unknown rounding mode: ${r}`)}})()?o+1n:o;return i?-c:c}function toDecimalString(e,t){let n=e<0n,r=n?-e:e,i=10n**BigInt(t),a=r/i;if(t===0)return`${n?`-`:``}${a}`;let o=(r%i).toString().padStart(t,`0`);return`${n?`-`:``}${a}.${o}`}function gcd(e,t){let n=e,r=t;for(;r!==0n;)[n,r]=[r,n%r];return n===0n?1n:n}function lcm(e,t){return e/gcd(e,t)*t}exports.assertRoundingMode=assertRoundingMode,exports.decimal=decimal,exports.gcd=gcd,exports.lcm=lcm,exports.roundDivision=roundDivision,exports.toDecimalString=toDecimalString;
2
2
  //# sourceMappingURL=_decimal.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_decimal.cjs","names":[],"sources":["../src/_decimal.ts"],"sourcesContent":["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;\n\nexport function decimal(value: string): Decimal {\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 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"],"mappings":"gCAGA,IAAM,EAAU,0BACV,EAAqB,IACrB,EAAqB,IAE3B,SAAgB,EAAQ,EAAwB,CAC9C,GAAI,EAAM,OAAS,EAAoB,MAAM,IAAI,EAAA,WAAW,kBAAmB,2BAA2B,EAE1G,IAAM,EAAQ,EAAQ,KAAK,CAAK,EAEhC,GAAI,CAAC,EAAO,MAAM,IAAI,EAAA,WAAW,kBAAmB,qBAAqB,EAAM,EAAE,EAEjF,IAAM,GAAY,EAAM,IAAM,GAAA,CAAI,QAAQ,MAAO,EAAE,EAEnD,GAAI,EAAS,OAAS,EACpB,MAAM,IAAI,EAAA,WAAW,kBAAmB,mCAAmC,EAAmB,QAAQ,EAGxG,IAAM,EAAc,KAAO,OAAO,EAAS,MAAM,EAC3C,EAAW,OAAO,EAAM,EAAG,EAAI,EAAc,OAAO,GAAY,GAAG,EACnE,EAAY,EAAM,KAAO,KAAO,IAAa,GAAK,CAAC,EAAW,EAC9D,EAAU,EAAI,EAAY,GAAK,CAAC,EAAY,EAAW,CAAW,EAExE,OAAO,OAAO,OAAO,CAAE,YAAa,EAAc,EAAS,UAAW,EAAY,CAAQ,CAAC,CAC7F,CAEA,SAAgB,EAAc,EAAmB,EAAqB,EAA4B,CAChG,GAAI,GAAe,GAAI,MAAM,IAAI,EAAA,WAAW,kBAAmB,sCAAsC,EAErG,IAAM,EAAW,EAAY,GACvB,EAAW,EAAW,CAAC,EAAY,EACnC,EAAW,EAAW,EACtB,EAAY,EAAW,EAE7B,GAAI,IAAc,GAAI,OAAO,EAAW,CAAC,EAAW,EAwBpD,IAAM,OAtBmB,CACvB,OAAQ,EAAR,CACE,IAAK,eACH,MAAO,GACT,IAAK,OACH,MAAO,CAAC,EACV,IAAK,QACH,OAAO,EACT,IAAK,mBACH,OAAO,EAAY,IAAM,EAC3B,IAAK,WAAY,CACf,IAAM,EAAU,EAAY,GAE5B,OAAO,EAAU,GAAgB,IAAY,GAAe,EAAW,IAAO,EAChF,CACA,IAAK,aACH,MAAO,GACT,QACE,MAAM,IAAI,EAAA,WAAW,mBAAoB,0BAA0B,GAAsB,CAC7F,CACF,EAAA,CAEe,EAAY,EAAW,GAAK,EAE3C,OAAO,EAAW,CAAC,EAAS,CAC9B,CAEA,SAAgB,EAAgB,EAAgB,EAA2B,CACzE,IAAM,EAAW,EAAS,GACpB,EAAW,EAAW,CAAC,EAAS,EAChC,EAAQ,KAAO,OAAO,CAAS,EAC/B,EAAQ,EAAW,EAEzB,GAAI,IAAc,EAAG,MAAO,GAAG,EAAW,IAAM,KAAK,IAErD,IAAM,GAAY,EAAW,EAAA,CAAO,SAAS,CAAC,CAAC,SAAS,EAAW,GAAG,EAEtE,MAAO,GAAG,EAAW,IAAM,KAAK,EAAM,GAAG,GAC3C,CAEA,SAAgB,EAAI,EAAc,EAAuB,CACvD,IAAI,EAAI,EACJ,EAAI,EAER,KAAO,IAAM,IAAI,CAAC,EAAG,GAAK,CAAC,EAAG,EAAI,CAAC,EAEnC,OAAO,IAAM,GAAK,GAAK,CACzB,CAEA,SAAgB,EAAI,EAAc,EAAuB,CACvD,OAAQ,EAAO,EAAI,EAAM,CAAK,EAAK,CACrC"}
1
+ {"version":3,"file":"_decimal.cjs","names":[],"sources":["../src/_decimal.ts"],"sourcesContent":["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"],"mappings":"gCAGA,IAAM,EAAU,0BACV,EAAqB,IACrB,EAAqB,IACrB,EAAiB,IAAI,IAAkB,CAC3C,eACA,OACA,QACA,mBACA,WACA,YACF,CAAC,EAED,SAAgB,mBAAmB,EAA+C,CAChF,GAAI,CAAC,EAAe,IAAI,CAAqB,EAC3C,MAAM,IAAI,EAAA,WAAW,mBAAoB,0BAA0B,OAAO,CAAK,GAAG,CAEtF,CAEA,SAAgB,QAAQ,EAAwB,CAC9C,GAAI,OAAO,GAAU,SAAU,MAAM,IAAI,EAAA,WAAW,kBAAmB,gCAAgC,EACvG,GAAI,EAAM,OAAS,EAAoB,MAAM,IAAI,EAAA,WAAW,kBAAmB,2BAA2B,EAE1G,IAAM,EAAQ,EAAQ,KAAK,CAAK,EAEhC,GAAI,CAAC,EAAO,MAAM,IAAI,EAAA,WAAW,kBAAmB,qBAAqB,EAAM,EAAE,EAEjF,IAAM,GAAY,EAAM,IAAM,GAAA,CAAI,QAAQ,MAAO,EAAE,EAEnD,GAAI,EAAS,OAAS,EACpB,MAAM,IAAI,EAAA,WAAW,kBAAmB,mCAAmC,EAAmB,QAAQ,EAGxG,IAAM,EAAc,KAAO,OAAO,EAAS,MAAM,EAC3C,EAAW,OAAO,EAAM,EAAG,EAAI,EAAc,OAAO,GAAY,GAAG,EACnE,EAAY,EAAM,KAAO,KAAO,IAAa,GAAK,CAAC,EAAW,EAC9D,EAAU,IAAI,EAAY,GAAK,CAAC,EAAY,EAAW,CAAW,EAExE,OAAO,OAAO,OAAO,CAAE,YAAa,EAAc,EAAS,UAAW,EAAY,CAAQ,CAAC,CAC7F,CAEA,SAAgB,cAAc,EAAmB,EAAqB,EAA4B,CAEhG,GADA,mBAAmB,CAAI,EACnB,GAAe,GAAI,MAAM,IAAI,EAAA,WAAW,kBAAmB,sCAAsC,EAErG,IAAM,EAAW,EAAY,GACvB,EAAW,EAAW,CAAC,EAAY,EACnC,EAAW,EAAW,EACtB,EAAY,EAAW,EAE7B,GAAI,IAAc,GAAI,OAAO,EAAW,CAAC,EAAW,EAwBpD,IAAM,OAtBmB,CACvB,OAAQ,EAAR,CACE,IAAK,eACH,MAAO,GACT,IAAK,OACH,MAAO,CAAC,EACV,IAAK,QACH,OAAO,EACT,IAAK,mBACH,OAAO,EAAY,IAAM,EAC3B,IAAK,WAAY,CACf,IAAM,EAAU,EAAY,GAE5B,OAAO,EAAU,GAAgB,IAAY,GAAe,EAAW,IAAO,EAChF,CACA,IAAK,aACH,MAAO,GACT,QACE,MAAM,IAAI,EAAA,WAAW,mBAAoB,0BAA0B,GAAsB,CAC7F,CACF,EAAA,CAEe,EAAY,EAAW,GAAK,EAE3C,OAAO,EAAW,CAAC,EAAS,CAC9B,CAEA,SAAgB,gBAAgB,EAAgB,EAA2B,CACzE,IAAM,EAAW,EAAS,GACpB,EAAW,EAAW,CAAC,EAAS,EAChC,EAAQ,KAAO,OAAO,CAAS,EAC/B,EAAQ,EAAW,EAEzB,GAAI,IAAc,EAAG,MAAO,GAAG,EAAW,IAAM,KAAK,IAErD,IAAM,GAAY,EAAW,EAAA,CAAO,SAAS,CAAC,CAAC,SAAS,EAAW,GAAG,EAEtE,MAAO,GAAG,EAAW,IAAM,KAAK,EAAM,GAAG,GAC3C,CAEA,SAAgB,IAAI,EAAc,EAAuB,CACvD,IAAI,EAAI,EACJ,EAAI,EAER,KAAO,IAAM,IAAI,CAAC,EAAG,GAAK,CAAC,EAAG,EAAI,CAAC,EAEnC,OAAO,IAAM,GAAK,GAAK,CACzB,CAEA,SAAgB,IAAI,EAAc,EAAuB,CACvD,OAAQ,EAAO,IAAI,EAAM,CAAK,EAAK,CACrC"}
@@ -1,4 +1,5 @@
1
1
  import type { Decimal, RoundingMode } from './types';
2
+ export declare function assertRoundingMode(value: unknown): asserts value is RoundingMode;
2
3
  export declare function decimal(value: string): Decimal;
3
4
  export declare function roundDivision(numerator: bigint, denominator: bigint, mode: RoundingMode): bigint;
4
5
  export declare function toDecimalString(amount: bigint, minorUnit: number): string;
@@ -1 +1 @@
1
- {"version":3,"file":"_decimal.d.ts","sourceRoot":"","sources":["../src/_decimal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAMrD,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAmB9C;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAmChG;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAWzE;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD"}
1
+ {"version":3,"file":"_decimal.d.ts","sourceRoot":"","sources":["../src/_decimal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAcrD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,YAAY,CAIhF;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAoB9C;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAoChG;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAWzE;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD"}
package/dist/_decimal.js CHANGED
@@ -1,53 +1,2 @@
1
- import { CoinsError as e } from "./errors.js";
2
- //#region src/_decimal.ts
3
- var t = /^(-?)(\d+)(?:\.(\d+))?$/, n = 1e3, r = 100;
4
- function i(i) {
5
- if (i.length > n) throw new e("INVALID_DECIMAL", "Decimal input is too long");
6
- let a = t.exec(i);
7
- if (!a) throw new e("INVALID_DECIMAL", `Invalid decimal: "${i}"`);
8
- let o = (a[3] ?? "").replace(/0+$/, "");
9
- if (o.length > r) throw new e("INVALID_DECIMAL", `Decimal precision cannot exceed ${r} places`);
10
- let c = 10n ** BigInt(o.length), l = BigInt(a[2]) * c + BigInt(o || "0"), u = a[1] === "-" && l !== 0n ? -l : l, d = s(u < 0n ? -u : u, c);
11
- return Object.freeze({
12
- denominator: c / d,
13
- numerator: u / d
14
- });
15
- }
16
- function a(t, n, r) {
17
- if (n <= 0n) throw new e("INVALID_DECIMAL", "Decimal denominator must be positive");
18
- let i = t < 0n, a = i ? -t : t, o = a / n, s = a % n;
19
- if (s === 0n) return i ? -o : o;
20
- let c = (() => {
21
- switch (r) {
22
- case "awayFromZero": return !0;
23
- case "ceil": return !i;
24
- case "floor": return i;
25
- case "halfAwayFromZero": return s * 2n >= n;
26
- case "halfEven": {
27
- let e = s * 2n;
28
- return e > n || e === n && o % 2n != 0n;
29
- }
30
- case "towardZero": return !1;
31
- default: throw new e("INVALID_ROUNDING", `Unknown rounding mode: ${r}`);
32
- }
33
- })() ? o + 1n : o;
34
- return i ? -c : c;
35
- }
36
- function o(e, t) {
37
- let n = e < 0n, r = n ? -e : e, i = 10n ** BigInt(t), a = r / i;
38
- if (t === 0) return `${n ? "-" : ""}${a}`;
39
- let o = (r % i).toString().padStart(t, "0");
40
- return `${n ? "-" : ""}${a}.${o}`;
41
- }
42
- function s(e, t) {
43
- let n = e, r = t;
44
- for (; r !== 0n;) [n, r] = [r, n % r];
45
- return n === 0n ? 1n : n;
46
- }
47
- function c(e, t) {
48
- return e / s(e, t) * t;
49
- }
50
- //#endregion
51
- export { i as decimal, s as gcd, c as lcm, a as roundDivision, o as toDecimalString };
52
-
1
+ import{CoinsError as e}from"./errors.js";var t=/^(-?)(\d+)(?:\.(\d+))?$/,n=1e3,r=100,i=new Set([`awayFromZero`,`ceil`,`floor`,`halfAwayFromZero`,`halfEven`,`towardZero`]);function assertRoundingMode(t){if(!i.has(t))throw new e(`INVALID_ROUNDING`,`Unknown rounding mode: ${String(t)}`)}function decimal(i){if(typeof i!=`string`)throw new e(`INVALID_DECIMAL`,`Decimal input must be a string`);if(i.length>n)throw new e(`INVALID_DECIMAL`,`Decimal input is too long`);let a=t.exec(i);if(!a)throw new e(`INVALID_DECIMAL`,`Invalid decimal: "${i}"`);let o=(a[3]??``).replace(/0+$/,``);if(o.length>r)throw new e(`INVALID_DECIMAL`,`Decimal precision cannot exceed ${r} places`);let s=10n**BigInt(o.length),c=BigInt(a[2])*s+BigInt(o||`0`),l=a[1]===`-`&&c!==0n?-c:c,u=gcd(l<0n?-l:l,s);return Object.freeze({denominator:s/u,numerator:l/u})}function roundDivision(t,n,r){if(assertRoundingMode(r),n<=0n)throw new e(`INVALID_DECIMAL`,`Decimal denominator must be positive`);let i=t<0n,a=i?-t:t,o=a/n,s=a%n;if(s===0n)return i?-o:o;let c=(()=>{switch(r){case`awayFromZero`:return!0;case`ceil`:return!i;case`floor`:return i;case`halfAwayFromZero`:return s*2n>=n;case`halfEven`:{let e=s*2n;return e>n||e===n&&o%2n!=0n}case`towardZero`:return!1;default:throw new e(`INVALID_ROUNDING`,`Unknown rounding mode: ${r}`)}})()?o+1n:o;return i?-c:c}function toDecimalString(e,t){let n=e<0n,r=n?-e:e,i=10n**BigInt(t),a=r/i;if(t===0)return`${n?`-`:``}${a}`;let o=(r%i).toString().padStart(t,`0`);return`${n?`-`:``}${a}.${o}`}function gcd(e,t){let n=e,r=t;for(;r!==0n;)[n,r]=[r,n%r];return n===0n?1n:n}function lcm(e,t){return e/gcd(e,t)*t}export{assertRoundingMode,decimal,gcd,lcm,roundDivision,toDecimalString};
53
2
  //# sourceMappingURL=_decimal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_decimal.js","names":[],"sources":["../src/_decimal.ts"],"sourcesContent":["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;\n\nexport function decimal(value: string): Decimal {\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 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"],"mappings":";;AAGA,IAAM,IAAU,2BACV,IAAqB,KACrB,IAAqB;AAE3B,SAAgB,EAAQ,GAAwB;CAC9C,IAAI,EAAM,SAAS,GAAoB,MAAM,IAAI,EAAW,mBAAmB,2BAA2B;CAE1G,IAAM,IAAQ,EAAQ,KAAK,CAAK;CAEhC,IAAI,CAAC,GAAO,MAAM,IAAI,EAAW,mBAAmB,qBAAqB,EAAM,EAAE;CAEjF,IAAM,KAAY,EAAM,MAAM,GAAA,CAAI,QAAQ,OAAO,EAAE;CAEnD,IAAI,EAAS,SAAS,GACpB,MAAM,IAAI,EAAW,mBAAmB,mCAAmC,EAAmB,QAAQ;CAGxG,IAAM,IAAc,OAAO,OAAO,EAAS,MAAM,GAC3C,IAAW,OAAO,EAAM,EAAG,IAAI,IAAc,OAAO,KAAY,GAAG,GACnE,IAAY,EAAM,OAAO,OAAO,MAAa,KAAK,CAAC,IAAW,GAC9D,IAAU,EAAI,IAAY,KAAK,CAAC,IAAY,GAAW,CAAW;CAExE,OAAO,OAAO,OAAO;EAAE,aAAa,IAAc;EAAS,WAAW,IAAY;CAAQ,CAAC;AAC7F;AAEA,SAAgB,EAAc,GAAmB,GAAqB,GAA4B;CAChG,IAAI,KAAe,IAAI,MAAM,IAAI,EAAW,mBAAmB,sCAAsC;CAErG,IAAM,IAAW,IAAY,IACvB,IAAW,IAAW,CAAC,IAAY,GACnC,IAAW,IAAW,GACtB,IAAY,IAAW;CAE7B,IAAI,MAAc,IAAI,OAAO,IAAW,CAAC,IAAW;CAwBpD,IAAM,WAtBmB;EACvB,QAAQ,GAAR;GACE,KAAK,gBACH,OAAO;GACT,KAAK,QACH,OAAO,CAAC;GACV,KAAK,SACH,OAAO;GACT,KAAK,oBACH,OAAO,IAAY,MAAM;GAC3B,KAAK,YAAY;IACf,IAAM,IAAU,IAAY;IAE5B,OAAO,IAAU,KAAgB,MAAY,KAAe,IAAW,MAAO;GAChF;GACA,KAAK,cACH,OAAO;GACT,SACE,MAAM,IAAI,EAAW,oBAAoB,0BAA0B,GAAsB;EAC7F;CACF,EAAA,CAEe,IAAY,IAAW,KAAK;CAE3C,OAAO,IAAW,CAAC,IAAS;AAC9B;AAEA,SAAgB,EAAgB,GAAgB,GAA2B;CACzE,IAAM,IAAW,IAAS,IACpB,IAAW,IAAW,CAAC,IAAS,GAChC,IAAQ,OAAO,OAAO,CAAS,GAC/B,IAAQ,IAAW;CAEzB,IAAI,MAAc,GAAG,OAAO,GAAG,IAAW,MAAM,KAAK;CAErD,IAAM,KAAY,IAAW,EAAA,CAAO,SAAS,CAAC,CAAC,SAAS,GAAW,GAAG;CAEtE,OAAO,GAAG,IAAW,MAAM,KAAK,EAAM,GAAG;AAC3C;AAEA,SAAgB,EAAI,GAAc,GAAuB;CACvD,IAAI,IAAI,GACJ,IAAI;CAER,OAAO,MAAM,KAAI,CAAC,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC;CAEnC,OAAO,MAAM,KAAK,KAAK;AACzB;AAEA,SAAgB,EAAI,GAAc,GAAuB;CACvD,OAAQ,IAAO,EAAI,GAAM,CAAK,IAAK;AACrC"}
1
+ {"version":3,"file":"_decimal.js","names":[],"sources":["../src/_decimal.ts"],"sourcesContent":["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"],"mappings":"yCAGA,IAAM,EAAU,0BACV,EAAqB,IACrB,EAAqB,IACrB,EAAiB,IAAI,IAAkB,CAC3C,eACA,OACA,QACA,mBACA,WACA,YACF,CAAC,EAED,SAAgB,mBAAmB,EAA+C,CAChF,GAAI,CAAC,EAAe,IAAI,CAAqB,EAC3C,MAAM,IAAI,EAAW,mBAAoB,0BAA0B,OAAO,CAAK,GAAG,CAEtF,CAEA,SAAgB,QAAQ,EAAwB,CAC9C,GAAI,OAAO,GAAU,SAAU,MAAM,IAAI,EAAW,kBAAmB,gCAAgC,EACvG,GAAI,EAAM,OAAS,EAAoB,MAAM,IAAI,EAAW,kBAAmB,2BAA2B,EAE1G,IAAM,EAAQ,EAAQ,KAAK,CAAK,EAEhC,GAAI,CAAC,EAAO,MAAM,IAAI,EAAW,kBAAmB,qBAAqB,EAAM,EAAE,EAEjF,IAAM,GAAY,EAAM,IAAM,GAAA,CAAI,QAAQ,MAAO,EAAE,EAEnD,GAAI,EAAS,OAAS,EACpB,MAAM,IAAI,EAAW,kBAAmB,mCAAmC,EAAmB,QAAQ,EAGxG,IAAM,EAAc,KAAO,OAAO,EAAS,MAAM,EAC3C,EAAW,OAAO,EAAM,EAAG,EAAI,EAAc,OAAO,GAAY,GAAG,EACnE,EAAY,EAAM,KAAO,KAAO,IAAa,GAAK,CAAC,EAAW,EAC9D,EAAU,IAAI,EAAY,GAAK,CAAC,EAAY,EAAW,CAAW,EAExE,OAAO,OAAO,OAAO,CAAE,YAAa,EAAc,EAAS,UAAW,EAAY,CAAQ,CAAC,CAC7F,CAEA,SAAgB,cAAc,EAAmB,EAAqB,EAA4B,CAEhG,GADA,mBAAmB,CAAI,EACnB,GAAe,GAAI,MAAM,IAAI,EAAW,kBAAmB,sCAAsC,EAErG,IAAM,EAAW,EAAY,GACvB,EAAW,EAAW,CAAC,EAAY,EACnC,EAAW,EAAW,EACtB,EAAY,EAAW,EAE7B,GAAI,IAAc,GAAI,OAAO,EAAW,CAAC,EAAW,EAwBpD,IAAM,OAtBmB,CACvB,OAAQ,EAAR,CACE,IAAK,eACH,MAAO,GACT,IAAK,OACH,MAAO,CAAC,EACV,IAAK,QACH,OAAO,EACT,IAAK,mBACH,OAAO,EAAY,IAAM,EAC3B,IAAK,WAAY,CACf,IAAM,EAAU,EAAY,GAE5B,OAAO,EAAU,GAAgB,IAAY,GAAe,EAAW,IAAO,EAChF,CACA,IAAK,aACH,MAAO,GACT,QACE,MAAM,IAAI,EAAW,mBAAoB,0BAA0B,GAAsB,CAC7F,CACF,EAAA,CAEe,EAAY,EAAW,GAAK,EAE3C,OAAO,EAAW,CAAC,EAAS,CAC9B,CAEA,SAAgB,gBAAgB,EAAgB,EAA2B,CACzE,IAAM,EAAW,EAAS,GACpB,EAAW,EAAW,CAAC,EAAS,EAChC,EAAQ,KAAO,OAAO,CAAS,EAC/B,EAAQ,EAAW,EAEzB,GAAI,IAAc,EAAG,MAAO,GAAG,EAAW,IAAM,KAAK,IAErD,IAAM,GAAY,EAAW,EAAA,CAAO,SAAS,CAAC,CAAC,SAAS,EAAW,GAAG,EAEtE,MAAO,GAAG,EAAW,IAAM,KAAK,EAAM,GAAG,GAC3C,CAEA,SAAgB,IAAI,EAAc,EAAuB,CACvD,IAAI,EAAI,EACJ,EAAI,EAER,KAAO,IAAM,IAAI,CAAC,EAAG,GAAK,CAAC,EAAG,EAAI,CAAC,EAEnC,OAAO,IAAM,GAAK,GAAK,CACzB,CAEA,SAAgB,IAAI,EAAc,EAAuB,CACvD,OAAQ,EAAO,IAAI,EAAM,CAAK,EAAK,CACrC"}
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./_decimal.cjs"),n=require("./money.cjs");function r(t,r){let i=0n,a=r?.currency;for(let r of t){if(n.assertMoney(r),a===void 0)a=r.currency;else if(r.currency!==a)throw new e.CurrencyMismatchError(a.code,r.currency.code);i+=r.amount}if(a===void 0)throw new e.CoinsError(`INVALID_MONEY`,`sum() of empty iterable requires { currency }`);return n.createMoney(i,a)}function i(r,i){if(n.assertMoney(r),typeof i==`number`)return a(r,i);if(i.length===0)throw new e.CoinsError(`INVALID_ALLOCATION`,`Allocation weights cannot be empty`);let o=i.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=r.amount<0n?-1n:1n,d=r.amount<0n?-r.amount:r.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=>n.createMoney(e*u,r.currency))}function a(t,r){if(!Number.isInteger(r)||r<1)throw new e.CoinsError(`INVALID_ALLOCATION`,`Allocation count must be a positive integer`);let i=BigInt(r),a=t.amount<0n?-1n:1n,o=t.amount<0n?-t.amount:t.amount,s=o/i,c=o%i;return Array.from({length:r},(e,r)=>n.createMoney((s+(BigInt(r)<c?1n:0n))*a,t.currency))}exports.allocate=i,exports.sum=r;
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;
2
2
  //# sourceMappingURL=aggregate.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate.cjs","names":[],"sources":["../src/aggregate.ts"],"sourcesContent":["import { decimal, lcm } from './_decimal';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { assertMoney, createMoney } from './money';\nimport type { Currency, Money } from './types';\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 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 (weightsOrCount.length === 0) throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot be empty');\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.isInteger(countValue) || countValue < 1) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation count must be a positive integer');\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":"qFAOA,SAAgB,EAAwB,EAA4B,EAAqC,CACvG,IAAI,EAAS,GACT,EAA0B,GAAS,SAEvC,IAAK,IAAM,KAAS,EAAQ,CAG1B,GAFA,EAAA,YAAY,CAAK,EAEb,IAAa,IAAA,GACf,EAAW,EAAM,cACZ,GAAI,EAAM,WAAa,EAC5B,MAAM,IAAI,EAAA,sBAAsB,EAAS,KAAM,EAAM,SAAS,IAAI,EAGpE,GAAU,EAAM,MAClB,CAEA,GAAI,IAAa,IAAA,GAAW,MAAM,IAAI,EAAA,WAAW,gBAAiB,+CAA+C,EAEjH,OAAO,EAAA,YAAY,EAAQ,CAAQ,CACrC,CAIA,SAAgB,EAA6B,EAAiB,EAAwD,CAGpH,GAFA,EAAA,YAAY,CAAK,EAEb,OAAO,GAAmB,SAAU,OAAO,EAAe,EAAO,CAAc,EAEnF,GAAI,EAAe,SAAW,EAAG,MAAM,IAAI,EAAA,WAAW,qBAAsB,oCAAoC,EAEhH,IAAM,EAAU,EAAe,IAAI,EAAA,OAAO,EAE1C,GAAI,EAAQ,KAAM,GAAW,EAAO,UAAY,EAAE,EAChD,MAAM,IAAI,EAAA,WAAW,qBAAsB,uCAAuC,EAGpF,IAAM,EAAoB,EAAQ,QAAQ,EAAQ,IAAW,EAAA,IAAI,EAAQ,EAAO,WAAW,EAAG,EAAE,EAC1F,EAAgB,EAAQ,IAAK,GAAW,EAAO,WAAa,EAAoB,EAAO,YAAY,EACnG,EAAc,EAAc,QAAQ,EAAQ,IAAW,EAAS,EAAQ,EAAE,EAEhF,GAAI,IAAgB,GAClB,MAAM,IAAI,EAAA,WAAW,qBAAsB,kDAAkD,EAE/F,IAAM,EAAO,EAAM,OAAS,GAAK,CAAC,GAAK,GACjC,EAAW,EAAM,OAAS,GAAK,CAAC,EAAM,OAAS,EAAM,OACrD,EAAS,EAAc,IAAK,GAAY,EAAW,EAAU,CAAW,EAC1E,EAAY,EAAW,EAAO,QAAQ,EAAQ,IAAW,EAAS,EAAQ,EAAE,EAC1E,EAAS,EACZ,KAAK,EAAQ,KAAW,CAAE,QAAO,UAAY,EAAW,EAAU,CAAY,EAAE,CAAC,CACjF,MAAM,EAAM,IACX,EAAK,YAAc,EAAM,UAAY,EAAK,MAAQ,EAAM,MAAQ,EAAK,UAAY,EAAM,UAAY,GAAK,CAC1G,EAEF,IAAK,IAAM,KAAS,EAAQ,CAC1B,GAAI,IAAc,GAAI,MAEtB,EAAO,EAAM,QAAW,GACxB,GAAa,EACf,CAEA,OAAO,EAAO,IAAK,GAAW,EAAA,YAAY,EAAS,EAAM,EAAM,QAAQ,CAAC,CAC1E,CAEA,SAAS,EAAmC,EAAiB,EAAgC,CAC3F,GAAI,CAAC,OAAO,UAAU,CAAU,GAAK,EAAa,EAChD,MAAM,IAAI,EAAA,WAAW,qBAAsB,6CAA6C,EAG1F,IAAM,EAAQ,OAAO,CAAU,EACzB,EAAO,EAAM,OAAS,GAAK,CAAC,GAAK,GACjC,EAAW,EAAM,OAAS,GAAK,CAAC,EAAM,OAAS,EAAM,OACrD,EAAO,EAAW,EAClB,EAAY,EAAW,EAE7B,OAAO,MAAM,KAAK,CAAE,OAAQ,CAAW,GAAI,EAAG,IAC5C,EAAA,aAAa,GAAQ,OAAO,CAAK,EAAI,EAAY,GAAK,KAAO,EAAM,EAAM,QAAQ,CACnF,CACF"}
1
+ {"version":3,"file":"aggregate.cjs","names":[],"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":"iHAMA,IAAM,EAAuB,IAI7B,SAAgB,IAAwB,EAA4B,EAAqC,CACvG,GAAI,GAAW,CAAC,EAAA,WAAW,EAAQ,QAAQ,EACzC,MAAM,IAAI,EAAA,WAAW,mBAAoB,qCAAqC,EAGhF,IAAI,EAAS,GACT,EAA0B,GAAS,SAEvC,IAAK,IAAM,KAAS,EAAQ,CAG1B,GAFA,EAAA,YAAY,CAAK,EAEb,IAAa,IAAA,GACf,EAAW,EAAM,cACZ,GAAI,EAAM,WAAa,EAC5B,MAAM,IAAI,EAAA,sBAAsB,EAAS,KAAM,EAAM,SAAS,IAAI,EAGpE,GAAU,EAAM,MAClB,CAEA,GAAI,IAAa,IAAA,GAAW,MAAM,IAAI,EAAA,WAAW,gBAAiB,+CAA+C,EAEjH,OAAO,EAAA,YAAY,EAAQ,CAAQ,CACrC,CAIA,SAAgB,SAA6B,EAAiB,EAAwD,CAGpH,GAFA,EAAA,YAAY,CAAK,EAEb,OAAO,GAAmB,SAAU,OAAO,eAAe,EAAO,CAAc,EAEnF,GAAI,CAAC,MAAM,QAAQ,CAAc,EAC/B,MAAM,IAAI,EAAA,WAAW,qBAAsB,qCAAqC,EAElF,GAAI,EAAe,SAAW,EAAG,MAAM,IAAI,EAAA,WAAW,qBAAsB,oCAAoC,EAChH,GAAI,EAAe,OAAS,EAC1B,MAAM,IAAI,EAAA,WAAW,qBAAsB,4BAA4B,EAAqB,OAAO,EAErG,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAe,OAAQ,IACjD,GAAI,CAAC,OAAO,OAAO,EAAgB,CAAK,EACtC,MAAM,IAAI,EAAA,WAAW,qBAAsB,+CAA+C,EAI9F,IAAM,EAAU,EAAe,IAAI,EAAA,OAAO,EAE1C,GAAI,EAAQ,KAAM,GAAW,EAAO,UAAY,EAAE,EAChD,MAAM,IAAI,EAAA,WAAW,qBAAsB,uCAAuC,EAGpF,IAAM,EAAoB,EAAQ,QAAQ,EAAQ,IAAW,EAAA,IAAI,EAAQ,EAAO,WAAW,EAAG,EAAE,EAC1F,EAAgB,EAAQ,IAAK,GAAW,EAAO,WAAa,EAAoB,EAAO,YAAY,EACnG,EAAc,EAAc,QAAQ,EAAQ,IAAW,EAAS,EAAQ,EAAE,EAEhF,GAAI,IAAgB,GAClB,MAAM,IAAI,EAAA,WAAW,qBAAsB,kDAAkD,EAE/F,IAAM,EAAO,EAAM,OAAS,GAAK,CAAC,GAAK,GACjC,EAAW,EAAM,OAAS,GAAK,CAAC,EAAM,OAAS,EAAM,OACrD,EAAS,EAAc,IAAK,GAAY,EAAW,EAAU,CAAW,EAC1E,EAAY,EAAW,EAAO,QAAQ,EAAQ,IAAW,EAAS,EAAQ,EAAE,EAC1E,EAAS,EACZ,KAAK,EAAQ,KAAW,CAAE,QAAO,UAAY,EAAW,EAAU,CAAY,EAAE,CAAC,CACjF,MAAM,EAAM,IACX,EAAK,YAAc,EAAM,UAAY,EAAK,MAAQ,EAAM,MAAQ,EAAK,UAAY,EAAM,UAAY,GAAK,CAC1G,EAEF,IAAK,IAAM,KAAS,EAAQ,CAC1B,GAAI,IAAc,GAAI,MAEtB,EAAO,EAAM,QAAW,GACxB,GAAa,EACf,CAEA,OAAO,EAAO,IAAK,GAAW,EAAA,YAAY,EAAS,EAAM,EAAM,QAAQ,CAAC,CAC1E,CAEA,SAAS,eAAmC,EAAiB,EAAgC,CAC3F,GAAI,CAAC,OAAO,cAAc,CAAU,GAAK,EAAa,GAAK,EAAa,EACtE,MAAM,IAAI,EAAA,WACR,qBACA,oEAAoE,GACtE,EAGF,IAAM,EAAQ,OAAO,CAAU,EACzB,EAAO,EAAM,OAAS,GAAK,CAAC,GAAK,GACjC,EAAW,EAAM,OAAS,GAAK,CAAC,EAAM,OAAS,EAAM,OACrD,EAAO,EAAW,EAClB,EAAY,EAAW,EAE7B,OAAO,MAAM,KAAK,CAAE,OAAQ,CAAW,GAAI,EAAG,IAC5C,EAAA,aAAa,GAAQ,OAAO,CAAK,EAAI,EAAY,GAAK,KAAO,EAAM,EAAM,QAAQ,CACnF,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../src/aggregate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAE/C,wBAAgB,GAAG,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;AAC/E,wBAAgB,GAAG,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE;IAAE,QAAQ,EAAE,CAAC,CAAA;CAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;AAsBxG,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACzF,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC"}
1
+ {"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../src/aggregate.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAI/C,wBAAgB,GAAG,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;AAC/E,wBAAgB,GAAG,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE;IAAE,QAAQ,EAAE,CAAC,CAAA;CAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;AA0BxG,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACzF,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC"}
package/dist/aggregate.js CHANGED
@@ -1,40 +1,2 @@
1
- import { CoinsError as e, CurrencyMismatchError as t } from "./errors.js";
2
- import { decimal as n, lcm as r } from "./_decimal.js";
3
- import { assertMoney as i, createMoney as a } from "./money.js";
4
- //#region src/aggregate.ts
5
- function o(n, r) {
6
- let o = 0n, s = r?.currency;
7
- for (let e of n) {
8
- if (i(e), s === void 0) s = e.currency;
9
- else if (e.currency !== s) throw new t(s.code, e.currency.code);
10
- o += e.amount;
11
- }
12
- if (s === void 0) throw new e("INVALID_MONEY", "sum() of empty iterable requires { currency }");
13
- return a(o, s);
14
- }
15
- function s(t, o) {
16
- if (i(t), typeof o == "number") return c(t, o);
17
- if (o.length === 0) throw new e("INVALID_ALLOCATION", "Allocation weights cannot be empty");
18
- let s = o.map(n);
19
- if (s.some((e) => e.numerator < 0n)) throw new e("INVALID_ALLOCATION", "Allocation weights cannot be negative");
20
- let l = s.reduce((e, t) => r(e, t.denominator), 1n), u = s.map((e) => e.numerator * (l / e.denominator)), d = u.reduce((e, t) => e + t, 0n);
21
- if (d === 0n) throw new e("INVALID_ALLOCATION", "Allocation weights must include a positive value");
22
- 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) => ({
23
- index: t,
24
- remainder: p * e % d
25
- })).sort((e, t) => e.remainder === t.remainder ? e.index - t.index : e.remainder > t.remainder ? -1 : 1);
26
- for (let e of g) {
27
- if (h === 0n) break;
28
- m[e.index] += 1n, h -= 1n;
29
- }
30
- return m.map((e) => a(e * f, t.currency));
31
- }
32
- function c(t, n) {
33
- if (!Number.isInteger(n) || n < 1) throw new e("INVALID_ALLOCATION", "Allocation count must be a positive integer");
34
- let r = BigInt(n), i = t.amount < 0n ? -1n : 1n, o = t.amount < 0n ? -t.amount : t.amount, s = o / r, c = o % r;
35
- return Array.from({ length: n }, (e, n) => a((s + (BigInt(n) < c ? 1n : 0n)) * i, t.currency));
36
- }
37
- //#endregion
38
- export { s as allocate, o as sum };
39
-
1
+ import{CoinsError as e,CurrencyMismatchError as t}from"./errors.js";import{decimal as n,lcm as r}from"./_decimal.js";import{isCurrency as i}from"./currency.js";import{assertMoney as a,createMoney as o}from"./money.js";var s=1e5;function sum(n,r){if(r&&!i(r.currency))throw new e(`INVALID_CURRENCY`,`sum() requires a canonical currency`);let s=0n,c=r?.currency;for(let e of n){if(a(e),c===void 0)c=e.currency;else if(e.currency!==c)throw new t(c.code,e.currency.code);s+=e.amount}if(c===void 0)throw new e(`INVALID_MONEY`,`sum() of empty iterable requires { currency }`);return o(s,c)}function allocate(t,i){if(a(t),typeof i==`number`)return allocateEvenly(t,i);if(!Array.isArray(i))throw new e(`INVALID_ALLOCATION`,`Allocation weights must be an array`);if(i.length===0)throw new e(`INVALID_ALLOCATION`,`Allocation weights cannot be empty`);if(i.length>s)throw new e(`INVALID_ALLOCATION`,`Allocation cannot exceed ${s} parts`);for(let t=0;t<i.length;t++)if(!Object.hasOwn(i,t))throw new e(`INVALID_ALLOCATION`,`Allocation weights cannot contain empty slots`);let c=i.map(n);if(c.some(e=>e.numerator<0n))throw new e(`INVALID_ALLOCATION`,`Allocation weights cannot be negative`);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);if(d===0n)throw new e(`INVALID_ALLOCATION`,`Allocation weights must include a positive value`);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)=>({index:t,remainder:p*e%d})).sort((e,t)=>e.remainder===t.remainder?e.index-t.index:e.remainder>t.remainder?-1:1);for(let e of g){if(h===0n)break;m[e.index]+=1n,h-=1n}return m.map(e=>o(e*f,t.currency))}function allocateEvenly(t,n){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}`);let r=BigInt(n),i=t.amount<0n?-1n:1n,a=t.amount<0n?-t.amount:t.amount,c=a/r,l=a%r;return Array.from({length:n},(e,n)=>o((c+(BigInt(n)<l?1n:0n))*i,t.currency))}export{allocate,sum};
40
2
  //# sourceMappingURL=aggregate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate.js","names":[],"sources":["../src/aggregate.ts"],"sourcesContent":["import { decimal, lcm } from './_decimal';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { assertMoney, createMoney } from './money';\nimport type { Currency, Money } from './types';\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 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 (weightsOrCount.length === 0) throw new CoinsError('INVALID_ALLOCATION', 'Allocation weights cannot be empty');\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.isInteger(countValue) || countValue < 1) {\n throw new CoinsError('INVALID_ALLOCATION', 'Allocation count must be a positive integer');\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":";;;;AAOA,SAAgB,EAAwB,GAA4B,GAAqC;CACvG,IAAI,IAAS,IACT,IAA0B,GAAS;CAEvC,KAAK,IAAM,KAAS,GAAQ;EAG1B,IAFA,EAAY,CAAK,GAEb,MAAa,KAAA,GACf,IAAW,EAAM;OACZ,IAAI,EAAM,aAAa,GAC5B,MAAM,IAAI,EAAsB,EAAS,MAAM,EAAM,SAAS,IAAI;EAGpE,KAAU,EAAM;CAClB;CAEA,IAAI,MAAa,KAAA,GAAW,MAAM,IAAI,EAAW,iBAAiB,+CAA+C;CAEjH,OAAO,EAAY,GAAQ,CAAQ;AACrC;AAIA,SAAgB,EAA6B,GAAiB,GAAwD;CAGpH,IAFA,EAAY,CAAK,GAEb,OAAO,KAAmB,UAAU,OAAO,EAAe,GAAO,CAAc;CAEnF,IAAI,EAAe,WAAW,GAAG,MAAM,IAAI,EAAW,sBAAsB,oCAAoC;CAEhH,IAAM,IAAU,EAAe,IAAI,CAAO;CAE1C,IAAI,EAAQ,MAAM,MAAW,EAAO,YAAY,EAAE,GAChD,MAAM,IAAI,EAAW,sBAAsB,uCAAuC;CAGpF,IAAM,IAAoB,EAAQ,QAAQ,GAAQ,MAAW,EAAI,GAAQ,EAAO,WAAW,GAAG,EAAE,GAC1F,IAAgB,EAAQ,KAAK,MAAW,EAAO,aAAa,IAAoB,EAAO,YAAY,GACnG,IAAc,EAAc,QAAQ,GAAQ,MAAW,IAAS,GAAQ,EAAE;CAEhF,IAAI,MAAgB,IAClB,MAAM,IAAI,EAAW,sBAAsB,kDAAkD;CAE/F,IAAM,IAAO,EAAM,SAAS,KAAK,CAAC,KAAK,IACjC,IAAW,EAAM,SAAS,KAAK,CAAC,EAAM,SAAS,EAAM,QACrD,IAAS,EAAc,KAAK,MAAY,IAAW,IAAU,CAAW,GAC1E,IAAY,IAAW,EAAO,QAAQ,GAAQ,MAAW,IAAS,GAAQ,EAAE,GAC1E,IAAS,EACZ,KAAK,GAAQ,OAAW;EAAE;EAAO,WAAY,IAAW,IAAU;CAAY,EAAE,CAAC,CACjF,MAAM,GAAM,MACX,EAAK,cAAc,EAAM,YAAY,EAAK,QAAQ,EAAM,QAAQ,EAAK,YAAY,EAAM,YAAY,KAAK,CAC1G;CAEF,KAAK,IAAM,KAAS,GAAQ;EAC1B,IAAI,MAAc,IAAI;EAGtB,AADA,EAAO,EAAM,UAAW,IACxB,KAAa;CACf;CAEA,OAAO,EAAO,KAAK,MAAW,EAAY,IAAS,GAAM,EAAM,QAAQ,CAAC;AAC1E;AAEA,SAAS,EAAmC,GAAiB,GAAgC;CAC3F,IAAI,CAAC,OAAO,UAAU,CAAU,KAAK,IAAa,GAChD,MAAM,IAAI,EAAW,sBAAsB,6CAA6C;CAG1F,IAAM,IAAQ,OAAO,CAAU,GACzB,IAAO,EAAM,SAAS,KAAK,CAAC,KAAK,IACjC,IAAW,EAAM,SAAS,KAAK,CAAC,EAAM,SAAS,EAAM,QACrD,IAAO,IAAW,GAClB,IAAY,IAAW;CAE7B,OAAO,MAAM,KAAK,EAAE,QAAQ,EAAW,IAAI,GAAG,MAC5C,GAAa,KAAQ,OAAO,CAAK,IAAI,IAAY,KAAK,OAAO,GAAM,EAAM,QAAQ,CACnF;AACF"}
1
+ {"version":3,"file":"aggregate.js","names":[],"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":"0NAMA,IAAM,EAAuB,IAI7B,SAAgB,IAAwB,EAA4B,EAAqC,CACvG,GAAI,GAAW,CAAC,EAAW,EAAQ,QAAQ,EACzC,MAAM,IAAI,EAAW,mBAAoB,qCAAqC,EAGhF,IAAI,EAAS,GACT,EAA0B,GAAS,SAEvC,IAAK,IAAM,KAAS,EAAQ,CAG1B,GAFA,EAAY,CAAK,EAEb,IAAa,IAAA,GACf,EAAW,EAAM,cACZ,GAAI,EAAM,WAAa,EAC5B,MAAM,IAAI,EAAsB,EAAS,KAAM,EAAM,SAAS,IAAI,EAGpE,GAAU,EAAM,MAClB,CAEA,GAAI,IAAa,IAAA,GAAW,MAAM,IAAI,EAAW,gBAAiB,+CAA+C,EAEjH,OAAO,EAAY,EAAQ,CAAQ,CACrC,CAIA,SAAgB,SAA6B,EAAiB,EAAwD,CAGpH,GAFA,EAAY,CAAK,EAEb,OAAO,GAAmB,SAAU,OAAO,eAAe,EAAO,CAAc,EAEnF,GAAI,CAAC,MAAM,QAAQ,CAAc,EAC/B,MAAM,IAAI,EAAW,qBAAsB,qCAAqC,EAElF,GAAI,EAAe,SAAW,EAAG,MAAM,IAAI,EAAW,qBAAsB,oCAAoC,EAChH,GAAI,EAAe,OAAS,EAC1B,MAAM,IAAI,EAAW,qBAAsB,4BAA4B,EAAqB,OAAO,EAErG,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAe,OAAQ,IACjD,GAAI,CAAC,OAAO,OAAO,EAAgB,CAAK,EACtC,MAAM,IAAI,EAAW,qBAAsB,+CAA+C,EAI9F,IAAM,EAAU,EAAe,IAAI,CAAO,EAE1C,GAAI,EAAQ,KAAM,GAAW,EAAO,UAAY,EAAE,EAChD,MAAM,IAAI,EAAW,qBAAsB,uCAAuC,EAGpF,IAAM,EAAoB,EAAQ,QAAQ,EAAQ,IAAW,EAAI,EAAQ,EAAO,WAAW,EAAG,EAAE,EAC1F,EAAgB,EAAQ,IAAK,GAAW,EAAO,WAAa,EAAoB,EAAO,YAAY,EACnG,EAAc,EAAc,QAAQ,EAAQ,IAAW,EAAS,EAAQ,EAAE,EAEhF,GAAI,IAAgB,GAClB,MAAM,IAAI,EAAW,qBAAsB,kDAAkD,EAE/F,IAAM,EAAO,EAAM,OAAS,GAAK,CAAC,GAAK,GACjC,EAAW,EAAM,OAAS,GAAK,CAAC,EAAM,OAAS,EAAM,OACrD,EAAS,EAAc,IAAK,GAAY,EAAW,EAAU,CAAW,EAC1E,EAAY,EAAW,EAAO,QAAQ,EAAQ,IAAW,EAAS,EAAQ,EAAE,EAC1E,EAAS,EACZ,KAAK,EAAQ,KAAW,CAAE,QAAO,UAAY,EAAW,EAAU,CAAY,EAAE,CAAC,CACjF,MAAM,EAAM,IACX,EAAK,YAAc,EAAM,UAAY,EAAK,MAAQ,EAAM,MAAQ,EAAK,UAAY,EAAM,UAAY,GAAK,CAC1G,EAEF,IAAK,IAAM,KAAS,EAAQ,CAC1B,GAAI,IAAc,GAAI,MAEtB,EAAO,EAAM,QAAW,GACxB,GAAa,EACf,CAEA,OAAO,EAAO,IAAK,GAAW,EAAY,EAAS,EAAM,EAAM,QAAQ,CAAC,CAC1E,CAEA,SAAS,eAAmC,EAAiB,EAAgC,CAC3F,GAAI,CAAC,OAAO,cAAc,CAAU,GAAK,EAAa,GAAK,EAAa,EACtE,MAAM,IAAI,EACR,qBACA,oEAAoE,GACtE,EAGF,IAAM,EAAQ,OAAO,CAAU,EACzB,EAAO,EAAM,OAAS,GAAK,CAAC,GAAK,GACjC,EAAW,EAAM,OAAS,GAAK,CAAC,EAAM,OAAS,EAAM,OACrD,EAAO,EAAW,EAClB,EAAY,EAAW,EAE7B,OAAO,MAAM,KAAK,CAAE,OAAQ,CAAW,GAAI,EAAG,IAC5C,GAAa,GAAQ,OAAO,CAAK,EAAI,EAAY,GAAK,KAAO,EAAM,EAAM,QAAQ,CACnF,CACF"}
package/dist/coins.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class extends Error{code;constructor(e,t,n){super(t,n),this.code=e,this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{expected;received;constructor(e,t){super(`CURRENCY_MISMATCH`,`Currency mismatch: ${e} and ${t}`),this.expected=e,this.received=t}},n=class extends e{value;constructor(e){super(`INVALID_CURRENCY`,`Unsupported currency: "${String(e)}"`),this.value=e}},r=/^(-?)(\d+)(?:\.(\d+))?$/,i=1e3,a=100;function o(t){if(t.length>i)throw new e(`INVALID_DECIMAL`,`Decimal input is too long`);let n=r.exec(t);if(!n)throw new e(`INVALID_DECIMAL`,`Invalid decimal: "${t}"`);let o=(n[3]??``).replace(/0+$/,``);if(o.length>a)throw new e(`INVALID_DECIMAL`,`Decimal precision cannot exceed ${a} places`);let s=10n**BigInt(o.length),c=BigInt(n[2])*s+BigInt(o||`0`),u=n[1]===`-`&&c!==0n?-c:c,d=l(u<0n?-u:u,s);return Object.freeze({denominator:s/d,numerator:u/d})}function s(t,n,r){if(n<=0n)throw new e(`INVALID_DECIMAL`,`Decimal denominator must be positive`);let i=t<0n,a=i?-t:t,o=a/n,s=a%n;if(s===0n)return i?-o:o;let c=(()=>{switch(r){case`awayFromZero`:return!0;case`ceil`:return!i;case`floor`:return i;case`halfAwayFromZero`:return s*2n>=n;case`halfEven`:{let e=s*2n;return e>n||e===n&&o%2n!=0n}case`towardZero`:return!1;default:throw new e(`INVALID_ROUNDING`,`Unknown rounding mode: ${r}`)}})()?o+1n:o;return i?-c:c}function c(e,t){let n=e<0n,r=n?-e:e,i=10n**BigInt(t),a=r/i;if(t===0)return`${n?`-`:``}${a}`;let o=(r%i).toString().padStart(t,`0`);return`${n?`-`:``}${a}.${o}`}function l(e,t){let n=e,r=t;for(;r!==0n;)[n,r]=[r,n%r];return n===0n?1n:n}function u(e,t){return e/l(e,t)*t}var d=new Map,f=new WeakSet;function p(e,t){C(e,t);let n=Object.freeze({code:e,minorUnit:t});return f.add(n),n}function m(e,t){let n=p(e,t);return d.set(e,n),n}var h=m(`USD`,2),g=m(`EUR`,2),_=m(`GBP`,2),ee=m(`JPY`,0),v=m(`KRW`,0),y=m(`BHD`,3),b=m(`KWD`,3);function x(e){if(typeof e==`string`){let t=d.get(e);if(!t)throw new n(e);return t}return p(e.code,e.minorUnit)}function S(e){return typeof e==`object`&&!!e&&f.has(e)}function C(t,n){if(!/^[A-Z]{3}$/.test(t))throw new e(`INVALID_CURRENCY`,`Currency code must be three uppercase letters: "${t}"`);if(!Number.isInteger(n)||n<0||n>6)throw new e(`INVALID_CURRENCY`,`Currency "${t}" must have 0–6 minor-unit digits`)}var w=new WeakSet,T=`halfAwayFromZero`;function E(t,n,r){if(z(n),typeof t==`bigint`){if(!r||!(`unit`in r)||r.unit!==`minor`)throw new e(`INVALID_MONEY`,`Bigint amounts require { unit: 'minor' }`);return I(t,n)}if(r&&`unit`in r)throw new e(`INVALID_MONEY`,`Decimal strings do not accept a unit option`);let i=o(t),a=i.numerator*10n**BigInt(n.minorUnit);if(a%i.denominator!==0n&&r?.rounding===void 0)throw new e(`INVALID_MONEY`,`Amount "${t}" exceeds ${n.code} precision; provide rounding`);return I(s(a,i.denominator,r?.rounding??T),n)}function D(t){if(!B(t))throw new e(`INVALID_MONEY`,`Money must be a plain data object`);let n=Object.getOwnPropertyDescriptors(t),r=n.amount,i=n.currency;if(!V(r)||!V(i))throw new e(`INVALID_MONEY`,`Money properties must be plain data values`);if(typeof r.value!=`bigint`||!S(i.value))throw new e(`INVALID_MONEY`,`Money must contain bigint amount and a registered currency`);return I(r.value,i.value)}function O(e){return typeof e==`object`&&!!e&&w.has(e)}function k(e,t){return R(e,t),I(e.amount+t.amount,e.currency)}function A(e,t){return R(e,t),I(e.amount-t.amount,e.currency)}function te(e,t,n={}){L(e);let r=o(t);return I(s(e.amount*r.numerator,r.denominator,n.rounding??T),e.currency)}function ne(t,n,r={}){L(t);let i=o(n);if(i.numerator===0n)throw new e(`DIVISION_BY_ZERO`,`Cannot divide money by zero`);let a=i.numerator<0n?-i.numerator:i.numerator,c=t.amount*i.denominator;return I(s(i.numerator<0n?-c:c,a,r.rounding??T),t.currency)}function j(e,t){return R(e,t),e.amount===t.amount?0:e.amount<t.amount?-1:1}function re(t,n){if(j(n.min,n.max)===1)throw new e(`INVALID_RANGE`,`Clamp minimum cannot exceed maximum`);return j(t,n.min)===-1?n.min:j(t,n.max)===1?n.max:t}function M(e){return L(e),I(e.amount<0n?-e.amount:e.amount,e.currency)}function N(e){return L(e),I(-e.amount,e.currency)}function P(t,n){L(t);let{fractionDigits:r,rounding:i=T}=n;if(!Number.isInteger(r)||r<0||r>t.currency.minorUnit)throw new e(`INVALID_ROUNDING`,`fractionDigits must be an integer from 0 to ${t.currency.minorUnit}`);let a=10n**BigInt(t.currency.minorUnit-r);return I(s(t.amount,a,i)*a,t.currency)}function F(e){return L(e),c(e.amount,e.currency.minorUnit)}function I(e,t){let n=Object.freeze({amount:e,currency:t});return w.add(n),n}function L(t){if(!O(t))throw new e(`INVALID_MONEY`,`Money must be a canonical @vielzeug/coins value`)}function R(e,n){if(L(e),L(n),e.currency!==n.currency)throw new t(e.currency.code,n.currency.code)}function z(t){if(!S(t))throw new e(`INVALID_CURRENCY`,`Money requires a registered currency`)}function B(e){if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function V(e){return e!==void 0&&`value`in e&&e.get===void 0&&e.set===void 0}function H(n,r){let i=0n,a=r?.currency;for(let e of n){if(L(e),a===void 0)a=e.currency;else if(e.currency!==a)throw new t(a.code,e.currency.code);i+=e.amount}if(a===void 0)throw new e(`INVALID_MONEY`,`sum() of empty iterable requires { currency }`);return I(i,a)}function U(t,n){if(L(t),typeof n==`number`)return W(t,n);if(n.length===0)throw new e(`INVALID_ALLOCATION`,`Allocation weights cannot be empty`);let r=n.map(o);if(r.some(e=>e.numerator<0n))throw new e(`INVALID_ALLOCATION`,`Allocation weights cannot be negative`);let i=r.reduce((e,t)=>u(e,t.denominator),1n),a=r.map(e=>e.numerator*(i/e.denominator)),s=a.reduce((e,t)=>e+t,0n);if(s===0n)throw new e(`INVALID_ALLOCATION`,`Allocation weights must include a positive value`);let c=t.amount<0n?-1n:1n,l=t.amount<0n?-t.amount:t.amount,d=a.map(e=>l*e/s),f=l-d.reduce((e,t)=>e+t,0n),p=a.map((e,t)=>({index:t,remainder:l*e%s})).sort((e,t)=>e.remainder===t.remainder?e.index-t.index:e.remainder>t.remainder?-1:1);for(let e of p){if(f===0n)break;d[e.index]+=1n,f-=1n}return d.map(e=>I(e*c,t.currency))}function W(t,n){if(!Number.isInteger(n)||n<1)throw new e(`INVALID_ALLOCATION`,`Allocation count must be a positive integer`);let r=BigInt(n),i=t.amount<0n?-1n:1n,a=t.amount<0n?-t.amount:t.amount,o=a/r,s=a%r;return Array.from({length:n},(e,n)=>I((o+(BigInt(n)<s?1n:0n))*i,t.currency))}var G=new WeakSet;function K({from:t,to:n,value:r}){if(!S(t)||!S(n))throw new e(`INVALID_CURRENCY`,`Exchange rate requires registered currencies`);let i=o(r);if(i.numerator<0n)throw new e(`INVALID_DECIMAL`,`Exchange rates cannot be negative`);let a=Object.freeze({from:t,to:n,value:i});return G.add(a),a}function q(e){return typeof e==`object`&&!!e&&G.has(e)}function J(n,r,i={}){if(L(n),!q(r))throw new e(`INVALID_EXCHANGE_RATE`,`Exchange requires a canonical exchange rate`);if(n.currency!==r.from)throw new t(n.currency.code,r.from.code);return I(s(n.amount*r.value.numerator*10n**BigInt(r.to.minorUnit),r.value.denominator*10n**BigInt(r.from.minorUnit),i.rounding??`halfAwayFromZero`),r.to)}var Y=20,ie=`halfAwayFromZero`,X=new Map,Z=new Map;function ae(e,t={}){return Q(e,t).map(e=>e.value).join(``)}function Q(e,t={}){L(e);let{locale:n=`en-US`,maximumFractionDigits:r=e.currency.minorUnit,minimumFractionDigits:i=e.currency.minorUnit,rounding:a=ie,style:o=`symbol`}=t;oe(i,r);let c=10n**BigInt(r),l=r>=e.currency.minorUnit?e.amount*10n**BigInt(r-e.currency.minorUnit):s(e.amount,10n**BigInt(e.currency.minorUnit-r),a),u=l<0n,d=u?-l:l,f=d/c,p=(r===0?``:(d%c).toString().padStart(r,`0`)).replace(RegExp(`0{0,${Math.max(0,r-i)}}$`),``),m=se(n,e.currency.code,o,u),h=[],g=!1;for(let e of m)e.type!==`group`&&(e.type===`integer`?g||=(h.push({type:`integer`,value:ce(n).format(f)}),!0):e.type===`decimal`?p&&h.push({type:`decimal`,value:e.value}):e.type===`fraction`?p&&h.push({type:`fraction`,value:p}):e.type===`currency`||e.type===`minusSign`||e.type===`plusSign`?h.push({type:e.type,value:e.value}):h.push({type:`literal`,value:e.value}));return h}function oe(t,n){if(!Number.isInteger(n)||!Number.isInteger(t)||t<0||n<t||n>Y)throw new e(`FORMAT_ERROR`,`Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${Y}`)}function se(t,n,r,i){let a=`${t}\0${n}\0${r}\0${i}`,o=Z.get(a);if(o)return o;try{let e=new Intl.NumberFormat(t,{currency:n,currencyDisplay:r,maximumFractionDigits:1,minimumFractionDigits:1,style:`currency`}).formatToParts(i?-1.1:1.1);return Z.set(a,e),e}catch(r){throw new e(`FORMAT_ERROR`,`Cannot format currency "${n}" for locale "${t}"`,{cause:r})}}function ce(t){let n=X.get(t);if(n)return n;try{let e=new Intl.NumberFormat(t,{maximumFractionDigits:0,useGrouping:!0});return X.set(t,e),e}catch(n){throw new e(`FORMAT_ERROR`,`Cannot create number formatter for locale "${t}"`,{cause:n})}}var le=/^(?:0|-[1-9]\d*|[1-9]\d*)$/,$=[`amount`,`currency`,`unit`];function ue(e){return L(e),{amount:e.amount.toString(),currency:e.currency.code,unit:`minor`}}function de(t,n={}){try{let e=fe(t),r=n.currency??x;return E(BigInt(e.amount),r(e.currency),{unit:`minor`})}catch(t){throw new e(`INVALID_MONEY`,`Invalid Money JSON`,{cause:t})}}function fe(e){if(typeof e!=`object`||!e||Object.getPrototypeOf(e)!==Object.prototype)throw TypeError(`Money JSON must be a plain object`);let t=Object.getOwnPropertyDescriptors(e),n=Reflect.ownKeys(e);if(n.length!==$.length||!$.every(e=>n.includes(e)))throw TypeError(`Money JSON must contain only amount, currency, and unit`);for(let e of $){let n=t[e];if(!n||!(`value`in n)||n.get||n.set)throw TypeError(`Money JSON property "${e}" must be a data value`)}let r=t.amount?.value,i=t.currency?.value,a=t.unit?.value;if(typeof r!=`string`||!le.test(r))throw TypeError(`Money JSON amount must be a canonical integer string`);if(typeof i!=`string`||a!==`minor`)throw TypeError(`Money JSON currency/unit are invalid`);return{amount:r,currency:i,unit:a}}exports.BHD=y,exports.CoinsError=e,exports.CurrencyMismatchError=t,exports.EUR=g,exports.GBP=_,exports.InvalidCurrencyError=n,exports.JPY=ee,exports.KRW=v,exports.KWD=b,exports.USD=h,exports.abs=M,exports.add=k,exports.allocate=U,exports.clamp=re,exports.compare=j,exports.currency=x,exports.divide=ne,exports.exchange=J,exports.exchangeRate=K,exports.format=ae,exports.formatParts=Q,exports.isCurrency=S,exports.isExchangeRate=q,exports.isMoney=O,exports.money=E,exports.multiply=te,exports.negate=N,exports.parseMoney=D,exports.parseMoneyJSON=de,exports.round=P,exports.subtract=A,exports.sum=H,exports.toDecimal=F,exports.toJSON=ue;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var CoinsError=class extends Error{code;constructor(e,t,n){super(t,n),this.code=e,this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},CurrencyMismatchError=class extends CoinsError{expected;received;constructor(e,t){super(`CURRENCY_MISMATCH`,`Currency mismatch: canonical ${e} and ${t} definitions differ`),this.expected=e,this.received=t}};function describe(e){try{return String(e)}catch{return`<unprintable>`}}var InvalidCurrencyError=class extends CoinsError{value;constructor(e){super(`INVALID_CURRENCY`,`Unsupported currency: "${describe(e)}"`),this.value=e}},e=/^(-?)(\d+)(?:\.(\d+))?$/,t=1e3,n=100,r=new Set([`awayFromZero`,`ceil`,`floor`,`halfAwayFromZero`,`halfEven`,`towardZero`]);function assertRoundingMode(e){if(!r.has(e))throw new CoinsError(`INVALID_ROUNDING`,`Unknown rounding mode: ${String(e)}`)}function decimal(r){if(typeof r!=`string`)throw new CoinsError(`INVALID_DECIMAL`,`Decimal input must be a string`);if(r.length>t)throw new CoinsError(`INVALID_DECIMAL`,`Decimal input is too long`);let i=e.exec(r);if(!i)throw new CoinsError(`INVALID_DECIMAL`,`Invalid decimal: "${r}"`);let a=(i[3]??``).replace(/0+$/,``);if(a.length>n)throw new CoinsError(`INVALID_DECIMAL`,`Decimal precision cannot exceed ${n} places`);let o=10n**BigInt(a.length),s=BigInt(i[2])*o+BigInt(a||`0`),c=i[1]===`-`&&s!==0n?-s:s,l=gcd(c<0n?-c:c,o);return Object.freeze({denominator:o/l,numerator:c/l})}function roundDivision(e,t,n){if(assertRoundingMode(n),t<=0n)throw new CoinsError(`INVALID_DECIMAL`,`Decimal denominator must be positive`);let r=e<0n,i=r?-e:e,a=i/t,o=i%t;if(o===0n)return r?-a:a;let s=(()=>{switch(n){case`awayFromZero`:return!0;case`ceil`:return!r;case`floor`:return r;case`halfAwayFromZero`:return o*2n>=t;case`halfEven`:{let e=o*2n;return e>t||e===t&&a%2n!=0n}case`towardZero`:return!1;default:throw new CoinsError(`INVALID_ROUNDING`,`Unknown rounding mode: ${n}`)}})()?a+1n:a;return r?-s:s}function toDecimalString(e,t){let n=e<0n,r=n?-e:e,i=10n**BigInt(t),a=r/i;if(t===0)return`${n?`-`:``}${a}`;let o=(r%i).toString().padStart(t,`0`);return`${n?`-`:``}${a}.${o}`}function gcd(e,t){let n=e,r=t;for(;r!==0n;)[n,r]=[r,n%r];return n===0n?1n:n}function lcm(e,t){return e/gcd(e,t)*t}var i=new Map,a=new WeakSet;function register(e,t){validateDefinition(e,t);let n=Object.freeze({code:e,minorUnit:t});return a.add(n),n}function builtin(e,t){let n=register(e,t);return i.set(e,n),n}var o=builtin(`USD`,2),s=builtin(`EUR`,2),c=builtin(`GBP`,2),l=builtin(`JPY`,0),u=builtin(`KRW`,0),d=builtin(`BHD`,3),f=builtin(`KWD`,3);function currency(e){if(typeof e==`string`){let t=i.get(e);if(!t)throw new InvalidCurrencyError(e);return t}if(typeof e!=`object`||!e)throw new CoinsError(`INVALID_CURRENCY`,`Currency definition must be an object`);return register(e.code,e.minorUnit)}function isCurrency(e){return typeof e==`object`&&!!e&&a.has(e)}function validateDefinition(e,t){if(!/^[A-Z]{3}$/.test(e))throw new CoinsError(`INVALID_CURRENCY`,`Currency code must be three uppercase letters: "${e}"`);if(!Number.isInteger(t)||t<0||t>6)throw new CoinsError(`INVALID_CURRENCY`,`Currency "${e}" must have 0–6 minor-unit digits`)}var p=new WeakSet,m=`halfAwayFromZero`,h=/^(?:0|-[1-9]\d*|[1-9]\d*)$/,g=1e3;function money(e,t,n){if(assertCurrency(t),typeof e==`bigint`){if(!n||!(`unit`in n)||n.unit!==`minor`)throw new CoinsError(`INVALID_MONEY`,`Bigint amounts require { unit: 'minor' }`);return createMoney(e,t)}if(n&&`unit`in n)throw new CoinsError(`INVALID_MONEY`,`Decimal strings do not accept a unit option`);let r=decimal(e),i=r.numerator*10n**BigInt(t.minorUnit);if(i%r.denominator!==0n&&n?.rounding===void 0)throw new CoinsError(`INVALID_MONEY`,`Amount "${e}" exceeds ${t.code} precision; provide rounding`);return createMoney(roundDivision(i,r.denominator,n?.rounding??m),t)}function decodeMoney(e,t){if(!isPlainDataObject(e))throw new CoinsError(`INVALID_MONEY`,`Money must be a plain data object`);let n=Object.getOwnPropertyDescriptors(e),r=n.amount,i=n.currency;if(!isDataProperty(r)||!isDataProperty(i))throw new CoinsError(`INVALID_MONEY`,`Money properties must be plain data values`);let a=Reflect.ownKeys(e);if(typeof r.value==`bigint`&&isCurrency(i.value)){if(a.length!==2)throw new CoinsError(`INVALID_MONEY`,`Plain money must have exactly the keys amount and currency`);return createMoney(r.value,i.value)}let o=n.unit;if(typeof r.value==`string`&&typeof i.value==`string`&&isDataProperty(o)&&o.value===`minor`){if(a.length!==3)throw new CoinsError(`INVALID_MONEY`,`MoneyJSON must have exactly the keys amount, currency, unit`);if(r.value.length>g)throw new CoinsError(`INVALID_MONEY`,`Money JSON amount is too long; maximum ${g} characters`);if(!h.test(r.value))throw new CoinsError(`INVALID_MONEY`,`Invalid Money JSON amount "${r.value}"`);let e=t?.currency??currency;try{let t=e(i.value);if(!isCurrency(t)||t.code!==i.value)throw new CoinsError(`INVALID_CURRENCY`,`Currency resolver must return the canonical "${i.value}" definition`);return money(BigInt(r.value),t,{unit:`minor`})}catch(e){throw new CoinsError(`INVALID_MONEY`,`Invalid Money JSON for currency "${i.value}"`,{cause:e})}}throw new 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&&p.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,t,n={}){assertMoney(e);let r=decimal(t);return createMoney(roundDivision(e.amount*r.numerator,r.denominator,n.rounding??m),e.currency)}function divide(e,t,n={}){assertMoney(e);let r=decimal(t);if(r.numerator===0n)throw new CoinsError(`DIVISION_BY_ZERO`,`Cannot divide money by zero`);let i=r.numerator<0n?-r.numerator:r.numerator,a=e.amount*r.denominator;return createMoney(roundDivision(r.numerator<0n?-a:a,i,n.rounding??m),e.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 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(e,t){assertMoney(e);let{fractionDigits:n,rounding:r=m}=t;if(!Number.isInteger(n)||n<0||n>e.currency.minorUnit)throw new CoinsError(`INVALID_ROUNDING`,`fractionDigits must be an integer from 0 to ${e.currency.minorUnit}`);let i=10n**BigInt(e.currency.minorUnit-n);return createMoney(roundDivision(e.amount,i,r)*i,e.currency)}function toDecimal(e){return assertMoney(e),toDecimalString(e.amount,e.currency.minorUnit)}function createMoney(e,t){let n=Object.freeze({amount:e,currency:t});return p.add(n),n}function assertMoney(e){if(!isMoney(e))throw new 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 CurrencyMismatchError(e.currency.code,t.currency.code)}function assertCurrency(e){if(!isCurrency(e))throw new 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}var _=1e5;function sum(e,t){if(t&&!isCurrency(t.currency))throw new CoinsError(`INVALID_CURRENCY`,`sum() requires a canonical currency`);let n=0n,r=t?.currency;for(let t of e){if(assertMoney(t),r===void 0)r=t.currency;else if(t.currency!==r)throw new CurrencyMismatchError(r.code,t.currency.code);n+=t.amount}if(r===void 0)throw new CoinsError(`INVALID_MONEY`,`sum() of empty iterable requires { currency }`);return createMoney(n,r)}function allocate(e,t){if(assertMoney(e),typeof t==`number`)return allocateEvenly(e,t);if(!Array.isArray(t))throw new CoinsError(`INVALID_ALLOCATION`,`Allocation weights must be an array`);if(t.length===0)throw new CoinsError(`INVALID_ALLOCATION`,`Allocation weights cannot be empty`);if(t.length>_)throw new CoinsError(`INVALID_ALLOCATION`,`Allocation cannot exceed ${_} parts`);for(let e=0;e<t.length;e++)if(!Object.hasOwn(t,e))throw new CoinsError(`INVALID_ALLOCATION`,`Allocation weights cannot contain empty slots`);let n=t.map(decimal);if(n.some(e=>e.numerator<0n))throw new CoinsError(`INVALID_ALLOCATION`,`Allocation weights cannot be negative`);let r=n.reduce((e,t)=>lcm(e,t.denominator),1n),i=n.map(e=>e.numerator*(r/e.denominator)),a=i.reduce((e,t)=>e+t,0n);if(a===0n)throw new CoinsError(`INVALID_ALLOCATION`,`Allocation weights must include a positive value`);let o=e.amount<0n?-1n:1n,s=e.amount<0n?-e.amount:e.amount,c=i.map(e=>s*e/a),l=s-c.reduce((e,t)=>e+t,0n),u=i.map((e,t)=>({index:t,remainder:s*e%a})).sort((e,t)=>e.remainder===t.remainder?e.index-t.index:e.remainder>t.remainder?-1:1);for(let e of u){if(l===0n)break;c[e.index]+=1n,l-=1n}return c.map(t=>createMoney(t*o,e.currency))}function allocateEvenly(e,t){if(!Number.isSafeInteger(t)||t<1||t>_)throw new CoinsError(`INVALID_ALLOCATION`,`Allocation count must be a positive safe integer no greater than ${_}`);let n=BigInt(t),r=e.amount<0n?-1n:1n,i=e.amount<0n?-e.amount:e.amount,a=i/n,o=i%n;return Array.from({length:t},(t,n)=>createMoney((a+(BigInt(n)<o?1n:0n))*r,e.currency))}var v=new WeakSet;function exchangeRate({from:e,to:t,value:n}){if(!isCurrency(e)||!isCurrency(t))throw new CoinsError(`INVALID_CURRENCY`,`Exchange rate requires registered currencies`);let r=decimal(n);if(r.numerator<=0n)throw new CoinsError(`INVALID_DECIMAL`,`Exchange rates must be positive`);let i=Object.freeze({from:e,to:t,value:r});return v.add(i),i}function isExchangeRate(e){return typeof e==`object`&&!!e&&v.has(e)}function exchange(e,t,n={}){if(assertMoney(e),!isExchangeRate(t))throw new CoinsError(`INVALID_EXCHANGE_RATE`,`Exchange requires a canonical exchange rate`);if(e.currency!==t.from)throw new CurrencyMismatchError(e.currency.code,t.from.code);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)}var y=20,b=`halfAwayFromZero`,x={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(e,t={}){assertMoney(e),assertRoundingMode(t.rounding??b);let{maximum:n,minimum:r}=fractionDigits(e.currency.minorUnit,t),i=t.style??`symbol`;try{return normalizeParts(new Intl.NumberFormat(t.locale??`en-US`,{currency:e.currency.code,currencyDisplay:i,maximumFractionDigits:n,minimumFractionDigits:r,roundingMode:x[t.rounding??b],style:`currency`,useGrouping:!0}).formatToParts(toDecimal(e)))}catch(n){throw n instanceof CoinsError?n:new CoinsError(`FORMAT_ERROR`,`Cannot format currency "${e.currency.code}" for locale "${t.locale??`en-US`}"`,{cause:n})}}function fractionDigits(e,t){let n=t.maximumFractionDigits,r=t.minimumFractionDigits;validateFractionDigit(`maximumFractionDigits`,n),validateFractionDigit(`minimumFractionDigits`,r);let i=n??Math.max(e,r??e),a=r??Math.min(e,n??e);if(a>i)throw new CoinsError(`FORMAT_ERROR`,`minimumFractionDigits cannot exceed maximumFractionDigits`);return{maximum:i,minimum:a}}function validateFractionDigit(e,t){if(t!==void 0&&(!Number.isInteger(t)||t<0||t>y))throw new CoinsError(`FORMAT_ERROR`,`Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${y} (${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}function toJSON(e){return assertMoney(e),{amount:e.amount.toString(),currency:e.currency.code,unit:`minor`}}exports.BHD=d,exports.CoinsError=CoinsError,exports.CurrencyMismatchError=CurrencyMismatchError,exports.EUR=s,exports.GBP=c,exports.InvalidCurrencyError=InvalidCurrencyError,exports.JPY=l,exports.KRW=u,exports.KWD=f,exports.USD=o,exports.abs=abs,exports.add=add,exports.allocate=allocate,exports.clamp=clamp,exports.compare=compare,exports.currency=currency,exports.decodeMoney=decodeMoney,exports.divide=divide,exports.exchange=exchange,exports.exchangeRate=exchangeRate,exports.format=format,exports.formatParts=formatParts,exports.isCurrency=isCurrency,exports.isExchangeRate=isExchangeRate,exports.isMoney=isMoney,exports.money=money,exports.multiply=multiply,exports.negate=negate,exports.round=round,exports.subtract=subtract,exports.sum=sum,exports.toDecimal=toDecimal,exports.toJSON=toJSON;
2
2
  //# sourceMappingURL=coins.cjs.map