monetra 0.0.1
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/LICENSE +21 -0
- package/README.md +140 -0
- package/dist/index 2.js +424 -0
- package/dist/index 2.mjs +381 -0
- package/dist/index.d 2.mts +76 -0
- package/dist/index.d 2.ts +76 -0
- package/dist/index.d.mts +250 -0
- package/dist/index.d.ts +250 -0
- package/dist/index.js +516 -0
- package/dist/index.js 2.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +473 -0
- package/dist/index.mjs 2.map +1 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +46 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
// src/errors/BaseError.ts
|
|
2
|
+
var MonetraError = class extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = this.constructor.name;
|
|
6
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// src/errors/CurrencyMismatchError.ts
|
|
11
|
+
var CurrencyMismatchError = class extends MonetraError {
|
|
12
|
+
constructor(expected, actual) {
|
|
13
|
+
super(`Currency mismatch: expected ${expected}, got ${actual}`);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/errors/InvalidPrecisionError.ts
|
|
18
|
+
var InvalidPrecisionError = class extends MonetraError {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// src/errors/RoundingRequiredError.ts
|
|
25
|
+
var RoundingRequiredError = class extends MonetraError {
|
|
26
|
+
constructor() {
|
|
27
|
+
super("Rounding is required for this operation but was not provided.");
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// src/errors/InsufficientFundsError.ts
|
|
32
|
+
var InsufficientFundsError = class extends MonetraError {
|
|
33
|
+
constructor() {
|
|
34
|
+
super("Insufficient funds for this operation.");
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/errors/OverflowError.ts
|
|
39
|
+
var OverflowError = class extends MonetraError {
|
|
40
|
+
constructor(message = "Arithmetic overflow") {
|
|
41
|
+
super(message);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// src/money/guards.ts
|
|
46
|
+
function assertSameCurrency(a, b) {
|
|
47
|
+
if (a.currency.code !== b.currency.code) {
|
|
48
|
+
throw new CurrencyMismatchError(a.currency.code, b.currency.code);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/rounding/strategies.ts
|
|
53
|
+
var RoundingMode = /* @__PURE__ */ ((RoundingMode3) => {
|
|
54
|
+
RoundingMode3["HALF_UP"] = "HALF_UP";
|
|
55
|
+
RoundingMode3["HALF_DOWN"] = "HALF_DOWN";
|
|
56
|
+
RoundingMode3["HALF_EVEN"] = "HALF_EVEN";
|
|
57
|
+
RoundingMode3["FLOOR"] = "FLOOR";
|
|
58
|
+
RoundingMode3["CEIL"] = "CEIL";
|
|
59
|
+
return RoundingMode3;
|
|
60
|
+
})(RoundingMode || {});
|
|
61
|
+
|
|
62
|
+
// src/rounding/index.ts
|
|
63
|
+
function divideWithRounding(numerator, denominator, mode) {
|
|
64
|
+
if (denominator === 0n) {
|
|
65
|
+
throw new Error("Division by zero");
|
|
66
|
+
}
|
|
67
|
+
const quotient = numerator / denominator;
|
|
68
|
+
const remainder = numerator % denominator;
|
|
69
|
+
if (remainder === 0n) {
|
|
70
|
+
return quotient;
|
|
71
|
+
}
|
|
72
|
+
const sign = (numerator >= 0n ? 1n : -1n) * (denominator >= 0n ? 1n : -1n);
|
|
73
|
+
const absRemainder = remainder < 0n ? -remainder : remainder;
|
|
74
|
+
const absDenominator = denominator < 0n ? -denominator : denominator;
|
|
75
|
+
const isHalf = absRemainder * 2n === absDenominator;
|
|
76
|
+
const isMoreThanHalf = absRemainder * 2n > absDenominator;
|
|
77
|
+
switch (mode) {
|
|
78
|
+
case "FLOOR" /* FLOOR */:
|
|
79
|
+
return sign > 0n ? quotient : quotient - 1n;
|
|
80
|
+
case "CEIL" /* CEIL */:
|
|
81
|
+
return sign > 0n ? quotient + 1n : quotient;
|
|
82
|
+
case "HALF_UP" /* HALF_UP */:
|
|
83
|
+
if (isMoreThanHalf || isHalf) {
|
|
84
|
+
return sign > 0n ? quotient + 1n : quotient - 1n;
|
|
85
|
+
}
|
|
86
|
+
return quotient;
|
|
87
|
+
case "HALF_DOWN" /* HALF_DOWN */:
|
|
88
|
+
if (isMoreThanHalf) {
|
|
89
|
+
return sign > 0n ? quotient + 1n : quotient - 1n;
|
|
90
|
+
}
|
|
91
|
+
return quotient;
|
|
92
|
+
case "HALF_EVEN" /* HALF_EVEN */:
|
|
93
|
+
if (isMoreThanHalf) {
|
|
94
|
+
return sign > 0n ? quotient + 1n : quotient - 1n;
|
|
95
|
+
}
|
|
96
|
+
if (isHalf) {
|
|
97
|
+
if (quotient % 2n !== 0n) {
|
|
98
|
+
return sign > 0n ? quotient + 1n : quotient - 1n;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return quotient;
|
|
102
|
+
default:
|
|
103
|
+
throw new Error(`Unsupported rounding mode: ${mode}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/money/arithmetic.ts
|
|
108
|
+
function add(a, b) {
|
|
109
|
+
return a + b;
|
|
110
|
+
}
|
|
111
|
+
function subtract(a, b) {
|
|
112
|
+
return a - b;
|
|
113
|
+
}
|
|
114
|
+
function multiply(amount, multiplier, rounding) {
|
|
115
|
+
const { numerator, denominator } = parseMultiplier(multiplier);
|
|
116
|
+
const product = amount * numerator;
|
|
117
|
+
if (product % denominator === 0n) {
|
|
118
|
+
return product / denominator;
|
|
119
|
+
}
|
|
120
|
+
if (!rounding) {
|
|
121
|
+
throw new RoundingRequiredError();
|
|
122
|
+
}
|
|
123
|
+
return divideWithRounding(product, denominator, rounding);
|
|
124
|
+
}
|
|
125
|
+
function parseMultiplier(multiplier) {
|
|
126
|
+
const s = multiplier.toString();
|
|
127
|
+
if (/[eE]/.test(s)) {
|
|
128
|
+
throw new Error("Scientific notation not supported");
|
|
129
|
+
}
|
|
130
|
+
const parts = s.split(".");
|
|
131
|
+
if (parts.length > 2) {
|
|
132
|
+
throw new Error("Invalid number format");
|
|
133
|
+
}
|
|
134
|
+
const integerPart = parts[0];
|
|
135
|
+
const fractionalPart = parts[1] || "";
|
|
136
|
+
const denominator = 10n ** BigInt(fractionalPart.length);
|
|
137
|
+
const numerator = BigInt(integerPart + fractionalPart);
|
|
138
|
+
return { numerator, denominator };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/money/allocation.ts
|
|
142
|
+
function allocate(amount, ratios) {
|
|
143
|
+
if (ratios.length === 0) {
|
|
144
|
+
throw new Error("Cannot allocate to empty ratios");
|
|
145
|
+
}
|
|
146
|
+
const scaledRatios = ratios.map((r) => {
|
|
147
|
+
const s = r.toString();
|
|
148
|
+
if (/[eE]/.test(s)) throw new Error("Scientific notation not supported");
|
|
149
|
+
const parts = s.split(".");
|
|
150
|
+
const decimals = parts[1] ? parts[1].length : 0;
|
|
151
|
+
const value = BigInt(parts[0] + (parts[1] || ""));
|
|
152
|
+
return { value, decimals };
|
|
153
|
+
});
|
|
154
|
+
const maxDecimals = Math.max(...scaledRatios.map((r) => r.decimals));
|
|
155
|
+
const normalizedRatios = scaledRatios.map((r) => {
|
|
156
|
+
const factor = 10n ** BigInt(maxDecimals - r.decimals);
|
|
157
|
+
return r.value * factor;
|
|
158
|
+
});
|
|
159
|
+
const total = normalizedRatios.reduce((sum, r) => sum + r, 0n);
|
|
160
|
+
if (total === 0n) {
|
|
161
|
+
throw new Error("Total ratio must be greater than zero");
|
|
162
|
+
}
|
|
163
|
+
const results = [];
|
|
164
|
+
let allocatedTotal = 0n;
|
|
165
|
+
for (let i = 0; i < normalizedRatios.length; i++) {
|
|
166
|
+
const ratio = normalizedRatios[i];
|
|
167
|
+
const share = amount * ratio / total;
|
|
168
|
+
const remainder = amount * ratio % total;
|
|
169
|
+
results.push({ share, remainder, index: i });
|
|
170
|
+
allocatedTotal += share;
|
|
171
|
+
}
|
|
172
|
+
let leftOver = amount - allocatedTotal;
|
|
173
|
+
results.sort((a, b) => {
|
|
174
|
+
if (b.remainder > a.remainder) return 1;
|
|
175
|
+
if (b.remainder < a.remainder) return -1;
|
|
176
|
+
return 0;
|
|
177
|
+
});
|
|
178
|
+
for (let i = 0; i < Number(leftOver); i++) {
|
|
179
|
+
results[i].share += 1n;
|
|
180
|
+
}
|
|
181
|
+
results.sort((a, b) => a.index - b.index);
|
|
182
|
+
return results.map((r) => r.share);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/format/parser.ts
|
|
186
|
+
function parseToMinor(amount, currency) {
|
|
187
|
+
if (/[eE]/.test(amount)) {
|
|
188
|
+
throw new Error("Scientific notation not supported");
|
|
189
|
+
}
|
|
190
|
+
if (/[^0-9.-]/.test(amount)) {
|
|
191
|
+
throw new Error("Invalid characters in amount");
|
|
192
|
+
}
|
|
193
|
+
const parts = amount.split(".");
|
|
194
|
+
if (parts.length > 2) {
|
|
195
|
+
throw new Error("Invalid format: multiple decimal points");
|
|
196
|
+
}
|
|
197
|
+
const integerPart = parts[0];
|
|
198
|
+
const fractionalPart = parts[1] || "";
|
|
199
|
+
if (fractionalPart.length > currency.decimals) {
|
|
200
|
+
throw new InvalidPrecisionError(
|
|
201
|
+
`Precision ${fractionalPart.length} exceeds currency decimals ${currency.decimals}`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
const paddedFractional = fractionalPart.padEnd(currency.decimals, "0");
|
|
205
|
+
const combined = integerPart + paddedFractional;
|
|
206
|
+
if (combined === "-" || combined === "") {
|
|
207
|
+
throw new Error("Invalid format");
|
|
208
|
+
}
|
|
209
|
+
return BigInt(combined);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/format/formatter.ts
|
|
213
|
+
function format(money, options) {
|
|
214
|
+
const locale = options?.locale || money.currency.locale || "en-US";
|
|
215
|
+
const showSymbol = options?.symbol ?? true;
|
|
216
|
+
const decimals = money.currency.decimals;
|
|
217
|
+
const minor = money.minor;
|
|
218
|
+
const absMinor = minor < 0n ? -minor : minor;
|
|
219
|
+
const divisor = 10n ** BigInt(decimals);
|
|
220
|
+
const integerPart = absMinor / divisor;
|
|
221
|
+
const fractionalPart = absMinor % divisor;
|
|
222
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
223
|
+
const parts = new Intl.NumberFormat(locale, {
|
|
224
|
+
style: "decimal",
|
|
225
|
+
minimumFractionDigits: 1
|
|
226
|
+
}).formatToParts(1.1);
|
|
227
|
+
const decimalSeparator = parts.find((p) => p.type === "decimal")?.value || ".";
|
|
228
|
+
const integerFormatted = new Intl.NumberFormat(locale, {
|
|
229
|
+
style: "decimal",
|
|
230
|
+
useGrouping: true
|
|
231
|
+
}).format(integerPart);
|
|
232
|
+
const absString = decimals > 0 ? `${integerFormatted}${decimalSeparator}${fractionalStr}` : integerFormatted;
|
|
233
|
+
if (!showSymbol) {
|
|
234
|
+
return minor < 0n ? `-${absString}` : absString;
|
|
235
|
+
}
|
|
236
|
+
const templateParts = new Intl.NumberFormat(locale, {
|
|
237
|
+
style: "currency",
|
|
238
|
+
currency: money.currency.code
|
|
239
|
+
}).formatToParts(minor < 0n ? -1 : 1);
|
|
240
|
+
let result = "";
|
|
241
|
+
let numberInserted = false;
|
|
242
|
+
for (const part of templateParts) {
|
|
243
|
+
if (["integer", "group", "decimal", "fraction"].includes(part.type)) {
|
|
244
|
+
if (!numberInserted) {
|
|
245
|
+
result += absString;
|
|
246
|
+
numberInserted = true;
|
|
247
|
+
}
|
|
248
|
+
} else if (part.type === "currency") {
|
|
249
|
+
result += part.value;
|
|
250
|
+
} else {
|
|
251
|
+
result += part.value;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// src/money/Money.ts
|
|
258
|
+
var Money = class _Money {
|
|
259
|
+
constructor(minor, currency) {
|
|
260
|
+
this.minor = minor;
|
|
261
|
+
this.currency = currency;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Creates a Money instance from minor units (e.g., cents).
|
|
265
|
+
*
|
|
266
|
+
* @param minor - The amount in minor units. Can be a number or BigInt.
|
|
267
|
+
* @param currency - The currency of the money.
|
|
268
|
+
* @returns A new Money instance.
|
|
269
|
+
* @example
|
|
270
|
+
* const m = Money.fromMinor(100, USD); // $1.00
|
|
271
|
+
*/
|
|
272
|
+
static fromMinor(minor, currency) {
|
|
273
|
+
return new _Money(BigInt(minor), currency);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Creates a Money instance from major units (e.g., "10.50").
|
|
277
|
+
*
|
|
278
|
+
* @param amount - The amount as a string. Must be a valid decimal number.
|
|
279
|
+
* @param currency - The currency of the money.
|
|
280
|
+
* @returns A new Money instance.
|
|
281
|
+
* @throws {InvalidPrecisionError} If the amount has more decimal places than the currency allows.
|
|
282
|
+
* @example
|
|
283
|
+
* const m = Money.fromMajor("10.50", USD); // $10.50
|
|
284
|
+
*/
|
|
285
|
+
static fromMajor(amount, currency) {
|
|
286
|
+
const minor = parseToMinor(amount, currency);
|
|
287
|
+
return new _Money(minor, currency);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Creates a Money instance representing zero in the given currency.
|
|
291
|
+
*
|
|
292
|
+
* @param currency - The currency.
|
|
293
|
+
* @returns A new Money instance with value 0.
|
|
294
|
+
*/
|
|
295
|
+
static zero(currency) {
|
|
296
|
+
return new _Money(0n, currency);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Adds another Money value to this one.
|
|
300
|
+
*
|
|
301
|
+
* @param other - The Money value to add.
|
|
302
|
+
* @returns A new Money instance representing the sum.
|
|
303
|
+
* @throws {CurrencyMismatchError} If the currencies do not match.
|
|
304
|
+
*/
|
|
305
|
+
add(other) {
|
|
306
|
+
assertSameCurrency(this, other);
|
|
307
|
+
return new _Money(add(this.minor, other.minor), this.currency);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Subtracts another Money value from this one.
|
|
311
|
+
*
|
|
312
|
+
* @param other - The Money value to subtract.
|
|
313
|
+
* @returns A new Money instance representing the difference.
|
|
314
|
+
* @throws {CurrencyMismatchError} If the currencies do not match.
|
|
315
|
+
*/
|
|
316
|
+
subtract(other) {
|
|
317
|
+
assertSameCurrency(this, other);
|
|
318
|
+
return new _Money(subtract(this.minor, other.minor), this.currency);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Multiplies this Money value by a scalar.
|
|
322
|
+
*
|
|
323
|
+
* @param multiplier - The number to multiply by.
|
|
324
|
+
* @param options - Options for rounding if the result is not an integer.
|
|
325
|
+
* @returns A new Money instance representing the product.
|
|
326
|
+
* @throws {RoundingRequiredError} If the result is fractional and no rounding mode is provided.
|
|
327
|
+
*/
|
|
328
|
+
multiply(multiplier, options) {
|
|
329
|
+
const result = multiply(this.minor, multiplier, options?.rounding);
|
|
330
|
+
return new _Money(result, this.currency);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Allocates (splits) this Money value according to a list of ratios.
|
|
334
|
+
*
|
|
335
|
+
* The sum of the parts will always equal the original amount.
|
|
336
|
+
* Remainders are distributed to the parts with the largest fractional remainders.
|
|
337
|
+
*
|
|
338
|
+
* @param ratios - A list of numbers representing the ratios to split by.
|
|
339
|
+
* @returns An array of Money instances.
|
|
340
|
+
*/
|
|
341
|
+
allocate(ratios) {
|
|
342
|
+
const shares = allocate(this.minor, ratios);
|
|
343
|
+
return shares.map((share) => new _Money(share, this.currency));
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Formats this Money value as a string.
|
|
347
|
+
*
|
|
348
|
+
* @param options - Formatting options.
|
|
349
|
+
* @returns The formatted string.
|
|
350
|
+
*/
|
|
351
|
+
format(options) {
|
|
352
|
+
return format(this, options);
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Checks if this Money value is equal to another.
|
|
356
|
+
*
|
|
357
|
+
* @param other - The other Money value.
|
|
358
|
+
* @returns True if amounts and currencies are equal.
|
|
359
|
+
*/
|
|
360
|
+
equals(other) {
|
|
361
|
+
return this.currency.code === other.currency.code && this.minor === other.minor;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Checks if this Money value is greater than another.
|
|
365
|
+
*
|
|
366
|
+
* @param other - The other Money value.
|
|
367
|
+
* @returns True if this value is greater.
|
|
368
|
+
* @throws {CurrencyMismatchError} If the currencies do not match.
|
|
369
|
+
*/
|
|
370
|
+
greaterThan(other) {
|
|
371
|
+
assertSameCurrency(this, other);
|
|
372
|
+
return this.minor > other.minor;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Checks if this Money value is less than another.
|
|
376
|
+
*
|
|
377
|
+
* @param other - The other Money value.
|
|
378
|
+
* @returns True if this value is less.
|
|
379
|
+
* @throws {CurrencyMismatchError} If the currencies do not match.
|
|
380
|
+
*/
|
|
381
|
+
lessThan(other) {
|
|
382
|
+
assertSameCurrency(this, other);
|
|
383
|
+
return this.minor < other.minor;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Checks if this Money value is zero.
|
|
387
|
+
*
|
|
388
|
+
* @returns True if the amount is zero.
|
|
389
|
+
*/
|
|
390
|
+
isZero() {
|
|
391
|
+
return this.minor === 0n;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Checks if this Money value is negative.
|
|
395
|
+
*
|
|
396
|
+
* @returns True if the amount is negative.
|
|
397
|
+
*/
|
|
398
|
+
isNegative() {
|
|
399
|
+
return this.minor < 0n;
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
// src/currency/iso4217.ts
|
|
404
|
+
var USD = {
|
|
405
|
+
code: "USD",
|
|
406
|
+
decimals: 2,
|
|
407
|
+
symbol: "$",
|
|
408
|
+
locale: "en-US"
|
|
409
|
+
};
|
|
410
|
+
var EUR = {
|
|
411
|
+
code: "EUR",
|
|
412
|
+
decimals: 2,
|
|
413
|
+
symbol: "\u20AC",
|
|
414
|
+
locale: "de-DE"
|
|
415
|
+
// Default locale, can be overridden
|
|
416
|
+
};
|
|
417
|
+
var GBP = {
|
|
418
|
+
code: "GBP",
|
|
419
|
+
decimals: 2,
|
|
420
|
+
symbol: "\xA3",
|
|
421
|
+
locale: "en-GB"
|
|
422
|
+
};
|
|
423
|
+
var JPY = {
|
|
424
|
+
code: "JPY",
|
|
425
|
+
decimals: 0,
|
|
426
|
+
symbol: "\xA5",
|
|
427
|
+
locale: "ja-JP"
|
|
428
|
+
};
|
|
429
|
+
var ZAR = {
|
|
430
|
+
code: "ZAR",
|
|
431
|
+
decimals: 2,
|
|
432
|
+
symbol: "R",
|
|
433
|
+
locale: "en-ZA"
|
|
434
|
+
};
|
|
435
|
+
var CURRENCIES = {
|
|
436
|
+
USD,
|
|
437
|
+
EUR,
|
|
438
|
+
GBP,
|
|
439
|
+
JPY,
|
|
440
|
+
ZAR
|
|
441
|
+
};
|
|
442
|
+
function getCurrency(code) {
|
|
443
|
+
const currency = CURRENCIES[code.toUpperCase()];
|
|
444
|
+
if (!currency) {
|
|
445
|
+
throw new Error(`Unknown currency code: ${code}`);
|
|
446
|
+
}
|
|
447
|
+
return currency;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/currency/precision.ts
|
|
451
|
+
function getMinorUnitExponent(currency) {
|
|
452
|
+
return 10n ** BigInt(currency.decimals);
|
|
453
|
+
}
|
|
454
|
+
export {
|
|
455
|
+
CURRENCIES,
|
|
456
|
+
CurrencyMismatchError,
|
|
457
|
+
EUR,
|
|
458
|
+
GBP,
|
|
459
|
+
InsufficientFundsError,
|
|
460
|
+
InvalidPrecisionError,
|
|
461
|
+
JPY,
|
|
462
|
+
MonetraError,
|
|
463
|
+
Money,
|
|
464
|
+
OverflowError,
|
|
465
|
+
RoundingMode,
|
|
466
|
+
RoundingRequiredError,
|
|
467
|
+
USD,
|
|
468
|
+
ZAR,
|
|
469
|
+
divideWithRounding,
|
|
470
|
+
getCurrency,
|
|
471
|
+
getMinorUnitExponent
|
|
472
|
+
};
|
|
473
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors/BaseError.ts","../src/errors/CurrencyMismatchError.ts","../src/errors/InvalidPrecisionError.ts","../src/errors/RoundingRequiredError.ts","../src/errors/InsufficientFundsError.ts","../src/errors/OverflowError.ts","../src/money/guards.ts","../src/rounding/strategies.ts","../src/rounding/index.ts","../src/money/arithmetic.ts","../src/money/allocation.ts","../src/format/parser.ts","../src/format/formatter.ts","../src/money/Money.ts","../src/currency/iso4217.ts","../src/currency/precision.ts"],"sourcesContent":["export class MonetraError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class CurrencyMismatchError extends MonetraError {\n constructor(expected: string, actual: string) {\n super(`Currency mismatch: expected ${expected}, got ${actual}`);\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class InvalidPrecisionError extends MonetraError {\n constructor(message: string) {\n super(message);\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class RoundingRequiredError extends MonetraError {\n constructor() {\n super('Rounding is required for this operation but was not provided.');\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class InsufficientFundsError extends MonetraError {\n constructor() {\n super('Insufficient funds for this operation.');\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class OverflowError extends MonetraError {\n constructor(message: string = 'Arithmetic overflow') {\n super(message);\n }\n}\n","import { Money } from './Money';\nimport { CurrencyMismatchError, InsufficientFundsError } from '../errors';\n\nexport function assertSameCurrency(a: Money, b: Money): void {\n if (a.currency.code !== b.currency.code) {\n throw new CurrencyMismatchError(a.currency.code, b.currency.code);\n }\n}\n\nexport function assertNonNegative(money: Money): void {\n if (money.isNegative()) {\n // Spec doesn't explicitly name a NegativeError, but InsufficientFundsError might fit or just a generic error.\n // Spec lists \"InsufficientFundsError\" in 4.7.\n // But \"assertNonNegative\" is a guard.\n // Let's throw an Error or a specific one if defined.\n // \"Any violation is considered a bug\" -> maybe just Error?\n // But for \"InsufficientFunds\", that's usually when subtracting.\n // Let's use a generic Error for now or create a specific one if needed.\n throw new Error('Money value must be non-negative');\n }\n}\n","export enum RoundingMode {\n HALF_UP = 'HALF_UP',\n HALF_DOWN = 'HALF_DOWN',\n HALF_EVEN = 'HALF_EVEN',\n FLOOR = 'FLOOR',\n CEIL = 'CEIL',\n}\n","import { RoundingMode } from './strategies';\n\nexport * from './strategies';\n\nexport function divideWithRounding(\n numerator: bigint,\n denominator: bigint,\n mode: RoundingMode\n): bigint {\n if (denominator === 0n) {\n throw new Error('Division by zero');\n }\n\n const quotient = numerator / denominator;\n const remainder = numerator % denominator;\n\n if (remainder === 0n) {\n return quotient;\n }\n\n const sign = (numerator >= 0n ? 1n : -1n) * (denominator >= 0n ? 1n : -1n);\n const absRemainder = remainder < 0n ? -remainder : remainder;\n const absDenominator = denominator < 0n ? -denominator : denominator;\n\n // Check for exact half\n const isHalf = absRemainder * 2n === absDenominator;\n const isMoreThanHalf = absRemainder * 2n > absDenominator;\n\n switch (mode) {\n case RoundingMode.FLOOR:\n // If positive, quotient is already floor (truncation).\n // If negative, quotient is ceil (truncation towards zero), so we need to subtract 1.\n return sign > 0n ? quotient : quotient - 1n;\n case RoundingMode.CEIL:\n // If positive, quotient is floor, so add 1.\n // If negative, quotient is ceil, so keep it.\n return sign > 0n ? quotient + 1n : quotient;\n case RoundingMode.HALF_UP:\n if (isMoreThanHalf || isHalf) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n return quotient;\n case RoundingMode.HALF_DOWN:\n if (isMoreThanHalf) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n return quotient;\n case RoundingMode.HALF_EVEN:\n if (isMoreThanHalf) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n if (isHalf) {\n // If quotient is odd, round up (away from zero? no, to nearest even).\n // If quotient is even, keep it.\n if (quotient % 2n !== 0n) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n }\n return quotient;\n default:\n throw new Error(`Unsupported rounding mode: ${mode}`);\n }\n}\n","import { RoundingMode, divideWithRounding } from '../rounding';\nimport { RoundingRequiredError } from '../errors';\n\nexport function add(a: bigint, b: bigint): bigint {\n return a + b;\n}\n\nexport function subtract(a: bigint, b: bigint): bigint {\n return a - b;\n}\n\nexport function multiply(\n amount: bigint,\n multiplier: string | number,\n rounding?: RoundingMode\n): bigint {\n const { numerator, denominator } = parseMultiplier(multiplier);\n \n // result = amount * (numerator / denominator)\n // result = (amount * numerator) / denominator\n \n const product = amount * numerator;\n \n if (product % denominator === 0n) {\n return product / denominator;\n }\n \n if (!rounding) {\n throw new RoundingRequiredError();\n }\n \n return divideWithRounding(product, denominator, rounding);\n}\n\nfunction parseMultiplier(multiplier: string | number): { numerator: bigint; denominator: bigint } {\n const s = multiplier.toString();\n \n // Check for scientific notation\n if (/[eE]/.test(s)) {\n throw new Error('Scientific notation not supported');\n }\n \n const parts = s.split('.');\n if (parts.length > 2) {\n throw new Error('Invalid number format');\n }\n \n const integerPart = parts[0];\n const fractionalPart = parts[1] || '';\n \n const denominator = 10n ** BigInt(fractionalPart.length);\n const numerator = BigInt(integerPart + fractionalPart);\n \n return { numerator, denominator };\n}\n","export function allocate(amount: bigint, ratios: number[]): bigint[] {\n if (ratios.length === 0) {\n throw new Error('Cannot allocate to empty ratios');\n }\n\n // Scale ratios to integers\n const scaledRatios = ratios.map(r => {\n const s = r.toString();\n if (/[eE]/.test(s)) throw new Error('Scientific notation not supported');\n const parts = s.split('.');\n const decimals = parts[1] ? parts[1].length : 0;\n const value = BigInt(parts[0] + (parts[1] || ''));\n return { value, decimals };\n });\n\n const maxDecimals = Math.max(...scaledRatios.map(r => r.decimals));\n\n const normalizedRatios = scaledRatios.map(r => {\n const factor = 10n ** BigInt(maxDecimals - r.decimals);\n return r.value * factor;\n });\n\n const total = normalizedRatios.reduce((sum, r) => sum + r, 0n);\n\n if (total === 0n) {\n throw new Error('Total ratio must be greater than zero');\n }\n\n const results: { share: bigint; remainder: bigint; index: number }[] = [];\n let allocatedTotal = 0n;\n\n for (let i = 0; i < normalizedRatios.length; i++) {\n const ratio = normalizedRatios[i];\n const share = (amount * ratio) / total;\n const remainder = (amount * ratio) % total;\n \n results.push({ share, remainder, index: i });\n allocatedTotal += share;\n }\n\n let leftOver = amount - allocatedTotal;\n\n // Distribute leftover to those with largest remainder\n // Sort by remainder desc\n results.sort((a, b) => {\n if (b.remainder > a.remainder) return 1;\n if (b.remainder < a.remainder) return -1;\n return 0;\n });\n\n for (let i = 0; i < Number(leftOver); i++) {\n results[i].share += 1n;\n }\n\n // Sort back by index\n results.sort((a, b) => a.index - b.index);\n\n return results.map(r => r.share);\n}\n","import { Currency } from '../currency/Currency';\nimport { InvalidPrecisionError } from '../errors';\n\nexport function parseToMinor(amount: string, currency: Currency): bigint {\n // Validate format\n if (/[eE]/.test(amount)) {\n throw new Error('Scientific notation not supported');\n }\n \n // Reject ambiguous separators (commas, spaces, etc.)\n if (/[^0-9.-]/.test(amount)) {\n throw new Error('Invalid characters in amount');\n }\n \n const parts = amount.split('.');\n if (parts.length > 2) {\n throw new Error('Invalid format: multiple decimal points');\n }\n \n const integerPart = parts[0];\n const fractionalPart = parts[1] || '';\n \n if (fractionalPart.length > currency.decimals) {\n throw new InvalidPrecisionError(\n `Precision ${fractionalPart.length} exceeds currency decimals ${currency.decimals}`\n );\n }\n \n // Pad fractional part\n const paddedFractional = fractionalPart.padEnd(currency.decimals, '0');\n \n const combined = integerPart + paddedFractional;\n \n // Handle edge case where integer part is just \"-\"\n if (combined === '-' || combined === '') {\n throw new Error('Invalid format');\n }\n \n return BigInt(combined);\n}\n","import { Money } from '../money/Money';\n\nexport interface FormatOptions {\n locale?: string;\n symbol?: boolean;\n}\n\nexport function format(money: Money, options?: FormatOptions): string {\n const locale = options?.locale || money.currency.locale || 'en-US';\n const showSymbol = options?.symbol ?? true;\n \n const decimals = money.currency.decimals;\n const minor = money.minor;\n const absMinor = minor < 0n ? -minor : minor;\n \n const divisor = 10n ** BigInt(decimals);\n const integerPart = absMinor / divisor;\n const fractionalPart = absMinor % divisor;\n \n // Pad fractional\n const fractionalStr = fractionalPart.toString().padStart(decimals, '0');\n \n // Get separators\n // We use a dummy format to extract the decimal separator for the locale\n const parts = new Intl.NumberFormat(locale, {\n style: 'decimal',\n minimumFractionDigits: 1,\n }).formatToParts(1.1);\n \n const decimalSeparator = parts.find(p => p.type === 'decimal')?.value || '.';\n \n // Format integer part with grouping using Intl (supports BigInt)\n const integerFormatted = new Intl.NumberFormat(locale, {\n style: 'decimal',\n useGrouping: true,\n }).format(integerPart);\n \n const absString = decimals > 0 \n ? `${integerFormatted}${decimalSeparator}${fractionalStr}`\n : integerFormatted;\n \n if (!showSymbol) {\n // If no symbol, we just return the number with sign\n // But we should respect locale sign format? \n // Usually just \"-\" is fine for \"no symbol\" raw format, but spec says \"Locale-aware formatting\".\n // If we disable symbol, we probably just want the number.\n return minor < 0n ? `-${absString}` : absString;\n }\n\n // Use formatToParts to get the template (sign position, currency position)\n // We use 1 or -1 to get the sign/currency pattern\n const templateParts = new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: money.currency.code,\n }).formatToParts(minor < 0n ? -1 : 1);\n \n let result = '';\n let numberInserted = false;\n \n for (const part of templateParts) {\n if (['integer', 'group', 'decimal', 'fraction'].includes(part.type)) {\n if (!numberInserted) {\n result += absString;\n numberInserted = true;\n }\n } else if (part.type === 'currency') {\n // Use the symbol from currency metadata if available, or the one from Intl\n // Spec says \"Configurable grouping and symbol display\".\n // We'll use the one from Intl usually, but Money.currency.symbol is available.\n // Let's use Intl's symbol to match the locale, unless it's wrong?\n // Spec: \"symbol: 'R'\".\n // If Intl returns \"ZAR\", we might want \"R\".\n // Let's prefer the one from Intl for consistency with locale, \n // but if Intl gives code (e.g. \"USD\" instead of \"$\"), maybe we want symbol.\n // Usually Intl gives symbol.\n result += part.value;\n } else {\n result += part.value; // literals, minusSign, parentheses, etc.\n }\n }\n \n return result;\n}\n","import { Currency } from '../currency/Currency';\nimport { RoundingMode } from '../rounding/strategies';\nimport { assertSameCurrency } from './guards';\nimport { add, subtract, multiply } from './arithmetic';\nimport { allocate } from './allocation';\nimport { parseToMinor } from '../format/parser';\nimport { format } from '../format/formatter';\n\nexport class Money {\n readonly minor: bigint;\n readonly currency: Currency;\n\n private constructor(minor: bigint, currency: Currency) {\n this.minor = minor;\n this.currency = currency;\n }\n\n static fromMinor(minor: bigint | number, currency: Currency): Money {\n return new Money(BigInt(minor), currency);\n }\n\n static fromMajor(amount: string, currency: Currency): Money {\n const minor = parseToMinor(amount, currency);\n return new Money(minor, currency);\n }\n\n static zero(currency: Currency): Money {\n return new Money(0n, currency);\n }\n\n add(other: Money): Money {\n assertSameCurrency(this, other);\n return new Money(add(this.minor, other.minor), this.currency);\n }\n\n subtract(other: Money): Money {\n assertSameCurrency(this, other);\n return new Money(subtract(this.minor, other.minor), this.currency);\n }\n\n multiply(multiplier: string | number, options?: { rounding?: RoundingMode }): Money {\n const result = multiply(this.minor, multiplier, options?.rounding);\n return new Money(result, this.currency);\n }\n\n allocate(ratios: number[]): Money[] {\n const shares = allocate(this.minor, ratios);\n return shares.map(share => new Money(share, this.currency));\n }\n\n format(options?: { locale?: string; symbol?: boolean }): string {\n return format(this, options);\n }\n\n equals(other: Money): boolean {\n return this.currency.code === other.currency.code && this.minor === other.minor;\n }\n\n greaterThan(other: Money): boolean {\n assertSameCurrency(this, other);\n return this.minor > other.minor;\n }\n\n lessThan(other: Money): boolean {\n assertSameCurrency(this, other);\n return this.minor < other.minor;\n }\n\n isZero(): boolean {\n return this.minor === 0n;\n }\n\n isNegative(): boolean {\n return this.minor < 0n;\n }\n}\n","import { Currency } from './Currency';\n\nexport const USD: Currency = {\n code: 'USD',\n decimals: 2,\n symbol: '$',\n locale: 'en-US',\n};\n\nexport const EUR: Currency = {\n code: 'EUR',\n decimals: 2,\n symbol: '€',\n locale: 'de-DE', // Default locale, can be overridden\n};\n\nexport const GBP: Currency = {\n code: 'GBP',\n decimals: 2,\n symbol: '£',\n locale: 'en-GB',\n};\n\nexport const JPY: Currency = {\n code: 'JPY',\n decimals: 0,\n symbol: '¥',\n locale: 'ja-JP',\n};\n\nexport const ZAR: Currency = {\n code: 'ZAR',\n decimals: 2,\n symbol: 'R',\n locale: 'en-ZA',\n};\n\nexport const CURRENCIES: Record<string, Currency> = {\n USD,\n EUR,\n GBP,\n JPY,\n ZAR,\n};\n\nexport function getCurrency(code: string): Currency {\n const currency = CURRENCIES[code.toUpperCase()];\n if (!currency) {\n // Fallback or error? Spec says \"Currency Defines Precision\".\n // If unknown, we might need to throw or allow custom registration.\n // For now, let's throw if not found, or allow creating one on the fly if we want to be flexible.\n // But spec says \"Currency Module... Defines currency metadata\".\n // Let's assume for now we only support known ones or user must provide the object.\n throw new Error(`Unknown currency code: ${code}`);\n }\n return currency;\n}\n","import { Currency } from './Currency';\n\nexport function getMinorUnitExponent(currency: Currency): bigint {\n return 10n ** BigInt(currency.decimals);\n}\n"],"mappings":"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","names":["RoundingMode"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors/BaseError.ts","../src/errors/CurrencyMismatchError.ts","../src/errors/InvalidPrecisionError.ts","../src/errors/RoundingRequiredError.ts","../src/errors/InsufficientFundsError.ts","../src/errors/OverflowError.ts","../src/money/guards.ts","../src/rounding/strategies.ts","../src/rounding/index.ts","../src/money/arithmetic.ts","../src/money/allocation.ts","../src/format/parser.ts","../src/format/formatter.ts","../src/money/Money.ts","../src/currency/iso4217.ts","../src/currency/precision.ts"],"sourcesContent":["export class MonetraError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class CurrencyMismatchError extends MonetraError {\n constructor(expected: string, actual: string) {\n super(`Currency mismatch: expected ${expected}, got ${actual}`);\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class InvalidPrecisionError extends MonetraError {\n constructor(message: string) {\n super(message);\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class RoundingRequiredError extends MonetraError {\n constructor() {\n super('Rounding is required for this operation but was not provided.');\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class InsufficientFundsError extends MonetraError {\n constructor() {\n super('Insufficient funds for this operation.');\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class OverflowError extends MonetraError {\n constructor(message: string = 'Arithmetic overflow') {\n super(message);\n }\n}\n","import { Money } from './Money';\nimport { CurrencyMismatchError, InsufficientFundsError } from '../errors';\n\nexport function assertSameCurrency(a: Money, b: Money): void {\n if (a.currency.code !== b.currency.code) {\n throw new CurrencyMismatchError(a.currency.code, b.currency.code);\n }\n}\n\nexport function assertNonNegative(money: Money): void {\n if (money.isNegative()) {\n // Spec doesn't explicitly name a NegativeError, but InsufficientFundsError might fit or just a generic error.\n // Spec lists \"InsufficientFundsError\" in 4.7.\n // But \"assertNonNegative\" is a guard.\n // Let's throw an Error or a specific one if defined.\n // \"Any violation is considered a bug\" -> maybe just Error?\n // But for \"InsufficientFunds\", that's usually when subtracting.\n // Let's use a generic Error for now or create a specific one if needed.\n throw new Error('Money value must be non-negative');\n }\n}\n","/**\n * Enumeration of rounding modes for handling fractional minor units.\n */\nexport enum RoundingMode {\n /**\n * Rounds towards the nearest neighbor. If equidistant, rounds away from zero.\n * Example: 2.5 -> 3, -2.5 -> -3\n */\n HALF_UP = 'HALF_UP',\n\n /**\n * Rounds towards the nearest neighbor. If equidistant, rounds towards zero.\n * Example: 2.5 -> 2, -2.5 -> -2\n */\n HALF_DOWN = 'HALF_DOWN',\n\n /**\n * Rounds towards the nearest neighbor. If equidistant, rounds towards the nearest even integer.\n * Also known as Banker's Rounding.\n * Example: 2.5 -> 2, 3.5 -> 4\n */\n HALF_EVEN = 'HALF_EVEN',\n\n /**\n * Rounds towards negative infinity.\n * Example: 2.9 -> 2, -2.1 -> -3\n */\n FLOOR = 'FLOOR',\n\n /**\n * Rounds towards positive infinity.\n * Example: 2.1 -> 3, -2.9 -> -2\n */\n CEIL = 'CEIL',\n}\n","import { RoundingMode } from './strategies';\n\nexport * from './strategies';\n\nexport function divideWithRounding(\n numerator: bigint,\n denominator: bigint,\n mode: RoundingMode\n): bigint {\n if (denominator === 0n) {\n throw new Error('Division by zero');\n }\n\n const quotient = numerator / denominator;\n const remainder = numerator % denominator;\n\n if (remainder === 0n) {\n return quotient;\n }\n\n const sign = (numerator >= 0n ? 1n : -1n) * (denominator >= 0n ? 1n : -1n);\n const absRemainder = remainder < 0n ? -remainder : remainder;\n const absDenominator = denominator < 0n ? -denominator : denominator;\n\n // Check for exact half\n const isHalf = absRemainder * 2n === absDenominator;\n const isMoreThanHalf = absRemainder * 2n > absDenominator;\n\n switch (mode) {\n case RoundingMode.FLOOR:\n // If positive, quotient is already floor (truncation).\n // If negative, quotient is ceil (truncation towards zero), so we need to subtract 1.\n return sign > 0n ? quotient : quotient - 1n;\n case RoundingMode.CEIL:\n // If positive, quotient is floor, so add 1.\n // If negative, quotient is ceil, so keep it.\n return sign > 0n ? quotient + 1n : quotient;\n case RoundingMode.HALF_UP:\n if (isMoreThanHalf || isHalf) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n return quotient;\n case RoundingMode.HALF_DOWN:\n if (isMoreThanHalf) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n return quotient;\n case RoundingMode.HALF_EVEN:\n if (isMoreThanHalf) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n if (isHalf) {\n // If quotient is odd, round up (away from zero? no, to nearest even).\n // If quotient is even, keep it.\n if (quotient % 2n !== 0n) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n }\n return quotient;\n default:\n throw new Error(`Unsupported rounding mode: ${mode}`);\n }\n}\n","import { RoundingMode, divideWithRounding } from '../rounding';\nimport { RoundingRequiredError } from '../errors';\n\n/**\n * Adds two BigInt values.\n */\nexport function add(a: bigint, b: bigint): bigint {\n return a + b;\n}\n\n/**\n * Subtracts two BigInt values.\n */\nexport function subtract(a: bigint, b: bigint): bigint {\n return a - b;\n}\n\n/**\n * Multiplies a BigInt amount by a multiplier.\n * \n * Handles fractional multipliers by converting them to a rational number (numerator/denominator).\n * If the result is not an integer, a rounding mode must be provided.\n * \n * @param amount - The amount to multiply.\n * @param multiplier - The multiplier (number or string).\n * @param rounding - Optional rounding mode.\n * @returns The result as a BigInt.\n * @throws {RoundingRequiredError} If rounding is needed but not provided.\n */\nexport function multiply(\n amount: bigint,\n multiplier: string | number,\n rounding?: RoundingMode\n): bigint {\n const { numerator, denominator } = parseMultiplier(multiplier);\n \n // result = amount * (numerator / denominator)\n // result = (amount * numerator) / denominator\n \n const product = amount * numerator;\n \n if (product % denominator === 0n) {\n return product / denominator;\n }\n \n if (!rounding) {\n throw new RoundingRequiredError();\n }\n \n return divideWithRounding(product, denominator, rounding);\n}\n\nfunction parseMultiplier(multiplier: string | number): { numerator: bigint; denominator: bigint } {\n const s = multiplier.toString();\n \n // Check for scientific notation\n if (/[eE]/.test(s)) {\n throw new Error('Scientific notation not supported');\n }\n \n const parts = s.split('.');\n if (parts.length > 2) {\n throw new Error('Invalid number format');\n }\n \n const integerPart = parts[0];\n const fractionalPart = parts[1] || '';\n \n const denominator = 10n ** BigInt(fractionalPart.length);\n const numerator = BigInt(integerPart + fractionalPart);\n \n return { numerator, denominator };\n}\n","/**\n * Allocates a monetary amount according to a list of ratios.\n * \n * Uses the \"Largest Remainder Method\" to ensure that the sum of the allocated\n * parts equals the original amount. Remainders are distributed to the parts\n * that had the largest fractional remainders during the division.\n * \n * @param amount - The total amount to allocate (in minor units).\n * @param ratios - An array of ratios (e.g., [1, 1] for 50/50 split).\n * @returns An array of allocated amounts (in minor units).\n * @throws {Error} If ratios are empty or total ratio is zero.\n */\nexport function allocate(amount: bigint, ratios: number[]): bigint[] {\n if (ratios.length === 0) {\n throw new Error('Cannot allocate to empty ratios');\n }\n\n // Scale ratios to integers\n const scaledRatios = ratios.map(r => {\n const s = r.toString();\n if (/[eE]/.test(s)) throw new Error('Scientific notation not supported');\n const parts = s.split('.');\n const decimals = parts[1] ? parts[1].length : 0;\n const value = BigInt(parts[0] + (parts[1] || ''));\n return { value, decimals };\n });\n\n const maxDecimals = Math.max(...scaledRatios.map(r => r.decimals));\n\n const normalizedRatios = scaledRatios.map(r => {\n const factor = 10n ** BigInt(maxDecimals - r.decimals);\n return r.value * factor;\n });\n\n const total = normalizedRatios.reduce((sum, r) => sum + r, 0n);\n\n if (total === 0n) {\n throw new Error('Total ratio must be greater than zero');\n }\n\n const results: { share: bigint; remainder: bigint; index: number }[] = [];\n let allocatedTotal = 0n;\n\n for (let i = 0; i < normalizedRatios.length; i++) {\n const ratio = normalizedRatios[i];\n const share = (amount * ratio) / total;\n const remainder = (amount * ratio) % total;\n \n results.push({ share, remainder, index: i });\n allocatedTotal += share;\n }\n\n let leftOver = amount - allocatedTotal;\n\n // Distribute leftover to those with largest remainder\n // Sort by remainder desc\n results.sort((a, b) => {\n if (b.remainder > a.remainder) return 1;\n if (b.remainder < a.remainder) return -1;\n return 0;\n });\n\n for (let i = 0; i < Number(leftOver); i++) {\n results[i].share += 1n;\n }\n\n // Sort back by index\n results.sort((a, b) => a.index - b.index);\n\n return results.map(r => r.share);\n}\n","import { Currency } from '../currency/Currency';\nimport { InvalidPrecisionError } from '../errors';\n\n/**\n * Parses a string representation of a major unit amount into minor units.\n * \n * Validates the input format to ensure it is a valid decimal number without\n * scientific notation or ambiguous characters. Checks that the precision\n * does not exceed the currency's allowed decimals.\n * \n * @param amount - The amount string (e.g., \"10.50\").\n * @param currency - The currency to validate against.\n * @returns The amount in minor units as a BigInt.\n * @throws {Error} If the format is invalid (scientific notation, non-numeric chars).\n * @throws {InvalidPrecisionError} If the precision exceeds the currency's decimals.\n */\nexport function parseToMinor(amount: string, currency: Currency): bigint {\n // Validate format\n if (/[eE]/.test(amount)) {\n throw new Error('Scientific notation not supported');\n }\n \n // Reject ambiguous separators (commas, spaces, etc.)\n if (/[^0-9.-]/.test(amount)) {\n throw new Error('Invalid characters in amount');\n }\n \n const parts = amount.split('.');\n if (parts.length > 2) {\n throw new Error('Invalid format: multiple decimal points');\n }\n \n const integerPart = parts[0];\n const fractionalPart = parts[1] || '';\n \n if (fractionalPart.length > currency.decimals) {\n throw new InvalidPrecisionError(\n `Precision ${fractionalPart.length} exceeds currency decimals ${currency.decimals}`\n );\n }\n \n // Pad fractional part\n const paddedFractional = fractionalPart.padEnd(currency.decimals, '0');\n \n const combined = integerPart + paddedFractional;\n \n // Handle edge case where integer part is just \"-\"\n if (combined === '-' || combined === '') {\n throw new Error('Invalid format');\n }\n \n return BigInt(combined);\n}\n","import { Money } from '../money/Money';\n\n/**\n * Options for formatting Money values.\n */\nexport interface FormatOptions {\n /**\n * The locale to use for formatting (e.g., \"en-US\", \"de-DE\").\n * If not provided, defaults to the currency's default locale or \"en-US\".\n */\n locale?: string;\n\n /**\n * Whether to include the currency symbol in the output.\n * Defaults to true.\n */\n symbol?: boolean;\n}\n\n/**\n * Formats a Money object into a string representation.\n * \n * Uses `Intl.NumberFormat` for locale-aware formatting of numbers and currency symbols.\n * \n * @param money - The Money object to format.\n * @param options - Formatting options.\n * @returns The formatted string.\n */\nexport function format(money: Money, options?: FormatOptions): string {\n const locale = options?.locale || money.currency.locale || 'en-US';\n const showSymbol = options?.symbol ?? true;\n \n const decimals = money.currency.decimals;\n const minor = money.minor;\n const absMinor = minor < 0n ? -minor : minor;\n \n const divisor = 10n ** BigInt(decimals);\n const integerPart = absMinor / divisor;\n const fractionalPart = absMinor % divisor;\n \n // Pad fractional\n const fractionalStr = fractionalPart.toString().padStart(decimals, '0');\n \n // Get separators\n // We use a dummy format to extract the decimal separator for the locale\n const parts = new Intl.NumberFormat(locale, {\n style: 'decimal',\n minimumFractionDigits: 1,\n }).formatToParts(1.1);\n \n const decimalSeparator = parts.find(p => p.type === 'decimal')?.value || '.';\n \n // Format integer part with grouping using Intl (supports BigInt)\n const integerFormatted = new Intl.NumberFormat(locale, {\n style: 'decimal',\n useGrouping: true,\n }).format(integerPart);\n \n const absString = decimals > 0 \n ? `${integerFormatted}${decimalSeparator}${fractionalStr}`\n : integerFormatted;\n \n if (!showSymbol) {\n // If no symbol, we just return the number with sign\n // But we should respect locale sign format? \n // Usually just \"-\" is fine for \"no symbol\" raw format, but spec says \"Locale-aware formatting\".\n // If we disable symbol, we probably just want the number.\n return minor < 0n ? `-${absString}` : absString;\n }\n\n // Use formatToParts to get the template (sign position, currency position)\n // We use 1 or -1 to get the sign/currency pattern\n const templateParts = new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: money.currency.code,\n }).formatToParts(minor < 0n ? -1 : 1);\n \n let result = '';\n let numberInserted = false;\n \n for (const part of templateParts) {\n if (['integer', 'group', 'decimal', 'fraction'].includes(part.type)) {\n if (!numberInserted) {\n result += absString;\n numberInserted = true;\n }\n } else if (part.type === 'currency') {\n // Use the symbol from currency metadata if available, or the one from Intl\n // Spec says \"Configurable grouping and symbol display\".\n // We'll use the one from Intl usually, but Money.currency.symbol is available.\n // Let's use Intl's symbol to match the locale, unless it's wrong?\n // Spec: \"symbol: 'R'\".\n // If Intl returns \"ZAR\", we might want \"R\".\n // Let's prefer the one from Intl for consistency with locale, \n // but if Intl gives code (e.g. \"USD\" instead of \"$\"), maybe we want symbol.\n // Usually Intl gives symbol.\n result += part.value;\n } else {\n result += part.value; // literals, minusSign, parentheses, etc.\n }\n }\n \n return result;\n}\n","import { Currency } from '../currency/Currency';\nimport { RoundingMode } from '../rounding/strategies';\nimport { assertSameCurrency } from './guards';\nimport { add, subtract, multiply } from './arithmetic';\nimport { allocate } from './allocation';\nimport { parseToMinor } from '../format/parser';\nimport { format } from '../format/formatter';\n\n/**\n * Represents a monetary value in a specific currency.\n * \n * Money objects are immutable and store values in minor units (e.g., cents) using BigInt\n * to avoid floating-point precision errors.\n */\nexport class Money {\n /**\n * The amount in minor units (e.g., cents).\n */\n readonly minor: bigint;\n\n /**\n * The currency of this money value.\n */\n readonly currency: Currency;\n\n private constructor(minor: bigint, currency: Currency) {\n this.minor = minor;\n this.currency = currency;\n }\n\n /**\n * Creates a Money instance from minor units (e.g., cents).\n * \n * @param minor - The amount in minor units. Can be a number or BigInt.\n * @param currency - The currency of the money.\n * @returns A new Money instance.\n * @example\n * const m = Money.fromMinor(100, USD); // $1.00\n */\n static fromMinor(minor: bigint | number, currency: Currency): Money {\n return new Money(BigInt(minor), currency);\n }\n\n /**\n * Creates a Money instance from major units (e.g., \"10.50\").\n * \n * @param amount - The amount as a string. Must be a valid decimal number.\n * @param currency - The currency of the money.\n * @returns A new Money instance.\n * @throws {InvalidPrecisionError} If the amount has more decimal places than the currency allows.\n * @example\n * const m = Money.fromMajor(\"10.50\", USD); // $10.50\n */\n static fromMajor(amount: string, currency: Currency): Money {\n const minor = parseToMinor(amount, currency);\n return new Money(minor, currency);\n }\n\n /**\n * Creates a Money instance representing zero in the given currency.\n * \n * @param currency - The currency.\n * @returns A new Money instance with value 0.\n */\n static zero(currency: Currency): Money {\n return new Money(0n, currency);\n }\n\n /**\n * Adds another Money value to this one.\n * \n * @param other - The Money value to add.\n * @returns A new Money instance representing the sum.\n * @throws {CurrencyMismatchError} If the currencies do not match.\n */\n add(other: Money): Money {\n assertSameCurrency(this, other);\n return new Money(add(this.minor, other.minor), this.currency);\n }\n\n /**\n * Subtracts another Money value from this one.\n * \n * @param other - The Money value to subtract.\n * @returns A new Money instance representing the difference.\n * @throws {CurrencyMismatchError} If the currencies do not match.\n */\n subtract(other: Money): Money {\n assertSameCurrency(this, other);\n return new Money(subtract(this.minor, other.minor), this.currency);\n }\n\n /**\n * Multiplies this Money value by a scalar.\n * \n * @param multiplier - The number to multiply by.\n * @param options - Options for rounding if the result is not an integer.\n * @returns A new Money instance representing the product.\n * @throws {RoundingRequiredError} If the result is fractional and no rounding mode is provided.\n */\n multiply(multiplier: string | number, options?: { rounding?: RoundingMode }): Money {\n const result = multiply(this.minor, multiplier, options?.rounding);\n return new Money(result, this.currency);\n }\n\n /**\n * Allocates (splits) this Money value according to a list of ratios.\n * \n * The sum of the parts will always equal the original amount.\n * Remainders are distributed to the parts with the largest fractional remainders.\n * \n * @param ratios - A list of numbers representing the ratios to split by.\n * @returns An array of Money instances.\n */\n allocate(ratios: number[]): Money[] {\n const shares = allocate(this.minor, ratios);\n return shares.map(share => new Money(share, this.currency));\n }\n\n /**\n * Formats this Money value as a string.\n * \n * @param options - Formatting options.\n * @returns The formatted string.\n */\n format(options?: { locale?: string; symbol?: boolean }): string {\n return format(this, options);\n }\n\n /**\n * Checks if this Money value is equal to another.\n * \n * @param other - The other Money value.\n * @returns True if amounts and currencies are equal.\n */\n equals(other: Money): boolean {\n return this.currency.code === other.currency.code && this.minor === other.minor;\n }\n\n /**\n * Checks if this Money value is greater than another.\n * \n * @param other - The other Money value.\n * @returns True if this value is greater.\n * @throws {CurrencyMismatchError} If the currencies do not match.\n */\n greaterThan(other: Money): boolean {\n assertSameCurrency(this, other);\n return this.minor > other.minor;\n }\n\n /**\n * Checks if this Money value is less than another.\n * \n * @param other - The other Money value.\n * @returns True if this value is less.\n * @throws {CurrencyMismatchError} If the currencies do not match.\n */\n lessThan(other: Money): boolean {\n assertSameCurrency(this, other);\n return this.minor < other.minor;\n }\n\n /**\n * Checks if this Money value is zero.\n * \n * @returns True if the amount is zero.\n */\n isZero(): boolean {\n return this.minor === 0n;\n }\n\n /**\n * Checks if this Money value is negative.\n * \n * @returns True if the amount is negative.\n */\n isNegative(): boolean {\n return this.minor < 0n;\n }\n}\n","import { Currency } from './Currency';\n\n/**\n * United States Dollar (USD).\n */\nexport const USD: Currency = {\n code: 'USD',\n decimals: 2,\n symbol: '$',\n locale: 'en-US',\n};\n\n/**\n * Euro (EUR).\n */\nexport const EUR: Currency = {\n code: 'EUR',\n decimals: 2,\n symbol: '€',\n locale: 'de-DE', // Default locale, can be overridden\n};\n\n/**\n * British Pound Sterling (GBP).\n */\nexport const GBP: Currency = {\n code: 'GBP',\n decimals: 2,\n symbol: '£',\n locale: 'en-GB',\n};\n\n/**\n * Japanese Yen (JPY).\n */\nexport const JPY: Currency = {\n code: 'JPY',\n decimals: 0,\n symbol: '¥',\n locale: 'ja-JP',\n};\n\n/**\n * South African Rand (ZAR).\n */\nexport const ZAR: Currency = {\n code: 'ZAR',\n decimals: 2,\n symbol: 'R',\n locale: 'en-ZA',\n};\n\n/**\n * A collection of common currencies.\n */\nexport const CURRENCIES: Record<string, Currency> = {\n USD,\n EUR,\n GBP,\n JPY,\n ZAR,\n};\n\n/**\n * Retrieves a Currency object by its ISO 4217 code.\n * \n * @param code - The currency code (case-insensitive).\n * @returns The Currency object corresponding to the code.\n * @throws {Error} If the currency code is not found in the registry.\n */\nexport function getCurrency(code: string): Currency {\n const currency = CURRENCIES[code.toUpperCase()];\n if (!currency) {\n // Fallback or error? Spec says \"Currency Defines Precision\".\n // If unknown, we might need to throw or allow custom registration.\n // For now, let's throw if not found, or allow creating one on the fly if we want to be flexible.\n // But spec says \"Currency Module... Defines currency metadata\".\n // Let's assume for now we only support known ones or user must provide the object.\n throw new Error(`Unknown currency code: ${code}`);\n }\n return currency;\n}\n","import { Currency } from './Currency';\n\nexport function getMinorUnitExponent(currency: Currency): bigint {\n return 10n ** BigInt(currency.decimals);\n}\n"],"mappings":"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","names":["RoundingMode"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "monetra",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A currency-aware, integer-based money engine designed for financial correctness.",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"import": "./dist/index.mjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"test": "vitest",
|
|
18
|
+
"lint": "tsc --noEmit",
|
|
19
|
+
"format": "prettier --write \"src/**/*.ts\""
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"money",
|
|
23
|
+
"currency",
|
|
24
|
+
"financial",
|
|
25
|
+
"integer",
|
|
26
|
+
"typescript"
|
|
27
|
+
],
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE",
|
|
32
|
+
"SPEC.md"
|
|
33
|
+
],
|
|
34
|
+
"author": "",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"typescript": "^5.3.0",
|
|
38
|
+
"tsup": "^8.0.0",
|
|
39
|
+
"vitest": "^1.0.0",
|
|
40
|
+
"prettier": "^3.0.0",
|
|
41
|
+
"@types/node": "^20.0.0"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18"
|
|
45
|
+
}
|
|
46
|
+
}
|