@ducatjs/money 0.0.0-stage → 0.1.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 (285) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +51 -0
  3. package/README.md +617 -2
  4. package/assets/CREDITS.md +12 -0
  5. package/data/adyen-fx.json +19 -0
  6. package/data/adyen.json +171 -0
  7. package/data/braintree.json +223 -0
  8. package/data/cash-rounding.json +399 -0
  9. package/data/checkout.json +230 -0
  10. package/data/iso4217-list-one.xml +1956 -0
  11. package/data/klarna.json +95 -0
  12. package/data/paypal.json +65 -0
  13. package/data/stripe-fx.json +17 -0
  14. package/data/stripe.json +285 -0
  15. package/dist/adyen-fx.d.ts +25 -0
  16. package/dist/adyen-fx.d.ts.map +1 -0
  17. package/dist/adyen-fx.js +122 -0
  18. package/dist/adyen-fx.js.map +1 -0
  19. package/dist/adyen.d.ts +25 -0
  20. package/dist/adyen.d.ts.map +1 -0
  21. package/dist/adyen.js +34 -0
  22. package/dist/adyen.js.map +1 -0
  23. package/dist/allocate.d.ts +72 -0
  24. package/dist/allocate.d.ts.map +1 -0
  25. package/dist/allocate.js +314 -0
  26. package/dist/allocate.js.map +1 -0
  27. package/dist/bag.d.ts +58 -0
  28. package/dist/bag.d.ts.map +1 -0
  29. package/dist/bag.js +122 -0
  30. package/dist/bag.js.map +1 -0
  31. package/dist/braintree.d.ts +34 -0
  32. package/dist/braintree.d.ts.map +1 -0
  33. package/dist/braintree.js +45 -0
  34. package/dist/braintree.js.map +1 -0
  35. package/dist/cash.d.ts +35 -0
  36. package/dist/cash.d.ts.map +1 -0
  37. package/dist/cash.js +86 -0
  38. package/dist/cash.js.map +1 -0
  39. package/dist/checkout.d.ts +42 -0
  40. package/dist/checkout.d.ts.map +1 -0
  41. package/dist/checkout.js +52 -0
  42. package/dist/checkout.js.map +1 -0
  43. package/dist/convert.d.ts +51 -0
  44. package/dist/convert.d.ts.map +1 -0
  45. package/dist/convert.js +55 -0
  46. package/dist/convert.js.map +1 -0
  47. package/dist/currency.d.ts +22 -0
  48. package/dist/currency.d.ts.map +1 -0
  49. package/dist/currency.js +38 -0
  50. package/dist/currency.js.map +1 -0
  51. package/dist/decimal.d.ts +52 -0
  52. package/dist/decimal.d.ts.map +1 -0
  53. package/dist/decimal.js +251 -0
  54. package/dist/decimal.js.map +1 -0
  55. package/dist/ecb.d.ts +17 -0
  56. package/dist/ecb.d.ts.map +1 -0
  57. package/dist/ecb.js +147 -0
  58. package/dist/ecb.js.map +1 -0
  59. package/dist/exact.d.ts +62 -0
  60. package/dist/exact.d.ts.map +1 -0
  61. package/dist/exact.js +147 -0
  62. package/dist/exact.js.map +1 -0
  63. package/dist/fetch.d.ts +58 -0
  64. package/dist/fetch.d.ts.map +1 -0
  65. package/dist/fetch.js +79 -0
  66. package/dist/fetch.js.map +1 -0
  67. package/dist/fixed.d.ts +121 -0
  68. package/dist/fixed.d.ts.map +1 -0
  69. package/dist/fixed.js +420 -0
  70. package/dist/fixed.js.map +1 -0
  71. package/dist/fn.d.ts +27 -0
  72. package/dist/fn.d.ts.map +1 -0
  73. package/dist/fn.js +30 -0
  74. package/dist/fn.js.map +1 -0
  75. package/dist/format.d.ts +13 -0
  76. package/dist/format.d.ts.map +1 -0
  77. package/dist/format.js +90 -0
  78. package/dist/format.js.map +1 -0
  79. package/dist/generated/adyen.d.ts +3 -0
  80. package/dist/generated/adyen.d.ts.map +1 -0
  81. package/dist/generated/adyen.js +144 -0
  82. package/dist/generated/adyen.js.map +1 -0
  83. package/dist/generated/braintree.d.ts +3 -0
  84. package/dist/generated/braintree.d.ts.map +1 -0
  85. package/dist/generated/braintree.js +140 -0
  86. package/dist/generated/braintree.js.map +1 -0
  87. package/dist/generated/cash.d.ts +12 -0
  88. package/dist/generated/cash.d.ts.map +1 -0
  89. package/dist/generated/cash.js +364 -0
  90. package/dist/generated/cash.js.map +1 -0
  91. package/dist/generated/checkout.d.ts +3 -0
  92. package/dist/generated/checkout.d.ts.map +1 -0
  93. package/dist/generated/checkout.js +151 -0
  94. package/dist/generated/checkout.js.map +1 -0
  95. package/dist/generated/iso4217.d.ts +174 -0
  96. package/dist/generated/iso4217.d.ts.map +1 -0
  97. package/dist/generated/iso4217.js +175 -0
  98. package/dist/generated/iso4217.js.map +1 -0
  99. package/dist/generated/kernel.d.ts +17 -0
  100. package/dist/generated/kernel.d.ts.map +1 -0
  101. package/dist/generated/kernel.js +208 -0
  102. package/dist/generated/kernel.js.map +1 -0
  103. package/dist/generated/klarna.d.ts +3 -0
  104. package/dist/generated/klarna.d.ts.map +1 -0
  105. package/dist/generated/klarna.js +21 -0
  106. package/dist/generated/klarna.js.map +1 -0
  107. package/dist/generated/paypal.d.ts +3 -0
  108. package/dist/generated/paypal.d.ts.map +1 -0
  109. package/dist/generated/paypal.js +31 -0
  110. package/dist/generated/paypal.js.map +1 -0
  111. package/dist/generated/stripe.d.ts +8 -0
  112. package/dist/generated/stripe.d.ts.map +1 -0
  113. package/dist/generated/stripe.js +150 -0
  114. package/dist/generated/stripe.js.map +1 -0
  115. package/dist/index.d.ts +11 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +11 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/klarna.d.ts +31 -0
  120. package/dist/klarna.d.ts.map +1 -0
  121. package/dist/klarna.js +38 -0
  122. package/dist/klarna.js.map +1 -0
  123. package/dist/money.d.ts +218 -0
  124. package/dist/money.d.ts.map +1 -0
  125. package/dist/money.js +577 -0
  126. package/dist/money.js.map +1 -0
  127. package/dist/parse.d.ts +11 -0
  128. package/dist/parse.d.ts.map +1 -0
  129. package/dist/parse.js +132 -0
  130. package/dist/parse.js.map +1 -0
  131. package/dist/paypal.d.ts +41 -0
  132. package/dist/paypal.d.ts.map +1 -0
  133. package/dist/paypal.js +58 -0
  134. package/dist/paypal.js.map +1 -0
  135. package/dist/plugins/adyen.d.ts +15 -0
  136. package/dist/plugins/adyen.d.ts.map +1 -0
  137. package/dist/plugins/adyen.js +10 -0
  138. package/dist/plugins/adyen.js.map +1 -0
  139. package/dist/plugins/all.d.ts +22 -0
  140. package/dist/plugins/all.d.ts.map +1 -0
  141. package/dist/plugins/all.js +23 -0
  142. package/dist/plugins/all.js.map +1 -0
  143. package/dist/plugins/allocate.d.ts +20 -0
  144. package/dist/plugins/allocate.d.ts.map +1 -0
  145. package/dist/plugins/allocate.js +26 -0
  146. package/dist/plugins/allocate.js.map +1 -0
  147. package/dist/plugins/braintree.d.ts +12 -0
  148. package/dist/plugins/braintree.d.ts.map +1 -0
  149. package/dist/plugins/braintree.js +12 -0
  150. package/dist/plugins/braintree.js.map +1 -0
  151. package/dist/plugins/cash.d.ts +19 -0
  152. package/dist/plugins/cash.d.ts.map +1 -0
  153. package/dist/plugins/cash.js +16 -0
  154. package/dist/plugins/cash.js.map +1 -0
  155. package/dist/plugins/checkout.d.ts +21 -0
  156. package/dist/plugins/checkout.d.ts.map +1 -0
  157. package/dist/plugins/checkout.js +16 -0
  158. package/dist/plugins/checkout.js.map +1 -0
  159. package/dist/plugins/convert.d.ts +20 -0
  160. package/dist/plugins/convert.d.ts.map +1 -0
  161. package/dist/plugins/convert.js +13 -0
  162. package/dist/plugins/convert.js.map +1 -0
  163. package/dist/plugins/exact.d.ts +15 -0
  164. package/dist/plugins/exact.d.ts.map +1 -0
  165. package/dist/plugins/exact.js +13 -0
  166. package/dist/plugins/exact.js.map +1 -0
  167. package/dist/plugins/fixed.d.ts +17 -0
  168. package/dist/plugins/fixed.d.ts.map +1 -0
  169. package/dist/plugins/fixed.js +13 -0
  170. package/dist/plugins/fixed.js.map +1 -0
  171. package/dist/plugins/format.d.ts +9 -0
  172. package/dist/plugins/format.d.ts.map +1 -0
  173. package/dist/plugins/format.js +16 -0
  174. package/dist/plugins/format.js.map +1 -0
  175. package/dist/plugins/klarna.d.ts +10 -0
  176. package/dist/plugins/klarna.d.ts.map +1 -0
  177. package/dist/plugins/klarna.js +10 -0
  178. package/dist/plugins/klarna.js.map +1 -0
  179. package/dist/plugins/paypal.d.ts +14 -0
  180. package/dist/plugins/paypal.d.ts.map +1 -0
  181. package/dist/plugins/paypal.js +16 -0
  182. package/dist/plugins/paypal.js.map +1 -0
  183. package/dist/plugins/prisma.d.ts +8 -0
  184. package/dist/plugins/prisma.d.ts.map +1 -0
  185. package/dist/plugins/prisma.js +13 -0
  186. package/dist/plugins/prisma.js.map +1 -0
  187. package/dist/plugins/stripe.d.ts +14 -0
  188. package/dist/plugins/stripe.d.ts.map +1 -0
  189. package/dist/plugins/stripe.js +16 -0
  190. package/dist/plugins/stripe.js.map +1 -0
  191. package/dist/plugins/tax.d.ts +21 -0
  192. package/dist/plugins/tax.d.ts.map +1 -0
  193. package/dist/plugins/tax.js +18 -0
  194. package/dist/plugins/tax.js.map +1 -0
  195. package/dist/prisma.d.ts +66 -0
  196. package/dist/prisma.d.ts.map +1 -0
  197. package/dist/prisma.js +66 -0
  198. package/dist/prisma.js.map +1 -0
  199. package/dist/processor.d.ts +28 -0
  200. package/dist/processor.d.ts.map +1 -0
  201. package/dist/processor.js +118 -0
  202. package/dist/processor.js.map +1 -0
  203. package/dist/provider.d.ts +89 -0
  204. package/dist/provider.d.ts.map +1 -0
  205. package/dist/provider.js +240 -0
  206. package/dist/provider.js.map +1 -0
  207. package/dist/rate.d.ts +119 -0
  208. package/dist/rate.d.ts.map +1 -0
  209. package/dist/rate.js +271 -0
  210. package/dist/rate.js.map +1 -0
  211. package/dist/rounding-error.d.ts +9 -0
  212. package/dist/rounding-error.d.ts.map +1 -0
  213. package/dist/rounding-error.js +9 -0
  214. package/dist/rounding-error.js.map +1 -0
  215. package/dist/rounding.d.ts +50 -0
  216. package/dist/rounding.d.ts.map +1 -0
  217. package/dist/rounding.js +87 -0
  218. package/dist/rounding.js.map +1 -0
  219. package/dist/stripe-fx.d.ts +22 -0
  220. package/dist/stripe-fx.d.ts.map +1 -0
  221. package/dist/stripe-fx.js +30 -0
  222. package/dist/stripe-fx.js.map +1 -0
  223. package/dist/stripe.d.ts +36 -0
  224. package/dist/stripe.d.ts.map +1 -0
  225. package/dist/stripe.js +39 -0
  226. package/dist/stripe.js.map +1 -0
  227. package/dist/tax.d.ts +29 -0
  228. package/dist/tax.d.ts.map +1 -0
  229. package/dist/tax.js +39 -0
  230. package/dist/tax.js.map +1 -0
  231. package/package.json +171 -4
  232. package/src/adyen-fx.ts +154 -0
  233. package/src/adyen.ts +46 -0
  234. package/src/allocate.ts +393 -0
  235. package/src/bag.ts +170 -0
  236. package/src/braintree.ts +71 -0
  237. package/src/cash.ts +115 -0
  238. package/src/checkout.ts +77 -0
  239. package/src/convert.ts +159 -0
  240. package/src/currency.ts +61 -0
  241. package/src/decimal.ts +283 -0
  242. package/src/ecb.ts +162 -0
  243. package/src/exact.ts +182 -0
  244. package/src/fetch.ts +127 -0
  245. package/src/fixed.ts +558 -0
  246. package/src/fn.ts +80 -0
  247. package/src/format.ts +106 -0
  248. package/src/generated/adyen.ts +144 -0
  249. package/src/generated/braintree.ts +140 -0
  250. package/src/generated/cash.ts +375 -0
  251. package/src/generated/checkout.ts +151 -0
  252. package/src/generated/iso4217.ts +179 -0
  253. package/src/generated/kernel.ts +230 -0
  254. package/src/generated/klarna.ts +21 -0
  255. package/src/generated/paypal.ts +31 -0
  256. package/src/generated/stripe.ts +150 -0
  257. package/src/index.ts +37 -0
  258. package/src/klarna.ts +54 -0
  259. package/src/money.ts +781 -0
  260. package/src/parse.ts +144 -0
  261. package/src/paypal.ts +93 -0
  262. package/src/plugins/adyen.ts +25 -0
  263. package/src/plugins/all.ts +47 -0
  264. package/src/plugins/allocate.ts +53 -0
  265. package/src/plugins/braintree.ts +23 -0
  266. package/src/plugins/cash.ts +33 -0
  267. package/src/plugins/checkout.ts +32 -0
  268. package/src/plugins/convert.ts +50 -0
  269. package/src/plugins/exact.ts +23 -0
  270. package/src/plugins/fixed.ts +28 -0
  271. package/src/plugins/format.ts +28 -0
  272. package/src/plugins/klarna.ts +21 -0
  273. package/src/plugins/paypal.ts +29 -0
  274. package/src/plugins/prisma.ts +24 -0
  275. package/src/plugins/stripe.ts +29 -0
  276. package/src/plugins/tax.ts +41 -0
  277. package/src/prisma.ts +118 -0
  278. package/src/processor.ts +181 -0
  279. package/src/provider.ts +324 -0
  280. package/src/rate.ts +377 -0
  281. package/src/rounding-error.ts +8 -0
  282. package/src/rounding.ts +122 -0
  283. package/src/stripe-fx.ts +49 -0
  284. package/src/stripe.ts +69 -0
  285. package/src/tax.ts +71 -0
package/dist/rate.js ADDED
@@ -0,0 +1,271 @@
1
+ // Exchange rates as exact rationals with metadata. A Rate never loses digits: inverses,
2
+ // compositions and margins are exact products of ratios, and rounding happens once, when an amount
3
+ // is converted.
4
+ import { currencyExponent } from './currency.js';
5
+ import { formatScaled, POW10, pow10n, ratio, ratioToDecimal, normalizeMinor, } from './decimal.js';
6
+ import { CurrencyMismatchError, make } from './money.js';
7
+ import { DEFAULT_ROUNDING, divideAndRound, divideAndRoundBigint, } from './rounding.js';
8
+ const MAX_SAFE_MINOR = Number.MAX_SAFE_INTEGER;
9
+ /** Thrown by a strict-validity conversion with a rate past its `validUntil`. */
10
+ export class RateExpiredError extends RangeError {
11
+ validUntil;
12
+ constructor(from, to, validUntil) {
13
+ super(`Exchange rate ${from}→${to} expired at ${validUntil}`);
14
+ this.name = 'RateExpiredError';
15
+ this.validUntil = validUntil;
16
+ }
17
+ }
18
+ const OPPOSITE_SIDE = { bid: 'ask', ask: 'bid' };
19
+ const ISO_DATE = /^\d{4}-\d\d-\d\d$/;
20
+ /**
21
+ * @internal
22
+ * A date stays as given ('2026-11-06'); an instant becomes UTC ISO ('2026-11-06T15:00:00.000Z'), so
23
+ * that times with different offsets compare correctly as strings.
24
+ */
25
+ export function toIsoString(time) {
26
+ if (typeof time === 'string' && ISO_DATE.test(time)) {
27
+ return time;
28
+ }
29
+ const date = time instanceof Date ? time : new Date(time);
30
+ if (Number.isNaN(date.getTime())) {
31
+ throw new RangeError(`Invalid date or time ${String(time)}`);
32
+ }
33
+ return date.toISOString();
34
+ }
35
+ /**
36
+ * The earlier of two ISO-8601 strings (one format compares lexicographically); undefined = no
37
+ * limit.
38
+ */
39
+ function earlier(first, second) {
40
+ if (first === undefined) {
41
+ return second;
42
+ }
43
+ if (second === undefined || first <= second) {
44
+ return first;
45
+ }
46
+ return second;
47
+ }
48
+ /**
49
+ * minor × rate × 10^scaleShift, rounded once with `rounding`. This is the kernel of every
50
+ * conversion: `scaleShift` is the result scale minus the input scale. Pure, and `rate.den > 0` by
51
+ * construction.
52
+ */
53
+ export function multiplyByRatioAtScale(minor, rate, scaleShift, rounding) {
54
+ // Fast path: all factors are safe integers, so a product that stays in the safe range is exact.
55
+ // `rate.n === rate.n` is false only for NaN, which marks a numerator that isn't a safe integer.
56
+ const rateIsSafe = rate.n === rate.n && rate.d === rate.d;
57
+ if (typeof minor === 'number' && rateIsSafe && scaleShift >= -15 && scaleShift <= 15) {
58
+ let numerator = rate.n;
59
+ let denominator = rate.d;
60
+ if (scaleShift > 0) {
61
+ numerator *= POW10[scaleShift];
62
+ }
63
+ else if (scaleShift < 0) {
64
+ denominator *= POW10[-scaleShift];
65
+ }
66
+ if (numerator <= MAX_SAFE_MINOR && denominator <= MAX_SAFE_MINOR) {
67
+ const product = minor * numerator;
68
+ if (product <= MAX_SAFE_MINOR && product >= -MAX_SAFE_MINOR) {
69
+ return divideAndRound(product, denominator, rounding);
70
+ }
71
+ }
72
+ }
73
+ // Slow path: the same single division, in bigint.
74
+ let numerator = BigInt(minor) * rate.num;
75
+ let denominator = rate.den;
76
+ if (scaleShift > 0) {
77
+ numerator *= pow10n(scaleShift);
78
+ }
79
+ else if (scaleShift < 0) {
80
+ denominator *= pow10n(-scaleShift);
81
+ }
82
+ return normalizeMinor(divideAndRoundBigint(numerator, denominator, rounding));
83
+ }
84
+ /**
85
+ * An exact exchange rate: one unit of `from` is worth `ratio` units of `to`. Build it with
86
+ * `exchangeRate()`. Derived rates (`inverse`, `andThen`, `withMargin`, `round`) keep their inputs
87
+ * in `derivedFrom`, the audit chain. Their `asOf` is the oldest input's, so staleness can't hide,
88
+ * and their `validUntil` is the earliest.
89
+ */
90
+ export class Rate {
91
+ from;
92
+ to;
93
+ ratio;
94
+ /** @internal use exchangeRate() */
95
+ constructor(from, to, rate, info, derivedFrom) {
96
+ // Rebuilt so that a hand-made Ratio object is reduced, has a positive denominator, and has
97
+ // number fields that match its bigints (the fast path relies on them).
98
+ const normalized = ratio(rate.num, rate.den);
99
+ if (normalized.num <= 0n) {
100
+ throw new RangeError('Exchange rate must be positive');
101
+ }
102
+ this.from = from;
103
+ this.to = to;
104
+ this.ratio = normalized;
105
+ // Only defined fields are assigned, so rates compare and serialise without undefined keys.
106
+ const writable = this;
107
+ if (info?.source !== undefined) {
108
+ writable.source = info.source;
109
+ }
110
+ if (info?.asOf !== undefined) {
111
+ writable.asOf = toIsoString(info.asOf);
112
+ }
113
+ if (info?.validUntil !== undefined) {
114
+ writable.validUntil = toIsoString(info.validUntil);
115
+ }
116
+ if (info?.type !== undefined) {
117
+ writable.type = info.type;
118
+ }
119
+ if (derivedFrom !== undefined) {
120
+ writable.derivedFrom = derivedFrom;
121
+ }
122
+ }
123
+ /** The exact reciprocal, `to` → `from`. A bid becomes an ask and vice versa. */
124
+ inverse() {
125
+ let type = this.type;
126
+ if (type !== undefined) {
127
+ type = OPPOSITE_SIDE[type] ?? type;
128
+ }
129
+ const reciprocal = ratio(this.ratio.den, this.ratio.num);
130
+ return new Rate(this.to, this.from, reciprocal, timingOf(this, type), [this]);
131
+ }
132
+ /**
133
+ * The exact composite `from` → `next.to`. Nothing is rounded: converting with it rounds once.
134
+ * (Named `andThen`, not `then`, so that a Rate is never mistaken for a promise.)
135
+ */
136
+ andThen(next) {
137
+ if (next.from !== this.to) {
138
+ throw new CurrencyMismatchError(this.to, next.from);
139
+ }
140
+ const product = ratio(this.ratio.num * next.ratio.num, this.ratio.den * next.ratio.den);
141
+ // A composite keeps a type only when both legs agree, so it can't be mislabelled.
142
+ let type;
143
+ if (this.type === next.type) {
144
+ type = this.type;
145
+ }
146
+ const asOf = earlier(this.asOf, next.asOf);
147
+ const validUntil = earlier(this.validUntil, next.validUntil);
148
+ return new Rate(this.from, next.to, product, timing(asOf, validUntil, type), [this, next]);
149
+ }
150
+ /** This rate × (1 + margin), exactly: `withMargin('-0.02')` gives 2% less `to` per `from`. */
151
+ withMargin(margin) {
152
+ const marginRatio = ratio(margin);
153
+ const numerator = this.ratio.num * (marginRatio.den + marginRatio.num);
154
+ const denominator = this.ratio.den * marginRatio.den;
155
+ return new Rate(this.from, this.to, ratio(numerator, denominator), timingOf(this), [this]);
156
+ }
157
+ /**
158
+ * A new Rate rounded to `places` decimals, for businesses that quote rounded (e.g. inverse)
159
+ * rates.
160
+ */
161
+ round(places, rounding = 'halfEven') {
162
+ const rounded = ratio(this.toDecimal(places, rounding));
163
+ return new Rate(this.from, this.to, rounded, timingOf(this, this.type), [this]);
164
+ }
165
+ /** The rate with exactly `places` decimals, for display. The Rate itself keeps every digit. */
166
+ toDecimal(places, rounding = 'halfEven') {
167
+ const scaled = divideAndRoundBigint(this.ratio.num * pow10n(places), this.ratio.den, rounding);
168
+ return formatScaled(scaled, places);
169
+ }
170
+ /** The exact rate: '1.1165', or '2000/2233' when its decimal expansion doesn't terminate. */
171
+ toString() {
172
+ return ratioToDecimal(this.ratio) ?? `${this.ratio.num}/${this.ratio.den}`;
173
+ }
174
+ /** The rate, its metadata and its audit chain as JSON. `rateFromJSON` reverses it. */
175
+ toJSON() {
176
+ const json = { from: this.from, to: this.to, rate: this.toString(), ...infoOf(this) };
177
+ if (this.derivedFrom !== undefined) {
178
+ json.derivedFrom = this.derivedFrom.map((input) => input.toJSON());
179
+ }
180
+ return json;
181
+ }
182
+ /**
183
+ * Whether `validUntil` has passed at `now` (default: the system clock). A date is valid all day
184
+ * (UTC).
185
+ */
186
+ isExpired(now = new Date()) {
187
+ if (this.validUntil === undefined) {
188
+ return false;
189
+ }
190
+ let instant = toIsoString(now);
191
+ if (ISO_DATE.test(instant)) {
192
+ instant = `${instant}T00:00:00.000Z`;
193
+ }
194
+ if (ISO_DATE.test(this.validUntil)) {
195
+ return instant.slice(0, 10) > this.validUntil;
196
+ }
197
+ return instant > this.validUntil;
198
+ }
199
+ /** Converts `money`, rounding once to the target currency's exponent (or `options.scale`). */
200
+ convert(money, rounding = DEFAULT_ROUNDING, options) {
201
+ if (money.currency !== this.from) {
202
+ throw new CurrencyMismatchError(this.from, money.currency);
203
+ }
204
+ if (options?.strictValidity === true && this.validUntil !== undefined && this.isExpired()) {
205
+ throw new RateExpiredError(this.from, this.to, this.validUntil);
206
+ }
207
+ const scale = options?.scale ?? currencyExponent(this.to);
208
+ const minor = multiplyByRatioAtScale(money.minor, this.ratio, scale - money.scale, rounding);
209
+ return make(this.to, minor, scale);
210
+ }
211
+ }
212
+ /** The metadata fields of `info` that are defined (no undefined keys). */
213
+ function infoOf(info) {
214
+ const defined = {};
215
+ if (info.source !== undefined) {
216
+ defined.source = info.source;
217
+ }
218
+ if (info.asOf !== undefined) {
219
+ defined.asOf = toIsoString(info.asOf);
220
+ }
221
+ if (info.validUntil !== undefined) {
222
+ defined.validUntil = toIsoString(info.validUntil);
223
+ }
224
+ if (info.type !== undefined) {
225
+ defined.type = info.type;
226
+ }
227
+ return defined;
228
+ }
229
+ function timing(asOf, validUntil, type) {
230
+ return infoOf({ asOf, validUntil, type });
231
+ }
232
+ /**
233
+ * A derived rate keeps the timing of its input; the input's source stays with it, in derivedFrom.
234
+ */
235
+ function timingOf(input, type) {
236
+ return timing(input.asOf, input.validUntil, type);
237
+ }
238
+ /**
239
+ * An exact rate pinned to a currency pair, so converting the wrong currency is a type error. Give
240
+ * the rate as a decimal string ('1.1165', every digit kept), a "num/den" string, a bigint or a
241
+ * Ratio. Numbers are read at their shortest decimal form (1.234 is exactly 1234/1000).
242
+ *
243
+ * const eurUsd = exchangeRate('EUR', 'USD', '1.1165', { source: 'ECB', asOf: '2026-11-06' });
244
+ * eurUsd.convert(money('100', 'EUR'), 'halfEven'); // 111.65 USD
245
+ * eurUsd.inverse().ratio // 2000/2233, exactly
246
+ */
247
+ export function exchangeRate(from, to, rate, info) {
248
+ return new Rate(from, to, ratio(rate), info);
249
+ }
250
+ /** Rebuilds a Rate, with its audit chain, from `rate.toJSON()`. */
251
+ export function rateFromJSON(json) {
252
+ const derivedFrom = json.derivedFrom?.map(rateFromJSON);
253
+ return new Rate(json.from, json.to, ratio(json.rate), infoOf(json), derivedFrom);
254
+ }
255
+ /** Whether `value` is a Rate. */
256
+ export function isRate(value) {
257
+ return value instanceof Rate;
258
+ }
259
+ /**
260
+ * The exact rate a processor actually applied: settled ÷ presented. Use it to audit a quoted rate,
261
+ * which after the processor's own rounding often doesn't reproduce the settled amount.
262
+ *
263
+ * impliedRate(money('10.00', 'EUR'), money('12.34', 'USD')) // EUR→USD 1.234
264
+ */
265
+ export function impliedRate(presented, settled, info) {
266
+ // (settled.minor / 10^settled.scale) ÷ (presented.minor / 10^presented.scale)
267
+ const numerator = BigInt(settled.minor) * pow10n(presented.scale);
268
+ const denominator = BigInt(presented.minor) * pow10n(settled.scale);
269
+ return new Rate(presented.currency, settled.currency, ratio(numerator, denominator), info);
270
+ }
271
+ //# sourceMappingURL=rate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate.js","sourceRoot":"","sources":["../src/rate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Thrown when an operation would have to round but the rounding mode is `'unnecessary'`. It lives
3
+ * in its own module so the generated kernels can throw it without importing `rounding.ts`, which
4
+ * imports them.
5
+ */
6
+ export declare class RoundingError extends RangeError {
7
+ name: string;
8
+ }
9
+ //# sourceMappingURL=rounding-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rounding-error.d.ts","sourceRoot":"","sources":["../src/rounding-error.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAClC,IAAI,SAAmB;CACjC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Thrown when an operation would have to round but the rounding mode is `'unnecessary'`. It lives
3
+ * in its own module so the generated kernels can throw it without importing `rounding.ts`, which
4
+ * imports them.
5
+ */
6
+ export class RoundingError extends RangeError {
7
+ name = 'RoundingError';
8
+ }
9
+ //# sourceMappingURL=rounding-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rounding-error.js","sourceRoot":"","sources":["../src/rounding-error.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,aAAc,SAAQ,UAAU;IAClC,IAAI,GAAG,eAAe,CAAC;CACjC"}
@@ -0,0 +1,50 @@
1
+ import { RoundingError } from './rounding-error.js';
2
+ /**
3
+ * Rounding modes. The string values are the ones `Intl.NumberFormat` uses for its `roundingMode`
4
+ * option (ES2023), so a mode can be passed straight through to formatting. `Rounding` gives the
5
+ * familiar names (HalfUp, HalfEven, ...) for the same strings.
6
+ *
7
+ * An invalid mode (possible only from untyped code) is reported, with a RangeError, only when
8
+ * rounding is actually needed: an exact result is returned without looking at the mode, which keeps
9
+ * the exact path as fast as it can be.
10
+ */
11
+ export type RoundingMode = 'halfEven' | 'halfExpand' | 'halfTrunc' | 'halfCeil' | 'halfFloor' | 'expand' | 'trunc' | 'ceil' | 'floor' | 'unnecessary';
12
+ /**
13
+ * Familiar names for the rounding modes (HalfUp, HalfEven, Bankers, ...), mapped to the mode
14
+ * strings.
15
+ */
16
+ export declare const Rounding: {
17
+ readonly HalfEven: "halfEven";
18
+ readonly Bankers: "halfEven";
19
+ readonly HalfUp: "halfExpand";
20
+ readonly HalfDown: "halfTrunc";
21
+ readonly HalfCeil: "halfCeil";
22
+ readonly HalfFloor: "halfFloor";
23
+ readonly Up: "expand";
24
+ readonly Down: "trunc";
25
+ readonly Ceil: "ceil";
26
+ readonly Floor: "floor";
27
+ readonly Unnecessary: "unnecessary";
28
+ };
29
+ /**
30
+ * The rounding mode arithmetic uses when none is given: ties away from zero ('half up'). It is the
31
+ * default of decimal.js, big.js, bignumber.js, Prisma.Decimal and Intl.NumberFormat, and what
32
+ * Math.round-based money libraries (currency.js, js-money, ts-money) intend. Parsing
33
+ * (`money('1.005', 'EUR')`, `fromDecimal`) and processor amounts (`toStripeAmount`, `toAdyen`) stay
34
+ * strict: they throw unless a mode is passed.
35
+ */
36
+ export declare const DEFAULT_ROUNDING: RoundingMode;
37
+ export { RoundingError };
38
+ /**
39
+ * dividend / divisor rounded with `mode`, for safe-integer dividends and divisor > 0. Exact: the
40
+ * float quotient is only a first guess, corrected until the remainder has the dividend's sign and
41
+ * is smaller than the divisor.
42
+ */
43
+ export declare function divideAndRound(dividend: number, divisor: number, mode: RoundingMode): number;
44
+ /**
45
+ * dividend / divisor rounded with `mode`, for divisor > 0n. The bigint kernel is generated from the
46
+ * Lean proofs in proofs/Ducat/Rounding.lean (`src/generated/kernel.ts`), and so is the mode switch
47
+ * `divideAndRound` uses.
48
+ */
49
+ export declare function divideAndRoundBigint(dividend: bigint, divisor: bigint, mode: RoundingMode): bigint;
50
+ //# sourceMappingURL=rounding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rounding.d.ts","sourceRoot":"","sources":["../src/rounding.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAEpB,UAAU,GACV,YAAY,GACZ,WAAW,GACX,UAAU,GACV,WAAW,GACX,QAAQ,GACR,OAAO,GACP,MAAM,GACN,OAAO,GAEP,aAAa,CAAC;AAElB;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;CAY4B,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAA2B,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,CAAC;AAIzB;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAwC5F;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,YAAY,GACjB,MAAM,CAKR"}
@@ -0,0 +1,87 @@
1
+ import { divideAndRoundBigint as provedDivideAndRoundBigint, roundTruncatedQuotient, } from './generated/kernel.js';
2
+ import { RoundingError } from './rounding-error.js';
3
+ /**
4
+ * Familiar names for the rounding modes (HalfUp, HalfEven, Bankers, ...), mapped to the mode
5
+ * strings.
6
+ */
7
+ export const Rounding = {
8
+ HalfEven: 'halfEven',
9
+ Bankers: 'halfEven',
10
+ HalfUp: 'halfExpand',
11
+ HalfDown: 'halfTrunc',
12
+ HalfCeil: 'halfCeil',
13
+ HalfFloor: 'halfFloor',
14
+ Up: 'expand',
15
+ Down: 'trunc',
16
+ Ceil: 'ceil',
17
+ Floor: 'floor',
18
+ Unnecessary: 'unnecessary',
19
+ };
20
+ /**
21
+ * The rounding mode arithmetic uses when none is given: ties away from zero ('half up'). It is the
22
+ * default of decimal.js, big.js, bignumber.js, Prisma.Decimal and Intl.NumberFormat, and what
23
+ * Math.round-based money libraries (currency.js, js-money, ts-money) intend. Parsing
24
+ * (`money('1.005', 'EUR')`, `fromDecimal`) and processor amounts (`toStripeAmount`, `toAdyen`) stay
25
+ * strict: they throw unless a mode is passed.
26
+ */
27
+ export const DEFAULT_ROUNDING = 'halfExpand';
28
+ export { RoundingError };
29
+ const MAX_SAFE_MINOR = Number.MAX_SAFE_INTEGER;
30
+ /**
31
+ * dividend / divisor rounded with `mode`, for safe-integer dividends and divisor > 0. Exact: the
32
+ * float quotient is only a first guess, corrected until the remainder has the dividend's sign and
33
+ * is smaller than the divisor.
34
+ */
35
+ export function divideAndRound(dividend, divisor, mode) {
36
+ if (divisor === 1) {
37
+ return dividend;
38
+ }
39
+ // Beyond MAX_SAFE_MINOR the product quotient * divisor below could round, so take the bigint
40
+ // path.
41
+ if (!(Math.abs(dividend) + divisor <= MAX_SAFE_MINOR)) {
42
+ return Number(divideAndRoundBigint(BigInt(dividend), BigInt(divisor), mode));
43
+ }
44
+ let quotient = Math.trunc(dividend / divisor);
45
+ // exact: |quotient * divisor| <= |dividend| + divisor <= MAX_SAFE_MINOR
46
+ let remainder = dividend - quotient * divisor;
47
+ // The float quotient can be off by one ulp; fix it so the remainder has the dividend's sign and
48
+ // is smaller than the divisor in magnitude.
49
+ if (dividend >= 0) {
50
+ if (remainder < 0) {
51
+ quotient -= 1;
52
+ remainder += divisor;
53
+ }
54
+ else if (remainder >= divisor) {
55
+ quotient += 1;
56
+ remainder -= divisor;
57
+ }
58
+ }
59
+ else if (remainder > 0) {
60
+ quotient += 1;
61
+ remainder -= divisor;
62
+ }
63
+ else if (remainder <= -divisor) {
64
+ quotient -= 1;
65
+ remainder += divisor;
66
+ }
67
+ // The generated switch handles a zero remainder too; returning here saves the call on the hottest
68
+ // path.
69
+ if (remainder === 0) {
70
+ return quotient + 0; // + 0 normalises -0
71
+ }
72
+ // quotient and remainder are now the exact truncated quotient and remainder, as the generated
73
+ // switch expects
74
+ return roundTruncatedQuotient(dividend, divisor, quotient, remainder, mode) + 0;
75
+ }
76
+ /**
77
+ * dividend / divisor rounded with `mode`, for divisor > 0n. The bigint kernel is generated from the
78
+ * Lean proofs in proofs/Ducat/Rounding.lean (`src/generated/kernel.ts`), and so is the mode switch
79
+ * `divideAndRound` uses.
80
+ */
81
+ export function divideAndRoundBigint(dividend, divisor, mode) {
82
+ if (divisor === 1n) {
83
+ return dividend;
84
+ }
85
+ return provedDivideAndRoundBigint(dividend, divisor, mode);
86
+ }
87
+ //# sourceMappingURL=rounding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rounding.js","sourceRoot":"","sources":["../src/rounding.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,IAAI,0BAA0B,EAClD,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAyBpD;;;GAGG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,UAAU;IACnB,MAAM,EAAE,YAAY;IACpB,QAAQ,EAAE,WAAW;IACrB,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,WAAW;IACtB,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,OAAO;IACb,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;IACd,WAAW,EAAE,aAAa;CACqB,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAiB,YAAY,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,CAAC;AAEzB,MAAM,cAAc,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAE/C;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,OAAe,EAAE,IAAkB;IAClF,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;QAClB,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,6FAA6F;IAC7F,QAAQ;IACR,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,OAAO,IAAI,cAAc,CAAC,EAAE,CAAC;QACtD,OAAO,MAAM,CAAC,oBAAoB,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/E,CAAC;IAED,IAAI,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC;IAC9C,wEAAwE;IACxE,IAAI,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IAE9C,gGAAgG;IAChG,4CAA4C;IAC5C,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAClB,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,QAAQ,IAAI,CAAC,CAAC;YACd,SAAS,IAAI,OAAO,CAAC;QACvB,CAAC;aAAM,IAAI,SAAS,IAAI,OAAO,EAAE,CAAC;YAChC,QAAQ,IAAI,CAAC,CAAC;YACd,SAAS,IAAI,OAAO,CAAC;QACvB,CAAC;IACH,CAAC;SAAM,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QACzB,QAAQ,IAAI,CAAC,CAAC;QACd,SAAS,IAAI,OAAO,CAAC;IACvB,CAAC;SAAM,IAAI,SAAS,IAAI,CAAC,OAAO,EAAE,CAAC;QACjC,QAAQ,IAAI,CAAC,CAAC;QACd,SAAS,IAAI,OAAO,CAAC;IACvB,CAAC;IAED,kGAAkG;IAClG,QAAQ;IACR,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,QAAQ,GAAG,CAAC,CAAC,CAAC,oBAAoB;IAC3C,CAAC;IACD,8FAA8F;IAC9F,iBAAiB;IACjB,OAAO,sBAAsB,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;AAClF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAgB,EAChB,OAAe,EACf,IAAkB;IAElB,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACnB,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,0BAA0B,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Money } from './money.js';
2
+ import { type Rate } from './rate.js';
3
+ /** The fields of a Stripe BalanceTransaction used here. */
4
+ export interface StripeBalanceTransaction {
5
+ readonly amount: number;
6
+ readonly currency: string;
7
+ readonly exchange_rate?: number | null;
8
+ /** Seconds since the Unix epoch. */
9
+ readonly created?: number;
10
+ }
11
+ /**
12
+ * The rate Stripe quotes on a balance transaction, from the charge currency `from` to the balance
13
+ * currency: "the amount in currency A, multiplied by the exchange_rate, equals the amount in
14
+ * currency B". The JSON number is read at its shortest decimal form, so 1.234 is exactly 1234/1000.
15
+ * `asOf` is the balance transaction's `created` time, the closest timestamp Stripe gives for the
16
+ * conversion; it is not the charge's creation time, and Stripe doesn't say when the rate was
17
+ * quoted. `undefined` when nothing was converted.
18
+ */
19
+ export declare function stripeRate<From extends string>(from: From, transaction: StripeBalanceTransaction): Rate<Uppercase<From>, Uppercase<string>> | undefined;
20
+ /** The exact rate Stripe applied: the balance transaction's gross amount ÷ the charged amount. */
21
+ export declare function stripeImpliedRate<From extends string>(charged: Money<From>, transaction: StripeBalanceTransaction): Rate<From, Uppercase<string>>;
22
+ //# sourceMappingURL=stripe-fx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe-fx.d.ts","sourceRoot":"","sources":["../src/stripe-fx.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAA6B,KAAK,IAAI,EAAiB,MAAM,WAAW,CAAC;AAGhF,2DAA2D;AAC3D,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,oCAAoC;IACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AASD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,SAAS,MAAM,EAC5C,IAAI,EAAE,IAAI,EACV,WAAW,EAAE,wBAAwB,GACpC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAOtD;AAED,kGAAkG;AAClG,wBAAgB,iBAAiB,CAAC,IAAI,SAAS,MAAM,EACnD,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,EACpB,WAAW,EAAE,wBAAwB,GACpC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAG/B"}
@@ -0,0 +1,30 @@
1
+ import { exchangeRate, impliedRate } from './rate.js';
2
+ import { fromStripe } from './stripe.js';
3
+ function stripeInfo(transaction) {
4
+ if (transaction.created === undefined) {
5
+ return { source: 'stripe' };
6
+ }
7
+ return { source: 'stripe', asOf: new Date(transaction.created * 1000) };
8
+ }
9
+ /**
10
+ * The rate Stripe quotes on a balance transaction, from the charge currency `from` to the balance
11
+ * currency: "the amount in currency A, multiplied by the exchange_rate, equals the amount in
12
+ * currency B". The JSON number is read at its shortest decimal form, so 1.234 is exactly 1234/1000.
13
+ * `asOf` is the balance transaction's `created` time, the closest timestamp Stripe gives for the
14
+ * conversion; it is not the charge's creation time, and Stripe doesn't say when the rate was
15
+ * quoted. `undefined` when nothing was converted.
16
+ */
17
+ export function stripeRate(from, transaction) {
18
+ if (transaction.exchange_rate === undefined || transaction.exchange_rate === null) {
19
+ return undefined;
20
+ }
21
+ const charged = from.toUpperCase();
22
+ const balance = transaction.currency.toUpperCase();
23
+ return exchangeRate(charged, balance, transaction.exchange_rate, stripeInfo(transaction));
24
+ }
25
+ /** The exact rate Stripe applied: the balance transaction's gross amount ÷ the charged amount. */
26
+ export function stripeImpliedRate(charged, transaction) {
27
+ const settled = fromStripe(transaction.amount, transaction.currency.toUpperCase());
28
+ return impliedRate(charged, settled, stripeInfo(transaction));
29
+ }
30
+ //# sourceMappingURL=stripe-fx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe-fx.js","sourceRoot":"","sources":["../src/stripe-fx.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,WAAW,EAA4B,MAAM,WAAW,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAWzC,SAAS,UAAU,CAAC,WAAqC;IACvD,IAAI,WAAW,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;AAC1E,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CACxB,IAAU,EACV,WAAqC;IAErC,IAAI,WAAW,CAAC,aAAa,KAAK,SAAS,IAAI,WAAW,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;QAClF,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAqB,CAAC;IACtD,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,WAAW,EAAuB,CAAC;IACxE,OAAO,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,aAAa,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;AAC5F,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,iBAAiB,CAC/B,OAAoB,EACpB,WAAqC;IAErC,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACnF,OAAO,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Stripe's minor-unit view. Stripe uses ISO exponents except where documented otherwise
3
+ * (https://docs.stripe.com/currencies, encoded in data/stripe.json):
4
+ * - zero-decimal: BIF CLP DJF GNF JPY KMF KRW MGA PYG RWF VND VUV XAF XOF XPF (note MGA: ISO says
5
+ * 2)
6
+ * - ISK, UGX: sent as two-decimal values whose last two digits are 00 (5 ISK -> 500)
7
+ * - HUF, TWD: two decimals for charges, but payouts must be whole units (multiple of 100)
8
+ * - BHD JOD KWD OMR TND: three decimals, last digit must be 0 (UAE accounts only)
9
+ */
10
+ import { STRIPE_RULES } from './generated/stripe.js';
11
+ import { type CurrencyInput, type Money } from './money.js';
12
+ import type { RoundingMode } from './rounding.js';
13
+ export { STRIPE_RULES };
14
+ /** Whether Stripe supports the currency as a presentment currency. */
15
+ export declare const isStripeCurrency: (currency: string) => boolean;
16
+ /** The exponent Stripe's API uses for a currency (ISK 2, JPY 0, KWD 3, MGA 0). */
17
+ export declare const stripeExponent: (currency: string) => number;
18
+ /**
19
+ * Options for the Stripe amount: payout rules and how to round when Stripe needs a coarser amount.
20
+ */
21
+ export interface StripeAmountOptions {
22
+ /** Payout amounts have stricter rules (HUF, TWD: whole units). */
23
+ readonly payout?: boolean;
24
+ /** How to round when Stripe needs a coarser amount (default 'unnecessary': throw). */
25
+ readonly rounding?: RoundingMode;
26
+ }
27
+ /** The integer `amount` to send to Stripe, e.g. toStripeAmount(money('5', 'ISK')) === 500. */
28
+ export declare function toStripeAmount(money: Money, options?: StripeAmountOptions): number;
29
+ /** `{ amount, currency }` with Stripe's lowercase currency code, ready for a PaymentIntent. */
30
+ export declare function toStripe(money: Money, options?: StripeAmountOptions): {
31
+ amount: number;
32
+ currency: string;
33
+ };
34
+ /** Reads a Stripe amount (e.g. from a webhook): fromStripe(500, 'isk') is 5 ISK. */
35
+ export declare function fromStripe<C extends CurrencyInput>(amount: number | bigint, currency: C): Money<Uppercase<C>>;
36
+ //# sourceMappingURL=stripe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe.d.ts","sourceRoot":"","sources":["../src/stripe.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,YAAY,EAAE,CAAC;AAUxB,sEAAsE;AACtE,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,KAAG,OACA,CAAC;AAErD,kFAAkF;AAClF,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,KAAG,MAA2B,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,sFAAsF;IACtF,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;CAClC;AAED,8FAA8F;AAC9F,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,MAAM,CASlF;AAED,+FAA+F;AAC/F,wBAAgB,QAAQ,CACtB,KAAK,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,mBAAmB,GAC5B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAEtC;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,CAAC,SAAS,aAAa,EAChD,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,QAAQ,EAAE,CAAC,GACV,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAGrB"}
package/dist/stripe.js ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Stripe's minor-unit view. Stripe uses ISO exponents except where documented otherwise
3
+ * (https://docs.stripe.com/currencies, encoded in data/stripe.json):
4
+ * - zero-decimal: BIF CLP DJF GNF JPY KMF KRW MGA PYG RWF VND VUV XAF XOF XPF (note MGA: ISO says
5
+ * 2)
6
+ * - ISK, UGX: sent as two-decimal values whose last two digits are 00 (5 ISK -> 500)
7
+ * - HUF, TWD: two decimals for charges, but payouts must be whole units (multiple of 100)
8
+ * - BHD JOD KWD OMR TND: three decimals, last digit must be 0 (UAE accounts only)
9
+ */
10
+ import { STRIPE_RULES } from './generated/stripe.js';
11
+ import {} from './money.js';
12
+ import { fromProcessorAmount, toProcessorAmount } from './processor.js';
13
+ export { STRIPE_RULES };
14
+ function rule(currency) {
15
+ const found = STRIPE_RULES[currency.toUpperCase()];
16
+ if (!found) {
17
+ throw new RangeError(`${currency} is not a Stripe presentment currency`);
18
+ }
19
+ return found;
20
+ }
21
+ /** Whether Stripe supports the currency as a presentment currency. */
22
+ export const isStripeCurrency = (currency) => STRIPE_RULES[currency.toUpperCase()] !== undefined;
23
+ /** The exponent Stripe's API uses for a currency (ISK 2, JPY 0, KWD 3, MGA 0). */
24
+ export const stripeExponent = (currency) => rule(currency)[0];
25
+ /** The integer `amount` to send to Stripe, e.g. toStripeAmount(money('5', 'ISK')) === 500. */
26
+ export function toStripeAmount(money, options) {
27
+ const [exp, charge, payout] = rule(money.currency);
28
+ return toProcessorAmount('Stripe', money, exp, options?.payout ? payout : charge, options?.rounding ?? 'unnecessary');
29
+ }
30
+ /** `{ amount, currency }` with Stripe's lowercase currency code, ready for a PaymentIntent. */
31
+ export function toStripe(money, options) {
32
+ return { amount: toStripeAmount(money, options), currency: money.currency.toLowerCase() };
33
+ }
34
+ /** Reads a Stripe amount (e.g. from a webhook): fromStripe(500, 'isk') is 5 ISK. */
35
+ export function fromStripe(amount, currency) {
36
+ const code = currency.toUpperCase();
37
+ return fromProcessorAmount('Stripe', amount, code, rule(code)[0]);
38
+ }
39
+ //# sourceMappingURL=stripe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe.js","sourceRoot":"","sources":["../src/stripe.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAkC,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxE,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB,SAAS,IAAI,CAAC,QAAgB;IAC5B,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACnD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,UAAU,CAAC,GAAG,QAAQ,uCAAuC,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,sEAAsE;AACtE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,QAAgB,EAAW,EAAE,CAC5D,YAAY,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,KAAK,SAAS,CAAC;AAErD,kFAAkF;AAClF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAgB,EAAU,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AAY9E,8FAA8F;AAC9F,MAAM,UAAU,cAAc,CAAC,KAAY,EAAE,OAA6B;IACxE,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACnD,OAAO,iBAAiB,CACtB,QAAQ,EACR,KAAK,EACL,GAAG,EACH,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EACjC,OAAO,EAAE,QAAQ,IAAI,aAAa,CACnC,CAAC;AACJ,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,QAAQ,CACtB,KAAY,EACZ,OAA6B;IAE7B,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;AAC5F,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,UAAU,CACxB,MAAuB,EACvB,QAAW;IAEX,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,EAAkB,CAAC;IACpD,OAAO,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC"}
package/dist/tax.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { type Factor } from './decimal.js';
2
+ import { type Money } from './money.js';
3
+ import { type RoundingMode } from './rounding.js';
4
+ /**
5
+ * percent% of m: percentage(EUR 80, '12.5', 'halfEven') = EUR 10.00. Rounding defaults to
6
+ * 'halfExpand'.
7
+ */
8
+ export declare function percentage<C extends string>(money: Money<C>, percent: Factor, rounding?: RoundingMode): Money<C>;
9
+ /** A tax split: net + tax is always exactly gross. */
10
+ export interface TaxBreakdown<C extends string> {
11
+ readonly net: Money<C>;
12
+ readonly tax: Money<C>;
13
+ /** Always exactly net + tax. */
14
+ readonly gross: Money<C>;
15
+ }
16
+ /**
17
+ * Tax-exclusive: adds percent% tax to a net amount. addTax(EUR 10, '20', 'halfEven') -> net 10, tax
18
+ * 2, gross 12.
19
+ */
20
+ export declare function addTax<C extends string>(net: Money<C>, percent: Factor, rounding?: RoundingMode): TaxBreakdown<C>;
21
+ /**
22
+ * Tax-inclusive: extracts percent% tax contained in a gross amount (VAT extraction).
23
+ * net = gross / (1 + percent/100) rounded; tax = gross - net, so the parts always add up.
24
+ * extractTax(EUR 12, '20', 'halfEven') -> net 10.00, tax 2.00.
25
+ */
26
+ export declare function extractTax<C extends string>(gross: Money<C>, percent: Factor, rounding?: RoundingMode): TaxBreakdown<C>;
27
+ /** Price after a percent% discount; the discount amount is rounded with `rounding`. */
28
+ export declare function discount<C extends string>(price: Money<C>, percent: Factor, rounding?: RoundingMode): Money<C>;
29
+ //# sourceMappingURL=tax.d.ts.map