@vielzeug/coins 1.0.4 → 2.0.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.
- package/README.md +50 -222
- package/dist/aggregate.cjs +2 -0
- package/dist/aggregate.cjs.map +1 -0
- package/dist/aggregate.d.ts +11 -0
- package/dist/aggregate.d.ts.map +1 -0
- package/dist/aggregate.js +44 -0
- package/dist/aggregate.js.map +1 -0
- package/dist/coins.cjs +1 -1
- package/dist/coins.cjs.map +1 -1
- package/dist/coins.iife.js +1 -1
- package/dist/coins.iife.js.map +1 -1
- package/dist/coins.js +1 -1
- package/dist/coins.js.map +1 -1
- package/dist/currency.cjs +2 -0
- package/dist/currency.cjs.map +1 -0
- package/dist/currency.d.ts +38 -0
- package/dist/currency.d.ts.map +1 -0
- package/dist/currency.js +55 -0
- package/dist/currency.js.map +1 -0
- package/dist/decimal.cjs +2 -0
- package/dist/decimal.cjs.map +1 -0
- package/dist/decimal.d.ts +7 -0
- package/dist/decimal.d.ts.map +1 -0
- package/dist/decimal.js +52 -0
- package/dist/decimal.js.map +1 -0
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +6 -42
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -6
- package/dist/errors.js.map +1 -1
- package/dist/exchange.cjs +1 -1
- package/dist/exchange.cjs.map +1 -1
- package/dist/exchange.d.ts +9 -27
- package/dist/exchange.d.ts.map +1 -1
- package/dist/exchange.js +24 -12
- package/dist/exchange.js.map +1 -1
- package/dist/format.cjs +1 -1
- package/dist/format.cjs.map +1 -1
- package/dist/format.d.ts +2 -39
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +57 -94
- package/dist/format.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -5
- package/dist/money.cjs +1 -1
- package/dist/money.cjs.map +1 -1
- package/dist/money.d.ts +26 -245
- package/dist/money.d.ts.map +1 -1
- package/dist/money.js +71 -184
- package/dist/money.js.map +1 -1
- package/dist/serialization.cjs +2 -0
- package/dist/serialization.cjs.map +1 -0
- package/dist/serialization.d.ts +6 -0
- package/dist/serialization.d.ts.map +1 -0
- package/dist/serialization.js +45 -0
- package/dist/serialization.js.map +1 -0
- package/dist/types.d.ts +35 -52
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/_cache.cjs +0 -2
- package/dist/_cache.cjs.map +0 -1
- package/dist/_cache.d.ts +0 -6
- package/dist/_cache.d.ts.map +0 -1
- package/dist/_cache.js +0 -14
- package/dist/_cache.js.map +0 -1
- package/dist/_dev.cjs +0 -2
- package/dist/_dev.cjs.map +0 -1
- package/dist/_dev.d.ts +0 -2
- package/dist/_dev.d.ts.map +0 -1
- package/dist/_dev.js +0 -9
- package/dist/_dev.js.map +0 -1
- package/dist/utils.cjs +0 -2
- package/dist/utils.cjs.map +0 -1
- package/dist/utils.d.ts +0 -66
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -66
- package/dist/utils.js.map +0 -1
package/dist/money.js
CHANGED
|
@@ -1,208 +1,95 @@
|
|
|
1
1
|
import { CoinsError as e, CurrencyMismatchError as t } from "./errors.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { decimal as n, roundDivision as r, toDecimalString as i } from "./decimal.js";
|
|
3
|
+
import { isCurrency as a } from "./currency.js";
|
|
4
4
|
//#region src/money.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (typeof
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
if (typeof e == "number") {
|
|
12
|
-
let t = a(r), o = String(e), s = n.test(o) ? i(o) : o, c = s.indexOf("."), u = c === -1 ? 0 : s.length - c - 1;
|
|
13
|
-
return u > t && l(`money(): number input "${e}" has more decimal places (${u}) than ${r} supports (${t}). Use a decimal string to avoid IEEE-754 precision loss.`), {
|
|
14
|
-
amount: f(o, t),
|
|
15
|
-
currency: r
|
|
16
|
-
};
|
|
5
|
+
var o = "halfAwayFromZero";
|
|
6
|
+
function s(t, i, a) {
|
|
7
|
+
if (C(i), typeof t == "bigint") {
|
|
8
|
+
if (!a || !("unit" in a) || a.unit !== "minor") throw new e("INVALID_MONEY", "Bigint amounts require { unit: 'minor' }");
|
|
9
|
+
return b(t, i);
|
|
17
10
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
function
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return i
|
|
29
|
-
}
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
function m(e) {
|
|
37
|
-
if (typeof e != "object" || !e) return !1;
|
|
38
|
-
let t = e;
|
|
39
|
-
return Object.hasOwn(t, "amount") && typeof t.amount == "bigint" && Object.hasOwn(t, "currency") && typeof t.currency == "string";
|
|
40
|
-
}
|
|
41
|
-
function h(e, t) {
|
|
42
|
-
return d(e, t), {
|
|
43
|
-
amount: e.amount + t.amount,
|
|
44
|
-
currency: e.currency
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
function g(e, t) {
|
|
48
|
-
return d(e, t), {
|
|
49
|
-
amount: e.amount - t.amount,
|
|
50
|
-
currency: e.currency
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
function _(e, t, n = "half-away-from-zero") {
|
|
54
|
-
let { denominator: i, negative: a, numerator: s } = o(String(t)), c = e.amount < 0n !== a, l = (e.amount < 0n ? -e.amount : e.amount) * s, u = r(l / i, l % i, i, n, c);
|
|
55
|
-
return {
|
|
56
|
-
amount: c ? -u : u,
|
|
57
|
-
currency: e.currency
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
function v(t, n, i = "half-away-from-zero") {
|
|
61
|
-
let { denominator: a, negative: s, numerator: c } = o(String(n));
|
|
62
|
-
if (c === 0n) throw new e("Division by zero");
|
|
63
|
-
let l = t.amount < 0n !== s, u = (t.amount < 0n ? -t.amount : t.amount) * a, d = r(u / c, u % c, c, i, l);
|
|
64
|
-
return {
|
|
65
|
-
amount: l ? -d : d,
|
|
66
|
-
currency: t.currency
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
function y(t, n) {
|
|
70
|
-
if (n.length === 0) throw new e("allocate requires at least one ratio");
|
|
71
|
-
let r = n.map((e) => o(String(e)));
|
|
72
|
-
if (r.some((e) => e.negative)) throw new e("All ratios must be non-negative");
|
|
73
|
-
if (!r.some((e) => e.numerator > 0n)) throw new e("At least one ratio must be positive");
|
|
74
|
-
let i = t.amount < 0n, a = i ? -t.amount : t.amount, s = r.reduce((e, t) => t.denominator > e ? t.denominator : e, 1n), c = r.map((e) => e.numerator * (s / e.denominator)), l = c.reduce((e, t) => e + t, 0n), u = [], d = [];
|
|
75
|
-
for (let e of c) {
|
|
76
|
-
let t = a * e;
|
|
77
|
-
u.push(t / l), d.push(t % l);
|
|
11
|
+
if (a && "unit" in a) throw new e("INVALID_MONEY", "Decimal strings do not accept a unit option");
|
|
12
|
+
let s = n(t), c = s.numerator * 10n ** BigInt(i.minorUnit);
|
|
13
|
+
if (c % s.denominator !== 0n && !a) throw new e("INVALID_MONEY", `Amount "${t}" exceeds ${i.code} precision; provide rounding`);
|
|
14
|
+
return b(r(c, s.denominator, a?.rounding ?? o), i);
|
|
15
|
+
}
|
|
16
|
+
function c(t) {
|
|
17
|
+
if (!w(t)) throw new e("INVALID_MONEY", "Money must be a plain data object");
|
|
18
|
+
let n = Object.getOwnPropertyDescriptors(t), r = n.amount, i = n.currency;
|
|
19
|
+
if (!T(r) || !T(i)) throw new e("INVALID_MONEY", "Money properties must be plain data values");
|
|
20
|
+
if (typeof r.value != "bigint" || !a(i.value)) throw new e("INVALID_MONEY", "Money must contain bigint amount and a registered currency");
|
|
21
|
+
return b(r.value, i.value);
|
|
22
|
+
}
|
|
23
|
+
function l(e) {
|
|
24
|
+
try {
|
|
25
|
+
return c(e), !0;
|
|
26
|
+
} catch {
|
|
27
|
+
return !1;
|
|
78
28
|
}
|
|
79
|
-
let f = n.map((e, t) => t).sort((e, t) => {
|
|
80
|
-
let n = d[t] - d[e];
|
|
81
|
-
return n > 0n ? 1 : n < 0n ? -1 : 0;
|
|
82
|
-
}), p = [...u], m = a - u.reduce((e, t) => e + t, 0n);
|
|
83
|
-
for (let e = 0; m > 0n; e++, m--) p[f[e]] += 1n;
|
|
84
|
-
return p.map((e) => ({
|
|
85
|
-
amount: i ? -e : e,
|
|
86
|
-
currency: t.currency
|
|
87
|
-
}));
|
|
88
|
-
}
|
|
89
|
-
function b(n) {
|
|
90
|
-
if (n.length === 0) throw new e("sum requires at least one Money value");
|
|
91
|
-
let r = n[0].currency;
|
|
92
|
-
for (let e = 1; e < n.length; e++) if (n[e].currency !== r) throw new t(r, n[e].currency);
|
|
93
|
-
return {
|
|
94
|
-
amount: n.reduce((e, t) => e + t.amount, 0n),
|
|
95
|
-
currency: r
|
|
96
|
-
};
|
|
97
29
|
}
|
|
98
|
-
function
|
|
99
|
-
|
|
100
|
-
let n = t[0];
|
|
101
|
-
for (let e = 1; e < t.length; e++) n = D(n, t[e]) <= 0 ? n : t[e];
|
|
102
|
-
return n;
|
|
103
|
-
}
|
|
104
|
-
function S(t) {
|
|
105
|
-
if (t.length === 0) throw new e("max requires at least one Money value");
|
|
106
|
-
let n = t[0];
|
|
107
|
-
for (let e = 1; e < t.length; e++) n = D(n, t[e]) >= 0 ? n : t[e];
|
|
108
|
-
return n;
|
|
109
|
-
}
|
|
110
|
-
function C(t, n) {
|
|
111
|
-
if (!Number.isInteger(n) || n <= 0) throw new e("splitEvenly requires a positive integer number of parts");
|
|
112
|
-
return y(t, Array.from({ length: n }, () => 1));
|
|
113
|
-
}
|
|
114
|
-
function w(n, r, i) {
|
|
115
|
-
if (n.currency !== r.currency || n.currency !== i.currency) throw new t(n.currency, n.currency === r.currency ? i.currency : r.currency);
|
|
116
|
-
if (D(r, i) > 0) throw new e(`clamp: lower (${B(r)} ${r.currency}) must be <= upper (${B(i)} ${i.currency})`);
|
|
117
|
-
return S([r, x([n, i])]);
|
|
30
|
+
function u(e, t) {
|
|
31
|
+
return S(e, t), b(e.amount + t.amount, e.currency);
|
|
118
32
|
}
|
|
119
|
-
function
|
|
120
|
-
return
|
|
121
|
-
amount: e.amount < 0n ? -e.amount : e.amount,
|
|
122
|
-
currency: e.currency
|
|
123
|
-
};
|
|
33
|
+
function d(e, t) {
|
|
34
|
+
return S(e, t), b(e.amount - t.amount, e.currency);
|
|
124
35
|
}
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
};
|
|
36
|
+
function f(e, t, i = {}) {
|
|
37
|
+
x(e);
|
|
38
|
+
let a = n(t);
|
|
39
|
+
return b(r(e.amount * a.numerator, a.denominator, i.rounding ?? o), e.currency);
|
|
130
40
|
}
|
|
131
|
-
function
|
|
132
|
-
|
|
41
|
+
function p(t, i, a = {}) {
|
|
42
|
+
x(t);
|
|
43
|
+
let s = n(i);
|
|
44
|
+
if (s.numerator === 0n) throw new e("DIVISION_BY_ZERO", "Cannot divide money by zero");
|
|
45
|
+
return b(r(t.amount * s.denominator, s.numerator < 0n ? -s.numerator : s.numerator, a.rounding ?? o) * (s.numerator < 0n ? -1n : 1n), t.currency);
|
|
133
46
|
}
|
|
134
|
-
function
|
|
135
|
-
return e.
|
|
47
|
+
function m(e, t) {
|
|
48
|
+
return S(e, t), e.amount === t.amount ? 0 : e.amount < t.amount ? -1 : 1;
|
|
136
49
|
}
|
|
137
|
-
function
|
|
138
|
-
return
|
|
50
|
+
function h(e) {
|
|
51
|
+
return x(e), b(e.amount < 0n ? -e.amount : e.amount, e.currency);
|
|
139
52
|
}
|
|
140
|
-
function
|
|
141
|
-
return
|
|
53
|
+
function g(e) {
|
|
54
|
+
return x(e), b(-e.amount, e.currency);
|
|
142
55
|
}
|
|
143
|
-
function
|
|
144
|
-
|
|
56
|
+
function _(t, n) {
|
|
57
|
+
x(t);
|
|
58
|
+
let { fractionDigits: i, rounding: a = o } = n;
|
|
59
|
+
if (!Number.isInteger(i) || i < 0 || i > t.currency.minorUnit) throw new e("INVALID_ROUNDING", `fractionDigits must be an integer from 0 to ${t.currency.minorUnit}`);
|
|
60
|
+
let s = 10n ** BigInt(t.currency.minorUnit - i);
|
|
61
|
+
return b(r(t.amount, s, a) * s, t.currency);
|
|
145
62
|
}
|
|
146
|
-
function
|
|
147
|
-
return
|
|
63
|
+
function v(e) {
|
|
64
|
+
return x(e), i(e.amount, e.currency.minorUnit);
|
|
148
65
|
}
|
|
149
|
-
function
|
|
150
|
-
return e
|
|
66
|
+
function y(e, t) {
|
|
67
|
+
return C(t), b(e, t);
|
|
151
68
|
}
|
|
152
|
-
function
|
|
153
|
-
return
|
|
69
|
+
function b(e, t) {
|
|
70
|
+
return Object.freeze({
|
|
71
|
+
amount: e,
|
|
72
|
+
currency: t
|
|
73
|
+
});
|
|
154
74
|
}
|
|
155
|
-
function
|
|
156
|
-
|
|
75
|
+
function x(t) {
|
|
76
|
+
if (!Object.isFrozen(t) || !a(t.currency)) throw new e("INVALID_MONEY", "Money must be a canonical Coins value");
|
|
157
77
|
}
|
|
158
|
-
function
|
|
159
|
-
|
|
78
|
+
function S(e, n) {
|
|
79
|
+
if (x(e), x(n), e.currency !== n.currency) throw new t(e.currency.code, n.currency.code);
|
|
160
80
|
}
|
|
161
|
-
function
|
|
162
|
-
|
|
81
|
+
function C(t) {
|
|
82
|
+
if (!a(t)) throw new e("INVALID_CURRENCY", "Money requires a registered currency");
|
|
163
83
|
}
|
|
164
|
-
function
|
|
165
|
-
return
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
};
|
|
84
|
+
function w(e) {
|
|
85
|
+
if (typeof e != "object" || !e) return !1;
|
|
86
|
+
let t = Object.getPrototypeOf(e);
|
|
87
|
+
return t === Object.prototype || t === null;
|
|
169
88
|
}
|
|
170
|
-
function
|
|
171
|
-
|
|
172
|
-
if (typeof t.amount != "string") throw new e(`Invalid money amount in JSON: ${String(t.amount)} (expected an integer string, e.g. '123456')`);
|
|
173
|
-
let r;
|
|
174
|
-
try {
|
|
175
|
-
r = BigInt(t.amount);
|
|
176
|
-
} catch {
|
|
177
|
-
throw new e(`Invalid money amount in JSON: "${t.amount}" (expected an integer string, e.g. '123456')`);
|
|
178
|
-
}
|
|
179
|
-
return {
|
|
180
|
-
amount: r,
|
|
181
|
-
currency: n
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
function B(e) {
|
|
185
|
-
let t = a(e.currency);
|
|
186
|
-
if (t === 0) return String(e.amount);
|
|
187
|
-
let n = e.amount < 0n, r = n ? -e.amount : e.amount, i = s(t), o = r / i, c = (r % i).toString().padStart(t, "0");
|
|
188
|
-
return `${n ? "-" : ""}${o}.${c}`;
|
|
189
|
-
}
|
|
190
|
-
function V(e) {
|
|
191
|
-
let t = a(e.currency);
|
|
192
|
-
return Number(e.amount) / Number(s(t));
|
|
193
|
-
}
|
|
194
|
-
function H(t, n, i = "half-away-from-zero") {
|
|
195
|
-
if (!Number.isInteger(n) || n < 0) throw new e(`roundTo: places must be a non-negative integer, got ${n}`);
|
|
196
|
-
let o = a(t.currency);
|
|
197
|
-
if (n > o) throw new e(`roundTo: places (${n}) exceeds the decimal places for ${t.currency} (${o})`);
|
|
198
|
-
if (n === o) return t;
|
|
199
|
-
let c = s(o - n), l = t.amount < 0n, u = l ? -t.amount : t.amount, d = r(u / c, u % c, c, i, l);
|
|
200
|
-
return {
|
|
201
|
-
amount: l ? -d : d,
|
|
202
|
-
currency: t.currency
|
|
203
|
-
};
|
|
89
|
+
function T(e) {
|
|
90
|
+
return e !== void 0 && "value" in e && e.get === void 0 && e.set === void 0;
|
|
204
91
|
}
|
|
205
92
|
//#endregion
|
|
206
|
-
export {
|
|
93
|
+
export { h as abs, u as add, m as compare, p as divide, l as isMoney, s as money, f as multiply, g as negate, c as parseMoney, _ as round, d as subtract, v as toDecimal, y as withMinor };
|
|
207
94
|
|
|
208
95
|
//# sourceMappingURL=money.js.map
|
package/dist/money.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"money.js","names":[],"sources":["../src/money.ts"],"sourcesContent":["import type { CurrencyCode, Money, MoneyJSON, RoundingMode } from './types';\n\nimport { warn } from './_dev';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport {\n applyRounding,\n expandScientific,\n getCurrencyDecimals,\n parseRational,\n pow10,\n SCIENTIFIC_RE,\n validateCurrencyCode,\n} from './utils';\n\nexport { CoinsError, CurrencyMismatchError, InvalidCurrencyError } from './errors';\n\n// ─── Factories ───────────────────────────────────────────────────────────────\n\n/**\n * Creates a `Money` value from a decimal string, number, or raw bigint minor units.\n * Validates the currency code — throws `InvalidCurrencyError` for unrecognised ISO 4217 codes.\n *\n * - **string** `'1234.56'` → parsed losslessly; respects the currency's decimal places.\n * - **number** `1234.56` → converted via `String()` first; inherits IEEE-754 limits.\n * Prefer strings when precision matters.\n * - **bigint** `123456n` → used as-is (already in minor units).\n *\n * @example\n * ```ts\n * money('1234.56', 'USD') // { amount: 123456n, currency: 'USD' }\n * money(1234, 'JPY') // { amount: 1234n, currency: 'JPY' }\n * money(123456n, 'USD') // { amount: 123456n, currency: 'USD' }\n * ```\n */\nexport function money(amount: bigint | number | string, currency: CurrencyCode): Money {\n const validCurrency = validateCurrencyCode(currency);\n\n if (typeof amount === 'bigint') {\n return { amount, currency: validCurrency };\n }\n\n if (typeof amount === 'number') {\n const decimals = getCurrencyDecimals(validCurrency);\n const str = String(amount);\n // Expand scientific notation (e.g. String(1e-7) === '1e-7') before measuring fraction\n // length — otherwise inputs like 1e-7 report 0 fractional digits and skip the warning\n // below despite having far more precision than the currency supports.\n const normalized = SCIENTIFIC_RE.test(str) ? expandScientific(str) : str;\n const dotIndex = normalized.indexOf('.');\n const fracLen = dotIndex === -1 ? 0 : normalized.length - dotIndex - 1;\n\n if (fracLen > decimals) {\n warn(\n `money(): number input \"${amount}\" has more decimal places (${fracLen}) than ${validCurrency} supports (${decimals}). ` +\n `Use a decimal string to avoid IEEE-754 precision loss.`,\n );\n }\n\n // getCurrencyDecimals is cached from the validateCurrencyCode call above.\n return { amount: parseToMinorUnits(str, decimals), currency: validCurrency };\n }\n\n // getCurrencyDecimals is cached from the validateCurrencyCode call above.\n return { amount: parseToMinorUnits(String(amount), getCurrencyDecimals(validCurrency)), currency: validCurrency };\n}\n\n// ─── Internal ─────────────────────────────────────────────────────────────────\n\nfunction assertSameCurrency(a: Money, b: Money): void {\n if (a.currency !== b.currency) {\n throw new CurrencyMismatchError(a.currency, b.currency);\n }\n}\n\n/**\n * Converts a decimal string to minor units for the given decimal precision.\n * Uses parseRational + applyRounding to share the single rounding implementation.\n */\nfunction parseToMinorUnits(str: string, decimals: number): bigint {\n const { denominator, negative, numerator } = parseRational(str);\n // numerator/denominator × 10^decimals = numerator × 10^decimals / denominator\n const scale = pow10(decimals);\n const raw = numerator * scale;\n const quotient = raw / denominator;\n const result = applyRounding(quotient, raw % denominator, denominator, 'half-away-from-zero');\n\n return negative ? -result : result;\n}\n\n/**\n * Creates a new `Money` value with the given `amount` and the same currency as `m`.\n * Useful when you compute a raw `bigint` amount externally and need to wrap it\n * back into a `Money` value without re-validating the currency.\n *\n * @example\n * ```ts\n * const price = money('9.99', 'USD');\n * withAmount(price, 1999n) // { amount: 1999n, currency: 'USD' }\n * ```\n */\nexport function withAmount(m: Money, amount: bigint): Money {\n return { amount, currency: m.currency };\n}\n\n/**\n * Type guard that returns `true` if `value` is a `Money`-shaped object\n * (has a `bigint` `amount` and a `string` `currency`).\n * Useful for narrowing unknown payloads from APIs or deserialized storage.\n *\n * Note: does **not** validate the currency code — use `money(0n, currency)` or\n * `validateCurrencyCode()` if you also need to confirm it is a recognized ISO 4217 code.\n *\n * @example\n * ```ts\n * isMoney({ amount: 100n, currency: 'USD' }) // true\n * isMoney({ amount: 1.5, currency: 'USD' }) // false\n * isMoney(null) // false\n * ```\n */\nexport function isMoney(value: unknown): value is Money {\n if (typeof value !== 'object' || value === null) return false;\n\n const v = value as Record<string, unknown>;\n\n return (\n Object.hasOwn(v, 'amount') &&\n typeof v.amount === 'bigint' &&\n Object.hasOwn(v, 'currency') &&\n typeof v.currency === 'string'\n );\n}\n\n// ─── Arithmetic ──────────────────────────────────────────────────────────────\n\n/** Adds two `Money` values. Throws if their currencies differ. */\nexport function add(a: Money, b: Money): Money {\n assertSameCurrency(a, b);\n\n return { amount: a.amount + b.amount, currency: a.currency };\n}\n\n/** Subtracts `b` from `a`. Throws if their currencies differ. */\nexport function subtract(a: Money, b: Money): Money {\n assertSameCurrency(a, b);\n\n return { amount: a.amount - b.amount, currency: a.currency };\n}\n\n/**\n * Multiplies a `Money` value by a scalar factor.\n * The factor can be a decimal string (lossless) or a number.\n *\n * @param mode Rounding mode for fractional minor units. Defaults to `'half-away-from-zero'`.\n *\n * @example\n * ```ts\n * multiply(money('100.00', 'USD'), '1.5') // $150.00\n * multiply(money('1.00', 'USD'), '0.339', 'floor') // $0.33\n * multiply(money('1.00', 'USD'), '0.339', 'ceiling') // $0.34\n * ```\n */\nexport function multiply(m: Money, factor: number | string, mode: RoundingMode = 'half-away-from-zero'): Money {\n const { denominator, negative: factorNegative, numerator } = parseRational(String(factor));\n const negative = m.amount < 0n !== factorNegative;\n const absAmount = m.amount < 0n ? -m.amount : m.amount;\n const raw = absAmount * numerator;\n const quotient = raw / denominator;\n const result = applyRounding(quotient, raw % denominator, denominator, mode, negative);\n\n return { amount: negative ? -result : result, currency: m.currency };\n}\n\n/**\n * Divides a `Money` value by a scalar divisor.\n * The divisor can be a decimal string (lossless) or a number.\n *\n * @param mode Rounding mode for fractional minor units. Defaults to `'half-away-from-zero'`.\n * @throws {CoinsError} On division by zero.\n *\n * @example\n * ```ts\n * divide(money('100.00', 'USD'), 3) // $33.33\n * divide(money('100.00', 'USD'), 3, 'ceiling') // $33.34\n * ```\n */\nexport function divide(m: Money, divisor: number | string, mode: RoundingMode = 'half-away-from-zero'): Money {\n const { denominator: parsedDenom, negative: divisorNegative, numerator } = parseRational(String(divisor));\n\n if (numerator === 0n) throw new CoinsError('Division by zero');\n\n const negative = m.amount < 0n !== divisorNegative;\n const absAmount = m.amount < 0n ? -m.amount : m.amount;\n // Dividing by (numerator/parsedDenom) = multiplying by (parsedDenom/numerator).\n const raw = absAmount * parsedDenom;\n const quotient = raw / numerator;\n const result = applyRounding(quotient, raw % numerator, numerator, mode, negative);\n\n return { amount: negative ? -result : result, currency: m.currency };\n}\n\n/**\n * Distributes `money` across `ratios` without losing or gaining a single minor unit.\n * Accepts both number and string ratios — use strings for lossless decimal weights.\n *\n * Uses the Largest Remainder Method: each share gets its floor allocation first,\n * then any remainder units are assigned one-by-one to the shares with the largest\n * fractional parts, breaking ties by original index (stable, left-to-right).\n *\n * Throws if `ratios` is empty, contains negative values (including negative strings like `'-0.5'`), or sums to zero.\n *\n * @example\n * ```ts\n * allocate(money('10.00', 'USD'), [1, 1, 1])\n * // → [$3.34, $3.33, $3.33] (not three × $3.33 — that loses a penny)\n *\n * allocate(money('10.00', 'USD'), ['0.3', '0.7'])\n * // → [$3.00, $7.00]\n * ```\n */\nexport function allocate(m: Money, ratios: readonly (number | string)[]): [Money, ...Money[]] {\n if (ratios.length === 0) throw new CoinsError('allocate requires at least one ratio');\n\n const parsedRatios = ratios.map((r) => parseRational(String(r)));\n\n if (parsedRatios.some((p) => p.negative)) throw new CoinsError('All ratios must be non-negative');\n\n if (!parsedRatios.some((p) => p.numerator > 0n)) throw new CoinsError('At least one ratio must be positive');\n\n const negative = m.amount < 0n;\n const absAmount = negative ? -m.amount : m.amount;\n\n // All denominators are powers of 10; the max is their LCM — use it to normalise weights to integers.\n const maxDenominator = parsedRatios.reduce((max, p) => (p.denominator > max ? p.denominator : max), 1n);\n const bigWeights = parsedRatios.map((p) => p.numerator * (maxDenominator / p.denominator));\n const bigTotal = bigWeights.reduce((a, b) => a + b, 0n);\n\n // Floor allocation + fractional remainders.\n const floors: bigint[] = [];\n const fracRemainders: bigint[] = [];\n\n for (const w of bigWeights) {\n const ideal = absAmount * w;\n\n floors.push(ideal / bigTotal);\n fracRemainders.push(ideal % bigTotal);\n }\n\n // Distribute leftover units to the shares with the largest fractional remainders.\n // Relies on V8/SpiderMonkey's stable sort (ECMAScript 2019+ spec-mandated): ties\n // in fractional remainders are broken by original index (left-to-right), ensuring\n // deterministic output for equal-weight ratios (e.g. [1, 1, 1]).\n const indices = ratios\n .map((_, i) => i)\n .sort((a, b) => {\n const diff = fracRemainders[b]! - fracRemainders[a]!;\n\n return diff > 0n ? 1 : diff < 0n ? -1 : 0;\n });\n\n const result = [...floors];\n // `remaining` is always < ratios.length (at most one unit per share), safe for this loop.\n let remaining = absAmount - floors.reduce((a, b) => a + b, 0n);\n\n for (let i = 0; remaining > 0n; i++, remaining--) {\n result[indices[i]!]! += 1n;\n }\n\n return result.map((amount) => ({ amount: negative ? -amount : amount, currency: m.currency })) as [Money, ...Money[]];\n}\n\n// ─── Aggregates ──────────────────────────────────────────────────────────────\n\n/**\n * Sums an array of `Money` values. Throws if the array is empty or currencies differ.\n *\n * @example\n * ```ts\n * sum([money('1.00', 'USD'), money('2.50', 'USD')]) // $3.50\n * ```\n */\nexport function sum(moneys: readonly Money[]): Money {\n if (moneys.length === 0) throw new CoinsError('sum requires at least one Money value');\n\n const currency = moneys[0]!.currency;\n\n for (let i = 1; i < moneys.length; i++) {\n if (moneys[i]!.currency !== currency) {\n throw new CurrencyMismatchError(currency, moneys[i]!.currency);\n }\n }\n\n return { amount: moneys.reduce((acc, m) => acc + m.amount, 0n), currency };\n}\n\n/**\n * Returns the smallest `Money` value from a non-empty array. Throws if currencies differ.\n *\n * @example\n * ```ts\n * min([money('3.00', 'USD'), money('1.00', 'USD'), money('2.00', 'USD')]) // $1.00\n * ```\n */\nexport function min(moneys: readonly Money[]): Money {\n if (moneys.length === 0) throw new CoinsError('min requires at least one Money value');\n\n let result = moneys[0]!;\n\n for (let i = 1; i < moneys.length; i++) result = compare(result, moneys[i]!) <= 0 ? result : moneys[i]!;\n\n return result;\n}\n\n/**\n * Returns the largest `Money` value from a non-empty array. Throws if currencies differ.\n *\n * @example\n * ```ts\n * max([money('1.00', 'USD'), money('3.00', 'USD'), money('2.00', 'USD')]) // $3.00\n * ```\n */\nexport function max(moneys: readonly Money[]): Money {\n if (moneys.length === 0) throw new CoinsError('max requires at least one Money value');\n\n let result = moneys[0]!;\n\n for (let i = 1; i < moneys.length; i++) result = compare(result, moneys[i]!) >= 0 ? result : moneys[i]!;\n\n return result;\n}\n\n/**\n * Splits `money` into `parts` equal shares without losing or gaining a single minor unit.\n * Equivalent to `allocate(money, Array.from({ length: parts }, () => 1))`.\n *\n * @throws {CoinsError} If `parts` is not a positive integer (≥ 1).\n *\n * @example\n * ```ts\n * splitEvenly(money('10.00', 'USD'), 3)\n * // → [$3.34, $3.33, $3.33]\n * ```\n */\nexport function splitEvenly(m: Money, parts: number): [Money, ...Money[]] {\n if (!Number.isInteger(parts) || parts <= 0) {\n throw new CoinsError('splitEvenly requires a positive integer number of parts');\n }\n\n return allocate(\n m,\n Array.from({ length: parts }, () => 1),\n );\n}\n\n/**\n * Clamps `m` to the inclusive range `[lower, upper]`.\n *\n * @throws {CurrencyMismatchError} If `m`, `lower`, or `upper` have different currencies.\n * @throws {CoinsError} If `lower > upper`.\n *\n * @example\n * ```ts\n * clamp(money('5.00', 'USD'), money('1.00', 'USD'), money('10.00', 'USD')) // $5.00\n * clamp(money('0.00', 'USD'), money('1.00', 'USD'), money('10.00', 'USD')) // $1.00\n * clamp(money('15.00', 'USD'), money('1.00', 'USD'), money('10.00', 'USD')) // $10.00\n * ```\n */\nexport function clamp(m: Money, lower: Money, upper: Money): Money {\n // Validate currency consistency upfront so the error always names the mismatch,\n // rather than surfacing mid-computation from min() or max().\n if (m.currency !== lower.currency || m.currency !== upper.currency) {\n throw new CurrencyMismatchError(m.currency, m.currency !== lower.currency ? lower.currency : upper.currency);\n }\n\n if (compare(lower, upper) > 0) {\n throw new CoinsError(\n `clamp: lower (${toDecimal(lower)} ${lower.currency}) must be <= upper (${toDecimal(upper)} ${upper.currency})`,\n );\n }\n\n return max([lower, min([m, upper])]);\n}\n\n// ─── Unary ───────────────────────────────────────────────────────────────────\n\n/** Returns the absolute value of `money`. Negative amounts become positive. */\nexport function abs(m: Money): Money {\n return { amount: m.amount < 0n ? -m.amount : m.amount, currency: m.currency };\n}\n\n/** Returns `money` with its sign flipped. */\nexport function negate(m: Money): Money {\n return { amount: -m.amount, currency: m.currency };\n}\n\n// ─── Comparison ──────────────────────────────────────────────────────────────\n\n/**\n * Compares two `Money` values. Returns `-1`, `0`, or `1`.\n * Throws if their currencies differ.\n */\nexport function compare(a: Money, b: Money): -1 | 0 | 1 {\n assertSameCurrency(a, b);\n\n if (a.amount < b.amount) return -1;\n\n if (a.amount > b.amount) return 1;\n\n return 0;\n}\n\n/**\n * Returns `true` if both `Money` values have the same currency and amount.\n * Returns `false` if currencies differ — safe to use in `.filter()` and conditional chains.\n */\nexport function isEqual(a: Money, b: Money): boolean {\n return a.currency === b.currency && a.amount === b.amount;\n}\n\n/** Returns `true` if `a` is strictly greater than `b`. Throws on currency mismatch. */\nexport function greaterThan(a: Money, b: Money): boolean {\n return compare(a, b) > 0;\n}\n\n/** Returns `true` if `a` is greater than or equal to `b`. Throws on currency mismatch. */\nexport function greaterThanOrEqual(a: Money, b: Money): boolean {\n return compare(a, b) >= 0;\n}\n\n/** Returns `true` if `a` is strictly less than `b`. Throws on currency mismatch. */\nexport function lessThan(a: Money, b: Money): boolean {\n return compare(a, b) < 0;\n}\n\n/** Returns `true` if `a` is less than or equal to `b`. Throws on currency mismatch. */\nexport function lessThanOrEqual(a: Money, b: Money): boolean {\n return compare(a, b) <= 0;\n}\n\n// ─── Predicates ──────────────────────────────────────────────────────────────\n\n/** Returns `true` if the amount is exactly zero. */\nexport function isZero(m: Money): boolean {\n return m.amount === 0n;\n}\n\n/** Returns `true` if the amount is strictly positive (> 0). */\nexport function isPositive(m: Money): boolean {\n return m.amount > 0n;\n}\n\n/** Returns `true` if the amount is strictly negative (< 0). */\nexport function isNegative(m: Money): boolean {\n return m.amount < 0n;\n}\n\n/** Returns `true` if the amount is zero or positive (>= 0). */\nexport function isNonNegative(m: Money): boolean {\n return m.amount >= 0n;\n}\n\n/** Returns `true` if the amount is zero or negative (<= 0). */\nexport function isNonPositive(m: Money): boolean {\n return m.amount <= 0n;\n}\n\n// ─── Serialization ───────────────────────────────────────────────────────────\n\n/**\n * Serializes a `Money` value to a plain-object JSON-safe form.\n * `amount` is a bigint string (e.g. `'123456'`) to avoid `JSON.stringify` throwing.\n *\n * @example\n * ```ts\n * toJSON(money('1234.56', 'USD')) // { amount: '123456', currency: 'USD' }\n * JSON.stringify(toJSON(price)) // '{\"amount\":\"123456\",\"currency\":\"USD\"}'\n * ```\n */\nexport function toJSON(m: Money): MoneyJSON {\n return { amount: String(m.amount), currency: m.currency };\n}\n\n/**\n * Deserializes a `MoneyJSON` object back into a `Money` value.\n * Validates the currency code and the amount string.\n *\n * @example\n * ```ts\n * fromJSON({ amount: '123456', currency: 'USD' }) // { amount: 123456n, currency: 'USD' }\n * ```\n */\nexport function fromJSON(json: MoneyJSON): Money {\n const validCurrency = validateCurrencyCode(json.currency);\n\n if (typeof json.amount !== 'string') {\n throw new CoinsError(\n `Invalid money amount in JSON: ${String(json.amount)} (expected an integer string, e.g. '123456')`,\n );\n }\n\n let amount: bigint;\n\n try {\n amount = BigInt(json.amount);\n } catch {\n throw new CoinsError(`Invalid money amount in JSON: \"${json.amount}\" (expected an integer string, e.g. '123456')`);\n }\n\n return { amount, currency: validCurrency };\n}\n\n/**\n * Serializes a `Money` value to a decimal string (e.g. `'1234.56'`).\n * Round-trips losslessly with `money()`.\n *\n * @example\n * ```ts\n * toDecimal(money('1234.56', 'USD')) // '1234.56'\n * toDecimal(money(5n, 'USD')) // '0.05'\n * toDecimal(money(1234n, 'JPY')) // '1234'\n * ```\n */\nexport function toDecimal(m: Money): string {\n const decimals = getCurrencyDecimals(m.currency);\n\n if (decimals === 0) return String(m.amount);\n\n const negative = m.amount < 0n;\n const abs = negative ? -m.amount : m.amount;\n const scale = pow10(decimals);\n const whole = abs / scale;\n const frac = (abs % scale).toString().padStart(decimals, '0');\n\n return `${negative ? '-' : ''}${whole}.${frac}`;\n}\n\n/**\n * Converts a `Money` value to a floating-point number.\n * Useful for charting and display; **not** for arithmetic (lossy).\n *\n * @example\n * ```ts\n * toNumber(money('1234.56', 'USD')) // 1234.56\n * ```\n */\nexport function toNumber(m: Money): number {\n const decimals = getCurrencyDecimals(m.currency);\n\n return Number(m.amount) / Number(pow10(decimals));\n}\n\n// ─── Rounding ─────────────────────────────────────────────────────────────────\n\n/**\n * Rounds a `Money` value to fewer decimal places than the currency's default.\n * `places` must be a non-negative integer in the range `0..currencyDecimals`.\n *\n * Useful for display purposes (e.g. rounding USD to whole dollars).\n * @throws {CoinsError} If `places` is out of the allowed range.\n *\n * @param mode Rounding mode. Defaults to `'half-away-from-zero'`.\n *\n * @example\n * ```ts\n * roundTo(money('1234.56', 'USD'), 0) // $1235 (whole dollars)\n * roundTo(money('1234.56', 'USD'), 1) // $1234.6\n * roundTo(money('1234.56', 'USD'), 1, 'floor') // $1234.5\n * roundTo(money('1234', 'JPY'), 0) // ¥1234 (no-op: JPY is already 0-decimal)\n * ```\n */\nexport function roundTo(m: Money, places: number, mode: RoundingMode = 'half-away-from-zero'): Money {\n if (!Number.isInteger(places) || places < 0) {\n throw new CoinsError(`roundTo: places must be a non-negative integer, got ${places}`);\n }\n\n const currencyDecimals = getCurrencyDecimals(m.currency);\n\n if (places > currencyDecimals) {\n throw new CoinsError(\n `roundTo: places (${places}) exceeds the decimal places for ${m.currency} (${currencyDecimals})`,\n );\n }\n\n if (places === currencyDecimals) return m;\n\n const drop = currencyDecimals - places;\n const divisor = pow10(drop);\n const negative = m.amount < 0n;\n const absAmount = negative ? -m.amount : m.amount;\n const quotient = absAmount / divisor;\n const remainder = absAmount % divisor;\n const rounded = applyRounding(quotient, remainder, divisor, mode, negative);\n\n return { amount: negative ? -rounded : rounded, currency: m.currency };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"money.js","names":[],"sources":["../src/money.ts"],"sourcesContent":["import type { Currency, Money, RoundingMode } from './types';\n\nimport { isCurrency } from './currency';\nimport { decimal, roundDivision, toDecimalString } from './decimal';\nimport { CoinsError, CurrencyMismatchError } from './errors';\n\nconst defaultRounding: RoundingMode = 'halfAwayFromZero';\n\nexport function money<C extends Currency>(amount: string, currency: C): Money<C>;\nexport function money<C extends Currency>(amount: string, currency: C, options: { rounding: RoundingMode }): Money<C>;\nexport function money<C extends Currency>(amount: bigint, currency: C, options: { unit: 'minor' }): Money<C>;\nexport function money<C extends Currency>(\n amount: bigint | string,\n currency: C,\n options?: { rounding: RoundingMode } | { unit: 'minor' },\n): Money<C> {\n assertCurrency(currency);\n\n if (typeof amount === 'bigint') {\n if (!options || !('unit' in options) || options.unit !== 'minor') {\n throw new CoinsError('INVALID_MONEY', \"Bigint amounts require { unit: 'minor' }\");\n }\n\n return createMoney(amount, currency);\n }\n\n if (options && 'unit' in options)\n throw new CoinsError('INVALID_MONEY', 'Decimal strings do not accept a unit option');\n\n const value = decimal(amount);\n const scaled = value.numerator * 10n ** BigInt(currency.minorUnit);\n const remainder = scaled % value.denominator;\n\n if (remainder !== 0n && !options) {\n throw new CoinsError('INVALID_MONEY', `Amount \"${amount}\" exceeds ${currency.code} precision; provide rounding`);\n }\n\n return createMoney(roundDivision(scaled, value.denominator, options?.rounding ?? defaultRounding), currency);\n}\n\nexport function parseMoney(value: unknown): Money {\n if (!isPlainDataObject(value)) throw new CoinsError('INVALID_MONEY', 'Money must be a plain data object');\n\n const descriptors = Object.getOwnPropertyDescriptors(value);\n const amountDescriptor = descriptors.amount;\n const currencyDescriptor = descriptors.currency;\n\n if (!isDataProperty(amountDescriptor) || !isDataProperty(currencyDescriptor)) {\n throw new CoinsError('INVALID_MONEY', 'Money properties must be plain data values');\n }\n\n if (typeof amountDescriptor.value !== 'bigint' || !isCurrency(currencyDescriptor.value)) {\n throw new CoinsError('INVALID_MONEY', 'Money must contain bigint amount and a registered currency');\n }\n\n return createMoney(amountDescriptor.value, currencyDescriptor.value);\n}\n\nexport function isMoney(value: unknown): value is Money {\n try {\n parseMoney(value);\n\n return true;\n } catch {\n return false;\n }\n}\n\nexport function add<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount + right.amount, left.currency);\n}\n\nexport function subtract<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount - right.amount, left.currency);\n}\n\nexport function multiply<C extends Currency>(\n value: Money<C>,\n factor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(factor);\n\n return createMoney(\n roundDivision(value.amount * scalar.numerator, scalar.denominator, options.rounding ?? defaultRounding),\n value.currency,\n );\n}\n\nexport function divide<C extends Currency>(\n value: Money<C>,\n divisor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(divisor);\n\n if (scalar.numerator === 0n) throw new CoinsError('DIVISION_BY_ZERO', 'Cannot divide money by zero');\n\n return createMoney(\n roundDivision(\n value.amount * scalar.denominator,\n scalar.numerator < 0n ? -scalar.numerator : scalar.numerator,\n options.rounding ?? defaultRounding,\n ) * (scalar.numerator < 0n ? -1n : 1n),\n value.currency,\n );\n}\n\nexport function compare<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): -1 | 0 | 1 {\n assertSameCurrency(left, right);\n\n return left.amount === right.amount ? 0 : left.amount < right.amount ? -1 : 1;\n}\n\nexport function abs<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(value.amount < 0n ? -value.amount : value.amount, value.currency);\n}\n\nexport function negate<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(-value.amount, value.currency);\n}\n\nexport function round<C extends Currency>(\n value: Money<C>,\n options: { fractionDigits: number; rounding?: RoundingMode },\n): Money<C> {\n assertMoney(value);\n\n const { fractionDigits, rounding = defaultRounding } = options;\n\n if (!Number.isInteger(fractionDigits) || fractionDigits < 0 || fractionDigits > value.currency.minorUnit) {\n throw new CoinsError('INVALID_ROUNDING', `fractionDigits must be an integer from 0 to ${value.currency.minorUnit}`);\n }\n\n const factor = 10n ** BigInt(value.currency.minorUnit - fractionDigits);\n\n return createMoney(roundDivision(value.amount, factor, rounding) * factor, value.currency);\n}\n\nexport function toDecimal(value: Money): string {\n assertMoney(value);\n\n return toDecimalString(value.amount, value.currency.minorUnit);\n}\n\nexport function withMinor<C extends Currency>(amount: bigint, currency: C): Money<C> {\n assertCurrency(currency);\n\n return createMoney(amount, currency);\n}\n\nfunction createMoney<C extends Currency>(amount: bigint, currency: C): Money<C> {\n return Object.freeze({ amount, currency }) as Money<C>;\n}\n\nfunction assertMoney(value: Money): void {\n if (!Object.isFrozen(value) || !isCurrency(value.currency)) {\n throw new CoinsError('INVALID_MONEY', 'Money must be a canonical Coins value');\n }\n}\n\nfunction assertSameCurrency(left: Money, right: Money): void {\n assertMoney(left);\n assertMoney(right);\n\n if (left.currency !== right.currency) {\n throw new CurrencyMismatchError(left.currency.code, right.currency.code);\n }\n}\n\nfunction assertCurrency(value: Currency): void {\n if (!isCurrency(value)) throw new CoinsError('INVALID_CURRENCY', 'Money requires a registered currency');\n}\n\nfunction isPlainDataObject(value: unknown): value is Record<PropertyKey, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n\n const prototype = Object.getPrototypeOf(value);\n\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction isDataProperty(\n descriptor: PropertyDescriptor | undefined,\n): descriptor is PropertyDescriptor & { value: unknown } {\n return (\n descriptor !== undefined && 'value' in descriptor && descriptor.get === undefined && descriptor.set === undefined\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./currency.cjs"),n=require("./money.cjs");var r=/^(?:0|-[1-9]\d*|[1-9]\d*)$/,i=[`amount`,`currency`,`unit`];function a(e){return{amount:e.amount.toString(),currency:e.currency.code,unit:`minor`}}function o(r,i={}){try{let e=s(r),a=i.currency??t.resolveBuiltinCurrency;return n.money(BigInt(e.amount),a(e.currency),{unit:`minor`})}catch(t){throw new e.CoinsError(`INVALID_MONEY`,`Invalid Money JSON`,{cause:t})}}function s(e){if(typeof e!=`object`||!e||Object.getPrototypeOf(e)!==Object.prototype)throw TypeError(`Money JSON must be a plain object`);let t=Object.getOwnPropertyDescriptors(e),n=Reflect.ownKeys(e);if(n.length!==i.length||!i.every(e=>n.includes(e)))throw TypeError(`Money JSON must contain only amount, currency, and unit`);for(let e of i){let n=t[e];if(!n||!(`value`in n)||n.get||n.set)throw TypeError(`Money JSON property "${e}" must be a data value`)}let a=t.amount.value,o=t.currency.value,s=t.unit.value;if(typeof a!=`string`||!r.test(a))throw TypeError(`Money JSON amount must be a canonical integer string`);if(typeof o!=`string`||s!==`minor`)throw TypeError(`Money JSON currency/unit are invalid`);return{amount:a,currency:o,unit:s}}exports.parseMoneyJSON=o,exports.toJSON=a;
|
|
2
|
+
//# sourceMappingURL=serialization.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialization.cjs","names":[],"sources":["../src/serialization.ts"],"sourcesContent":["import type { Currency, Money, MoneyJSON } from './types';\n\nimport { resolveBuiltinCurrency } from './currency';\nimport { CoinsError } from './errors';\nimport { money } from './money';\n\nconst INTEGER = /^(?:0|-[1-9]\\d*|[1-9]\\d*)$/;\nconst KEYS = ['amount', 'currency', 'unit'] as const;\n\nexport function toJSON(value: Money): MoneyJSON {\n return { amount: value.amount.toString(), currency: value.currency.code, unit: 'minor' };\n}\n\nexport function parseMoneyJSON(value: unknown, options: { currency?: (code: string) => Currency } = {}): Money {\n try {\n const payload = readPayload(value);\n const resolveCurrency = options.currency ?? resolveBuiltinCurrency;\n\n return money(BigInt(payload.amount), resolveCurrency(payload.currency), { unit: 'minor' });\n } catch (error) {\n throw new CoinsError('INVALID_MONEY', 'Invalid Money JSON', { cause: error });\n }\n}\n\nfunction readPayload(value: unknown): MoneyJSON {\n if (typeof value !== 'object' || value === null || Object.getPrototypeOf(value) !== Object.prototype) {\n throw new TypeError('Money JSON must be a plain object');\n }\n\n const descriptors = Object.getOwnPropertyDescriptors(value);\n const ownKeys = Reflect.ownKeys(value);\n\n if (ownKeys.length !== KEYS.length || !KEYS.every((key) => ownKeys.includes(key))) {\n throw new TypeError('Money JSON must contain only amount, currency, and unit');\n }\n\n for (const key of KEYS) {\n const descriptor = descriptors[key];\n\n if (!descriptor || !('value' in descriptor) || descriptor.get || descriptor.set) {\n throw new TypeError(`Money JSON property \"${key}\" must be a data value`);\n }\n }\n\n const amount = descriptors.amount!.value;\n const currency = descriptors.currency!.value;\n const unit = descriptors.unit!.value;\n\n if (typeof amount !== 'string' || !INTEGER.test(amount))\n throw new TypeError('Money JSON amount must be a canonical integer string');\n\n if (typeof currency !== 'string' || unit !== 'minor') throw new TypeError('Money JSON currency/unit are invalid');\n\n return { amount, currency, unit };\n}\n"],"mappings":"qFAMA,IAAM,EAAU,6BACV,EAAO,CAAC,SAAU,WAAY,MAAM,EAE1C,SAAgB,EAAO,EAAyB,CAC9C,MAAO,CAAE,OAAQ,EAAM,OAAO,SAAS,EAAG,SAAU,EAAM,SAAS,KAAM,KAAM,OAAQ,CACzF,CAEA,SAAgB,EAAe,EAAgB,EAAqD,CAAC,EAAU,CAC7G,GAAI,CACF,IAAM,EAAU,EAAY,CAAK,EAC3B,EAAkB,EAAQ,UAAY,EAAA,uBAE5C,OAAO,EAAA,MAAM,OAAO,EAAQ,MAAM,EAAG,EAAgB,EAAQ,QAAQ,EAAG,CAAE,KAAM,OAAQ,CAAC,CAC3F,OAAS,EAAO,CACd,MAAM,IAAI,EAAA,WAAW,gBAAiB,qBAAsB,CAAE,MAAO,CAAM,CAAC,CAC9E,CACF,CAEA,SAAS,EAAY,EAA2B,CAC9C,GAAI,OAAO,GAAU,WAAY,GAAkB,OAAO,eAAe,CAAK,IAAM,OAAO,UACzF,MAAU,UAAU,mCAAmC,EAGzD,IAAM,EAAc,OAAO,0BAA0B,CAAK,EACpD,EAAU,QAAQ,QAAQ,CAAK,EAErC,GAAI,EAAQ,SAAW,EAAK,QAAU,CAAC,EAAK,MAAO,GAAQ,EAAQ,SAAS,CAAG,CAAC,EAC9E,MAAU,UAAU,yDAAyD,EAG/E,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAa,EAAY,GAE/B,GAAI,CAAC,GAAc,EAAE,UAAW,IAAe,EAAW,KAAO,EAAW,IAC1E,MAAU,UAAU,wBAAwB,EAAI,uBAAuB,CAE3E,CAEA,IAAM,EAAS,EAAY,OAAQ,MAC7B,EAAW,EAAY,SAAU,MACjC,EAAO,EAAY,KAAM,MAE/B,GAAI,OAAO,GAAW,UAAY,CAAC,EAAQ,KAAK,CAAM,EACpD,MAAU,UAAU,sDAAsD,EAE5E,GAAI,OAAO,GAAa,UAAY,IAAS,QAAS,MAAU,UAAU,sCAAsC,EAEhH,MAAO,CAAE,SAAQ,WAAU,MAAK,CAClC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Currency, Money, MoneyJSON } from './types';
|
|
2
|
+
export declare function toJSON(value: Money): MoneyJSON;
|
|
3
|
+
export declare function parseMoneyJSON(value: unknown, options?: {
|
|
4
|
+
currency?: (code: string) => Currency;
|
|
5
|
+
}): Money;
|
|
6
|
+
//# sourceMappingURL=serialization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialization.d.ts","sourceRoot":"","sources":["../src/serialization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAS1D,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAE9C;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,CAAA;CAAO,GAAG,KAAK,CAS7G"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { CoinsError as e } from "./errors.js";
|
|
2
|
+
import { resolveBuiltinCurrency as t } from "./currency.js";
|
|
3
|
+
import { money as n } from "./money.js";
|
|
4
|
+
//#region src/serialization.ts
|
|
5
|
+
var r = /^(?:0|-[1-9]\d*|[1-9]\d*)$/, i = [
|
|
6
|
+
"amount",
|
|
7
|
+
"currency",
|
|
8
|
+
"unit"
|
|
9
|
+
];
|
|
10
|
+
function a(e) {
|
|
11
|
+
return {
|
|
12
|
+
amount: e.amount.toString(),
|
|
13
|
+
currency: e.currency.code,
|
|
14
|
+
unit: "minor"
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function o(r, i = {}) {
|
|
18
|
+
try {
|
|
19
|
+
let e = s(r), a = i.currency ?? t;
|
|
20
|
+
return n(BigInt(e.amount), a(e.currency), { unit: "minor" });
|
|
21
|
+
} catch (t) {
|
|
22
|
+
throw new e("INVALID_MONEY", "Invalid Money JSON", { cause: t });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function s(e) {
|
|
26
|
+
if (typeof e != "object" || !e || Object.getPrototypeOf(e) !== Object.prototype) throw TypeError("Money JSON must be a plain object");
|
|
27
|
+
let t = Object.getOwnPropertyDescriptors(e), n = Reflect.ownKeys(e);
|
|
28
|
+
if (n.length !== i.length || !i.every((e) => n.includes(e))) throw TypeError("Money JSON must contain only amount, currency, and unit");
|
|
29
|
+
for (let e of i) {
|
|
30
|
+
let n = t[e];
|
|
31
|
+
if (!n || !("value" in n) || n.get || n.set) throw TypeError(`Money JSON property "${e}" must be a data value`);
|
|
32
|
+
}
|
|
33
|
+
let a = t.amount.value, o = t.currency.value, s = t.unit.value;
|
|
34
|
+
if (typeof a != "string" || !r.test(a)) throw TypeError("Money JSON amount must be a canonical integer string");
|
|
35
|
+
if (typeof o != "string" || s !== "minor") throw TypeError("Money JSON currency/unit are invalid");
|
|
36
|
+
return {
|
|
37
|
+
amount: a,
|
|
38
|
+
currency: o,
|
|
39
|
+
unit: s
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { o as parseMoneyJSON, a as toJSON };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=serialization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialization.js","names":[],"sources":["../src/serialization.ts"],"sourcesContent":["import type { Currency, Money, MoneyJSON } from './types';\n\nimport { resolveBuiltinCurrency } from './currency';\nimport { CoinsError } from './errors';\nimport { money } from './money';\n\nconst INTEGER = /^(?:0|-[1-9]\\d*|[1-9]\\d*)$/;\nconst KEYS = ['amount', 'currency', 'unit'] as const;\n\nexport function toJSON(value: Money): MoneyJSON {\n return { amount: value.amount.toString(), currency: value.currency.code, unit: 'minor' };\n}\n\nexport function parseMoneyJSON(value: unknown, options: { currency?: (code: string) => Currency } = {}): Money {\n try {\n const payload = readPayload(value);\n const resolveCurrency = options.currency ?? resolveBuiltinCurrency;\n\n return money(BigInt(payload.amount), resolveCurrency(payload.currency), { unit: 'minor' });\n } catch (error) {\n throw new CoinsError('INVALID_MONEY', 'Invalid Money JSON', { cause: error });\n }\n}\n\nfunction readPayload(value: unknown): MoneyJSON {\n if (typeof value !== 'object' || value === null || Object.getPrototypeOf(value) !== Object.prototype) {\n throw new TypeError('Money JSON must be a plain object');\n }\n\n const descriptors = Object.getOwnPropertyDescriptors(value);\n const ownKeys = Reflect.ownKeys(value);\n\n if (ownKeys.length !== KEYS.length || !KEYS.every((key) => ownKeys.includes(key))) {\n throw new TypeError('Money JSON must contain only amount, currency, and unit');\n }\n\n for (const key of KEYS) {\n const descriptor = descriptors[key];\n\n if (!descriptor || !('value' in descriptor) || descriptor.get || descriptor.set) {\n throw new TypeError(`Money JSON property \"${key}\" must be a data value`);\n }\n }\n\n const amount = descriptors.amount!.value;\n const currency = descriptors.currency!.value;\n const unit = descriptors.unit!.value;\n\n if (typeof amount !== 'string' || !INTEGER.test(amount))\n throw new TypeError('Money JSON amount must be a canonical integer string');\n\n if (typeof currency !== 'string' || unit !== 'minor') throw new TypeError('Money JSON currency/unit are invalid');\n\n return { amount, currency, unit };\n}\n"],"mappings":";;;;AAMA,IAAM,IAAU,8BACV,IAAO;CAAC;CAAU;CAAY;AAAM;AAE1C,SAAgB,EAAO,GAAyB;CAC9C,OAAO;EAAE,QAAQ,EAAM,OAAO,SAAS;EAAG,UAAU,EAAM,SAAS;EAAM,MAAM;CAAQ;AACzF;AAEA,SAAgB,EAAe,GAAgB,IAAqD,CAAC,GAAU;CAC7G,IAAI;EACF,IAAM,IAAU,EAAY,CAAK,GAC3B,IAAkB,EAAQ,YAAY;EAE5C,OAAO,EAAM,OAAO,EAAQ,MAAM,GAAG,EAAgB,EAAQ,QAAQ,GAAG,EAAE,MAAM,QAAQ,CAAC;CAC3F,SAAS,GAAO;EACd,MAAM,IAAI,EAAW,iBAAiB,sBAAsB,EAAE,OAAO,EAAM,CAAC;CAC9E;AACF;AAEA,SAAS,EAAY,GAA2B;CAC9C,IAAI,OAAO,KAAU,aAAY,KAAkB,OAAO,eAAe,CAAK,MAAM,OAAO,WACzF,MAAU,UAAU,mCAAmC;CAGzD,IAAM,IAAc,OAAO,0BAA0B,CAAK,GACpD,IAAU,QAAQ,QAAQ,CAAK;CAErC,IAAI,EAAQ,WAAW,EAAK,UAAU,CAAC,EAAK,OAAO,MAAQ,EAAQ,SAAS,CAAG,CAAC,GAC9E,MAAU,UAAU,yDAAyD;CAG/E,KAAK,IAAM,KAAO,GAAM;EACtB,IAAM,IAAa,EAAY;EAE/B,IAAI,CAAC,KAAc,EAAE,WAAW,MAAe,EAAW,OAAO,EAAW,KAC1E,MAAU,UAAU,wBAAwB,EAAI,uBAAuB;CAE3E;CAEA,IAAM,IAAS,EAAY,OAAQ,OAC7B,IAAW,EAAY,SAAU,OACjC,IAAO,EAAY,KAAM;CAE/B,IAAI,OAAO,KAAW,YAAY,CAAC,EAAQ,KAAK,CAAM,GACpD,MAAU,UAAU,sDAAsD;CAE5E,IAAI,OAAO,KAAa,YAAY,MAAS,SAAS,MAAU,UAAU,sCAAsC;CAEhH,OAAO;EAAE;EAAQ;EAAU;CAAK;AAClC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,60 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export type CurrencyCode = string;
|
|
7
|
-
/** A monetary value. `amount` is stored as bigint minor units (e.g. cents for USD). */
|
|
8
|
-
export type Money = {
|
|
9
|
-
readonly amount: bigint;
|
|
10
|
-
readonly currency: CurrencyCode;
|
|
1
|
+
declare const currencyBrand: unique symbol;
|
|
2
|
+
declare const decimalBrand: unique symbol;
|
|
3
|
+
declare const moneyBrand: unique symbol;
|
|
4
|
+
export type CurrencyCode<C extends string = string> = C & {
|
|
5
|
+
readonly [currencyBrand]: C;
|
|
11
6
|
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
readonly
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
7
|
+
export type Currency<C extends string = string> = Readonly<{
|
|
8
|
+
code: CurrencyCode<C>;
|
|
9
|
+
minorUnit: number;
|
|
10
|
+
}>;
|
|
11
|
+
export type Decimal = Readonly<{
|
|
12
|
+
readonly [decimalBrand]: true;
|
|
13
|
+
denominator: bigint;
|
|
14
|
+
numerator: bigint;
|
|
15
|
+
}>;
|
|
16
|
+
export type Money<C extends Currency = Currency> = Readonly<{
|
|
17
|
+
amount: bigint;
|
|
18
|
+
currency: C;
|
|
19
|
+
readonly [moneyBrand]: C;
|
|
20
|
+
}>;
|
|
21
|
+
export type ExchangeRate<From extends Currency = Currency, To extends Currency = Currency> = Readonly<{
|
|
22
|
+
from: From;
|
|
23
|
+
to: To;
|
|
24
|
+
value: Decimal;
|
|
25
|
+
}>;
|
|
26
|
+
export type FormatOptions = Readonly<{
|
|
29
27
|
locale?: string;
|
|
30
28
|
maximumFractionDigits?: number;
|
|
31
29
|
minimumFractionDigits?: number;
|
|
32
30
|
style?: 'code' | 'name' | 'narrowSymbol' | 'symbol';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
* | `'ceiling'` | Toward +∞ (up for positives, truncate for negatives) |
|
|
40
|
-
* | `'down'` | Toward zero (truncate, regardless of sign) |
|
|
41
|
-
* | `'floor'` | Toward −∞ (truncate for positives, down for negatives) |
|
|
42
|
-
* | `'half-away-from-zero'` | Round half away from zero (default, common in finance) |
|
|
43
|
-
* | `'half-even'` | Round half to even (banker's rounding, minimises cumulative error) |
|
|
44
|
-
* | `'up'` | Away from zero (regardless of sign) |
|
|
45
|
-
*/
|
|
46
|
-
export type RoundingMode = 'ceiling' | 'down' | 'floor' | 'half-away-from-zero' | 'half-even' | 'up';
|
|
47
|
-
/** JSON-safe representation of `Money`. `amount` is a decimal integer string to avoid bigint serialization issues. */
|
|
48
|
-
export type MoneyJSON = {
|
|
31
|
+
}>;
|
|
32
|
+
export type MoneyFormatPart = Readonly<{
|
|
33
|
+
type: 'currency' | 'decimal' | 'fraction' | 'integer' | 'literal' | 'minusSign' | 'plusSign';
|
|
34
|
+
value: string;
|
|
35
|
+
}>;
|
|
36
|
+
export type MoneyJSON = Readonly<{
|
|
49
37
|
amount: string;
|
|
50
38
|
currency: string;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*/
|
|
56
|
-
export type MoneyFormatPart = {
|
|
57
|
-
type: 'currency' | 'decimal' | 'fraction' | 'integer' | 'literal' | 'minusSign';
|
|
58
|
-
value: string;
|
|
59
|
-
};
|
|
39
|
+
unit: 'minor';
|
|
40
|
+
}>;
|
|
41
|
+
export type RoundingMode = 'awayFromZero' | 'ceil' | 'floor' | 'halfAwayFromZero' | 'halfEven' | 'towardZero';
|
|
42
|
+
export {};
|
|
60
43
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,aAAa,EAAE,OAAO,MAAM,CAAC;AAC3C,OAAO,CAAC,MAAM,YAAY,EAAE,OAAO,MAAM,CAAC;AAC1C,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAC;AAExC,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG;IAAE,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC;AAE1F,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IACzD,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AAEH,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC;IAC7B,QAAQ,CAAC,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AAEH,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,IAAI,QAAQ,CAAC;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,CAAC;IACZ,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;CAC1B,CAAC,CAAC;AAEH,MAAM,MAAM,YAAY,CAAC,IAAI,SAAS,QAAQ,GAAG,QAAQ,EAAE,EAAE,SAAS,QAAQ,GAAG,QAAQ,IAAI,QAAQ,CAAC;IACpG,IAAI,EAAE,IAAI,CAAC;IACX,EAAE,EAAE,EAAE,CAAC;IACP,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,cAAc,GAAG,QAAQ,CAAC;CACrD,CAAC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;IAC7F,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;CACf,CAAC,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,kBAAkB,GAAG,UAAU,GAAG,YAAY,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vielzeug/coins",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Bigint-based monetary arithmetic with currency formatting, exchange rates, and rounding policies",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"node": ">=22"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@types/node": "^26.1.
|
|
37
|
+
"@types/node": "^26.1.2",
|
|
38
38
|
"typescript": "^6.0.3",
|
|
39
|
-
"vite": "^8.
|
|
39
|
+
"vite": "^8.2.0",
|
|
40
40
|
"vitest": "^4.1.10"
|
|
41
41
|
}
|
|
42
42
|
}
|
package/dist/_cache.cjs
DELETED
package/dist/_cache.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_cache.cjs","names":[],"sources":["../src/_cache.ts"],"sourcesContent":["/** A FIFO bounded Map cache that evicts the oldest entry when full. */\nexport function boundedCache<K, V>(maxSize: number): { get(k: K): V | undefined; set(k: K, v: V): void } {\n const map = new Map<K, V>();\n\n return {\n get: (k) => map.get(k),\n set(k, v) {\n if (map.size >= maxSize) map.delete(map.keys().next().value as K);\n\n map.set(k, v);\n },\n };\n}\n"],"mappings":"AACA,SAAgB,EAAmB,EAAsE,CACvG,IAAM,EAAM,IAAI,IAEhB,MAAO,CACL,IAAM,GAAM,EAAI,IAAI,CAAC,EACrB,IAAI,EAAG,EAAG,CACJ,EAAI,MAAQ,GAAS,EAAI,OAAO,EAAI,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAU,EAEhE,EAAI,IAAI,EAAG,CAAC,CACd,CACF,CACF"}
|
package/dist/_cache.d.ts
DELETED
package/dist/_cache.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_cache.d.ts","sourceRoot":"","sources":["../src/_cache.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG;IAAE,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAAA;CAAE,CAWvG"}
|
package/dist/_cache.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
//#region src/_cache.ts
|
|
2
|
-
function e(e) {
|
|
3
|
-
let t = /* @__PURE__ */ new Map();
|
|
4
|
-
return {
|
|
5
|
-
get: (e) => t.get(e),
|
|
6
|
-
set(n, r) {
|
|
7
|
-
t.size >= e && t.delete(t.keys().next().value), t.set(n, r);
|
|
8
|
-
}
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
//#endregion
|
|
12
|
-
export { e as boundedCache };
|
|
13
|
-
|
|
14
|
-
//# sourceMappingURL=_cache.js.map
|