@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/pg.js ADDED
@@ -0,0 +1,343 @@
1
+ /**
2
+ * `@ducatjs/money/pg`: node-postgres type parsers that keep NUMERIC and BIGINT exact, a Postgres
3
+ * composite type for Money with a `sum` aggregate, and a guard for NUMERIC(p, s) columns.
4
+ *
5
+ * import pg from 'pg';
6
+ * import { exactTypes } from '@ducatjs/money/pg';
7
+ *
8
+ * const pool = new pg.Pool({ types: exactTypes(pg.types) });
9
+ *
10
+ * It imports nothing from pg at run time: pass `pg.types` in.
11
+ */
12
+ import { currencyExponent } from './currency.js';
13
+ import { money as parseMoney, toDecimal } from './money.js';
14
+ import { checkDecimalSize, checkIntegerDigits } from './sql.js';
15
+ export { DEFAULT_DECIMAL_SIZE } from './sql.js';
16
+ const INT8_OID = 20;
17
+ const NUMERIC_OID = 1700;
18
+ const INT8_ARRAY_OID = 1016;
19
+ const NUMERIC_ARRAY_OID = 1231;
20
+ const INTEGER_TEXT = /^-?\d+$/;
21
+ const NUMERIC_TEXT = /^-?\d+(?:\.\d+)?$/;
22
+ /**
23
+ * BIGINT text as a bigint. node-postgres returns it as a string by default, which is exact but not
24
+ * a number type the Money constructors accept.
25
+ */
26
+ export function parseBigInt(text) {
27
+ if (!INTEGER_TEXT.test(text)) {
28
+ throw new SyntaxError(`Expected BIGINT text, got "${text}"`);
29
+ }
30
+ return BigInt(text);
31
+ }
32
+ /** BIGINT text, checked and kept as the string. */
33
+ function checkBigIntText(text) {
34
+ parseBigInt(text);
35
+ return text;
36
+ }
37
+ /**
38
+ * NUMERIC text, checked and returned as the exact decimal string. Postgres NUMERIC can hold `NaN`
39
+ * and `±Infinity`, which are not amounts, so they throw.
40
+ */
41
+ export function parseNumeric(text) {
42
+ if (!NUMERIC_TEXT.test(text)) {
43
+ throw new SyntaxError(`Expected a finite NUMERIC, got "${text}"`);
44
+ }
45
+ return text;
46
+ }
47
+ /**
48
+ * Splits Postgres array text (`{1,2,NULL}`, `{{1,2},{3,4}}`, `{"(1.00,EUR)"}`) into nested arrays of
49
+ * element text, with `null` for NULL. Quoted elements are unescaped.
50
+ */
51
+ export function parseArray(text) {
52
+ let position = 0;
53
+ // an array may start with explicit bounds, `[1:2]={...}`
54
+ if (text.startsWith('[')) {
55
+ position = text.indexOf('=') + 1;
56
+ }
57
+ function parseLevel() {
58
+ if (text[position] !== '{') {
59
+ throw new SyntaxError(`Malformed array text "${text}"`);
60
+ }
61
+ position++;
62
+ const items = [];
63
+ if (text[position] === '}') {
64
+ position++;
65
+ return items;
66
+ }
67
+ for (;;) {
68
+ if (text[position] === '{') {
69
+ items.push(parseLevel());
70
+ }
71
+ else if (text[position] === '"') {
72
+ position++;
73
+ let value = '';
74
+ while (text[position] !== '"') {
75
+ if (position >= text.length) {
76
+ throw new SyntaxError(`Unterminated quote in array text "${text}"`);
77
+ }
78
+ if (text[position] === '\\') {
79
+ position++;
80
+ }
81
+ value += text[position];
82
+ position++;
83
+ }
84
+ position++;
85
+ items.push(value);
86
+ }
87
+ else {
88
+ const start = position;
89
+ while (position < text.length && text[position] !== ',' && text[position] !== '}') {
90
+ position++;
91
+ }
92
+ const value = text.slice(start, position);
93
+ items.push(value === 'NULL' ? null : value);
94
+ }
95
+ if (text[position] === ',') {
96
+ position++;
97
+ continue;
98
+ }
99
+ if (text[position] === '}') {
100
+ position++;
101
+ return items;
102
+ }
103
+ throw new SyntaxError(`Malformed array text "${text}"`);
104
+ }
105
+ }
106
+ const result = parseLevel();
107
+ if (position !== text.length) {
108
+ throw new SyntaxError(`Malformed array text "${text}"`);
109
+ }
110
+ return result;
111
+ }
112
+ function mapArray(items, parseElement) {
113
+ return items.map((item) => {
114
+ if (item === null) {
115
+ return null;
116
+ }
117
+ if (Array.isArray(item)) {
118
+ return mapArray(item, parseElement);
119
+ }
120
+ return parseElement(item);
121
+ });
122
+ }
123
+ /**
124
+ * Splits record text, `(12.34,EUR)` or `(12.34,"A\\B")`, into its fields, with `null` for an empty
125
+ * unquoted field. Quoted fields are unescaped: Postgres doubles `"` and `\` inside them on output,
126
+ * and accepts a backslash before any character on input.
127
+ */
128
+ function parseRecord(text) {
129
+ if (!text.startsWith('(') || !text.endsWith(')')) {
130
+ throw new SyntaxError(`Expected record text in parentheses, got "${text}"`);
131
+ }
132
+ const fields = [];
133
+ let position = 1;
134
+ const end = text.length - 1;
135
+ for (;;) {
136
+ let field = '';
137
+ let isQuotedOrNonEmpty = false;
138
+ while (position < end && text[position] !== ',') {
139
+ const character = text[position];
140
+ if (character === '"') {
141
+ isQuotedOrNonEmpty = true;
142
+ position++;
143
+ for (;;) {
144
+ if (position >= end) {
145
+ throw new SyntaxError(`Unbalanced quote in record text "${text}"`);
146
+ }
147
+ const quoted = text[position];
148
+ if (quoted === '\\') {
149
+ field += text[position + 1] ?? '';
150
+ position += 2;
151
+ }
152
+ else if (quoted === '"' && text[position + 1] === '"') {
153
+ field += '"';
154
+ position += 2;
155
+ }
156
+ else if (quoted === '"') {
157
+ position++;
158
+ break;
159
+ }
160
+ else {
161
+ field += quoted;
162
+ position++;
163
+ }
164
+ }
165
+ continue;
166
+ }
167
+ if (character === '\\') {
168
+ field += text[position + 1] ?? '';
169
+ position += 2;
170
+ }
171
+ else {
172
+ field += character;
173
+ position++;
174
+ }
175
+ isQuotedOrNonEmpty = true;
176
+ }
177
+ fields.push(isQuotedOrNonEmpty ? field : null);
178
+ if (position >= end) {
179
+ return fields;
180
+ }
181
+ position++;
182
+ }
183
+ }
184
+ /**
185
+ * Money from the text of the composite type, `(12.34,EUR)`. The amount keeps the scale it was
186
+ * stored with, and never less than the currency's exponent. NULL fields, `NaN`, `Infinity` and
187
+ * malformed text throw.
188
+ */
189
+ export function parseMoneyComposite(text) {
190
+ const fields = parseRecord(text);
191
+ if (fields.length !== 2) {
192
+ throw new SyntaxError(`Expected (amount,currency) composite text, got "${text}"`);
193
+ }
194
+ const [amount, currency] = fields;
195
+ if (amount === null || amount === undefined || currency === null || currency === undefined) {
196
+ throw new TypeError(`A money composite needs both an amount and a currency, got "${text}"`);
197
+ }
198
+ parseNumeric(amount);
199
+ const fractionDigits = amount.includes('.') ? amount.length - amount.indexOf('.') - 1 : 0;
200
+ const scale = Math.max(fractionDigits, currencyExponent(currency));
201
+ return parseMoney(amount, currency, { scale });
202
+ }
203
+ /**
204
+ * The composite type's text for a query parameter: `(12.34,"EUR")`, at the Money's own scale. The
205
+ * currency is always quoted and escaped, so custom codes containing `\`, `"`, `,` or `)` survive.
206
+ * Cast it in SQL (`$1::ducat_money`) where Postgres can't infer the type.
207
+ */
208
+ export function toMoneyComposite(money) {
209
+ const currency = money.currency.replace(/["\\]/g, (character) => `\\${character}`);
210
+ return `(${toDecimal(money)},"${currency}")`;
211
+ }
212
+ /**
213
+ * Type parsers for node-postgres that never turn an amount into a float: BIGINT as bigint, NUMERIC
214
+ * as an exact string (rejecting `NaN` and `Infinity`), their arrays the same way (pg's default
215
+ * parses NUMERIC arrays as floats), and optionally the composite money type as Money. Every other
216
+ * type uses `base`.
217
+ *
218
+ * new pg.Pool({ types: exactTypes(pg.types, { money: await moneyTypeOids(client) }) })
219
+ */
220
+ export function exactTypes(base, options) {
221
+ const moneyOids = options?.money;
222
+ const parseInt8 = options?.int8 === 'string' ? checkBigIntText : parseBigInt;
223
+ return {
224
+ getTypeParser(oid, format) {
225
+ // the binary format is only used when a query asks for it; leave it to pg
226
+ if (format === 'binary') {
227
+ return base.getTypeParser(oid, format);
228
+ }
229
+ if (oid === INT8_OID) {
230
+ return parseInt8;
231
+ }
232
+ if (oid === NUMERIC_OID) {
233
+ return parseNumeric;
234
+ }
235
+ if (oid === INT8_ARRAY_OID) {
236
+ return (text) => mapArray(parseArray(text), parseInt8);
237
+ }
238
+ if (oid === NUMERIC_ARRAY_OID) {
239
+ return (text) => mapArray(parseArray(text), parseNumeric);
240
+ }
241
+ if (moneyOids !== undefined && oid === moneyOids.oid) {
242
+ return parseMoneyComposite;
243
+ }
244
+ if (moneyOids !== undefined && oid === moneyOids.arrayOid) {
245
+ return (text) => mapArray(parseArray(text), parseMoneyComposite);
246
+ }
247
+ return base.getTypeParser(oid, format);
248
+ },
249
+ };
250
+ }
251
+ /** The default name of the composite money type. */
252
+ export const MONEY_TYPE_NAME = 'ducat_money';
253
+ const IDENTIFIER = /^[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)?$/;
254
+ // Postgres truncates longer names, which would make `name` and `name_add` collide with others
255
+ const MAX_IDENTIFIER_LENGTH = 63;
256
+ /**
257
+ * The DDL for a composite money type, `(amount numeric, currency text)`, and a `sum` aggregate over
258
+ * it that throws on mixed currencies, and on a value with a NULL amount or currency. The amount is
259
+ * an unconstrained NUMERIC, so it keeps every digit and its scale. A schema-qualified name puts the
260
+ * type, its add function and the aggregate in that schema.
261
+ *
262
+ * Run the statements once, in order, in a migration: they are not idempotent, and a second run
263
+ * fails with `duplicate_object`.
264
+ *
265
+ * for (const statement of moneyTypeSql()) await client.query(statement);
266
+ * SELECT sum(price) FROM items; -- (37.50,EUR)
267
+ */
268
+ export function moneyTypeSql(name = MONEY_TYPE_NAME) {
269
+ if (!IDENTIFIER.test(name)) {
270
+ throw new RangeError(`"${name}" is not a plain lower-case SQL name (optionally schema.name)`);
271
+ }
272
+ const dot = name.indexOf('.');
273
+ const schemaPrefix = dot < 0 ? '' : name.slice(0, dot + 1);
274
+ const typeName = name.slice(dot + 1);
275
+ if (`${typeName}_add`.length > MAX_IDENTIFIER_LENGTH) {
276
+ throw new RangeError(`"${typeName}" is too long for a Postgres name with an "_add" suffix`);
277
+ }
278
+ const add = `${name}_add`;
279
+ return [
280
+ `CREATE TYPE ${name} AS (amount numeric, currency text)`,
281
+ // search_path FROM CURRENT: the cast below resolves the same way whatever the caller's path
282
+ `CREATE FUNCTION ${add}(total ${name}, item ${name}) RETURNS ${name}
283
+ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE SET search_path FROM CURRENT AS $$
284
+ BEGIN
285
+ IF item IS NULL THEN
286
+ RETURN total;
287
+ END IF;
288
+ IF item.amount IS NULL OR item.currency IS NULL THEN
289
+ RAISE EXCEPTION 'a money value needs both an amount and a currency'
290
+ USING ERRCODE = 'null_value_not_allowed';
291
+ END IF;
292
+ IF total IS NULL THEN
293
+ RETURN item;
294
+ END IF;
295
+ IF total.currency IS DISTINCT FROM item.currency THEN
296
+ RAISE EXCEPTION 'cannot add % and % amounts', total.currency, item.currency
297
+ USING ERRCODE = 'data_exception';
298
+ END IF;
299
+ RETURN ROW(total.amount + item.amount, total.currency)::${name};
300
+ END
301
+ $$`,
302
+ `CREATE AGGREGATE ${schemaPrefix}sum(${name}) ` +
303
+ `(SFUNC = ${add}, STYPE = ${name}, COMBINEFUNC = ${add}, PARALLEL = SAFE)`,
304
+ ];
305
+ }
306
+ /**
307
+ * Looks up the OIDs of the composite money type, for `exactTypes`. Run it once at start-up on a
308
+ * client without the money parser. OIDs differ between databases, and change if the type is
309
+ * dropped and recreated; values of a type whose OID `exactTypes` wasn't given come back as pg's
310
+ * raw text. With a type per schema, look up and pass each one's OIDs.
311
+ */
312
+ export async function moneyTypeOids(client, name = MONEY_TYPE_NAME) {
313
+ const result = await client.query('SELECT oid, typarray FROM pg_type WHERE oid = to_regtype($1)', [name]);
314
+ const row = result.rows[0];
315
+ if (row === undefined) {
316
+ throw new RangeError(`No type "${name}" in this database; run moneyTypeSql() first`);
317
+ }
318
+ return { oid: Number(row.oid), arrayOid: Number(row.typarray) };
319
+ }
320
+ /**
321
+ * The exact decimal string for a NUMERIC(precision, scale) column, or a throw if Postgres would
322
+ * change it: it rounds extra decimals silently on insert, and rejects too many integer digits.
323
+ * Unlike the column types, any scale up to the column's is accepted, and trailing zeros beyond it
324
+ * are dropped since they don't change the value.
325
+ */
326
+ export function toNumeric(money, size) {
327
+ checkDecimalSize(money.currency, size);
328
+ const decimal = toDecimal(money);
329
+ const [integerPart, fraction = ''] = decimal.replace('-', '').split('.');
330
+ const significantFraction = fraction.replace(/0+$/, '');
331
+ if (significantFraction.length > size.scale) {
332
+ throw new RangeError(`${money} has ${significantFraction.length} decimals; NUMERIC(${size.precision},${size.scale}) ` +
333
+ `would round it to ${size.scale}`);
334
+ }
335
+ checkIntegerDigits(money, decimal, size);
336
+ if (fraction.length <= size.scale) {
337
+ return decimal;
338
+ }
339
+ const sign = decimal.startsWith('-') ? '-' : '';
340
+ const kept = fraction.slice(0, size.scale);
341
+ return kept === '' ? `${sign}${integerPart}` : `${sign}${integerPart}.${kept}`;
342
+ }
343
+ //# sourceMappingURL=pg.js.map
package/dist/pg.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pg.js","sourceRoot":"","sources":["../src/pg.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,21 @@
1
1
  /** `@ducatjs/money/all`: core plus every opt-in module, with all methods installed. */
2
2
  export * from '../index.js';
3
- export { add, subtract, negate, abs, multiply, divide, round, toScale, compare, equals, lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, isZero, isPositive, isNegative, sign, min, max, clamp, toDecimal, toMinor, toMinorNumber, } from '../money.js';
3
+ export { add, subtract, negate, abs, multiply, divide, round, toScale, compare, equals, identical, lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, isZero, isPositive, isNegative, sign, min, max, clamp, majorPart, minorPart, toDecimal, toMinor, toMinorNumber, } from '../money.js';
4
+ export { roundingPolicy, isoPolicy, assertScale, type RoundingPolicyOptions } from '../policy.js';
4
5
  export * from './allocate.js';
5
6
  export * from './tax.js';
6
7
  export * from './convert.js';
7
8
  export * from './exact.js';
8
9
  export * from '../ecb.js';
10
+ export * from '../boc.js';
11
+ export * from '../boe.js';
12
+ export * from '../hmrc.js';
13
+ export * from '../fed.js';
14
+ export * from '../history.js';
9
15
  export * from '../stripe-fx.js';
10
16
  export * from '../adyen-fx.js';
11
17
  export * from './cash.js';
18
+ export * from '../historic.js';
12
19
  export * from './format.js';
13
20
  export * from '../parse.js';
14
21
  export * from './stripe.js';
@@ -1 +1 @@
1
- {"version":3,"file":"all.d.ts","sourceRoot":"","sources":["../../src/plugins/all.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,cAAc,aAAa,CAAC;AAE5B,OAAO,EACL,GAAG,EACH,QAAQ,EACR,MAAM,EACN,GAAG,EACH,QAAQ,EACR,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACP,MAAM,EACN,QAAQ,EACR,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,MAAM,EACN,UAAU,EACV,UAAU,EACV,IAAI,EACJ,GAAG,EACH,GAAG,EACH,KAAK,EACL,SAAS,EACT,OAAO,EACP,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"all.d.ts","sourceRoot":"","sources":["../../src/plugins/all.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,cAAc,aAAa,CAAC;AAE5B,OAAO,EACL,GAAG,EACH,QAAQ,EACR,MAAM,EACN,GAAG,EACH,QAAQ,EACR,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACP,MAAM,EACN,SAAS,EACT,QAAQ,EACR,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,MAAM,EACN,UAAU,EACV,UAAU,EACV,IAAI,EACJ,GAAG,EACH,GAAG,EACH,KAAK,EACL,SAAS,EACT,SAAS,EACT,SAAS,EACT,OAAO,EACP,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAClG,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -1,15 +1,22 @@
1
1
  /** `@ducatjs/money/all`: core plus every opt-in module, with all methods installed. */
2
2
  export * from '../index.js';
3
3
  // The core Money methods as plain functions too, as in /fn: /all is everything.
4
- export { add, subtract, negate, abs, multiply, divide, round, toScale, compare, equals, lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, isZero, isPositive, isNegative, sign, min, max, clamp, toDecimal, toMinor, toMinorNumber, } from '../money.js';
4
+ export { add, subtract, negate, abs, multiply, divide, round, toScale, compare, equals, identical, lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, isZero, isPositive, isNegative, sign, min, max, clamp, majorPart, minorPart, toDecimal, toMinor, toMinorNumber, } from '../money.js';
5
+ export { roundingPolicy, isoPolicy, assertScale } from '../policy.js';
5
6
  export * from './allocate.js';
6
7
  export * from './tax.js';
7
8
  export * from './convert.js';
8
9
  export * from './exact.js';
9
10
  export * from '../ecb.js';
11
+ export * from '../boc.js';
12
+ export * from '../boe.js';
13
+ export * from '../hmrc.js';
14
+ export * from '../fed.js';
15
+ export * from '../history.js';
10
16
  export * from '../stripe-fx.js';
11
17
  export * from '../adyen-fx.js';
12
18
  export * from './cash.js';
19
+ export * from '../historic.js';
13
20
  export * from './format.js';
14
21
  export * from '../parse.js';
15
22
  export * from './stripe.js';
@@ -1 +1 @@
1
- {"version":3,"file":"all.js","sourceRoot":"","sources":["../../src/plugins/all.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,cAAc,aAAa,CAAC;AAC5B,gFAAgF;AAChF,OAAO,EACL,GAAG,EACH,QAAQ,EACR,MAAM,EACN,GAAG,EACH,QAAQ,EACR,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACP,MAAM,EACN,QAAQ,EACR,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,MAAM,EACN,UAAU,EACV,UAAU,EACV,IAAI,EACJ,GAAG,EACH,GAAG,EACH,KAAK,EACL,SAAS,EACT,OAAO,EACP,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"all.js","sourceRoot":"","sources":["../../src/plugins/all.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,cAAc,aAAa,CAAC;AAC5B,gFAAgF;AAChF,OAAO,EACL,GAAG,EACH,QAAQ,EACR,MAAM,EACN,GAAG,EACH,QAAQ,EACR,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACP,MAAM,EACN,SAAS,EACT,QAAQ,EACR,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,MAAM,EACN,UAAU,EACV,UAAU,EACV,IAAI,EACJ,GAAG,EACH,GAAG,EACH,KAAK,EACL,SAAS,EACT,SAAS,EACT,SAAS,EACT,OAAO,EACP,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,WAAW,EAA8B,MAAM,cAAc,CAAC;AAClG,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -2,19 +2,18 @@ import type { Factor } from '../decimal.js';
2
2
  import type { ProviderConvertOptions } from '../convert.js';
3
3
  import type { RateProvider } from '../provider.js';
4
4
  import type { ConvertOptions, Rate } from '../rate.js';
5
- import type { RoundingMode } from '../rounding.js';
6
5
  export * from '../convert.js';
7
6
  declare module '../money.js' {
8
7
  interface Money<C extends string> {
9
8
  /** Converts at a Rate, rounding once: `eur.convert(eurUsd, 'halfEven')`. */
10
- convert<To extends string>(rate: Rate<NoInfer<C>, To>, rounding?: RoundingMode, options?: ConvertOptions): Money<To>;
9
+ convert<To extends string>(rate: Rate<NoInfer<C>, To>, rounding?: RoundingInput, options?: ConvertOptions): Money<To>;
11
10
  /** Converts at an exact decimal-string rate: `eur.convert('USD', '1.0832', 'halfEven')`. */
12
- convert<To extends CurrencyInput>(to: To, rate: Factor, rounding?: RoundingMode, options?: ConvertOptions): Money<To>;
11
+ convert<To extends CurrencyInput>(to: To, rate: Factor, rounding?: RoundingInput, options?: ConvertOptions): Money<To>;
13
12
  /**
14
13
  * Converts through a provider, rounding once: `usd.convert('JPY', via('EUR', ecb),
15
14
  * 'halfEven')`.
16
15
  */
17
- convert<To extends CurrencyInput>(to: To, provider: RateProvider, rounding?: RoundingMode, options?: ProviderConvertOptions): Money<To>;
16
+ convert<To extends CurrencyInput>(to: To, provider: RateProvider, rounding?: RoundingInput, options?: ProviderConvertOptions): Money<To>;
18
17
  }
19
18
  }
20
19
  //# sourceMappingURL=convert.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/plugins/convert.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,cAAc,eAAe,CAAC;AAE9B,OAAO,QAAQ,aAAa,CAAC;IAC3B,UAAU,KAAK,CAAC,CAAC,SAAS,MAAM;QAC9B,4EAA4E;QAC5E,OAAO,CAAC,EAAE,SAAS,MAAM,EACvB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAC1B,QAAQ,CAAC,EAAE,YAAY,EACvB,OAAO,CAAC,EAAE,cAAc,GACvB,KAAK,CAAC,EAAE,CAAC,CAAC;QACb,4FAA4F;QAC5F,OAAO,CAAC,EAAE,SAAS,aAAa,EAC9B,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,YAAY,EACvB,OAAO,CAAC,EAAE,cAAc,GACvB,KAAK,CAAC,EAAE,CAAC,CAAC;QACb;;;WAGG;QACH,OAAO,CAAC,EAAE,SAAS,aAAa,EAC9B,EAAE,EAAE,EAAE,EACN,QAAQ,EAAE,YAAY,EACtB,QAAQ,CAAC,EAAE,YAAY,EACvB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,KAAK,CAAC,EAAE,CAAC,CAAC;KACd;CACF"}
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/plugins/convert.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvD,cAAc,eAAe,CAAC;AAE9B,OAAO,QAAQ,aAAa,CAAC;IAC3B,UAAU,KAAK,CAAC,CAAC,SAAS,MAAM;QAC9B,4EAA4E;QAC5E,OAAO,CAAC,EAAE,SAAS,MAAM,EACvB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAC1B,QAAQ,CAAC,EAAE,aAAa,EACxB,OAAO,CAAC,EAAE,cAAc,GACvB,KAAK,CAAC,EAAE,CAAC,CAAC;QACb,4FAA4F;QAC5F,OAAO,CAAC,EAAE,SAAS,aAAa,EAC9B,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,aAAa,EACxB,OAAO,CAAC,EAAE,cAAc,GACvB,KAAK,CAAC,EAAE,CAAC,CAAC;QACb;;;WAGG;QACH,OAAO,CAAC,EAAE,SAAS,aAAa,EAC9B,EAAE,EAAE,EAAE,EACN,QAAQ,EAAE,YAAY,EACtB,QAAQ,CAAC,EAAE,aAAa,EACxB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,KAAK,CAAC,EAAE,CAAC,CAAC;KACd;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"convert.js","sourceRoot":"","sources":["../../src/plugins/convert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,WAAW,EAAkC,MAAM,aAAa,CAAC;AAM1E,cAAc,eAAe,CAAC;AA8B9B,WAAW,CACT;IACE,OAAO,CAAc,GAAG,IAAqC;QAC3D,OAAQ,OAAuD,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;IACjF,CAAC;CACF,EACD,EAAE,KAAK,EAAE,wBAAwB,EAAE,CACpC,CAAC"}
1
+ {"version":3,"file":"convert.js","sourceRoot":"","sources":["../../src/plugins/convert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,WAAW,EAAsD,MAAM,aAAa,CAAC;AAK9F,cAAc,eAAe,CAAC;AA8B9B,WAAW,CACT;IACE,OAAO,CAAc,GAAG,IAAqC;QAC3D,OAAQ,OAAuD,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;IACjF,CAAC;CACF,EACD,EAAE,KAAK,EAAE,wBAAwB,EAAE,CACpC,CAAC"}
@@ -3,19 +3,18 @@
3
3
  * methods below.
4
4
  */
5
5
  import type { Factor } from '../decimal.js';
6
- import type { RoundingMode } from '../rounding.js';
7
6
  import { type TaxBreakdown } from '../tax.js';
8
7
  export * from '../tax.js';
9
8
  declare module '../money.js' {
10
9
  interface Money<C extends string> {
11
10
  /** `percent`% of this, e.g. `price.percentage(15, 'halfEven')` */
12
- percentage(percent: Factor, rounding?: RoundingMode): Money<C>;
11
+ percentage(percent: Factor, rounding?: RoundingInput): Money<C>;
13
12
  /** this minus `percent`% */
14
- discount(percent: Factor, rounding?: RoundingMode): Money<C>;
13
+ discount(percent: Factor, rounding?: RoundingInput): Money<C>;
15
14
  /** Treats this as net: `{ net, tax, gross }`, net + tax = gross exactly. */
16
- addTax(percent: Factor, rounding?: RoundingMode): TaxBreakdown<C>;
15
+ addTax(percent: Factor, rounding?: RoundingInput): TaxBreakdown<C>;
17
16
  /** Treats this as gross: `{ net, tax, gross }`, net + tax = gross exactly. */
18
- extractTax(percent: Factor, rounding?: RoundingMode): TaxBreakdown<C>;
17
+ extractTax(percent: Factor, rounding?: RoundingInput): TaxBreakdown<C>;
19
18
  }
20
19
  }
21
20
  //# sourceMappingURL=tax.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tax.d.ts","sourceRoot":"","sources":["../../src/plugins/tax.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAA4C,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAExF,cAAc,WAAW,CAAC;AAE1B,OAAO,QAAQ,aAAa,CAAC;IAC3B,UAAU,KAAK,CAAC,CAAC,SAAS,MAAM;QAC9B,kEAAkE;QAClE,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/D,4BAA4B;QAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7D,4EAA4E;QAC5E,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QAClE,8EAA8E;QAC9E,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;KACvE;CACF"}
1
+ {"version":3,"file":"tax.d.ts","sourceRoot":"","sources":["../../src/plugins/tax.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAA4C,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAExF,cAAc,WAAW,CAAC;AAE1B,OAAO,QAAQ,aAAa,CAAC;IAC3B,UAAU,KAAK,CAAC,CAAC,SAAS,MAAM;QAC9B,kEAAkE;QAClE,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAChE,4BAA4B;QAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9D,4EAA4E;QAC5E,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QACnE,8EAA8E;QAC9E,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;KACxE;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"tax.js","sourceRoot":"","sources":["../../src/plugins/tax.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAc,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAqB,MAAM,WAAW,CAAC;AAExF,cAAc,WAAW,CAAC;AAe1B,WAAW,CACT;IACE,UAAU,CAAc,OAAe,EAAE,QAAuB;QAC9D,OAAO,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC7C,CAAC;IACD,QAAQ,CAAc,OAAe,EAAE,QAAuB;QAC5D,OAAO,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,CAAc,OAAe,EAAE,QAAuB;QAC1D,OAAO,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IACzC,CAAC;IACD,UAAU,CAAc,OAAe,EAAE,QAAuB;QAC9D,OAAO,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC7C,CAAC;CACF,EACD,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAChC,CAAC"}
1
+ {"version":3,"file":"tax.js","sourceRoot":"","sources":["../../src/plugins/tax.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAkC,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAqB,MAAM,WAAW,CAAC;AAExF,cAAc,WAAW,CAAC;AAe1B,WAAW,CACT;IACE,UAAU,CAAc,OAAe,EAAE,QAAwB;QAC/D,OAAO,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC7C,CAAC;IACD,QAAQ,CAAc,OAAe,EAAE,QAAwB;QAC7D,OAAO,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,CAAc,OAAe,EAAE,QAAwB;QAC3D,OAAO,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IACzC,CAAC;IACD,UAAU,CAAc,OAAe,EAAE,QAAwB;QAC/D,OAAO,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC7C,CAAC;CACF,EACD,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAChC,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { type Money, type RoundingPolicy } from './money.js';
2
+ import { type RoundingMode } from './rounding.js';
3
+ export type { RoundingInput, RoundingPolicy } from './money.js';
4
+ /** Options for `roundingPolicy`. Every field is optional. */
5
+ export interface RoundingPolicyOptions {
6
+ /** Decimal places of the result (default: the currency's ISO exponent). */
7
+ readonly scale?: number;
8
+ /** A positive safe integer count of 10^-scale units to round to (default 1). */
9
+ readonly increment?: number;
10
+ /** The rounding mode (default 'halfExpand'). */
11
+ readonly rounding?: RoundingMode;
12
+ /** Restrict the policy to one currency. */
13
+ readonly currency?: string;
14
+ }
15
+ /**
16
+ * Builds a reusable rounding policy (see `RoundingPolicy`). The options are checked here, once, so
17
+ * applying the policy needs no checks.
18
+ *
19
+ * const unitPrice = roundingPolicy({ scale: 4, rounding: 'halfEven' })
20
+ * const fiveCents = roundingPolicy({ increment: 5 }) // 0.05 in a 2-decimal currency
21
+ */
22
+ export declare function roundingPolicy(options?: RoundingPolicyOptions): RoundingPolicy;
23
+ /**
24
+ * Rounding to the currency's ISO exponent, half up: the policy most amounts should end with.
25
+ * `assertScale(m, isoPolicy)` checks that an amount is already there.
26
+ */
27
+ export declare const isoPolicy: RoundingPolicy;
28
+ /**
29
+ * Throws a RangeError unless `money` is exactly what `policy` produces: at the policy's scale and a
30
+ * multiple of its increment (and in its currency, if it has one). Use it where an amount must
31
+ * already be rounded, such as before booking it or sending it to a processor.
32
+ */
33
+ export declare function assertScale(money: Money, policy: RoundingPolicy): void;
34
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAQA,OAAO,EAML,KAAK,KAAK,EACV,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAA8B,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAE9E,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEhE,6DAA6D;AAC7D,MAAM,WAAW,qBAAqB;IACpC,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACjC,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAKD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAoDlF;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,cAAiD,CAAC;AAE1E;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAetE"}
package/dist/policy.js ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * `@ducatjs/money/policy`: rounding policies, reusable values for a scale, an increment and a
3
+ * rounding mode ("CHF cash", "4 decimals, half even"), accepted wherever a rounding mode is. The
4
+ * core only hands the work to the policy, so none of this code is in a bundle that never imports
5
+ * this entry (or `cashPolicy` from `@ducatjs/money/cash`).
6
+ */
7
+ import { currencyExponent, isCurrency } from './currency.js';
8
+ import { MAX_DECIMAL_SCALE } from './decimal.js';
9
+ import { CurrencyMismatchError, multiply, multiplyByRatio, ratio, ROUND_PRODUCT, } from './money.js';
10
+ import { DEFAULT_ROUNDING, Rounding } from './rounding.js';
11
+ const ONE = ratio(1);
12
+ const MODES = new Set(Object.values(Rounding));
13
+ /**
14
+ * Builds a reusable rounding policy (see `RoundingPolicy`). The options are checked here, once, so
15
+ * applying the policy needs no checks.
16
+ *
17
+ * const unitPrice = roundingPolicy({ scale: 4, rounding: 'halfEven' })
18
+ * const fiveCents = roundingPolicy({ increment: 5 }) // 0.05 in a 2-decimal currency
19
+ */
20
+ export function roundingPolicy(options = {}) {
21
+ const scale = options.scale;
22
+ const increment = options.increment ?? 1;
23
+ const rounding = options.rounding ?? DEFAULT_ROUNDING;
24
+ const currency = options.currency;
25
+ if (scale !== undefined &&
26
+ !(Number.isInteger(scale) && scale >= 0 && scale <= MAX_DECIMAL_SCALE)) {
27
+ throw new RangeError(`Invalid scale ${scale}`);
28
+ }
29
+ if (!(Number.isSafeInteger(increment) && increment >= 1)) {
30
+ throw new RangeError(`Invalid increment ${increment}; it must be a positive integer`);
31
+ }
32
+ if (!MODES.has(rounding)) {
33
+ throw new RangeError(`Invalid rounding mode ${String(rounding)}`);
34
+ }
35
+ // A typo such as 'chf' would otherwise make a policy that rejects every amount.
36
+ if (currency !== undefined && !isCurrency(currency)) {
37
+ throw new RangeError(`Unknown currency ${currency}`);
38
+ }
39
+ const incrementAsBigint = BigInt(increment);
40
+ // money × factor rounded once to the policy: with an increment, count whole increments
41
+ // (money × factor / increment, rounded at the policy's scale) and multiply back, which is
42
+ // rounding to the nearest multiple of the increment. The count's ratio is left unreduced: the
43
+ // division rounds the same either way, and a gcd per call would cost more than it saves.
44
+ const roundProduct = (money, factor) => {
45
+ if (currency !== undefined && currency !== money.currency) {
46
+ throw new CurrencyMismatchError(currency, money.currency);
47
+ }
48
+ const resultScale = scale ?? currencyExponent(money.currency);
49
+ if (increment === 1) {
50
+ return multiplyByRatio(money, factor, rounding, resultScale);
51
+ }
52
+ const stepDenominator = factor.d * increment;
53
+ const perStep = {
54
+ num: factor.num,
55
+ den: factor.den * incrementAsBigint,
56
+ n: factor.n,
57
+ // NaN (not a safe integer) sends multiplyByRatio down its bigint path
58
+ d: Number.isSafeInteger(stepDenominator) ? stepDenominator : NaN,
59
+ };
60
+ return multiply(multiplyByRatio(money, perStep, rounding, resultScale), increment);
61
+ };
62
+ const policy = (money) => roundProduct(money, ONE);
63
+ return Object.freeze(Object.assign(policy, { scale, increment, rounding, currency, [ROUND_PRODUCT]: roundProduct }));
64
+ }
65
+ /**
66
+ * Rounding to the currency's ISO exponent, half up: the policy most amounts should end with.
67
+ * `assertScale(m, isoPolicy)` checks that an amount is already there.
68
+ */
69
+ export const isoPolicy = /* @__PURE__ */ roundingPolicy();
70
+ /**
71
+ * Throws a RangeError unless `money` is exactly what `policy` produces: at the policy's scale and a
72
+ * multiple of its increment (and in its currency, if it has one). Use it where an amount must
73
+ * already be rounded, such as before booking it or sending it to a processor.
74
+ */
75
+ export function assertScale(money, policy) {
76
+ if (policy.currency !== undefined && policy.currency !== money.currency) {
77
+ throw new CurrencyMismatchError(policy.currency, money.currency);
78
+ }
79
+ const scale = policy.scale ?? currencyExponent(money.currency);
80
+ if (money.scale !== scale) {
81
+ throw new RangeError(`${money} has scale ${money.scale}, expected ${scale}`);
82
+ }
83
+ const minor = money.minor;
84
+ const increment = policy.increment;
85
+ const isMultiple = typeof minor === 'number' ? minor % increment === 0 : minor % BigInt(increment) === 0n;
86
+ if (!isMultiple) {
87
+ throw new RangeError(`${money} is not a multiple of ${increment} at scale ${scale}`);
88
+ }
89
+ }
90
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}