@ducatjs/money 0.1.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 (201) hide show
  1. package/README.md +479 -62
  2. package/data/cash-rounding.json +123 -2
  3. package/data/cldr-cash.json +101 -0
  4. package/data/historic.json +78 -0
  5. package/data/iso4217-list-three.xml +1185 -0
  6. package/dist/adyen-fx.d.ts.map +1 -1
  7. package/dist/adyen-fx.js +22 -4
  8. package/dist/adyen-fx.js.map +1 -1
  9. package/dist/boc.d.ts +31 -0
  10. package/dist/boc.d.ts.map +1 -0
  11. package/dist/boc.js +114 -0
  12. package/dist/boc.js.map +1 -0
  13. package/dist/boe.d.ts +33 -0
  14. package/dist/boe.d.ts.map +1 -0
  15. package/dist/boe.js +114 -0
  16. package/dist/boe.js.map +1 -0
  17. package/dist/cash.d.ts +30 -1
  18. package/dist/cash.d.ts.map +1 -1
  19. package/dist/cash.js +50 -2
  20. package/dist/cash.js.map +1 -1
  21. package/dist/convert.d.ts +10 -9
  22. package/dist/convert.d.ts.map +1 -1
  23. package/dist/convert.js +13 -3
  24. package/dist/convert.js.map +1 -1
  25. package/dist/drizzle/mysql.d.ts +105 -0
  26. package/dist/drizzle/mysql.d.ts.map +1 -0
  27. package/dist/drizzle/mysql.js +60 -0
  28. package/dist/drizzle/mysql.js.map +1 -0
  29. package/dist/drizzle/pg.d.ts +103 -0
  30. package/dist/drizzle/pg.d.ts.map +1 -0
  31. package/dist/drizzle/pg.js +60 -0
  32. package/dist/drizzle/pg.js.map +1 -0
  33. package/dist/drizzle/sqlite.d.ts +102 -0
  34. package/dist/drizzle/sqlite.d.ts.map +1 -0
  35. package/dist/drizzle/sqlite.js +59 -0
  36. package/dist/drizzle/sqlite.js.map +1 -0
  37. package/dist/ecb.d.ts +37 -2
  38. package/dist/ecb.d.ts.map +1 -1
  39. package/dist/ecb.js +148 -10
  40. package/dist/ecb.js.map +1 -1
  41. package/dist/fed.d.ts +35 -0
  42. package/dist/fed.d.ts.map +1 -0
  43. package/dist/fed.js +134 -0
  44. package/dist/fed.js.map +1 -0
  45. package/dist/fetch.d.ts +45 -0
  46. package/dist/fetch.d.ts.map +1 -1
  47. package/dist/fetch.js +167 -24
  48. package/dist/fetch.js.map +1 -1
  49. package/dist/fn.d.ts +8 -1
  50. package/dist/fn.d.ts.map +1 -1
  51. package/dist/fn.js +8 -1
  52. package/dist/fn.js.map +1 -1
  53. package/dist/generated/cash.d.ts +2 -0
  54. package/dist/generated/cash.d.ts.map +1 -1
  55. package/dist/generated/cash.js +101 -0
  56. package/dist/generated/cash.js.map +1 -1
  57. package/dist/generated/historic.d.ts +337 -0
  58. package/dist/generated/historic.d.ts.map +1 -0
  59. package/dist/generated/historic.js +66 -0
  60. package/dist/generated/historic.js.map +1 -0
  61. package/dist/historic.d.ts +40 -0
  62. package/dist/historic.d.ts.map +1 -0
  63. package/dist/historic.js +43 -0
  64. package/dist/historic.js.map +1 -0
  65. package/dist/history.d.ts +96 -0
  66. package/dist/history.d.ts.map +1 -0
  67. package/dist/history.js +213 -0
  68. package/dist/history.js.map +1 -0
  69. package/dist/hmrc.d.ts +28 -0
  70. package/dist/hmrc.d.ts.map +1 -0
  71. package/dist/hmrc.js +108 -0
  72. package/dist/hmrc.js.map +1 -0
  73. package/dist/index.d.ts +1 -1
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +1 -1
  76. package/dist/index.js.map +1 -1
  77. package/dist/kysely.d.ts +120 -0
  78. package/dist/kysely.d.ts.map +1 -0
  79. package/dist/kysely.js +309 -0
  80. package/dist/kysely.js.map +1 -0
  81. package/dist/money.d.ts +93 -9
  82. package/dist/money.d.ts.map +1 -1
  83. package/dist/money.js +135 -9
  84. package/dist/money.js.map +1 -1
  85. package/dist/parse.d.ts +45 -1
  86. package/dist/parse.d.ts.map +1 -1
  87. package/dist/parse.js +290 -3
  88. package/dist/parse.js.map +1 -1
  89. package/dist/pg.d.ts +100 -0
  90. package/dist/pg.d.ts.map +1 -0
  91. package/dist/pg.js +343 -0
  92. package/dist/pg.js.map +1 -0
  93. package/dist/plugins/all.d.ts +8 -1
  94. package/dist/plugins/all.d.ts.map +1 -1
  95. package/dist/plugins/all.js +8 -1
  96. package/dist/plugins/all.js.map +1 -1
  97. package/dist/plugins/convert.d.ts +3 -4
  98. package/dist/plugins/convert.d.ts.map +1 -1
  99. package/dist/plugins/convert.js.map +1 -1
  100. package/dist/plugins/tax.d.ts +4 -5
  101. package/dist/plugins/tax.d.ts.map +1 -1
  102. package/dist/plugins/tax.js.map +1 -1
  103. package/dist/policy.d.ts +34 -0
  104. package/dist/policy.d.ts.map +1 -0
  105. package/dist/policy.js +90 -0
  106. package/dist/policy.js.map +1 -0
  107. package/dist/prisma.d.ts +25 -14
  108. package/dist/prisma.d.ts.map +1 -1
  109. package/dist/prisma.js +29 -19
  110. package/dist/prisma.js.map +1 -1
  111. package/dist/provider.d.ts +21 -7
  112. package/dist/provider.d.ts.map +1 -1
  113. package/dist/provider.js +51 -11
  114. package/dist/provider.js.map +1 -1
  115. package/dist/rate-text.d.ts +38 -0
  116. package/dist/rate-text.d.ts.map +1 -0
  117. package/dist/rate-text.js +143 -0
  118. package/dist/rate-text.js.map +1 -0
  119. package/dist/rate.d.ts +61 -5
  120. package/dist/rate.d.ts.map +1 -1
  121. package/dist/rate.js +133 -33
  122. package/dist/rate.js.map +1 -1
  123. package/dist/rounding.js +1 -1
  124. package/dist/rounding.js.map +1 -1
  125. package/dist/schema.d.ts +43 -0
  126. package/dist/schema.d.ts.map +1 -0
  127. package/dist/schema.js +77 -0
  128. package/dist/schema.js.map +1 -0
  129. package/dist/sql.d.ts +54 -0
  130. package/dist/sql.d.ts.map +1 -0
  131. package/dist/sql.js +131 -0
  132. package/dist/sql.js.map +1 -0
  133. package/dist/tax.d.ts +13 -9
  134. package/dist/tax.d.ts.map +1 -1
  135. package/dist/tax.js +20 -4
  136. package/dist/tax.js.map +1 -1
  137. package/dist/testing/arbitraries.d.ts +43 -0
  138. package/dist/testing/arbitraries.d.ts.map +1 -0
  139. package/dist/testing/arbitraries.js +98 -0
  140. package/dist/testing/arbitraries.js.map +1 -0
  141. package/dist/testing/index.d.ts +67 -0
  142. package/dist/testing/index.d.ts.map +1 -0
  143. package/dist/testing/index.js +159 -0
  144. package/dist/testing/index.js.map +1 -0
  145. package/dist/testing/jest.d.ts +2 -0
  146. package/dist/testing/jest.d.ts.map +1 -0
  147. package/dist/testing/jest.js +15 -0
  148. package/dist/testing/jest.js.map +1 -0
  149. package/dist/testing/vitest.d.ts +7 -0
  150. package/dist/testing/vitest.d.ts.map +1 -0
  151. package/dist/testing/vitest.js +12 -0
  152. package/dist/testing/vitest.js.map +1 -0
  153. package/dist/valibot.d.ts +33 -0
  154. package/dist/valibot.d.ts.map +1 -0
  155. package/dist/valibot.js +61 -0
  156. package/dist/valibot.js.map +1 -0
  157. package/dist/zod.d.ts +36 -0
  158. package/dist/zod.d.ts.map +1 -0
  159. package/dist/zod.js +58 -0
  160. package/dist/zod.js.map +1 -0
  161. package/package.json +116 -5
  162. package/src/adyen-fx.ts +25 -4
  163. package/src/boc.ts +136 -0
  164. package/src/boe.ts +131 -0
  165. package/src/cash.ts +70 -2
  166. package/src/convert.ts +33 -15
  167. package/src/drizzle/mysql.ts +78 -0
  168. package/src/drizzle/pg.ts +78 -0
  169. package/src/drizzle/sqlite.ts +70 -0
  170. package/src/ecb.ts +179 -13
  171. package/src/fed.ts +146 -0
  172. package/src/fetch.ts +207 -28
  173. package/src/fn.ts +14 -0
  174. package/src/generated/cash.ts +103 -0
  175. package/src/generated/historic.ts +67 -0
  176. package/src/historic.ts +77 -0
  177. package/src/history.ts +333 -0
  178. package/src/hmrc.ts +125 -0
  179. package/src/index.ts +4 -0
  180. package/src/kysely.ts +588 -0
  181. package/src/money.ts +210 -12
  182. package/src/parse.ts +371 -4
  183. package/src/pg.ts +410 -0
  184. package/src/plugins/all.ts +10 -0
  185. package/src/plugins/convert.ts +4 -5
  186. package/src/plugins/tax.ts +9 -10
  187. package/src/policy.ts +124 -0
  188. package/src/prisma.ts +91 -10
  189. package/src/provider.ts +81 -16
  190. package/src/rate-text.ts +151 -0
  191. package/src/rate.ts +205 -36
  192. package/src/rounding.ts +1 -1
  193. package/src/schema.ts +119 -0
  194. package/src/sql.ts +178 -0
  195. package/src/tax.ts +40 -9
  196. package/src/testing/arbitraries.ts +134 -0
  197. package/src/testing/index.ts +247 -0
  198. package/src/testing/jest.ts +18 -0
  199. package/src/testing/vitest.ts +18 -0
  200. package/src/valibot.ts +87 -0
  201. package/src/zod.ts +78 -0
package/dist/sql.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ import { type Money } from './money.js';
2
+ /** The declared size of a NUMERIC/DECIMAL column. */
3
+ export interface DecimalColumnSize {
4
+ /** Total number of significant digits the column holds. */
5
+ readonly precision: number;
6
+ /** Digits after the decimal point. */
7
+ readonly scale: number;
8
+ }
9
+ /** NUMERIC(19,4): 15 integer digits and 4 decimals, enough for every ISO 4217 exponent. */
10
+ export declare const DEFAULT_DECIMAL_SIZE: DecimalColumnSize;
11
+ /** Throws unless the money is in the column's currency (when the column has a fixed currency). */
12
+ export declare function checkCurrency(money: Money, currency: string | undefined): void;
13
+ /**
14
+ * Throws unless the money is at its currency's exponent. Columns store amounts at the currency's
15
+ * scale, so a value with sub-minor digits would have to be rounded, and the caller should choose how.
16
+ */
17
+ export declare function checkIsoScale(money: Money): void;
18
+ /**
19
+ * Minor units for a BIGINT column, as a bigint. Throws beyond the column's range, ±9.2·10^18:
20
+ * MySQL outside strict mode would store the nearest limit instead.
21
+ */
22
+ export declare function minorToDriver(money: Money, currency: string | undefined): bigint;
23
+ /**
24
+ * Money from a BIGINT driver value. Accepts a bigint, a safe-integer number, or an integer string
25
+ * (node-postgres returns int8 as a string). A number beyond 2^53 has already lost digits, so it
26
+ * throws.
27
+ */
28
+ export declare function minorFromDriver<C extends string>(value: unknown, currency: C): Money<C>;
29
+ /** Throws if a decimal column of this size would round amounts in this currency. */
30
+ export declare function checkDecimalSize(currency: string, size: DecimalColumnSize): void;
31
+ /**
32
+ * The exact decimal string for a NUMERIC/DECIMAL column. Throws if the column could not hold it
33
+ * unchanged: MySQL outside strict mode clamps an out-of-range value instead of failing.
34
+ */
35
+ export declare function decimalToDriver(money: Money, currency: string | undefined, size: DecimalColumnSize | undefined): string;
36
+ /**
37
+ * Throws if the decimal (the money's `toDecimal`) has more integer digits than a column of this
38
+ * size holds: Postgres rejects it, and MySQL outside strict mode clamps it.
39
+ */
40
+ export declare function checkIntegerDigits(money: Money, decimal: string, size: DecimalColumnSize): void;
41
+ /**
42
+ * Money from a NUMERIC/DECIMAL driver value, which must be a string: a number has been through a
43
+ * float. Trailing zeros (`12.3400` from NUMERIC(19,4)) are fine; more decimals than the currency
44
+ * has, `NaN` and `Infinity` throw.
45
+ */
46
+ export declare function decimalFromDriver<C extends string>(value: unknown, currency: C): Money<C>;
47
+ /** `{"amount":"12.34","currency":"EUR"}` for a JSON, JSONB or TEXT column. */
48
+ export declare function jsonToDriver(money: Money, currency: string | undefined): string;
49
+ /**
50
+ * Money from a JSON column: the parsed object (JSONB in node-postgres, JSON in mysql2) or the
51
+ * text (SQLite).
52
+ */
53
+ export declare function jsonFromDriver<C extends string>(value: unknown, currency: C | undefined): Money<C>;
54
+ //# sourceMappingURL=sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAKA,OAAO,EAOL,KAAK,KAAK,EAEX,MAAM,YAAY,CAAC;AAOpB,qDAAqD;AACrD,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,EAAE,iBAA+C,CAAC;AAEnF,kGAAkG;AAClG,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAI9E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAOhD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAShF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAWvF;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAkBhF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,iBAAiB,GAAG,SAAS,GAClC,MAAM,CASR;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAU/F;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAQzF;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAI/E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAC7C,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,CAAC,GAAG,SAAS,GACtB,KAAK,CAAC,CAAC,CAAC,CAOV"}
package/dist/sql.js ADDED
@@ -0,0 +1,131 @@
1
+ // Conversions between Money and the values SQL drivers send and receive, shared by the ORM and
2
+ // query-builder integrations (`/drizzle/*`, `/kysely`). Every reader is strict: a driver value that
3
+ // may already have lost digits (an unsafe number, a float for a DECIMAL) is rejected rather than
4
+ // trusted, and every writer refuses a value the column would change.
5
+ import { currencyExponent } from './currency.js';
6
+ import { CurrencyMismatchError, fromJSON, fromMinor, money as parseMoney, toDecimal, toMinor, } from './money.js';
7
+ const INTEGER_TEXT = /^-?\d+$/;
8
+ // a BIGINT (SQLite INTEGER) column's range: MySQL outside strict mode clips anything beyond it
9
+ const BIGINT_MIN = -(2n ** 63n);
10
+ const BIGINT_MAX = 2n ** 63n - 1n;
11
+ /** NUMERIC(19,4): 15 integer digits and 4 decimals, enough for every ISO 4217 exponent. */
12
+ export const DEFAULT_DECIMAL_SIZE = { precision: 19, scale: 4 };
13
+ /** Throws unless the money is in the column's currency (when the column has a fixed currency). */
14
+ export function checkCurrency(money, currency) {
15
+ if (currency !== undefined && money.currency !== currency) {
16
+ throw new CurrencyMismatchError(currency, money.currency);
17
+ }
18
+ }
19
+ /**
20
+ * Throws unless the money is at its currency's exponent. Columns store amounts at the currency's
21
+ * scale, so a value with sub-minor digits would have to be rounded, and the caller should choose how.
22
+ */
23
+ export function checkIsoScale(money) {
24
+ const exponent = currencyExponent(money.currency);
25
+ if (money.scale !== exponent) {
26
+ throw new RangeError(`${money} has scale ${money.scale}; round() it to ${exponent} decimals before storing it`);
27
+ }
28
+ }
29
+ /**
30
+ * Minor units for a BIGINT column, as a bigint. Throws beyond the column's range, ±9.2·10^18:
31
+ * MySQL outside strict mode would store the nearest limit instead.
32
+ */
33
+ export function minorToDriver(money, currency) {
34
+ checkCurrency(money, currency);
35
+ checkIsoScale(money);
36
+ const minor = toMinor(money);
37
+ if (minor < BIGINT_MIN || minor > BIGINT_MAX) {
38
+ throw new RangeError(`${money} is ${minor} minor units, beyond the range of a BIGINT column`);
39
+ }
40
+ return minor;
41
+ }
42
+ /**
43
+ * Money from a BIGINT driver value. Accepts a bigint, a safe-integer number, or an integer string
44
+ * (node-postgres returns int8 as a string). A number beyond 2^53 has already lost digits, so it
45
+ * throws.
46
+ */
47
+ export function minorFromDriver(value, currency) {
48
+ if (typeof value === 'string') {
49
+ if (!INTEGER_TEXT.test(value)) {
50
+ throw new SyntaxError(`Expected an integer from a BIGINT column, got "${value}"`);
51
+ }
52
+ return fromMinor(BigInt(value), currency);
53
+ }
54
+ if (typeof value === 'number' || typeof value === 'bigint') {
55
+ return fromMinor(value, currency);
56
+ }
57
+ throw new TypeError(`Expected an integer from a BIGINT column, got ${typeof value}`);
58
+ }
59
+ /** Throws if a decimal column of this size would round amounts in this currency. */
60
+ export function checkDecimalSize(currency, size) {
61
+ const { precision, scale } = size;
62
+ const isValidSize = Number.isInteger(precision) &&
63
+ Number.isInteger(scale) &&
64
+ precision >= 1 &&
65
+ scale >= 0 &&
66
+ scale <= precision;
67
+ if (!isValidSize) {
68
+ throw new RangeError(`NUMERIC(${precision},${scale}) is not a valid column size`);
69
+ }
70
+ const exponent = currencyExponent(currency);
71
+ if (size.scale < exponent) {
72
+ throw new RangeError(`NUMERIC(${size.precision},${size.scale}) would round ${currency}, which has ${exponent} decimals`);
73
+ }
74
+ }
75
+ /**
76
+ * The exact decimal string for a NUMERIC/DECIMAL column. Throws if the column could not hold it
77
+ * unchanged: MySQL outside strict mode clamps an out-of-range value instead of failing.
78
+ */
79
+ export function decimalToDriver(money, currency, size) {
80
+ checkCurrency(money, currency);
81
+ checkIsoScale(money);
82
+ const decimal = toDecimal(money);
83
+ if (size !== undefined) {
84
+ checkIntegerDigits(money, decimal, size);
85
+ }
86
+ return decimal;
87
+ }
88
+ /**
89
+ * Throws if the decimal (the money's `toDecimal`) has more integer digits than a column of this
90
+ * size holds: Postgres rejects it, and MySQL outside strict mode clamps it.
91
+ */
92
+ export function checkIntegerDigits(money, decimal, size) {
93
+ const integerPart = decimal.replace('-', '').split('.')[0];
94
+ // "0.12" has no integer digits: NUMERIC(4,4) holds it
95
+ const integerDigits = integerPart === '0' ? 0 : integerPart.length;
96
+ if (integerDigits > size.precision - size.scale) {
97
+ throw new RangeError(`${money} does not fit NUMERIC(${size.precision},${size.scale}), which holds ` +
98
+ `${size.precision - size.scale} integer digits`);
99
+ }
100
+ }
101
+ /**
102
+ * Money from a NUMERIC/DECIMAL driver value, which must be a string: a number has been through a
103
+ * float. Trailing zeros (`12.3400` from NUMERIC(19,4)) are fine; more decimals than the currency
104
+ * has, `NaN` and `Infinity` throw.
105
+ */
106
+ export function decimalFromDriver(value, currency) {
107
+ if (typeof value !== 'string') {
108
+ throw new TypeError(`Expected a string from a DECIMAL column, got ${typeof value}; ` +
109
+ 'configure the driver to return decimals as strings');
110
+ }
111
+ return parseMoney(value, currency);
112
+ }
113
+ /** `{"amount":"12.34","currency":"EUR"}` for a JSON, JSONB or TEXT column. */
114
+ export function jsonToDriver(money, currency) {
115
+ checkCurrency(money, currency);
116
+ checkIsoScale(money);
117
+ return JSON.stringify(money);
118
+ }
119
+ /**
120
+ * Money from a JSON column: the parsed object (JSONB in node-postgres, JSON in mysql2) or the
121
+ * text (SQLite).
122
+ */
123
+ export function jsonFromDriver(value, currency) {
124
+ if (typeof value !== 'string' && (typeof value !== 'object' || value === null)) {
125
+ throw new TypeError(`Expected JSON from a money column, got ${String(value)}`);
126
+ }
127
+ const parsed = fromJSON(value);
128
+ checkCurrency(parsed, currency);
129
+ return parsed;
130
+ }
131
+ //# sourceMappingURL=sql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql.js","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"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"}
package/dist/tax.d.ts CHANGED
@@ -1,11 +1,10 @@
1
1
  import { type Factor } from './decimal.js';
2
- import { type Money } from './money.js';
3
- import { type RoundingMode } from './rounding.js';
2
+ import { type Money, type RoundingInput } from './money.js';
4
3
  /**
5
4
  * percent% of m: percentage(EUR 80, '12.5', 'halfEven') = EUR 10.00. Rounding defaults to
6
5
  * 'halfExpand'.
7
6
  */
8
- export declare function percentage<C extends string>(money: Money<C>, percent: Factor, rounding?: RoundingMode): Money<C>;
7
+ export declare function percentage<C extends string>(money: Money<C>, percent: Factor, rounding?: RoundingInput): Money<C>;
9
8
  /** A tax split: net + tax is always exactly gross. */
10
9
  export interface TaxBreakdown<C extends string> {
11
10
  readonly net: Money<C>;
@@ -15,15 +14,20 @@ export interface TaxBreakdown<C extends string> {
15
14
  }
16
15
  /**
17
16
  * Tax-exclusive: adds percent% tax to a net amount. addTax(EUR 10, '20', 'halfEven') -> net 10, tax
18
- * 2, gross 12.
17
+ * 2, gross 12. A rounding mode rounds the tax; a rounding policy rounds the gross (the amount
18
+ * charged) to its scale and increment. The other part is always the exact difference.
19
19
  */
20
- export declare function addTax<C extends string>(net: Money<C>, percent: Factor, rounding?: RoundingMode): TaxBreakdown<C>;
20
+ export declare function addTax<C extends string>(net: Money<C>, percent: Factor, rounding?: RoundingInput): TaxBreakdown<C>;
21
21
  /**
22
22
  * Tax-inclusive: extracts percent% tax contained in a gross amount (VAT extraction).
23
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.
24
+ * extractTax(EUR 12, '20', 'halfEven') -> net 10.00, tax 2.00. A rounding policy rounds the net; the
25
+ * tax is on the policy's increment too whenever the gross is.
25
26
  */
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>;
27
+ export declare function extractTax<C extends string>(gross: Money<C>, percent: Factor, rounding?: RoundingInput): TaxBreakdown<C>;
28
+ /**
29
+ * Price after a percent% discount. A rounding mode rounds the discount amount; a rounding policy
30
+ * rounds the discounted price itself, so the price lands on the policy's scale and increment.
31
+ */
32
+ export declare function discount<C extends string>(price: Money<C>, percent: Factor, rounding?: RoundingInput): Money<C>;
29
33
  //# sourceMappingURL=tax.d.ts.map
package/dist/tax.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tax.d.ts","sourceRoot":"","sources":["../src/tax.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,MAAM,EAAc,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAkC,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAOpE;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,YAA+B,GACxC,KAAK,CAAC,CAAC,CAAC,CAEV;AAED,sDAAsD;AACtD,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM;IAC5C,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACvB,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EACrC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,EACb,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,YAA+B,GACxC,YAAY,CAAC,CAAC,CAAC,CAIjB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,YAA+B,GACxC,YAAY,CAAC,CAAC,CAAC,CASjB;AAED,uFAAuF;AACvF,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EACvC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,YAA+B,GACxC,KAAK,CAAC,CAAC,CAAC,CAEV"}
1
+ {"version":3,"file":"tax.d.ts","sourceRoot":"","sources":["../src/tax.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,MAAM,EAAc,MAAM,cAAc,CAAC;AAC9D,OAAO,EAKL,KAAK,KAAK,EACV,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AAQpB;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,aAAgC,GACzC,KAAK,CAAC,CAAC,CAAC,CAEV;AAED,sDAAsD;AACtD,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM;IAC5C,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACvB,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EACrC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,EACb,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,aAAgC,GACzC,YAAY,CAAC,CAAC,CAAC,CAejB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,aAAgC,GACzC,YAAY,CAAC,CAAC,CAAC,CASjB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EACvC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,aAAgC,GACzC,KAAK,CAAC,CAAC,CAAC,CAUV"}
package/dist/tax.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ratio } from './decimal.js';
2
- import { add, multiplyByRatio, subtract } from './money.js';
2
+ import { add, multiplyByRatio, roundWithPolicy, subtract, } from './money.js';
3
3
  import { DEFAULT_ROUNDING } from './rounding.js';
4
4
  const percentRatio = (percent) => {
5
5
  const asRatio = ratio(percent);
@@ -14,9 +14,17 @@ export function percentage(money, percent, rounding = DEFAULT_ROUNDING) {
14
14
  }
15
15
  /**
16
16
  * Tax-exclusive: adds percent% tax to a net amount. addTax(EUR 10, '20', 'halfEven') -> net 10, tax
17
- * 2, gross 12.
17
+ * 2, gross 12. A rounding mode rounds the tax; a rounding policy rounds the gross (the amount
18
+ * charged) to its scale and increment. The other part is always the exact difference.
18
19
  */
19
20
  export function addTax(net, percent, rounding = DEFAULT_ROUNDING) {
21
+ // A rounding mode rounds the tax; a policy rounds the gross, the amount charged, so it lands on
22
+ // the policy's increment (CHF 0.05 for cash). Either way the other part is the exact difference.
23
+ if (typeof rounding === 'function') {
24
+ const percentAsRatio = ratio(percent);
25
+ const gross = roundWithPolicy(net, ratio(100n * percentAsRatio.den + percentAsRatio.num, 100n * percentAsRatio.den), rounding);
26
+ return { net, tax: subtract(gross, net), gross };
27
+ }
20
28
  const tax = multiplyByRatio(net, percentRatio(percent), rounding);
21
29
  const gross = add(net, tax);
22
30
  return { net, tax, gross };
@@ -24,7 +32,8 @@ export function addTax(net, percent, rounding = DEFAULT_ROUNDING) {
24
32
  /**
25
33
  * Tax-inclusive: extracts percent% tax contained in a gross amount (VAT extraction).
26
34
  * net = gross / (1 + percent/100) rounded; tax = gross - net, so the parts always add up.
27
- * extractTax(EUR 12, '20', 'halfEven') -> net 10.00, tax 2.00.
35
+ * extractTax(EUR 12, '20', 'halfEven') -> net 10.00, tax 2.00. A rounding policy rounds the net; the
36
+ * tax is on the policy's increment too whenever the gross is.
28
37
  */
29
38
  export function extractTax(gross, percent, rounding = DEFAULT_ROUNDING) {
30
39
  const percentAsRatio = ratio(percent);
@@ -32,8 +41,15 @@ export function extractTax(gross, percent, rounding = DEFAULT_ROUNDING) {
32
41
  const net = multiplyByRatio(gross, ratio(100n * percentAsRatio.den, 100n * percentAsRatio.den + percentAsRatio.num), rounding);
33
42
  return { net, tax: subtract(gross, net), gross };
34
43
  }
35
- /** Price after a percent% discount; the discount amount is rounded with `rounding`. */
44
+ /**
45
+ * Price after a percent% discount. A rounding mode rounds the discount amount; a rounding policy
46
+ * rounds the discounted price itself, so the price lands on the policy's scale and increment.
47
+ */
36
48
  export function discount(price, percent, rounding = DEFAULT_ROUNDING) {
49
+ if (typeof rounding === 'function') {
50
+ const percentAsRatio = ratio(percent);
51
+ return roundWithPolicy(price, ratio(100n * percentAsRatio.den - percentAsRatio.num, 100n * percentAsRatio.den), rounding);
52
+ }
37
53
  return subtract(price, multiplyByRatio(price, percentRatio(percent), rounding));
38
54
  }
39
55
  //# sourceMappingURL=tax.js.map
package/dist/tax.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tax.js","sourceRoot":"","sources":["../src/tax.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAA2B,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAc,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAqB,MAAM,eAAe,CAAC;AAEpE,MAAM,YAAY,GAAG,CAAC,OAAe,EAAS,EAAE;IAC9C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;AAChD,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,KAAe,EACf,OAAe,EACf,WAAyB,gBAAgB;IAEzC,OAAO,eAAe,CAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AACjE,CAAC;AAUD;;;GAGG;AACH,MAAM,UAAU,MAAM,CACpB,GAAa,EACb,OAAe,EACf,WAAyB,gBAAgB;IAEzC,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC5B,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,KAAe,EACf,OAAe,EACf,WAAyB,gBAAgB;IAEzC,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IACtC,8CAA8C;IAC9C,MAAM,GAAG,GAAG,eAAe,CACzB,KAAK,EACL,KAAK,CAAC,IAAI,GAAG,cAAc,CAAC,GAAG,EAAE,IAAI,GAAG,cAAc,CAAC,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,EAChF,QAAQ,CACT,CAAC;IACF,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;AACnD,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,QAAQ,CACtB,KAAe,EACf,OAAe,EACf,WAAyB,gBAAgB;IAEzC,OAAO,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;AAClF,CAAC"}
1
+ {"version":3,"file":"tax.js","sourceRoot":"","sources":["../src/tax.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAA2B,MAAM,cAAc,CAAC;AAC9D,OAAO,EACL,GAAG,EACH,eAAe,EACf,eAAe,EACf,QAAQ,GAGT,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,YAAY,GAAG,CAAC,OAAe,EAAS,EAAE;IAC9C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;AAChD,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,KAAe,EACf,OAAe,EACf,WAA0B,gBAAgB;IAE1C,OAAO,eAAe,CAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AACjE,CAAC;AAUD;;;;GAIG;AACH,MAAM,UAAU,MAAM,CACpB,GAAa,EACb,OAAe,EACf,WAA0B,gBAAgB;IAE1C,gGAAgG;IAChG,iGAAiG;IACjG,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,eAAe,CAC3B,GAAG,EACH,KAAK,CAAC,IAAI,GAAG,cAAc,CAAC,GAAG,GAAG,cAAc,CAAC,GAAG,EAAE,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,EAChF,QAAQ,CACT,CAAC;QACF,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;IACnD,CAAC;IACD,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC5B,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CACxB,KAAe,EACf,OAAe,EACf,WAA0B,gBAAgB;IAE1C,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IACtC,8CAA8C;IAC9C,MAAM,GAAG,GAAG,eAAe,CACzB,KAAK,EACL,KAAK,CAAC,IAAI,GAAG,cAAc,CAAC,GAAG,EAAE,IAAI,GAAG,cAAc,CAAC,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,EAChF,QAAQ,CACT,CAAC;IACF,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CACtB,KAAe,EACf,OAAe,EACf,WAA0B,gBAAgB;IAE1C,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,OAAO,eAAe,CACpB,KAAK,EACL,KAAK,CAAC,IAAI,GAAG,cAAc,CAAC,GAAG,GAAG,cAAc,CAAC,GAAG,EAAE,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,EAChF,QAAQ,CACT,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;AAClF,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `@ducatjs/money/testing/arbitraries`: fast-check arbitraries for property tests. They cover what
3
+ * hand-written cases tend to miss: amounts beyond 2^53 (bigint minor units), currencies with
4
+ * unusual exponents (JPY 0, KWD 3, CLF 4) and scales above the currency's exponent. fast-check is
5
+ * an optional peer dependency, needed only for this entry.
6
+ */
7
+ import fc from 'fast-check';
8
+ import { type Money } from '../money.js';
9
+ /** Options for `arbCurrency`. */
10
+ export interface ArbCurrencyOptions {
11
+ /** Only currencies with one of these exponents (default: the representative mix). */
12
+ readonly exponents?: readonly number[];
13
+ /** Draw from every ISO 4217 code instead of the representative mix. */
14
+ readonly all?: boolean;
15
+ /** More codes to draw from, such as ones registered with `defineCurrency`. */
16
+ readonly extra?: readonly string[];
17
+ }
18
+ /**
19
+ * Currency codes, by default a representative mix of exponents 0, 2, 3 and 4 (JPY, EUR, KWD,
20
+ * CLF...). `{ all: true }` draws from every ISO 4217 code, `extra` adds codes (custom ones from
21
+ * `defineCurrency`), and `exponents` narrows whichever pool that is.
22
+ */
23
+ export declare function arbCurrency(options?: ArbCurrencyOptions): fc.Arbitrary<string>;
24
+ /** Options for `arbMoney`. */
25
+ export interface ArbMoneyOptions<C extends string = string> {
26
+ /** The currency, or an arbitrary of currencies (default: `arbCurrency()`). */
27
+ readonly currency?: C | fc.Arbitrary<C>;
28
+ /** The smallest amount, in major units (default: none, down to -10^30 minor units). */
29
+ readonly min?: string | number | bigint;
30
+ /** The largest amount, in major units (default: none, up to 10^30 minor units). */
31
+ readonly max?: string | number | bigint;
32
+ /**
33
+ * The scale, or an arbitrary of scales. By default mostly the currency's exponent, sometimes up
34
+ * to three more decimals.
35
+ */
36
+ readonly scale?: number | fc.Arbitrary<number>;
37
+ }
38
+ /**
39
+ * Money values for property tests: `arbMoney()`, `arbMoney({ currency: 'EUR', min: '0', max:
40
+ * '100' })`. With bounds, every value is within them; without, minor units reach ±10^30.
41
+ */
42
+ export declare function arbMoney<C extends string = string>(options?: ArbMoneyOptions<C>): fc.Arbitrary<Money<C>>;
43
+ //# sourceMappingURL=arbitraries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arbitraries.d.ts","sourceRoot":"","sources":["../../src/testing/arbitraries.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5B,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAK3D,iCAAiC;AACjC,MAAM,WAAW,kBAAkB;IACjC,qFAAqF;IACrF,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,uEAAuE;IACvE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAYlF;AAED,8BAA8B;AAC9B,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACxD,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACxC,uFAAuF;IACvF,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,mFAAmF;IACnF,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;CAChD;AA0BD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAChD,OAAO,GAAE,eAAe,CAAC,CAAC,CAAM,GAC/B,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CA6CxB"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * `@ducatjs/money/testing/arbitraries`: fast-check arbitraries for property tests. They cover what
3
+ * hand-written cases tend to miss: amounts beyond 2^53 (bigint minor units), currencies with
4
+ * unusual exponents (JPY 0, KWD 3, CLF 4) and scales above the currency's exponent. fast-check is
5
+ * an optional peer dependency, needed only for this entry.
6
+ */
7
+ import fc from 'fast-check';
8
+ import { currencyExponent, ISO_EXPONENTS } from '../currency.js';
9
+ import { MAX_DECIMAL_SCALE } from '../decimal.js';
10
+ import { fromMinor, money } from '../money.js';
11
+ /** A mix of exponents: the common two-decimal currencies and every unusual one. */
12
+ const DEFAULT_CURRENCIES = ['EUR', 'USD', 'GBP', 'JPY', 'KRW', 'KWD', 'BHD', 'TND', 'CLF'];
13
+ /**
14
+ * Currency codes, by default a representative mix of exponents 0, 2, 3 and 4 (JPY, EUR, KWD,
15
+ * CLF...). `{ all: true }` draws from every ISO 4217 code, `extra` adds codes (custom ones from
16
+ * `defineCurrency`), and `exponents` narrows whichever pool that is.
17
+ */
18
+ export function arbCurrency(options = {}) {
19
+ const base = options.all === true ? Object.keys(ISO_EXPONENTS) : DEFAULT_CURRENCIES;
20
+ const pool = [...base, ...(options.extra ?? [])];
21
+ const exponents = options.exponents;
22
+ const codes = exponents === undefined
23
+ ? pool
24
+ : pool.filter((code) => exponents.includes(currencyExponent(code)));
25
+ if (codes.length === 0) {
26
+ throw new RangeError('No currency has the requested exponents');
27
+ }
28
+ return fc.constantFrom(...codes);
29
+ }
30
+ const BIG = 10n ** 30n;
31
+ const SAFE = BigInt(Number.MAX_SAFE_INTEGER);
32
+ /** Minor units weighted toward the interesting places: small, near 2^53, and far beyond it. */
33
+ function arbMinor(min, max) {
34
+ const clamp = (low, high) => fc.bigInt({ min: low > min ? low : min, max: high < max ? high : max });
35
+ const parts = [];
36
+ if (min <= 10000n && max >= -10000n) {
37
+ parts.push(clamp(-10000n, 10000n));
38
+ }
39
+ if (min <= SAFE && max >= -SAFE) {
40
+ parts.push(clamp(-SAFE, SAFE));
41
+ }
42
+ // within 1000 of ±2^53, where number and bigint meet
43
+ for (const edge of [SAFE, -SAFE]) {
44
+ if (min <= edge + 1000n && max >= edge - 1000n) {
45
+ parts.push(clamp(edge - 1000n, edge + 1000n));
46
+ }
47
+ }
48
+ parts.push(fc.bigInt({ min, max }));
49
+ return fc.oneof(...parts);
50
+ }
51
+ /**
52
+ * Money values for property tests: `arbMoney()`, `arbMoney({ currency: 'EUR', min: '0', max:
53
+ * '100' })`. With bounds, every value is within them; without, minor units reach ±10^30.
54
+ */
55
+ export function arbMoney(options = {}) {
56
+ const given = options.currency;
57
+ let currencies;
58
+ if (given === undefined) {
59
+ currencies = arbCurrency();
60
+ }
61
+ else if (typeof given === 'string') {
62
+ currencies = fc.constant(given);
63
+ }
64
+ else {
65
+ currencies = given;
66
+ }
67
+ return currencies.chain((currency) => {
68
+ const exponent = currencyExponent(currency);
69
+ const scaleOption = options.scale;
70
+ let scales;
71
+ if (typeof scaleOption === 'number') {
72
+ scales = fc.constant(scaleOption);
73
+ }
74
+ else if (scaleOption === undefined) {
75
+ scales = fc.oneof({ weight: 3, arbitrary: fc.constant(exponent) }, { weight: 1, arbitrary: fc.integer({ min: exponent + 1, max: exponent + 3 }) });
76
+ }
77
+ else {
78
+ scales = scaleOption;
79
+ }
80
+ return scales.chain((scale) => {
81
+ if (!(Number.isInteger(scale) && scale >= 0 && scale <= MAX_DECIMAL_SCALE)) {
82
+ throw new RangeError(`Invalid scale ${scale}`);
83
+ }
84
+ // Bounds in minor units at this scale; a bound between two units rounds inward.
85
+ const min = options.min === undefined
86
+ ? -BIG
87
+ : BigInt(money(options.min, currency, { scale, rounding: 'ceil' }).toMinor());
88
+ const max = options.max === undefined
89
+ ? BIG
90
+ : BigInt(money(options.max, currency, { scale, rounding: 'floor' }).toMinor());
91
+ if (min > max) {
92
+ throw new RangeError(`No ${currency} amount at scale ${scale} between min and max`);
93
+ }
94
+ return arbMinor(min, max).map((minor) => fromMinor(minor, currency, scale));
95
+ });
96
+ });
97
+ }
98
+ //# sourceMappingURL=arbitraries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arbitraries.js","sourceRoot":"","sources":["../../src/testing/arbitraries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { type Money } from '../money.js';
2
+ /** An equality tester, as Vitest and Jest's `expect.addEqualityTesters` take them. */
3
+ export type MoneyEqualityTester = (left: unknown, right: unknown) => boolean | undefined;
4
+ /**
5
+ * Money compares by value in `toEqual`, `toStrictEqual` and nested structures: same currency and
6
+ * the same amount, whatever the scale (`1.0` and `1.00`) and whether the minor units are a number
7
+ * or a bigint. For anything other than two Money values it returns undefined, so the usual
8
+ * comparison applies.
9
+ */
10
+ export declare const moneyEquality: MoneyEqualityTester;
11
+ /** A snapshot serializer that prints Money as `12.34 EUR` (unquoted, so it isn't a string). */
12
+ export declare const moneySerializer: {
13
+ test: (value: unknown) => boolean;
14
+ serialize: (value: unknown) => string;
15
+ };
16
+ /** What the matchers receive as `this`, the part of Vitest's and Jest's matcher state used here. */
17
+ export interface MoneyMatcherContext {
18
+ readonly isNot: boolean;
19
+ }
20
+ /** A matcher's result, as Vitest and Jest expect it. */
21
+ export interface MoneyMatcherResult {
22
+ pass: boolean;
23
+ message: () => string;
24
+ actual?: unknown;
25
+ expected?: unknown;
26
+ }
27
+ /** The money matchers, typed for `declare module` augmentation of `Assertion` / `Matchers`. */
28
+ export interface MoneyMatchers<R = unknown> {
29
+ /** The value is Money equal to `amount` in `currency`, at any scale: `toBeMoney('12.34', 'EUR')`. */
30
+ toBeMoney(amount: string | number | bigint, currency: string): R;
31
+ /** The value is Money in `currency`. */
32
+ toHaveCurrency(currency: string): R;
33
+ /** The value is Money greater than `other` (same currency). */
34
+ toBeGreaterThanMoney(other: Money): R;
35
+ /** The value is Money greater than or equal to `other` (same currency). */
36
+ toBeGreaterThanOrEqualMoney(other: Money): R;
37
+ /** The value is Money less than `other` (same currency). */
38
+ toBeLessThanMoney(other: Money): R;
39
+ /** The value is Money less than or equal to `other` (same currency). */
40
+ toBeLessThanOrEqualMoney(other: Money): R;
41
+ /** The value is a list of Money that sums exactly to `total`, as allocations must. */
42
+ toSumTo(total: Money): R;
43
+ }
44
+ /** The matcher implementations, for `expect.extend`. */
45
+ export declare const moneyMatchers: {
46
+ toBeMoney(this: MoneyMatcherContext, received: unknown, amount: string | number | bigint, currency: string): MoneyMatcherResult;
47
+ toHaveCurrency(this: MoneyMatcherContext, received: unknown, currency: string): MoneyMatcherResult;
48
+ toBeGreaterThanMoney: (this: MoneyMatcherContext, received: unknown, other: Money) => MoneyMatcherResult;
49
+ toBeGreaterThanOrEqualMoney: (this: MoneyMatcherContext, received: unknown, other: Money) => MoneyMatcherResult;
50
+ toBeLessThanMoney: (this: MoneyMatcherContext, received: unknown, other: Money) => MoneyMatcherResult;
51
+ toBeLessThanOrEqualMoney: (this: MoneyMatcherContext, received: unknown, other: Money) => MoneyMatcherResult;
52
+ toSumTo(this: MoneyMatcherContext, received: unknown, total: Money): MoneyMatcherResult;
53
+ };
54
+ /** The parts of Vitest's and Jest's `expect` that `installMoneyMatchers` uses. */
55
+ export interface ExpectWithExtensions {
56
+ extend(matchers: any): void;
57
+ addEqualityTesters?(testers: any[]): void;
58
+ addSnapshotSerializer?(serializer: any): void;
59
+ }
60
+ /**
61
+ * Installs everything on a test runner's `expect`: the matchers, value equality and the snapshot
62
+ * serializer. It also gives Money a Node inspect hook, so `console.log(price)` prints
63
+ * `Money(12.34 EUR)`. Safe to call more than once. A runner that lacks `addEqualityTesters` or
64
+ * `addSnapshotSerializer` (Bun supports neither) gets the matchers and the inspect hook.
65
+ */
66
+ export declare function installMoneyMatchers(expect: ExpectWithExtensions): void;
67
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAA8C,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAErF,sFAAsF;AACtF,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,GAAG,SAAS,CAAC;AAEzF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,mBAO3B,CAAC;AAEF,+FAA+F;AAC/F,eAAO,MAAM,eAAe;kBACZ,OAAO,KAAG,OAAO;uBACZ,OAAO,KAAG,MAAM;CACpC,CAAC;AAEF,oGAAoG;AACpG,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,wDAAwD;AACxD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,+FAA+F;AAC/F,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACxC,qGAAqG;IACrG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,CAAC;IACjE,wCAAwC;IACxC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,CAAC;IACpC,+DAA+D;IAC/D,oBAAoB,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC;IACtC,2EAA2E;IAC3E,2BAA2B,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC;IAC7C,4DAA4D;IAC5D,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC;IACnC,wEAAwE;IACxE,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC;IAC1C,sFAAsF;IACtF,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC;CAC1B;AA8ED,wDAAwD;AACxD,eAAO,MAAM,aAAa;oBAEhB,mBAAmB,YACf,OAAO,UACT,MAAM,GAAG,MAAM,GAAG,MAAM,YACtB,MAAM,GACf,kBAAkB;yBAOb,mBAAmB,YACf,OAAO,YACP,MAAM,GACf,kBAAkB;iCAzBb,mBAAmB,YAAY,OAAO,SAAS,KAAK,KAAK,kBAAkB;wCAA3E,mBAAmB,YAAY,OAAO,SAAS,KAAK,KAAK,kBAAkB;8BAA3E,mBAAmB,YAAY,OAAO,SAAS,KAAK,KAAK,kBAAkB;qCAA3E,mBAAmB,YAAY,OAAO,SAAS,KAAK,KAAK,kBAAkB;kBA6CrE,mBAAmB,YAAY,OAAO,SAAS,KAAK,GAAG,kBAAkB;CAqBxF,CAAC;AAEF,kFAAkF;AAClF,MAAM,WAAW,oBAAoB;IAEnC,MAAM,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI,CAAC;IAE5B,kBAAkB,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAE1C,qBAAqB,CAAC,CAAC,UAAU,EAAE,GAAG,GAAG,IAAI,CAAC;CAC/C;AAID;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CA2BvE"}