@ducatjs/money 0.0.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (398) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +51 -0
  3. package/README.md +1034 -3
  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 +520 -0
  9. package/data/checkout.json +230 -0
  10. package/data/cldr-cash.json +101 -0
  11. package/data/historic.json +78 -0
  12. package/data/iso4217-list-one.xml +1956 -0
  13. package/data/iso4217-list-three.xml +1185 -0
  14. package/data/klarna.json +95 -0
  15. package/data/paypal.json +65 -0
  16. package/data/stripe-fx.json +17 -0
  17. package/data/stripe.json +285 -0
  18. package/dist/adyen-fx.d.ts +25 -0
  19. package/dist/adyen-fx.d.ts.map +1 -0
  20. package/dist/adyen-fx.js +140 -0
  21. package/dist/adyen-fx.js.map +1 -0
  22. package/dist/adyen.d.ts +25 -0
  23. package/dist/adyen.d.ts.map +1 -0
  24. package/dist/adyen.js +34 -0
  25. package/dist/adyen.js.map +1 -0
  26. package/dist/allocate.d.ts +72 -0
  27. package/dist/allocate.d.ts.map +1 -0
  28. package/dist/allocate.js +314 -0
  29. package/dist/allocate.js.map +1 -0
  30. package/dist/bag.d.ts +58 -0
  31. package/dist/bag.d.ts.map +1 -0
  32. package/dist/bag.js +122 -0
  33. package/dist/bag.js.map +1 -0
  34. package/dist/boc.d.ts +31 -0
  35. package/dist/boc.d.ts.map +1 -0
  36. package/dist/boc.js +114 -0
  37. package/dist/boc.js.map +1 -0
  38. package/dist/boe.d.ts +33 -0
  39. package/dist/boe.d.ts.map +1 -0
  40. package/dist/boe.js +114 -0
  41. package/dist/boe.js.map +1 -0
  42. package/dist/braintree.d.ts +34 -0
  43. package/dist/braintree.d.ts.map +1 -0
  44. package/dist/braintree.js +45 -0
  45. package/dist/braintree.js.map +1 -0
  46. package/dist/cash.d.ts +64 -0
  47. package/dist/cash.d.ts.map +1 -0
  48. package/dist/cash.js +134 -0
  49. package/dist/cash.js.map +1 -0
  50. package/dist/checkout.d.ts +42 -0
  51. package/dist/checkout.d.ts.map +1 -0
  52. package/dist/checkout.js +52 -0
  53. package/dist/checkout.js.map +1 -0
  54. package/dist/convert.d.ts +52 -0
  55. package/dist/convert.d.ts.map +1 -0
  56. package/dist/convert.js +65 -0
  57. package/dist/convert.js.map +1 -0
  58. package/dist/currency.d.ts +22 -0
  59. package/dist/currency.d.ts.map +1 -0
  60. package/dist/currency.js +38 -0
  61. package/dist/currency.js.map +1 -0
  62. package/dist/decimal.d.ts +52 -0
  63. package/dist/decimal.d.ts.map +1 -0
  64. package/dist/decimal.js +251 -0
  65. package/dist/decimal.js.map +1 -0
  66. package/dist/drizzle/mysql.d.ts +105 -0
  67. package/dist/drizzle/mysql.d.ts.map +1 -0
  68. package/dist/drizzle/mysql.js +60 -0
  69. package/dist/drizzle/mysql.js.map +1 -0
  70. package/dist/drizzle/pg.d.ts +103 -0
  71. package/dist/drizzle/pg.d.ts.map +1 -0
  72. package/dist/drizzle/pg.js +60 -0
  73. package/dist/drizzle/pg.js.map +1 -0
  74. package/dist/drizzle/sqlite.d.ts +102 -0
  75. package/dist/drizzle/sqlite.d.ts.map +1 -0
  76. package/dist/drizzle/sqlite.js +59 -0
  77. package/dist/drizzle/sqlite.js.map +1 -0
  78. package/dist/ecb.d.ts +52 -0
  79. package/dist/ecb.d.ts.map +1 -0
  80. package/dist/ecb.js +285 -0
  81. package/dist/ecb.js.map +1 -0
  82. package/dist/exact.d.ts +62 -0
  83. package/dist/exact.d.ts.map +1 -0
  84. package/dist/exact.js +147 -0
  85. package/dist/exact.js.map +1 -0
  86. package/dist/fed.d.ts +35 -0
  87. package/dist/fed.d.ts.map +1 -0
  88. package/dist/fed.js +134 -0
  89. package/dist/fed.js.map +1 -0
  90. package/dist/fetch.d.ts +103 -0
  91. package/dist/fetch.d.ts.map +1 -0
  92. package/dist/fetch.js +222 -0
  93. package/dist/fetch.js.map +1 -0
  94. package/dist/fixed.d.ts +121 -0
  95. package/dist/fixed.d.ts.map +1 -0
  96. package/dist/fixed.js +420 -0
  97. package/dist/fixed.js.map +1 -0
  98. package/dist/fn.d.ts +34 -0
  99. package/dist/fn.d.ts.map +1 -0
  100. package/dist/fn.js +37 -0
  101. package/dist/fn.js.map +1 -0
  102. package/dist/format.d.ts +13 -0
  103. package/dist/format.d.ts.map +1 -0
  104. package/dist/format.js +90 -0
  105. package/dist/format.js.map +1 -0
  106. package/dist/generated/adyen.d.ts +3 -0
  107. package/dist/generated/adyen.d.ts.map +1 -0
  108. package/dist/generated/adyen.js +144 -0
  109. package/dist/generated/adyen.js.map +1 -0
  110. package/dist/generated/braintree.d.ts +3 -0
  111. package/dist/generated/braintree.d.ts.map +1 -0
  112. package/dist/generated/braintree.js +140 -0
  113. package/dist/generated/braintree.js.map +1 -0
  114. package/dist/generated/cash.d.ts +14 -0
  115. package/dist/generated/cash.d.ts.map +1 -0
  116. package/dist/generated/cash.js +465 -0
  117. package/dist/generated/cash.js.map +1 -0
  118. package/dist/generated/checkout.d.ts +3 -0
  119. package/dist/generated/checkout.d.ts.map +1 -0
  120. package/dist/generated/checkout.js +151 -0
  121. package/dist/generated/checkout.js.map +1 -0
  122. package/dist/generated/historic.d.ts +337 -0
  123. package/dist/generated/historic.d.ts.map +1 -0
  124. package/dist/generated/historic.js +66 -0
  125. package/dist/generated/historic.js.map +1 -0
  126. package/dist/generated/iso4217.d.ts +174 -0
  127. package/dist/generated/iso4217.d.ts.map +1 -0
  128. package/dist/generated/iso4217.js +175 -0
  129. package/dist/generated/iso4217.js.map +1 -0
  130. package/dist/generated/kernel.d.ts +17 -0
  131. package/dist/generated/kernel.d.ts.map +1 -0
  132. package/dist/generated/kernel.js +208 -0
  133. package/dist/generated/kernel.js.map +1 -0
  134. package/dist/generated/klarna.d.ts +3 -0
  135. package/dist/generated/klarna.d.ts.map +1 -0
  136. package/dist/generated/klarna.js +21 -0
  137. package/dist/generated/klarna.js.map +1 -0
  138. package/dist/generated/paypal.d.ts +3 -0
  139. package/dist/generated/paypal.d.ts.map +1 -0
  140. package/dist/generated/paypal.js +31 -0
  141. package/dist/generated/paypal.js.map +1 -0
  142. package/dist/generated/stripe.d.ts +8 -0
  143. package/dist/generated/stripe.d.ts.map +1 -0
  144. package/dist/generated/stripe.js +150 -0
  145. package/dist/generated/stripe.js.map +1 -0
  146. package/dist/historic.d.ts +40 -0
  147. package/dist/historic.d.ts.map +1 -0
  148. package/dist/historic.js +43 -0
  149. package/dist/historic.js.map +1 -0
  150. package/dist/history.d.ts +96 -0
  151. package/dist/history.d.ts.map +1 -0
  152. package/dist/history.js +213 -0
  153. package/dist/history.js.map +1 -0
  154. package/dist/hmrc.d.ts +28 -0
  155. package/dist/hmrc.d.ts.map +1 -0
  156. package/dist/hmrc.js +108 -0
  157. package/dist/hmrc.js.map +1 -0
  158. package/dist/index.d.ts +11 -0
  159. package/dist/index.d.ts.map +1 -0
  160. package/dist/index.js +11 -0
  161. package/dist/index.js.map +1 -0
  162. package/dist/klarna.d.ts +31 -0
  163. package/dist/klarna.d.ts.map +1 -0
  164. package/dist/klarna.js +38 -0
  165. package/dist/klarna.js.map +1 -0
  166. package/dist/kysely.d.ts +120 -0
  167. package/dist/kysely.d.ts.map +1 -0
  168. package/dist/kysely.js +309 -0
  169. package/dist/kysely.js.map +1 -0
  170. package/dist/money.d.ts +302 -0
  171. package/dist/money.d.ts.map +1 -0
  172. package/dist/money.js +703 -0
  173. package/dist/money.js.map +1 -0
  174. package/dist/parse.d.ts +55 -0
  175. package/dist/parse.d.ts.map +1 -0
  176. package/dist/parse.js +419 -0
  177. package/dist/parse.js.map +1 -0
  178. package/dist/paypal.d.ts +41 -0
  179. package/dist/paypal.d.ts.map +1 -0
  180. package/dist/paypal.js +58 -0
  181. package/dist/paypal.js.map +1 -0
  182. package/dist/pg.d.ts +100 -0
  183. package/dist/pg.d.ts.map +1 -0
  184. package/dist/pg.js +343 -0
  185. package/dist/pg.js.map +1 -0
  186. package/dist/plugins/adyen.d.ts +15 -0
  187. package/dist/plugins/adyen.d.ts.map +1 -0
  188. package/dist/plugins/adyen.js +10 -0
  189. package/dist/plugins/adyen.js.map +1 -0
  190. package/dist/plugins/all.d.ts +29 -0
  191. package/dist/plugins/all.d.ts.map +1 -0
  192. package/dist/plugins/all.js +30 -0
  193. package/dist/plugins/all.js.map +1 -0
  194. package/dist/plugins/allocate.d.ts +20 -0
  195. package/dist/plugins/allocate.d.ts.map +1 -0
  196. package/dist/plugins/allocate.js +26 -0
  197. package/dist/plugins/allocate.js.map +1 -0
  198. package/dist/plugins/braintree.d.ts +12 -0
  199. package/dist/plugins/braintree.d.ts.map +1 -0
  200. package/dist/plugins/braintree.js +12 -0
  201. package/dist/plugins/braintree.js.map +1 -0
  202. package/dist/plugins/cash.d.ts +19 -0
  203. package/dist/plugins/cash.d.ts.map +1 -0
  204. package/dist/plugins/cash.js +16 -0
  205. package/dist/plugins/cash.js.map +1 -0
  206. package/dist/plugins/checkout.d.ts +21 -0
  207. package/dist/plugins/checkout.d.ts.map +1 -0
  208. package/dist/plugins/checkout.js +16 -0
  209. package/dist/plugins/checkout.js.map +1 -0
  210. package/dist/plugins/convert.d.ts +19 -0
  211. package/dist/plugins/convert.d.ts.map +1 -0
  212. package/dist/plugins/convert.js +13 -0
  213. package/dist/plugins/convert.js.map +1 -0
  214. package/dist/plugins/exact.d.ts +15 -0
  215. package/dist/plugins/exact.d.ts.map +1 -0
  216. package/dist/plugins/exact.js +13 -0
  217. package/dist/plugins/exact.js.map +1 -0
  218. package/dist/plugins/fixed.d.ts +17 -0
  219. package/dist/plugins/fixed.d.ts.map +1 -0
  220. package/dist/plugins/fixed.js +13 -0
  221. package/dist/plugins/fixed.js.map +1 -0
  222. package/dist/plugins/format.d.ts +9 -0
  223. package/dist/plugins/format.d.ts.map +1 -0
  224. package/dist/plugins/format.js +16 -0
  225. package/dist/plugins/format.js.map +1 -0
  226. package/dist/plugins/klarna.d.ts +10 -0
  227. package/dist/plugins/klarna.d.ts.map +1 -0
  228. package/dist/plugins/klarna.js +10 -0
  229. package/dist/plugins/klarna.js.map +1 -0
  230. package/dist/plugins/paypal.d.ts +14 -0
  231. package/dist/plugins/paypal.d.ts.map +1 -0
  232. package/dist/plugins/paypal.js +16 -0
  233. package/dist/plugins/paypal.js.map +1 -0
  234. package/dist/plugins/prisma.d.ts +8 -0
  235. package/dist/plugins/prisma.d.ts.map +1 -0
  236. package/dist/plugins/prisma.js +13 -0
  237. package/dist/plugins/prisma.js.map +1 -0
  238. package/dist/plugins/stripe.d.ts +14 -0
  239. package/dist/plugins/stripe.d.ts.map +1 -0
  240. package/dist/plugins/stripe.js +16 -0
  241. package/dist/plugins/stripe.js.map +1 -0
  242. package/dist/plugins/tax.d.ts +20 -0
  243. package/dist/plugins/tax.d.ts.map +1 -0
  244. package/dist/plugins/tax.js +18 -0
  245. package/dist/plugins/tax.js.map +1 -0
  246. package/dist/policy.d.ts +34 -0
  247. package/dist/policy.d.ts.map +1 -0
  248. package/dist/policy.js +90 -0
  249. package/dist/policy.js.map +1 -0
  250. package/dist/prisma.d.ts +77 -0
  251. package/dist/prisma.d.ts.map +1 -0
  252. package/dist/prisma.js +76 -0
  253. package/dist/prisma.js.map +1 -0
  254. package/dist/processor.d.ts +28 -0
  255. package/dist/processor.d.ts.map +1 -0
  256. package/dist/processor.js +118 -0
  257. package/dist/processor.js.map +1 -0
  258. package/dist/provider.d.ts +103 -0
  259. package/dist/provider.d.ts.map +1 -0
  260. package/dist/provider.js +280 -0
  261. package/dist/provider.js.map +1 -0
  262. package/dist/rate-text.d.ts +38 -0
  263. package/dist/rate-text.d.ts.map +1 -0
  264. package/dist/rate-text.js +143 -0
  265. package/dist/rate-text.js.map +1 -0
  266. package/dist/rate.d.ts +175 -0
  267. package/dist/rate.d.ts.map +1 -0
  268. package/dist/rate.js +371 -0
  269. package/dist/rate.js.map +1 -0
  270. package/dist/rounding-error.d.ts +9 -0
  271. package/dist/rounding-error.d.ts.map +1 -0
  272. package/dist/rounding-error.js +9 -0
  273. package/dist/rounding-error.js.map +1 -0
  274. package/dist/rounding.d.ts +50 -0
  275. package/dist/rounding.d.ts.map +1 -0
  276. package/dist/rounding.js +87 -0
  277. package/dist/rounding.js.map +1 -0
  278. package/dist/schema.d.ts +43 -0
  279. package/dist/schema.d.ts.map +1 -0
  280. package/dist/schema.js +77 -0
  281. package/dist/schema.js.map +1 -0
  282. package/dist/sql.d.ts +54 -0
  283. package/dist/sql.d.ts.map +1 -0
  284. package/dist/sql.js +131 -0
  285. package/dist/sql.js.map +1 -0
  286. package/dist/stripe-fx.d.ts +22 -0
  287. package/dist/stripe-fx.d.ts.map +1 -0
  288. package/dist/stripe-fx.js +30 -0
  289. package/dist/stripe-fx.js.map +1 -0
  290. package/dist/stripe.d.ts +36 -0
  291. package/dist/stripe.d.ts.map +1 -0
  292. package/dist/stripe.js +39 -0
  293. package/dist/stripe.js.map +1 -0
  294. package/dist/tax.d.ts +33 -0
  295. package/dist/tax.d.ts.map +1 -0
  296. package/dist/tax.js +55 -0
  297. package/dist/tax.js.map +1 -0
  298. package/dist/testing/arbitraries.d.ts +43 -0
  299. package/dist/testing/arbitraries.d.ts.map +1 -0
  300. package/dist/testing/arbitraries.js +98 -0
  301. package/dist/testing/arbitraries.js.map +1 -0
  302. package/dist/testing/index.d.ts +67 -0
  303. package/dist/testing/index.d.ts.map +1 -0
  304. package/dist/testing/index.js +159 -0
  305. package/dist/testing/index.js.map +1 -0
  306. package/dist/testing/jest.d.ts +2 -0
  307. package/dist/testing/jest.d.ts.map +1 -0
  308. package/dist/testing/jest.js +15 -0
  309. package/dist/testing/jest.js.map +1 -0
  310. package/dist/testing/vitest.d.ts +7 -0
  311. package/dist/testing/vitest.d.ts.map +1 -0
  312. package/dist/testing/vitest.js +12 -0
  313. package/dist/testing/vitest.js.map +1 -0
  314. package/dist/valibot.d.ts +33 -0
  315. package/dist/valibot.d.ts.map +1 -0
  316. package/dist/valibot.js +61 -0
  317. package/dist/valibot.js.map +1 -0
  318. package/dist/zod.d.ts +36 -0
  319. package/dist/zod.d.ts.map +1 -0
  320. package/dist/zod.js +58 -0
  321. package/dist/zod.js.map +1 -0
  322. package/package.json +280 -5
  323. package/src/adyen-fx.ts +175 -0
  324. package/src/adyen.ts +46 -0
  325. package/src/allocate.ts +393 -0
  326. package/src/bag.ts +170 -0
  327. package/src/boc.ts +136 -0
  328. package/src/boe.ts +131 -0
  329. package/src/braintree.ts +71 -0
  330. package/src/cash.ts +183 -0
  331. package/src/checkout.ts +77 -0
  332. package/src/convert.ts +177 -0
  333. package/src/currency.ts +61 -0
  334. package/src/decimal.ts +283 -0
  335. package/src/drizzle/mysql.ts +78 -0
  336. package/src/drizzle/pg.ts +78 -0
  337. package/src/drizzle/sqlite.ts +70 -0
  338. package/src/ecb.ts +328 -0
  339. package/src/exact.ts +182 -0
  340. package/src/fed.ts +146 -0
  341. package/src/fetch.ts +306 -0
  342. package/src/fixed.ts +558 -0
  343. package/src/fn.ts +94 -0
  344. package/src/format.ts +106 -0
  345. package/src/generated/adyen.ts +144 -0
  346. package/src/generated/braintree.ts +140 -0
  347. package/src/generated/cash.ts +478 -0
  348. package/src/generated/checkout.ts +151 -0
  349. package/src/generated/historic.ts +67 -0
  350. package/src/generated/iso4217.ts +179 -0
  351. package/src/generated/kernel.ts +230 -0
  352. package/src/generated/klarna.ts +21 -0
  353. package/src/generated/paypal.ts +31 -0
  354. package/src/generated/stripe.ts +150 -0
  355. package/src/historic.ts +77 -0
  356. package/src/history.ts +333 -0
  357. package/src/hmrc.ts +125 -0
  358. package/src/index.ts +41 -0
  359. package/src/klarna.ts +54 -0
  360. package/src/kysely.ts +588 -0
  361. package/src/money.ts +979 -0
  362. package/src/parse.ts +511 -0
  363. package/src/paypal.ts +93 -0
  364. package/src/pg.ts +410 -0
  365. package/src/plugins/adyen.ts +25 -0
  366. package/src/plugins/all.ts +57 -0
  367. package/src/plugins/allocate.ts +53 -0
  368. package/src/plugins/braintree.ts +23 -0
  369. package/src/plugins/cash.ts +33 -0
  370. package/src/plugins/checkout.ts +32 -0
  371. package/src/plugins/convert.ts +49 -0
  372. package/src/plugins/exact.ts +23 -0
  373. package/src/plugins/fixed.ts +28 -0
  374. package/src/plugins/format.ts +28 -0
  375. package/src/plugins/klarna.ts +21 -0
  376. package/src/plugins/paypal.ts +29 -0
  377. package/src/plugins/prisma.ts +24 -0
  378. package/src/plugins/stripe.ts +29 -0
  379. package/src/plugins/tax.ts +40 -0
  380. package/src/policy.ts +124 -0
  381. package/src/prisma.ts +199 -0
  382. package/src/processor.ts +181 -0
  383. package/src/provider.ts +389 -0
  384. package/src/rate-text.ts +151 -0
  385. package/src/rate.ts +546 -0
  386. package/src/rounding-error.ts +8 -0
  387. package/src/rounding.ts +122 -0
  388. package/src/schema.ts +119 -0
  389. package/src/sql.ts +178 -0
  390. package/src/stripe-fx.ts +49 -0
  391. package/src/stripe.ts +69 -0
  392. package/src/tax.ts +102 -0
  393. package/src/testing/arbitraries.ts +134 -0
  394. package/src/testing/index.ts +247 -0
  395. package/src/testing/jest.ts +18 -0
  396. package/src/testing/vitest.ts +18 -0
  397. package/src/valibot.ts +87 -0
  398. package/src/zod.ts +78 -0
package/dist/exact.js ADDED
@@ -0,0 +1,147 @@
1
+ // ExactMoney: an exact rational amount for chains of arithmetic that should round once, at the end.
2
+ // It has no formatting, JSON or minor units on purpose: it must be rounded back to Money before it
3
+ // leaves the process.
4
+ import { currencyExponent } from './currency.js';
5
+ import { pow10n, ratio } from './decimal.js';
6
+ import { CurrencyMismatchError, make } from './money.js';
7
+ import { DEFAULT_ROUNDING, divideAndRoundBigint } from './rounding.js';
8
+ /**
9
+ * An exact amount of `currency`: num/den major units, reduced, den > 0. Build it with `exact()`.
10
+ */
11
+ export class ExactMoney {
12
+ currency;
13
+ num;
14
+ den;
15
+ /** @internal use exact() */
16
+ constructor(currency, numerator, denominator) {
17
+ const reduced = ratio(numerator, denominator); // reduces and makes the denominator positive
18
+ this.currency = currency;
19
+ this.num = reduced.num;
20
+ this.den = reduced.den;
21
+ }
22
+ /** this + other, exactly. */
23
+ add(other) {
24
+ assertSameCurrency(this, other);
25
+ const numerator = this.num * other.den + other.num * this.den;
26
+ return new ExactMoney(this.currency, numerator, this.den * other.den);
27
+ }
28
+ /** this − other, exactly. */
29
+ subtract(other) {
30
+ return this.add(other.negate());
31
+ }
32
+ /** −this. */
33
+ negate() {
34
+ return new ExactMoney(this.currency, -this.num, this.den);
35
+ }
36
+ /** this × factor, exactly. */
37
+ multiply(factor) {
38
+ const factorRatio = ratio(factor);
39
+ return new ExactMoney(this.currency, this.num * factorRatio.num, this.den * factorRatio.den);
40
+ }
41
+ /** this ÷ divisor, exactly. */
42
+ divide(divisor) {
43
+ const divisorRatio = ratio(divisor);
44
+ if (divisorRatio.num === 0n) {
45
+ throw new RangeError('Division by zero');
46
+ }
47
+ return new ExactMoney(this.currency, this.num * divisorRatio.den, this.den * divisorRatio.num);
48
+ }
49
+ /** Converts exactly at `rate`; nothing is rounded. */
50
+ convert(rate) {
51
+ if (rate.from !== this.currency) {
52
+ throw new CurrencyMismatchError(rate.from, this.currency);
53
+ }
54
+ return new ExactMoney(rate.to, this.num * rate.ratio.num, this.den * rate.ratio.den);
55
+ }
56
+ /** −1, 0 or 1. */
57
+ compare(other) {
58
+ assertSameCurrency(this, other);
59
+ // Both denominators are positive, so cross-multiplying keeps the order.
60
+ const left = this.num * other.den;
61
+ const right = other.num * this.den;
62
+ if (left < right) {
63
+ return -1;
64
+ }
65
+ if (left > right) {
66
+ return 1;
67
+ }
68
+ return 0;
69
+ }
70
+ /** Same currency and the same exact value. */
71
+ equals(other) {
72
+ return this.currency === other.currency && this.num === other.num && this.den === other.den;
73
+ }
74
+ /** −1, 0 or 1. */
75
+ sign() {
76
+ if (this.num > 0n) {
77
+ return 1;
78
+ }
79
+ if (this.num < 0n) {
80
+ return -1;
81
+ }
82
+ return 0;
83
+ }
84
+ /** Whether the amount is zero. */
85
+ isZero() {
86
+ return this.num === 0n;
87
+ }
88
+ /** Whether the amount fits `scale` (default: the currency's exponent) without rounding. */
89
+ isExact(scale = currencyExponent(this.currency)) {
90
+ return (this.num * pow10n(scale)) % this.den === 0n;
91
+ }
92
+ /** Rounds once to `scale` (default: the currency's exponent). */
93
+ round(rounding = DEFAULT_ROUNDING, scale = currencyExponent(this.currency)) {
94
+ const minor = divideAndRoundBigint(this.num * pow10n(scale), this.den, rounding);
95
+ return make(this.currency, minor, scale);
96
+ }
97
+ /** Rounds once and reports the difference, for booking it (e.g. an "FX rounding" ledger line). */
98
+ roundWithResidual(rounding = DEFAULT_ROUNDING, scale) {
99
+ const value = this.round(rounding, scale);
100
+ const residual = this.subtract(exact(value));
101
+ return { value, residual };
102
+ }
103
+ /** "10/3 EUR", for debugging only. */
104
+ toString() {
105
+ if (this.den === 1n) {
106
+ return `${this.num} ${this.currency}`;
107
+ }
108
+ return `${this.num}/${this.den} ${this.currency}`;
109
+ }
110
+ /** Blocks `a + b` and `a > b`: round() first. */
111
+ [Symbol.toPrimitive](hint) {
112
+ if (hint === 'string') {
113
+ return this.toString();
114
+ }
115
+ throw new TypeError('ExactMoney cannot be converted to a number; round() it first');
116
+ }
117
+ }
118
+ function assertSameCurrency(money, other) {
119
+ if (money.currency !== other.currency) {
120
+ throw new CurrencyMismatchError(money.currency, other.currency);
121
+ }
122
+ }
123
+ /**
124
+ * The exact value of `money`, for chained arithmetic: `exact(price).divide(3).round('halfEven')`.
125
+ */
126
+ export function exact(money) {
127
+ return new ExactMoney(money.currency, BigInt(money.minor), pow10n(money.scale));
128
+ }
129
+ /**
130
+ * The exact sum (zero of `currency` for an empty list). Round it once for a batch's rounding line.
131
+ */
132
+ export function sumExact(items, currency) {
133
+ const sumCurrency = currency ?? items[0]?.currency;
134
+ if (sumCurrency === undefined) {
135
+ throw new RangeError('sumExact of an empty list needs a currency');
136
+ }
137
+ let sum = new ExactMoney(sumCurrency, 0n, 1n);
138
+ for (const item of items) {
139
+ sum = sum.add(item);
140
+ }
141
+ return sum;
142
+ }
143
+ /** Whether `value` is an ExactMoney. */
144
+ export function isExactMoney(value) {
145
+ return value instanceof ExactMoney;
146
+ }
147
+ //# sourceMappingURL=exact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exact.js","sourceRoot":"","sources":["../src/exact.ts"],"names":[],"mappings":"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"}
package/dist/fed.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ import type { RateTable } from './provider.js';
2
+ /**
3
+ * Parses the Federal Reserve Board's H.10 daily foreign exchange rates, as CSV from its Data
4
+ * Download Program (DDP), into one USD-based table per day, newest first. These are the noon
5
+ * buying rates in New York, certified by the New York Fed for customs purposes, with `source:
6
+ * 'FRB'` and `type: 'noon buying'`, back to 1971.
7
+ *
8
+ * - **Direction.** H.10 quotes "currency units per U.S. dollar except as noted": AUD, EUR, NZD and
9
+ * GBP (series `RXI$US_N.B.*`) are USD per unit. Those four are stored as exact inverses, so
10
+ * every table is USD → currency, and the published quote stays on the rate:
11
+ * `rate('USD', 'EUR').inverse().toString()` is '1.1750' again.
12
+ * - **Holidays** are rows of `ND` ("no data"); such a day is left out, so a lookback goes on to
13
+ * the day before, and a file of holidays only gives []. A single `ND` cell is a missing value.
14
+ * A file with no observation rows at all throws, as it is more likely cut short than empty.
15
+ * - **Strict:** each column's Unit, Multiplier and Currency must be "Currency", 1 and the code
16
+ * its series ID stands for (EU is EUR), a currency can't have two columns, and two different
17
+ * values for one day throw.
18
+ * - **Digits.** H.10 publishes four decimals and keeps trailing zeros; they are kept.
19
+ * - **Codes.** The Fed's column codes are used as they are, so the bolivar is `VEB`, as the Fed
20
+ * labels it.
21
+ * - **Publication.** The release is weekly (Mondays, 4:15 p.m. ET) and covers the week before,
22
+ * so a Tuesday's rate is known only up to six days later. The CSV doesn't say when each row was
23
+ * released, so no `availableAt` is set; give each table one yourself if a backtest needs it.
24
+ *
25
+ * **Where the data comes from.** The DDP at federalreserve.gov/datadownload (release H10, CSV,
26
+ * "series in columns", with labels) serves these files today. The Board is retiring the DDP: the
27
+ * "Build Your Package" feature goes in November 2026 and the rest later, and it points users to
28
+ * FRED (fred.stlouisfed.org) instead. FRED needs an API key, and its terms forbid storing or
29
+ * caching its content and any commercial use without consent, which is why ducat has no H.10
30
+ * fetcher. Board data itself is public domain ("please cite to the Board as the source").
31
+ *
32
+ * const tables = parseFedH10Csv(await readFile('FRB_H10.csv', 'utf8'));
33
+ */
34
+ export declare function parseFedH10Csv(csv: string): RateTable[];
35
+ //# sourceMappingURL=fed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fed.d.ts","sourceRoot":"","sources":["../src/fed.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAqC/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,CAyEvD"}
package/dist/fed.js ADDED
@@ -0,0 +1,134 @@
1
+ // `@ducatjs/money/convert/fed`: an offline parser for the Federal Reserve Board's H.10 foreign
2
+ // exchange rates. Parser only, no fetcher: see parseFedH10Csv for where the files come from.
3
+ import { quotedTable } from './history.js';
4
+ import { addDayRate, csvRows, isCalendarDay, newestFirst, readRateCell } from './rate-text.js';
5
+ // RXI$US_N.B.EU is USD per euro; RXI_N.B.JA is yen per USD.
6
+ const SERIES = /^(?:H10\/H10\/)?RXI(\$US)?_N\.B\.([A-Z]{2,3})$/;
7
+ // The country codes in H.10 series IDs, and the currency each one's column must carry.
8
+ const FED_COUNTRIES = {
9
+ AL: 'AUD',
10
+ EU: 'EUR',
11
+ NZ: 'NZD',
12
+ UK: 'GBP',
13
+ BZ: 'BRL',
14
+ CA: 'CAD',
15
+ CH: 'CNY',
16
+ DN: 'DKK',
17
+ HK: 'HKD',
18
+ IN: 'INR',
19
+ JA: 'JPY',
20
+ MA: 'MYR',
21
+ MX: 'MXN',
22
+ NO: 'NOK',
23
+ SF: 'ZAR',
24
+ SI: 'SGD',
25
+ KO: 'KRW',
26
+ SL: 'LKR',
27
+ SD: 'SEK',
28
+ SZ: 'CHF',
29
+ TA: 'TWD',
30
+ TH: 'THB',
31
+ VES: 'VEB',
32
+ };
33
+ function notH10(why) {
34
+ throw new SyntaxError(`Not a Federal Reserve H.10 CSV file: ${why}`);
35
+ }
36
+ /**
37
+ * Parses the Federal Reserve Board's H.10 daily foreign exchange rates, as CSV from its Data
38
+ * Download Program (DDP), into one USD-based table per day, newest first. These are the noon
39
+ * buying rates in New York, certified by the New York Fed for customs purposes, with `source:
40
+ * 'FRB'` and `type: 'noon buying'`, back to 1971.
41
+ *
42
+ * - **Direction.** H.10 quotes "currency units per U.S. dollar except as noted": AUD, EUR, NZD and
43
+ * GBP (series `RXI$US_N.B.*`) are USD per unit. Those four are stored as exact inverses, so
44
+ * every table is USD → currency, and the published quote stays on the rate:
45
+ * `rate('USD', 'EUR').inverse().toString()` is '1.1750' again.
46
+ * - **Holidays** are rows of `ND` ("no data"); such a day is left out, so a lookback goes on to
47
+ * the day before, and a file of holidays only gives []. A single `ND` cell is a missing value.
48
+ * A file with no observation rows at all throws, as it is more likely cut short than empty.
49
+ * - **Strict:** each column's Unit, Multiplier and Currency must be "Currency", 1 and the code
50
+ * its series ID stands for (EU is EUR), a currency can't have two columns, and two different
51
+ * values for one day throw.
52
+ * - **Digits.** H.10 publishes four decimals and keeps trailing zeros; they are kept.
53
+ * - **Codes.** The Fed's column codes are used as they are, so the bolivar is `VEB`, as the Fed
54
+ * labels it.
55
+ * - **Publication.** The release is weekly (Mondays, 4:15 p.m. ET) and covers the week before,
56
+ * so a Tuesday's rate is known only up to six days later. The CSV doesn't say when each row was
57
+ * released, so no `availableAt` is set; give each table one yourself if a backtest needs it.
58
+ *
59
+ * **Where the data comes from.** The DDP at federalreserve.gov/datadownload (release H10, CSV,
60
+ * "series in columns", with labels) serves these files today. The Board is retiring the DDP: the
61
+ * "Build Your Package" feature goes in November 2026 and the rest later, and it points users to
62
+ * FRED (fred.stlouisfed.org) instead. FRED needs an API key, and its terms forbid storing or
63
+ * caching its content and any commercial use without consent, which is why ducat has no H.10
64
+ * fetcher. Board data itself is public domain ("please cite to the Board as the source").
65
+ *
66
+ * const tables = parseFedH10Csv(await readFile('FRB_H10.csv', 'utf8'));
67
+ */
68
+ export function parseFedH10Csv(csv) {
69
+ const rows = csvRows(csv.charCodeAt(0) === 0xfeff ? csv.slice(1) : csv);
70
+ const series = rows.find((cells) => cells[0]?.trim() === 'Time Period') ?? notH10('no "Time Period" row');
71
+ const labelled = (label) => {
72
+ const row = rows.find((cells) => cells[0]?.trim() === label);
73
+ if (row === undefined || row.length !== series.length) {
74
+ notH10(`no "${label}" row matching the columns`);
75
+ }
76
+ return row;
77
+ };
78
+ const currencies = labelled('Currency:');
79
+ const multipliers = labelled('Multiplier:');
80
+ const units = labelled('Unit:');
81
+ // Each column: its currency, and whether it is quoted as USD per unit (to be inverted).
82
+ const columns = [];
83
+ for (let i = 1; i < series.length; i++) {
84
+ const id = SERIES.exec(series[i].trim());
85
+ const currency = currencies[i].trim();
86
+ if (id === null || !/^[A-Z]{3}$/.test(currency) || currency === 'USD') {
87
+ notH10(`column ${i + 1} is ${series[i]} for ${currency}`);
88
+ }
89
+ // The series ID names the country; it must agree with the currency code beside it.
90
+ const expected = FED_COUNTRIES[id[2]];
91
+ if (expected !== undefined && expected !== currency) {
92
+ notH10(`series ${series[i]} is ${expected}, not ${currency}`);
93
+ }
94
+ if (columns.some((column) => column.currency === currency)) {
95
+ notH10(`${currency} has two columns`);
96
+ }
97
+ // Other units or a multiplier other than 1 would make the published number mean something else.
98
+ if (units[i].trim() !== 'Currency' || multipliers[i].trim() !== '1') {
99
+ throw new SyntaxError(`Unsupported H.10 unit ${units[i]} × ${multipliers[i]} for ${currency}`);
100
+ }
101
+ columns.push({ currency, inverted: id[1] !== undefined });
102
+ }
103
+ const observations = rows.slice(rows.indexOf(series) + 1);
104
+ if (observations.length === 0) {
105
+ notH10('no observations');
106
+ }
107
+ const days = new Map();
108
+ for (const cells of observations) {
109
+ const day = cells[0].trim();
110
+ if (!isCalendarDay(day) || cells.length !== series.length) {
111
+ throw new SyntaxError(`Malformed H.10 row: ${cells.join(',')}`);
112
+ }
113
+ columns.forEach(({ currency }, i) => {
114
+ addDayRate(days, day, currency, readRateCell(cells[i + 1], `${currency} on ${day}`));
115
+ });
116
+ }
117
+ const tables = [];
118
+ for (const [day, published] of days) {
119
+ const rates = {};
120
+ let quoted = 0;
121
+ for (const { currency, inverted } of columns) {
122
+ const rate = published[currency] ?? null;
123
+ rates[currency] = rate === null ? null : { rate, inverted };
124
+ if (rate !== null) {
125
+ quoted++;
126
+ }
127
+ }
128
+ if (quoted > 0) {
129
+ tables.push(quotedTable({ base: 'USD', source: 'FRB', type: 'noon buying', asOf: day, rates }));
130
+ }
131
+ }
132
+ return newestFirst(tables);
133
+ }
134
+ //# sourceMappingURL=fed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fed.js","sourceRoot":"","sources":["../src/fed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import type { RateTable } from './provider.js';
2
+ /**
3
+ * The part of the platform AbortSignal used here. Declared structurally so the library needs no DOM
4
+ * or Node types; any real AbortSignal fits.
5
+ */
6
+ export interface AbortSignalLike {
7
+ readonly aborted: boolean;
8
+ readonly reason?: unknown;
9
+ addEventListener(type: 'abort', listener: () => void, options?: {
10
+ once?: boolean;
11
+ }): void;
12
+ removeEventListener(type: 'abort', listener: () => void): void;
13
+ }
14
+ /** The subset of the platform fetch used here (Node 24+, Deno, Bun, browsers, workers). */
15
+ export type Fetch = (url: string, init?: {
16
+ signal?: AbortSignalLike;
17
+ headers?: Record<string, string>;
18
+ }) => Promise<{
19
+ ok: boolean;
20
+ status: number;
21
+ text(): Promise<string>;
22
+ }>;
23
+ /** The ECB's eurofxref files: the latest day, the last 90 days, and the full history since 1999. */
24
+ export declare const ECB_URLS: {
25
+ readonly daily: "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";
26
+ readonly '90d': "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml";
27
+ readonly all: "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.xml";
28
+ };
29
+ /** Options for fetching ECB reference rates. */
30
+ export interface FetchEcbOptions {
31
+ /** 'daily' (default): the latest day; '90d': the last 90 days; 'all': since 1999 (several MB). */
32
+ readonly history?: keyof typeof ECB_URLS;
33
+ /** An AbortSignal to cancel the request; it is combined with the timeout. */
34
+ readonly signal?: AbortSignalLike;
35
+ /**
36
+ * Milliseconds before the request is aborted (default 30 000; 0 for no timeout). It needs the
37
+ * platform's AbortSignal.timeout, which Node 24, Deno, Bun and current browsers all have.
38
+ */
39
+ readonly timeout?: number;
40
+ /** A fetch to use instead of the global one (for proxies, retries or tests). */
41
+ readonly fetch?: Fetch;
42
+ }
43
+ /** Thrown when the HTTP request completes without a 2xx status. */
44
+ export declare class FetchRatesError extends Error {
45
+ readonly status: number;
46
+ readonly url: string;
47
+ constructor(url: string, status: number);
48
+ }
49
+ /**
50
+ * Fetches the ECB euro reference rates and parses them with parseEcbXml: EUR-based tables,
51
+ * newest first. The ECB publishes once per TARGET working day at about 16:00 CET and states
52
+ * that its rates are for information purposes only.
53
+ *
54
+ * const [latest] = await fetchEcb(); // the RateTable for the last published day
55
+ * const hist = snapshotsFrom(await fetchEcb({ history: '90d' }));
56
+ */
57
+ export declare function fetchEcb(options?: FetchEcbOptions): Promise<RateTable[]>;
58
+ /** The ECB Data Portal's SDMX REST API (no key, and CORS-enabled, so it works in browsers). */
59
+ export declare const ECB_DATA_API = "https://data-api.ecb.europa.eu/service/data/EXR";
60
+ /** Options for fetching a date range of ECB reference rates. */
61
+ export interface FetchEcbRangeOptions extends Omit<FetchEcbOptions, 'history'> {
62
+ /** The first day ('1999-01-04' at the earliest). */
63
+ readonly start: string | Date;
64
+ /** The last day (default: the latest published). */
65
+ readonly end?: string | Date;
66
+ /** ISO codes to fetch (default: every currency, including discontinued series). */
67
+ readonly currencies?: readonly string[];
68
+ }
69
+ /**
70
+ * Fetches a date range of ECB euro reference rates from the ECB Data Portal and parses them with
71
+ * parseEcbSdmxCsv: EUR-based tables, one per TARGET working day, newest first, with the published
72
+ * digits. It makes two requests: the series' DECIMALS (small) and the observations without
73
+ * attributes, which keeps the full history since 1999 to about 15 MB. Give a few days before the
74
+ * first day you need, so that a lookback from it has a table to fall back on. A range with no
75
+ * rates (a weekend, or after the latest day) gives []. Both requests share one timeout and are
76
+ * cancelled together: if one fails, the other is aborted. The ECB states that its rates are for
77
+ * information purposes only.
78
+ *
79
+ * const tables = await fetchEcbRange({ start: '2026-01-01', currencies: ['USD', 'JPY'] });
80
+ * const history = rateHistory(tables); // from @ducatjs/money/convert/history
81
+ */
82
+ export declare function fetchEcbRange(options: FetchEcbRangeOptions): Promise<RateTable[]>;
83
+ /** The Bank of Canada's Valet API for its daily exchange rates (no key; CORS-enabled). */
84
+ export declare const BANK_OF_CANADA_URL = "https://www.bankofcanada.ca/valet/observations/group/FX_RATES_DAILY/json";
85
+ /** Options for fetching Bank of Canada rates. */
86
+ export interface FetchBankOfCanadaOptions extends Omit<FetchEcbOptions, 'history'> {
87
+ /** The first day (2017-01-03 at the earliest). Default: only the latest published day. */
88
+ readonly start?: string | Date;
89
+ /** The last day (default: the latest published). Needs a `start`. */
90
+ readonly end?: string | Date;
91
+ }
92
+ /**
93
+ * Fetches the Bank of Canada's daily exchange rates from its Valet API and parses them with
94
+ * parseBankOfCanadaJson: CAD-based tables, newest first. Without `start`, only the latest day;
95
+ * without `end`, up to the latest. A range with no business days in it gives [].
96
+ * The Bank asks users to keep within its request limits, which it doesn't publish, and its rates
97
+ * are "released for statistical and analytical purposes only", not for transactions.
98
+ *
99
+ * const [latest] = await fetchBankOfCanada();
100
+ * const year = await fetchBankOfCanada({ start: '2026-01-01' });
101
+ */
102
+ export declare function fetchBankOfCanada(options?: FetchBankOfCanadaOptions): Promise<RateTable[]>;
103
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1F,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CAChE;AAED,2FAA2F;AAC3F,MAAM,MAAM,KAAK,GAAG,CAClB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,eAAe,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,KAClE,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAAC;AAUvE,oGAAoG;AACpG,eAAO,MAAM,QAAQ;;;;CAIX,CAAC;AAEX,gDAAgD;AAChD,MAAM,WAAW,eAAe;IAC9B,kGAAkG;IAClG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,OAAO,QAAQ,CAAC;IACzC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAED,mEAAmE;AACnE,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;gBACT,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAMxC;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAI9E;AA0BD,+FAA+F;AAC/F,eAAO,MAAM,YAAY,oDAAoD,CAAC;AAE9E,gEAAgE;AAChE,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC;IAC5E,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,oDAAoD;IACpD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AA2ED;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAyDvF;AAED,0FAA0F;AAC1F,eAAO,MAAM,kBAAkB,6EAC6C,CAAC;AAE7E,iDAAiD;AACjD,MAAM,WAAW,wBAAyB,SAAQ,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC;IAChF,0FAA0F;IAC1F,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAqBhG"}