monetra 2.2.0 → 2.2.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.
@@ -1,1323 +1,7 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/financial/index.ts
21
- var financial_exports = {};
22
- __export(financial_exports, {
23
- Rate: () => Rate,
24
- compound: () => compound,
25
- currentYield: () => currentYield,
26
- debtToAssets: () => debtToAssets,
27
- debtToEquity: () => debtToEquity,
28
- discount: () => discount,
29
- equityMultiplier: () => equityMultiplier,
30
- futureValue: () => futureValue,
31
- interestCoverage: () => interestCoverage,
32
- interestOnlyPayment: () => interestOnlyPayment,
33
- irr: () => irr,
34
- leverageRatios: () => leverageRatios,
35
- loan: () => loan,
36
- npv: () => npv,
37
- pmt: () => pmt,
38
- presentValue: () => presentValue,
39
- roi: () => roi,
40
- simpleInterest: () => simpleInterest,
41
- simpleInterestTotal: () => simpleInterestTotal,
42
- straightLineDepreciation: () => straightLineDepreciation,
43
- totalInterest: () => totalInterest,
44
- totalInterestFromSchedule: () => totalInterestFromSchedule
45
- });
46
- module.exports = __toCommonJS(financial_exports);
47
-
48
- // src/currency/registry.ts
49
- var registry = /* @__PURE__ */ new Map();
50
- function getCurrency(code) {
51
- const currency = registry.get(code);
52
- if (!currency) {
53
- throw new Error(`Currency '${code}' not found in registry.`);
54
- }
55
- return currency;
56
- }
57
-
58
- // src/errors/BaseError.ts
59
- var MonetraError = class extends Error {
60
- constructor(message, code) {
61
- super(message);
62
- this.name = this.constructor.name;
63
- this.code = code;
64
- Object.setPrototypeOf(this, new.target.prototype);
65
- }
66
- };
67
-
68
- // src/errors/CurrencyMismatchError.ts
69
- var CurrencyMismatchError = class extends MonetraError {
70
- constructor(expected, received) {
71
- super(
72
- `Currency mismatch: expected ${expected}, received ${received}.
1
+ 'use strict';var Y=new Map;function F(n){let r=Y.get(n);if(!r)throw new Error(`Currency '${n}' not found in registry.`);return r}var g=class extends Error{constructor(r,e){super(r),this.name=this.constructor.name,this.code=e,Object.setPrototypeOf(this,new.target.prototype);}};var h=class extends g{constructor(r,e){super(`Currency mismatch: expected ${r}, received ${e}.
73
2
  \u{1F4A1} Tip: Use a Converter to convert between currencies:
74
- const converter = new Converter('USD', { ${received}: rate });
75
- const converted = converter.convert(money, '${expected}');`,
76
- "MONETRA_CURRENCY_MISMATCH" /* CURRENCY_MISMATCH */
77
- );
78
- this.expected = expected;
79
- this.received = received;
80
- }
81
- };
82
-
83
- // src/errors/InvalidPrecisionError.ts
84
- var InvalidPrecisionError = class extends MonetraError {
85
- constructor(message) {
86
- super(message, "MONETRA_INVALID_PRECISION" /* INVALID_PRECISION */);
87
- }
88
- };
89
-
90
- // src/errors/RoundingRequiredError.ts
91
- var RoundingRequiredError = class extends MonetraError {
92
- constructor(operation, result) {
93
- let message = "Rounding is required for this operation but was not provided.";
94
- if (operation && result !== void 0) {
95
- message = `Rounding required for ${operation}: result ${result} is not an integer.
3
+ const converter = new Converter('USD', { ${e}: rate });
4
+ const converted = converter.convert(money, '${r}');`,"MONETRA_CURRENCY_MISMATCH"),this.expected=r,this.received=e;}};var C=class extends g{constructor(r){super(r,"MONETRA_INVALID_PRECISION");}};var x=class extends g{constructor(r,e){let t="Rounding is required for this operation but was not provided.";r&&e!==void 0&&(t=`Rounding required for ${r}: result ${e} is not an integer.
96
5
  \u{1F4A1} Tip: Provide a rounding mode:
97
- money.${operation}(value, { rounding: RoundingMode.HALF_UP })
98
- Available modes: HALF_UP, HALF_DOWN, HALF_EVEN, FLOOR, CEIL, TRUNCATE`;
99
- }
100
- super(message, "MONETRA_ROUNDING_REQUIRED" /* ROUNDING_REQUIRED */);
101
- }
102
- };
103
-
104
- // src/errors/InvalidArgumentError.ts
105
- var InvalidArgumentError = class extends MonetraError {
106
- constructor(message) {
107
- super(message, "MONETRA_INVALID_ARGUMENT" /* INVALID_ARGUMENT */);
108
- }
109
- };
110
-
111
- // src/money/guards.ts
112
- function assertSameCurrency(a, b) {
113
- if (a.currency.code !== b.currency.code) {
114
- throw new CurrencyMismatchError(a.currency.code, b.currency.code);
115
- }
116
- }
117
-
118
- // src/rounding/index.ts
119
- function divideWithRounding(numerator, denominator, mode) {
120
- if (denominator === 0n) {
121
- throw new Error("Division by zero");
122
- }
123
- const quotient = numerator / denominator;
124
- const remainder = numerator % denominator;
125
- if (remainder === 0n) {
126
- return quotient;
127
- }
128
- const isNegativeResult = numerator < 0n !== denominator < 0n;
129
- const isPositiveResult = !isNegativeResult;
130
- const absRemainder = remainder < 0n ? -remainder : remainder;
131
- const absDenominator = denominator < 0n ? -denominator : denominator;
132
- const isHalf = absRemainder * 2n === absDenominator;
133
- const isMoreThanHalf = absRemainder * 2n > absDenominator;
134
- switch (mode) {
135
- case "FLOOR" /* FLOOR */:
136
- return isPositiveResult ? quotient : quotient - 1n;
137
- case "CEIL" /* CEIL */:
138
- return isPositiveResult ? quotient + 1n : quotient;
139
- case "HALF_UP" /* HALF_UP */:
140
- if (isMoreThanHalf || isHalf) {
141
- return isPositiveResult ? quotient + 1n : quotient - 1n;
142
- }
143
- return quotient;
144
- case "HALF_DOWN" /* HALF_DOWN */:
145
- if (isMoreThanHalf) {
146
- return isPositiveResult ? quotient + 1n : quotient - 1n;
147
- }
148
- return quotient;
149
- case "HALF_EVEN" /* HALF_EVEN */:
150
- if (isMoreThanHalf) {
151
- return isPositiveResult ? quotient + 1n : quotient - 1n;
152
- }
153
- if (isHalf) {
154
- if (quotient % 2n !== 0n) {
155
- return isPositiveResult ? quotient + 1n : quotient - 1n;
156
- }
157
- }
158
- return quotient;
159
- case "TRUNCATE" /* TRUNCATE */:
160
- return quotient;
161
- default:
162
- throw new Error(`Unsupported rounding mode: ${mode}`);
163
- }
164
- }
165
-
166
- // src/money/arithmetic.ts
167
- function add(a, b) {
168
- return a + b;
169
- }
170
- function subtract(a, b) {
171
- return a - b;
172
- }
173
- function multiply(amount, multiplier, rounding) {
174
- const { numerator, denominator } = parseMultiplier(multiplier);
175
- const product = amount * numerator;
176
- if (product % denominator === 0n) {
177
- return product / denominator;
178
- }
179
- if (!rounding) {
180
- throw new RoundingRequiredError(
181
- "multiply",
182
- Number(product) / Number(denominator)
183
- );
184
- }
185
- return divideWithRounding(product, denominator, rounding);
186
- }
187
- function divide(amount, divisor, rounding) {
188
- const { numerator, denominator } = parseMultiplier(divisor);
189
- const product = amount * denominator;
190
- if (product % numerator === 0n) {
191
- return product / numerator;
192
- }
193
- if (!rounding) {
194
- throw new RoundingRequiredError(
195
- "divide",
196
- Number(product) / Number(numerator)
197
- );
198
- }
199
- return divideWithRounding(product, numerator, rounding);
200
- }
201
- function parseMultiplier(multiplier) {
202
- const s = multiplier.toString();
203
- if (/[eE]/.test(s)) {
204
- throw new Error("Scientific notation not supported");
205
- }
206
- const parts = s.split(".");
207
- if (parts.length > 2) {
208
- throw new Error("Invalid number format");
209
- }
210
- const integerPart = parts[0];
211
- const fractionalPart = parts[1] || "";
212
- const denominator = 10n ** BigInt(fractionalPart.length);
213
- const numerator = BigInt(integerPart + fractionalPart);
214
- return { numerator, denominator };
215
- }
216
-
217
- // src/money/allocation.ts
218
- function allocate(amount, ratios) {
219
- if (ratios.length === 0) {
220
- throw new Error("Cannot allocate to empty ratios");
221
- }
222
- const scaledRatios = ratios.map((r) => {
223
- const s = r.toString();
224
- if (/[eE]/.test(s)) throw new Error("Scientific notation not supported");
225
- const parts = s.split(".");
226
- const decimals = parts[1] ? parts[1].length : 0;
227
- const value = BigInt(parts[0] + (parts[1] || ""));
228
- return { value, decimals };
229
- });
230
- const maxDecimals = Math.max(...scaledRatios.map((r) => r.decimals));
231
- const normalizedRatios = scaledRatios.map((r) => {
232
- const factor = 10n ** BigInt(maxDecimals - r.decimals);
233
- return r.value * factor;
234
- });
235
- const total = normalizedRatios.reduce((sum, r) => sum + r, 0n);
236
- if (total === 0n) {
237
- throw new Error("Total ratio must be greater than zero");
238
- }
239
- const results = [];
240
- let allocatedTotal = 0n;
241
- for (let i = 0; i < normalizedRatios.length; i++) {
242
- const ratio = normalizedRatios[i];
243
- const share = amount * ratio / total;
244
- const remainder = amount * ratio % total;
245
- results.push({ share, remainder, index: i });
246
- allocatedTotal += share;
247
- }
248
- let leftOver = amount - allocatedTotal;
249
- results.sort((a, b) => {
250
- if (b.remainder > a.remainder) return 1;
251
- if (b.remainder < a.remainder) return -1;
252
- return 0;
253
- });
254
- for (let i = 0; i < Number(leftOver); i++) {
255
- results[i].share += 1n;
256
- }
257
- results.sort((a, b) => a.index - b.index);
258
- return results.map((r) => r.share);
259
- }
260
-
261
- // src/format/parser.ts
262
- function parseToMinor(amount, currency) {
263
- if (/[eE]/.test(amount)) {
264
- throw new Error("Scientific notation not supported");
265
- }
266
- if (/[^0-9.-]/.test(amount)) {
267
- throw new Error("Invalid characters in amount");
268
- }
269
- if (!/[0-9]/.test(amount)) {
270
- throw new Error("Invalid format");
271
- }
272
- const parts = amount.split(".");
273
- if (parts.length > 2) {
274
- throw new Error("Invalid format: multiple decimal points");
275
- }
276
- const integerPart = parts[0];
277
- const fractionalPart = parts[1] || "";
278
- if (fractionalPart.length > currency.decimals) {
279
- throw new InvalidPrecisionError(
280
- `Precision ${fractionalPart.length} exceeds currency decimals ${currency.decimals}`
281
- );
282
- }
283
- const paddedFractional = fractionalPart.padEnd(currency.decimals, "0");
284
- const combined = integerPart + paddedFractional;
285
- return BigInt(combined);
286
- }
287
-
288
- // src/format/formatter.ts
289
- function format(money, options) {
290
- const locale = options?.locale || money.currency.locale || "en-US";
291
- const showSymbol = options?.symbol ?? true;
292
- const display = options?.display || "symbol";
293
- const useAccounting = options?.accounting ?? false;
294
- const decimals = money.currency.decimals;
295
- const minor = money.minor;
296
- const isNegative = minor < 0n;
297
- const absMinor = isNegative ? -minor : minor;
298
- const divisor = 10n ** BigInt(decimals);
299
- const integerPart = absMinor / divisor;
300
- const fractionalPart = absMinor % divisor;
301
- const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
302
- const parts = new Intl.NumberFormat(locale, {
303
- style: "decimal",
304
- minimumFractionDigits: 1
305
- }).formatToParts(1.1);
306
- const decimalSeparator = parts.find((p) => p.type === "decimal")?.value || ".";
307
- const integerFormatted = new Intl.NumberFormat(locale, {
308
- style: "decimal",
309
- useGrouping: true
310
- }).format(integerPart);
311
- const absString = decimals > 0 ? `${integerFormatted}${decimalSeparator}${fractionalStr}` : integerFormatted;
312
- if (!showSymbol) {
313
- if (isNegative) {
314
- return useAccounting ? `(${absString})` : `-${absString}`;
315
- }
316
- return absString;
317
- }
318
- let templateParts;
319
- try {
320
- templateParts = new Intl.NumberFormat(locale, {
321
- style: "currency",
322
- currency: money.currency.code,
323
- currencyDisplay: display
324
- }).formatToParts(isNegative ? -1234.5 : 1234.5);
325
- } catch (e) {
326
- const symbol = display === "symbol" ? money.currency.symbol : money.currency.code;
327
- if (isNegative) {
328
- return useAccounting ? `(${symbol}${absString})` : `-${symbol}${absString}`;
329
- }
330
- return `${symbol}${absString}`;
331
- }
332
- let result = "";
333
- let numberInserted = false;
334
- for (const part of templateParts) {
335
- switch (part.type) {
336
- case "minusSign": {
337
- if (!useAccounting) {
338
- result += part.value;
339
- }
340
- break;
341
- }
342
- case "integer": {
343
- if (!numberInserted) {
344
- result += absString;
345
- numberInserted = true;
346
- }
347
- break;
348
- }
349
- // Skip the remaining numeric parts, since we already inserted the full number string.
350
- case "group":
351
- case "decimal":
352
- case "fraction": {
353
- break;
354
- }
355
- case "currency": {
356
- if (display === "symbol" && money.currency.symbol) {
357
- result += money.currency.symbol;
358
- } else {
359
- result += part.value;
360
- }
361
- break;
362
- }
363
- default: {
364
- result += part.value;
365
- break;
366
- }
367
- }
368
- }
369
- if (useAccounting && isNegative) {
370
- return `(${result.trim()})`;
371
- }
372
- return result;
373
- }
374
-
375
- // src/money/Money.ts
376
- var Money = class _Money {
377
- constructor(minor, currency) {
378
- this.minor = minor;
379
- this.currency = currency;
380
- }
381
- static resolveCurrency(currency) {
382
- if (typeof currency === "string") {
383
- return getCurrency(currency);
384
- }
385
- return currency;
386
- }
387
- /**
388
- * Creates a Money instance from minor units (e.g., cents).
389
- *
390
- * @param minor - The amount in minor units. Can be a number or BigInt.
391
- * @param currency - The currency of the money (object or code string).
392
- * @returns A new Money instance.
393
- * @example
394
- * const m = Money.fromMinor(100, USD); // $1.00
395
- * const m2 = Money.fromMinor(100, 'USD'); // $1.00
396
- */
397
- static fromMinor(minor, currency) {
398
- return new _Money(BigInt(minor), _Money.resolveCurrency(currency));
399
- }
400
- /**
401
- * Alias for `fromMinor`. Creates a Money instance from cents/minor units.
402
- *
403
- * @param cents - The amount in minor units.
404
- * @param currency - The currency of the money (object or code string).
405
- * @returns A new Money instance.
406
- * @example
407
- * const m = Money.fromCents(100, 'USD'); // $1.00
408
- */
409
- static fromCents(cents, currency) {
410
- return _Money.fromMinor(cents, currency);
411
- }
412
- /**
413
- * Creates a Money instance from major units (e.g., "10.50").
414
- *
415
- * @param amount - The amount as a string. Must be a valid decimal number.
416
- * @param currency - The currency of the money (object or code string).
417
- * @returns A new Money instance.
418
- * @throws {InvalidPrecisionError} If the amount has more decimal places than the currency allows.
419
- * @example
420
- * const m = Money.fromMajor("10.50", USD); // $10.50
421
- */
422
- static fromMajor(amount, currency) {
423
- const resolvedCurrency = _Money.resolveCurrency(currency);
424
- const minor = parseToMinor(amount, resolvedCurrency);
425
- return new _Money(minor, resolvedCurrency);
426
- }
427
- /**
428
- * Alias for `fromMajor`. Creates a Money instance from a decimal string.
429
- *
430
- * @param amount - The amount as a string (e.g., "10.50").
431
- * @param currency - The currency of the money (object or code string).
432
- * @returns A new Money instance.
433
- * @throws {InvalidPrecisionError} If the amount has more decimal places than the currency allows.
434
- * @example
435
- * const m = Money.fromDecimal("10.50", 'USD'); // $10.50
436
- */
437
- static fromDecimal(amount, currency) {
438
- return _Money.fromMajor(amount, currency);
439
- }
440
- /**
441
- * Creates a Money instance from a floating-point number.
442
- *
443
- * ⚠️ WARNING: Floating-point numbers can have precision issues.
444
- * Prefer `Money.fromMajor("10.50", currency)` for exact values.
445
- *
446
- * @param amount - The float amount in major units.
447
- * @param currency - The currency.
448
- * @param options - Options for handling precision.
449
- * @returns A new Money instance.
450
- */
451
- static fromFloat(amount, currency, options) {
452
- if (!options?.suppressWarning && process.env.NODE_ENV !== "production") {
453
- console.warn(
454
- '[monetra] Money.fromFloat() may lose precision. Consider using Money.fromMajor("' + amount + '", currency) instead.'
455
- );
456
- }
457
- const resolvedCurrency = _Money.resolveCurrency(currency);
458
- const factor = 10 ** resolvedCurrency.decimals;
459
- const minorUnits = Math.round(amount * factor);
460
- return new _Money(BigInt(minorUnits), resolvedCurrency);
461
- }
462
- /**
463
- * Returns the minimum of the provided Money values.
464
- * @param values - Money values to compare (must all be same currency).
465
- * @returns The Money with the smallest amount.
466
- * @throws {CurrencyMismatchError} If currencies don't match.
467
- */
468
- static min(...values) {
469
- if (values.length === 0)
470
- throw new Error("At least one Money value required");
471
- return values.reduce((min, current) => {
472
- assertSameCurrency(min, current);
473
- return current.lessThan(min) ? current : min;
474
- });
475
- }
476
- /**
477
- * Returns the maximum of the provided Money values.
478
- * @param values - Money values to compare (must all be same currency).
479
- * @returns The Money with the largest amount.
480
- * @throws {CurrencyMismatchError} If currencies don't match.
481
- */
482
- static max(...values) {
483
- if (values.length === 0)
484
- throw new Error("At least one Money value required");
485
- return values.reduce((max, current) => {
486
- assertSameCurrency(max, current);
487
- return current.greaterThan(max) ? current : max;
488
- });
489
- }
490
- /**
491
- * Creates a Money instance representing zero in the given currency.
492
- *
493
- * @param currency - The currency.
494
- * @returns A new Money instance with value 0.
495
- */
496
- static zero(currency) {
497
- return new _Money(0n, _Money.resolveCurrency(currency));
498
- }
499
- resolveOther(other) {
500
- if (other instanceof _Money) {
501
- return other;
502
- }
503
- if (typeof other === "string") {
504
- return _Money.fromMajor(other, this.currency);
505
- }
506
- return _Money.fromMinor(other, this.currency);
507
- }
508
- /**
509
- * Adds another Money value to this one.
510
- *
511
- * @param other - The value to add (Money, minor units as number/bigint, or major units as string).
512
- * @returns A new Money instance representing the sum.
513
- * @throws {CurrencyMismatchError} If the currencies do not match.
514
- */
515
- add(other) {
516
- const otherMoney = this.resolveOther(other);
517
- assertSameCurrency(this, otherMoney);
518
- return new _Money(add(this.minor, otherMoney.minor), this.currency);
519
- }
520
- /**
521
- * Subtracts another Money value from this one.
522
- *
523
- * @param other - The value to subtract (Money, minor units as number/bigint, or major units as string).
524
- * @returns A new Money instance representing the difference.
525
- * @throws {CurrencyMismatchError} If the currencies do not match.
526
- */
527
- subtract(other) {
528
- const otherMoney = this.resolveOther(other);
529
- assertSameCurrency(this, otherMoney);
530
- return new _Money(subtract(this.minor, otherMoney.minor), this.currency);
531
- }
532
- /**
533
- * Multiplies this Money value by a scalar.
534
- *
535
- * @param multiplier - The number to multiply by.
536
- * @param options - Options for rounding if the result is not an integer.
537
- * @returns A new Money instance representing the product.
538
- * @throws {RoundingRequiredError} If the result is fractional and no rounding mode is provided.
539
- */
540
- multiply(multiplier, options) {
541
- const result = multiply(this.minor, multiplier, options?.rounding);
542
- return new _Money(result, this.currency);
543
- }
544
- /**
545
- * Divides this Money value by a divisor.
546
- *
547
- * @param divisor - The number to divide by.
548
- * @param options - Options for rounding if the result is not an integer.
549
- * @returns A new Money instance representing the quotient.
550
- * @throws {RoundingRequiredError} If the result is fractional and no rounding mode is provided.
551
- * @throws {Error} If divisor is zero.
552
- */
553
- divide(divisor, options) {
554
- if (divisor === 0 || divisor === "0") {
555
- throw new Error("Division by zero");
556
- }
557
- const result = divide(this.minor, divisor, options?.rounding);
558
- return new _Money(result, this.currency);
559
- }
560
- /**
561
- * Returns the absolute value of this Money.
562
- * @returns A new Money instance with the absolute value.
563
- */
564
- abs() {
565
- return new _Money(this.minor < 0n ? -this.minor : this.minor, this.currency);
566
- }
567
- /**
568
- * Returns the negated value of this Money.
569
- * @returns A new Money instance with the negated value.
570
- */
571
- negate() {
572
- return new _Money(-this.minor, this.currency);
573
- }
574
- /**
575
- * Allocates (splits) this Money value according to a list of ratios.
576
- *
577
- * The sum of the parts will always equal the original amount.
578
- * Remainders are distributed to the parts with the largest fractional remainders.
579
- *
580
- * @param ratios - A list of numbers representing the ratios to split by.
581
- * @returns An array of Money instances.
582
- */
583
- allocate(ratios) {
584
- const shares = allocate(this.minor, ratios);
585
- return shares.map((share) => new _Money(share, this.currency));
586
- }
587
- /**
588
- * Formats this Money value as a string.
589
- *
590
- * @param options - Formatting options.
591
- * @returns The formatted string.
592
- */
593
- format(options) {
594
- return format(this, options);
595
- }
596
- /**
597
- * Checks if this Money value is equal to another.
598
- *
599
- * @param other - The other Money value (Money, minor units as number/bigint, or major units as string).
600
- * @returns True if amounts and currencies are equal.
601
- */
602
- equals(other) {
603
- const otherMoney = this.resolveOther(other);
604
- return this.currency.code === otherMoney.currency.code && this.minor === otherMoney.minor;
605
- }
606
- /**
607
- * Checks if this Money value is greater than another.
608
- *
609
- * @param other - The other Money value (Money, minor units as number/bigint, or major units as string).
610
- * @returns True if this value is greater.
611
- * @throws {CurrencyMismatchError} If the currencies do not match.
612
- */
613
- greaterThan(other) {
614
- const otherMoney = this.resolveOther(other);
615
- assertSameCurrency(this, otherMoney);
616
- return this.minor > otherMoney.minor;
617
- }
618
- /**
619
- * Checks if this Money value is less than another.
620
- *
621
- * @param other - The other Money value (Money, minor units as number/bigint, or major units as string).
622
- * @returns True if this value is less.
623
- * @throws {CurrencyMismatchError} If the currencies do not match.
624
- */
625
- lessThan(other) {
626
- const otherMoney = this.resolveOther(other);
627
- assertSameCurrency(this, otherMoney);
628
- return this.minor < otherMoney.minor;
629
- }
630
- /**
631
- * Checks if this Money value is greater than or equal to another.
632
- */
633
- greaterThanOrEqual(other) {
634
- const otherMoney = this.resolveOther(other);
635
- assertSameCurrency(this, otherMoney);
636
- return this.minor >= otherMoney.minor;
637
- }
638
- /**
639
- * Checks if this Money value is less than or equal to another.
640
- */
641
- lessThanOrEqual(other) {
642
- const otherMoney = this.resolveOther(other);
643
- assertSameCurrency(this, otherMoney);
644
- return this.minor <= otherMoney.minor;
645
- }
646
- /**
647
- * Checks if this Money value is positive (greater than zero).
648
- */
649
- isPositive() {
650
- return this.minor > 0n;
651
- }
652
- /**
653
- * Compares this Money to another, returning -1, 0, or 1.
654
- * Useful for sorting.
655
- */
656
- compare(other) {
657
- const otherMoney = this.resolveOther(other);
658
- assertSameCurrency(this, otherMoney);
659
- if (this.minor < otherMoney.minor) return -1;
660
- if (this.minor > otherMoney.minor) return 1;
661
- return 0;
662
- }
663
- /**
664
- * Calculates a percentage of the money.
665
- * @param percent - The percentage (e.g., 50 for 50%).
666
- * @param rounding - Rounding mode (defaults to HALF_EVEN).
667
- */
668
- percentage(percent, rounding = "HALF_EVEN" /* HALF_EVEN */) {
669
- return this.multiply(percent / 100, { rounding });
670
- }
671
- /**
672
- * Adds a percentage to the money.
673
- * @param percent - The percentage to add.
674
- * @param rounding - Rounding mode.
675
- */
676
- addPercent(percent, rounding = "HALF_EVEN" /* HALF_EVEN */) {
677
- return this.add(this.percentage(percent, rounding));
678
- }
679
- /**
680
- * Subtracts a percentage from the money.
681
- * @param percent - The percentage to subtract.
682
- * @param rounding - Rounding mode.
683
- */
684
- subtractPercent(percent, rounding = "HALF_EVEN" /* HALF_EVEN */) {
685
- return this.subtract(this.percentage(percent, rounding));
686
- }
687
- /**
688
- * Splits the money into equal parts.
689
- * @param parts - Number of parts.
690
- */
691
- split(parts) {
692
- const ratios = Array(parts).fill(1);
693
- return this.allocate(ratios);
694
- }
695
- /**
696
- * Checks if this Money value is zero.
697
- *
698
- * @returns True if the amount is zero.
699
- */
700
- isZero() {
701
- return this.minor === 0n;
702
- }
703
- /**
704
- * Checks if this Money value is negative.
705
- *
706
- * @returns True if the amount is negative.
707
- */
708
- isNegative() {
709
- return this.minor < 0n;
710
- }
711
- /**
712
- * Clamps this Money value between a minimum and maximum.
713
- *
714
- * @param min - The minimum Money value.
715
- * @param max - The maximum Money value.
716
- * @returns A new Money instance clamped between min and max.
717
- * @throws {CurrencyMismatchError} If currencies don't match.
718
- * @throws {Error} If min is greater than max.
719
- * @example
720
- * const price = Money.fromMajor("150", 'USD');
721
- * const clamped = price.clamp(Money.fromMajor("50", 'USD'), Money.fromMajor("100", 'USD'));
722
- * // clamped is $100.00
723
- */
724
- clamp(min, max) {
725
- assertSameCurrency(this, min);
726
- assertSameCurrency(this, max);
727
- if (min.greaterThan(max)) {
728
- throw new Error("Clamp min cannot be greater than max");
729
- }
730
- if (this.lessThan(min)) {
731
- return new _Money(min.minor, this.currency);
732
- }
733
- if (this.greaterThan(max)) {
734
- return new _Money(max.minor, this.currency);
735
- }
736
- return this;
737
- }
738
- /**
739
- * Returns the value as a decimal string without locale formatting.
740
- *
741
- * This returns a raw decimal representation suitable for storage or calculations,
742
- * without any currency symbols, grouping separators, or locale-specific formatting.
743
- *
744
- * @returns The decimal string representation (e.g., "10.50", "-5.25").
745
- * @example
746
- * const m = Money.fromMajor("1234.56", 'USD');
747
- * m.toDecimalString(); // "1234.56"
748
- */
749
- toDecimalString() {
750
- const decimals = this.currency.decimals;
751
- const isNegative = this.minor < 0n;
752
- const absMinor = isNegative ? -this.minor : this.minor;
753
- if (decimals === 0) {
754
- return isNegative ? `-${absMinor.toString()}` : absMinor.toString();
755
- }
756
- const divisor = 10n ** BigInt(decimals);
757
- const integerPart = absMinor / divisor;
758
- const fractionalPart = absMinor % divisor;
759
- const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
760
- const result = `${integerPart}.${fractionalStr}`;
761
- return isNegative ? `-${result}` : result;
762
- }
763
- /**
764
- * Returns a JSON representation of the Money object.
765
- *
766
- * @returns An object with amount (string), currency (code), and precision.
767
- */
768
- toJSON() {
769
- return {
770
- amount: this.minor.toString(),
771
- currency: this.currency.code,
772
- precision: this.currency.decimals
773
- };
774
- }
775
- /**
776
- * JSON reviver function for deserializing Money objects.
777
- *
778
- * Use with `JSON.parse()` to automatically reconstruct Money instances:
779
- *
780
- * @param key - The JSON key (unused).
781
- * @param value - The parsed JSON value.
782
- * @returns A Money instance if value is a serialized Money object, otherwise the original value.
783
- * @example
784
- * const json = '{"amount": "1050", "currency": "USD", "precision": 2}';
785
- * const money = JSON.parse(json, Money.reviver);
786
- * // money is Money instance: $10.50
787
- */
788
- static reviver(key, value) {
789
- if (value !== null && typeof value === "object" && "amount" in value && "currency" in value && "precision" in value && typeof value.amount === "string" && typeof value.currency === "string" && typeof value.precision === "number") {
790
- const obj = value;
791
- return _Money.fromMinor(BigInt(obj.amount), obj.currency);
792
- }
793
- return value;
794
- }
795
- /**
796
- * Returns a string representation of the Money object (formatted).
797
- */
798
- toString() {
799
- return this.format();
800
- }
801
- };
802
-
803
- // src/financial/loan.ts
804
- function loan(options) {
805
- const {
806
- principal,
807
- annualRate,
808
- periods,
809
- periodsPerYear = 12,
810
- rounding = "HALF_EVEN" /* HALF_EVEN */
811
- } = options;
812
- const periodicRate = annualRate / periodsPerYear;
813
- let payment;
814
- if (annualRate === 0) {
815
- payment = principal.divide(periods, { rounding });
816
- } else {
817
- const r = periodicRate;
818
- const n = periods;
819
- const numerator = r * Math.pow(1 + r, n);
820
- const denominator = Math.pow(1 + r, n) - 1;
821
- const pmtMultiplier = numerator / denominator;
822
- payment = principal.multiply(pmtMultiplier, { rounding });
823
- }
824
- const schedule = [];
825
- let balance = principal;
826
- for (let period = 1; period <= periods; period++) {
827
- const interest = annualRate === 0 ? Money.zero(principal.currency) : balance.multiply(periodicRate, { rounding });
828
- let principalPayment = payment.subtract(interest);
829
- balance = balance.subtract(principalPayment);
830
- if (period === periods && !balance.isZero()) {
831
- const adjustedPrincipal = principalPayment.add(balance);
832
- schedule.push({
833
- period,
834
- payment: interest.add(adjustedPrincipal),
835
- principal: adjustedPrincipal,
836
- interest,
837
- balance: Money.zero(principal.currency)
838
- });
839
- } else {
840
- schedule.push({
841
- period,
842
- payment,
843
- principal: principalPayment,
844
- interest,
845
- balance: balance.isNegative() ? Money.zero(principal.currency) : balance
846
- });
847
- }
848
- }
849
- return schedule;
850
- }
851
- function pmt(options) {
852
- const schedule = loan(options);
853
- return schedule[0].payment;
854
- }
855
- function totalInterest(options) {
856
- const {
857
- principal,
858
- annualRate,
859
- periods,
860
- rounding = "HALF_EVEN" /* HALF_EVEN */
861
- } = options;
862
- if (annualRate === 0) {
863
- return Money.zero(principal.currency);
864
- }
865
- const payment = pmt(options);
866
- const totalPaid = payment.multiply(periods, { rounding });
867
- return totalPaid.subtract(principal);
868
- }
869
- function totalInterestFromSchedule(schedule) {
870
- if (schedule.length === 0) {
871
- throw new Error("Schedule must have at least one entry");
872
- }
873
- const currency = schedule[0].interest.currency;
874
- return schedule.reduce(
875
- (sum, entry) => sum.add(entry.interest),
876
- Money.zero(currency)
877
- );
878
- }
879
- function interestOnlyPayment(options) {
880
- const {
881
- principal,
882
- annualRate,
883
- periodsPerYear = 12,
884
- rounding = "HALF_EVEN" /* HALF_EVEN */
885
- } = options;
886
- if (annualRate === 0) {
887
- return Money.zero(principal.currency);
888
- }
889
- const periodicRate = annualRate / periodsPerYear;
890
- return principal.multiply(periodicRate, { rounding });
891
- }
892
-
893
- // src/financial/compound.ts
894
- function futureValue(presentValue2, options) {
895
- const {
896
- rate,
897
- years,
898
- compoundingPerYear = 12,
899
- rounding = "HALF_EVEN" /* HALF_EVEN */
900
- } = options;
901
- const n = compoundingPerYear;
902
- const t = years;
903
- const multiplier = Math.pow(1 + rate / n, n * t);
904
- return presentValue2.multiply(multiplier, { rounding });
905
- }
906
- function presentValue(futureVal, options) {
907
- const {
908
- rate,
909
- years,
910
- compoundingPerYear = 12,
911
- rounding = "HALF_EVEN" /* HALF_EVEN */
912
- } = options;
913
- const n = compoundingPerYear;
914
- const t = years;
915
- const divisor = Math.pow(1 + rate / n, n * t);
916
- return futureVal.divide(divisor, { rounding });
917
- }
918
- var compound = futureValue;
919
- var discount = presentValue;
920
-
921
- // src/financial/investment.ts
922
- function roi(initialValue, finalValue) {
923
- if (initialValue.currency.code !== finalValue.currency.code) {
924
- throw new CurrencyMismatchError(
925
- initialValue.currency.code,
926
- finalValue.currency.code
927
- );
928
- }
929
- const gain = finalValue.subtract(initialValue);
930
- return Number(gain.minor) / Number(initialValue.minor);
931
- }
932
- function npv(discountRate, cashFlows) {
933
- if (cashFlows.length === 0) {
934
- throw new Error("At least one cash flow required");
935
- }
936
- const currency = cashFlows[0].currency;
937
- let total = Money.zero(currency);
938
- for (let i = 0; i < cashFlows.length; i++) {
939
- const discountFactor = Math.pow(1 + discountRate, i);
940
- const discounted = cashFlows[i].divide(discountFactor, {
941
- rounding: "HALF_EVEN" /* HALF_EVEN */
942
- });
943
- total = total.add(discounted);
944
- }
945
- return total;
946
- }
947
- function irr(cashFlows, guess = 0.1) {
948
- const values = cashFlows.map((cf) => Number(cf.minor));
949
- const maxIterations = 100;
950
- const tolerance = 1e-7;
951
- let rate = guess;
952
- for (let i = 0; i < maxIterations; i++) {
953
- let npvValue = 0;
954
- let derivative = 0;
955
- for (let j = 0; j < values.length; j++) {
956
- const denominator = Math.pow(1 + rate, j);
957
- npvValue += values[j] / denominator;
958
- if (j > 0) {
959
- derivative -= j * values[j] / Math.pow(1 + rate, j + 1);
960
- }
961
- }
962
- const newRate = rate - npvValue / derivative;
963
- if (Math.abs(newRate - rate) < tolerance) {
964
- return newRate;
965
- }
966
- rate = newRate;
967
- }
968
- throw new Error("IRR calculation did not converge");
969
- }
970
- function currentYield(annualCoupon, currentPrice) {
971
- if (annualCoupon.currency.code !== currentPrice.currency.code) {
972
- throw new CurrencyMismatchError(
973
- annualCoupon.currency.code,
974
- currentPrice.currency.code
975
- );
976
- }
977
- if (currentPrice.minor <= 0n) {
978
- throw new InvalidArgumentError("Current price must be positive");
979
- }
980
- return Number(annualCoupon.minor) / Number(currentPrice.minor);
981
- }
982
-
983
- // src/financial/rate.ts
984
- var _Rate = class _Rate {
985
- constructor(decimalValue) {
986
- this.decimalValue = decimalValue;
987
- }
988
- /**
989
- * Creates a Rate from a percentage value (e.g., 5 for 5%).
990
- *
991
- * @param percent - The percentage value.
992
- * @returns A new Rate instance.
993
- * @example
994
- * const rate = Rate.percent(5.5); // 5.5%
995
- */
996
- static percent(percent) {
997
- const decimal = typeof percent === "string" ? parseFloat(percent) : percent;
998
- const scaled = BigInt(Math.round(decimal / 100 * Number(_Rate.SCALE)));
999
- return new _Rate(scaled);
1000
- }
1001
- /**
1002
- * Creates a Rate from a decimal value (e.g., 0.05 for 5%).
1003
- *
1004
- * @param decimal - The decimal value.
1005
- * @returns A new Rate instance.
1006
- * @example
1007
- * const rate = Rate.decimal(0.055); // 5.5%
1008
- */
1009
- static decimal(decimal) {
1010
- const value = typeof decimal === "string" ? parseFloat(decimal) : decimal;
1011
- const scaled = BigInt(Math.round(value * Number(_Rate.SCALE)));
1012
- return new _Rate(scaled);
1013
- }
1014
- /**
1015
- * Creates a Rate representing zero.
1016
- *
1017
- * @returns A Rate of 0%.
1018
- */
1019
- static zero() {
1020
- return new _Rate(0n);
1021
- }
1022
- /**
1023
- * Returns the rate as a percentage (e.g., 5 for 5%).
1024
- *
1025
- * @returns The percentage value as a number.
1026
- */
1027
- toPercent() {
1028
- return Number(this.decimalValue) / Number(_Rate.SCALE) * 100;
1029
- }
1030
- /**
1031
- * Returns the rate as a decimal (e.g., 0.05 for 5%).
1032
- *
1033
- * @returns The decimal value as a number.
1034
- */
1035
- toDecimal() {
1036
- return Number(this.decimalValue) / Number(_Rate.SCALE);
1037
- }
1038
- /**
1039
- * Adds another rate to this one.
1040
- *
1041
- * @param other - The rate to add.
1042
- * @returns A new Rate representing the sum.
1043
- */
1044
- add(other) {
1045
- return new _Rate(this.decimalValue + other.decimalValue);
1046
- }
1047
- /**
1048
- * Subtracts another rate from this one.
1049
- *
1050
- * @param other - The rate to subtract.
1051
- * @returns A new Rate representing the difference.
1052
- */
1053
- subtract(other) {
1054
- return new _Rate(this.decimalValue - other.decimalValue);
1055
- }
1056
- /**
1057
- * Multiplies this rate by a scalar.
1058
- *
1059
- * @param multiplier - The number to multiply by.
1060
- * @returns A new Rate representing the product.
1061
- */
1062
- multiply(multiplier) {
1063
- const scaled = BigInt(Math.round(multiplier * Number(_Rate.SCALE)));
1064
- return new _Rate(this.decimalValue * scaled / _Rate.SCALE);
1065
- }
1066
- /**
1067
- * Divides this rate by a divisor.
1068
- *
1069
- * @param divisor - The number to divide by.
1070
- * @returns A new Rate representing the quotient.
1071
- */
1072
- divide(divisor) {
1073
- const scaled = BigInt(Math.round(divisor * Number(_Rate.SCALE)));
1074
- return new _Rate(this.decimalValue * _Rate.SCALE / scaled);
1075
- }
1076
- /**
1077
- * Checks if this rate equals another.
1078
- *
1079
- * @param other - The rate to compare.
1080
- * @returns True if the rates are equal.
1081
- */
1082
- equals(other) {
1083
- return this.decimalValue === other.decimalValue;
1084
- }
1085
- /**
1086
- * Checks if this rate is greater than another.
1087
- *
1088
- * @param other - The rate to compare.
1089
- * @returns True if this rate is greater.
1090
- */
1091
- greaterThan(other) {
1092
- return this.decimalValue > other.decimalValue;
1093
- }
1094
- /**
1095
- * Checks if this rate is less than another.
1096
- *
1097
- * @param other - The rate to compare.
1098
- * @returns True if this rate is less.
1099
- */
1100
- lessThan(other) {
1101
- return this.decimalValue < other.decimalValue;
1102
- }
1103
- /**
1104
- * Checks if this rate is zero.
1105
- *
1106
- * @returns True if the rate is 0%.
1107
- */
1108
- isZero() {
1109
- return this.decimalValue === 0n;
1110
- }
1111
- /**
1112
- * Checks if this rate is negative.
1113
- *
1114
- * @returns True if the rate is negative.
1115
- */
1116
- isNegative() {
1117
- return this.decimalValue < 0n;
1118
- }
1119
- /**
1120
- * Returns a string representation of the rate as a percentage.
1121
- *
1122
- * @returns The rate as a percentage string (e.g., "5.5%").
1123
- */
1124
- toString() {
1125
- return `${this.toPercent()}%`;
1126
- }
1127
- /**
1128
- * Computes compound factor: (1 + rate)^periods.
1129
- *
1130
- * @param periods - The number of compounding periods.
1131
- * @returns The compound factor as a number.
1132
- * @example
1133
- * Rate.percent(10).compoundFactor(3); // (1.10)^3 = 1.331
1134
- */
1135
- compoundFactor(periods) {
1136
- return Math.pow(1 + this.toDecimal(), periods);
1137
- }
1138
- /**
1139
- * Returns the periodic rate for a given frequency.
1140
- *
1141
- * @param periodsPerYear - Number of periods per year (e.g., 12 for monthly).
1142
- * @returns A new Rate representing the periodic rate.
1143
- * @example
1144
- * Rate.percent(12).periodic(12); // 1% monthly
1145
- */
1146
- periodic(periodsPerYear) {
1147
- return this.divide(periodsPerYear);
1148
- }
1149
- /**
1150
- * Converts an effective rate to a nominal rate.
1151
- *
1152
- * @param periodsPerYear - Number of compounding periods per year.
1153
- * @returns The nominal annual rate.
1154
- */
1155
- toNominal(periodsPerYear) {
1156
- const effectiveDecimal = this.toDecimal();
1157
- const nominalDecimal = periodsPerYear * (Math.pow(1 + effectiveDecimal, 1 / periodsPerYear) - 1);
1158
- return _Rate.decimal(nominalDecimal);
1159
- }
1160
- /**
1161
- * Converts a nominal rate to an effective annual rate.
1162
- *
1163
- * @param periodsPerYear - Number of compounding periods per year.
1164
- * @returns The effective annual rate.
1165
- */
1166
- toEffective(periodsPerYear) {
1167
- const nominalDecimal = this.toDecimal();
1168
- const effectiveDecimal = Math.pow(1 + nominalDecimal / periodsPerYear, periodsPerYear) - 1;
1169
- return _Rate.decimal(effectiveDecimal);
1170
- }
1171
- /**
1172
- * Returns a JSON representation.
1173
- */
1174
- toJSON() {
1175
- return {
1176
- percent: this.toPercent(),
1177
- decimal: this.toDecimal()
1178
- };
1179
- }
1180
- };
1181
- /**
1182
- * Scale factor for internal precision (1e18 for 18 decimal places).
1183
- */
1184
- _Rate.SCALE = 10n ** 18n;
1185
- var Rate = _Rate;
1186
-
1187
- // src/financial/simple.ts
1188
- function simpleInterest(principal, options) {
1189
- const { rate, years, rounding = "HALF_EVEN" /* HALF_EVEN */ } = options;
1190
- if (rate.isZero() || years === 0) {
1191
- return Money.zero(principal.currency);
1192
- }
1193
- const interestMultiplier = rate.toDecimal() * years;
1194
- return principal.multiply(interestMultiplier, { rounding });
1195
- }
1196
- function simpleInterestTotal(principal, options) {
1197
- const { rate, years, rounding = "HALF_EVEN" /* HALF_EVEN */ } = options;
1198
- if (rate.isZero() || years === 0) {
1199
- return principal;
1200
- }
1201
- const totalMultiplier = 1 + rate.toDecimal() * years;
1202
- return principal.multiply(totalMultiplier, { rounding });
1203
- }
1204
-
1205
- // src/financial/leverage.ts
1206
- function debtToEquity(totalDebt, totalEquity) {
1207
- assertSameCurrency(totalDebt, totalEquity);
1208
- if (totalEquity.isZero()) {
1209
- throw new Error("Total equity cannot be zero");
1210
- }
1211
- return Number(totalDebt.minor) / Number(totalEquity.minor);
1212
- }
1213
- function debtToAssets(totalDebt, totalAssets) {
1214
- assertSameCurrency(totalDebt, totalAssets);
1215
- if (totalAssets.isZero()) {
1216
- throw new Error("Total assets cannot be zero");
1217
- }
1218
- return Number(totalDebt.minor) / Number(totalAssets.minor);
1219
- }
1220
- function interestCoverage(ebit, interestExpense) {
1221
- assertSameCurrency(ebit, interestExpense);
1222
- if (interestExpense.isZero()) {
1223
- return Infinity;
1224
- }
1225
- return Number(ebit.minor) / Number(interestExpense.minor);
1226
- }
1227
- function equityMultiplier(totalAssets, totalEquity) {
1228
- assertSameCurrency(totalAssets, totalEquity);
1229
- if (totalEquity.isZero()) {
1230
- throw new Error("Total equity cannot be zero");
1231
- }
1232
- return Number(totalAssets.minor) / Number(totalEquity.minor);
1233
- }
1234
- function leverageRatios(inputs) {
1235
- return {
1236
- debtToEquity: debtToEquity(inputs.totalDebt, inputs.totalEquity),
1237
- debtToAssets: debtToAssets(inputs.totalDebt, inputs.totalAssets),
1238
- interestCoverage: interestCoverage(inputs.ebit, inputs.interestExpense),
1239
- equityMultiplier: equityMultiplier(inputs.totalAssets, inputs.totalEquity)
1240
- };
1241
- }
1242
-
1243
- // src/financial/depreciation.ts
1244
- function straightLineDepreciation(options) {
1245
- const {
1246
- cost,
1247
- salvageValue,
1248
- usefulLife,
1249
- rounding = "HALF_EVEN" /* HALF_EVEN */
1250
- } = options;
1251
- if (cost.currency.code !== salvageValue.currency.code) {
1252
- throw new CurrencyMismatchError(
1253
- cost.currency.code,
1254
- salvageValue.currency.code
1255
- );
1256
- }
1257
- if (usefulLife <= 0) {
1258
- throw new InvalidArgumentError("Useful life must be positive");
1259
- }
1260
- if (salvageValue.greaterThan(cost)) {
1261
- throw new InvalidArgumentError("Salvage value cannot be greater than cost");
1262
- }
1263
- const depreciableAmount = cost.subtract(salvageValue);
1264
- const annualDepreciation = depreciableAmount.divide(usefulLife, { rounding });
1265
- const result = {
1266
- annualDepreciation,
1267
- bookValueAtYear(year) {
1268
- if (year < 0) {
1269
- throw new InvalidArgumentError("Year must be non-negative");
1270
- }
1271
- if (year === 0) return cost;
1272
- const totalDepreciation = annualDepreciation.multiply(year);
1273
- const bookValue = cost.subtract(totalDepreciation);
1274
- if (bookValue.lessThan(salvageValue)) {
1275
- return salvageValue;
1276
- }
1277
- return bookValue;
1278
- },
1279
- schedule() {
1280
- const schedule = [];
1281
- let previousBookValue = cost;
1282
- const maxYears = Math.ceil(usefulLife);
1283
- for (let year = 1; year <= maxYears; year++) {
1284
- const currentBookValue = result.bookValueAtYear(year);
1285
- const depreciation = previousBookValue.subtract(currentBookValue);
1286
- schedule.push({
1287
- year,
1288
- depreciation,
1289
- bookValue: currentBookValue
1290
- });
1291
- previousBookValue = currentBookValue;
1292
- }
1293
- return schedule;
1294
- }
1295
- };
1296
- return result;
1297
- }
1298
- // Annotate the CommonJS export names for ESM import in node:
1299
- 0 && (module.exports = {
1300
- Rate,
1301
- compound,
1302
- currentYield,
1303
- debtToAssets,
1304
- debtToEquity,
1305
- discount,
1306
- equityMultiplier,
1307
- futureValue,
1308
- interestCoverage,
1309
- interestOnlyPayment,
1310
- irr,
1311
- leverageRatios,
1312
- loan,
1313
- npv,
1314
- pmt,
1315
- presentValue,
1316
- roi,
1317
- simpleInterest,
1318
- simpleInterestTotal,
1319
- straightLineDepreciation,
1320
- totalInterest,
1321
- totalInterestFromSchedule
1322
- });
1323
- //# sourceMappingURL=index.js.map
6
+ money.${r}(value, { rounding: RoundingMode.HALF_UP })
7
+ Available modes: HALF_UP, HALF_DOWN, HALF_EVEN, FLOOR, CEIL, TRUNCATE`),super(t,"MONETRA_ROUNDING_REQUIRED");}};var M=class extends g{constructor(r){super(r,"MONETRA_INVALID_ARGUMENT");}};function d(n,r){if(n.currency.code!==r.currency.code)throw new h(n.currency.code,r.currency.code)}function A(n,r,e){if(r===0n)throw new Error("Division by zero");let t=n/r,o=n%r;if(o===0n)return t;let a=!(n<0n!=r<0n),m=o<0n?-o:o,u=r<0n?-r:r,s=m*2n===u,c=m*2n>u;switch(e){case "FLOOR":return a?t:t-1n;case "CEIL":return a?t+1n:t;case "HALF_UP":return c||s?a?t+1n:t-1n:t;case "HALF_DOWN":return c?a?t+1n:t-1n:t;case "HALF_EVEN":return c||s&&t%2n!==0n?a?t+1n:t-1n:t;case "TRUNCATE":return t;default:throw new Error(`Unsupported rounding mode: ${e}`)}}function V(n,r){return n+r}function D(n,r){return n-r}function P(n,r,e){let{numerator:t,denominator:o}=H(r),i=n*t;if(i%o===0n)return i/o;if(!e)throw new x("multiply",Number(i)/Number(o));return A(i,o,e)}function U(n,r,e){let{numerator:t,denominator:o}=H(r),i=n*o;if(i%t===0n)return i/t;if(!e)throw new x("divide",Number(i)/Number(t));return A(i,t,e)}function H(n){let r=n.toString();if(/[eE]/.test(r))throw new Error("Scientific notation not supported");let e=r.split(".");if(e.length>2)throw new Error("Invalid number format");let t=e[0],o=e[1]||"",i=10n**BigInt(o.length);return {numerator:BigInt(t+o),denominator:i}}function $(n,r){if(r.length===0)throw new Error("Cannot allocate to empty ratios");let e=r.map(s=>{let c=s.toString();if(/[eE]/.test(c))throw new Error("Scientific notation not supported");let l=c.split("."),y=l[1]?l[1].length:0;return {value:BigInt(l[0]+(l[1]||"")),decimals:y}}),t=Math.max(...e.map(s=>s.decimals)),o=e.map(s=>{let c=10n**BigInt(t-s.decimals);return s.value*c}),i=o.reduce((s,c)=>s+c,0n);if(i===0n)throw new Error("Total ratio must be greater than zero");let a=[],m=0n;for(let s=0;s<o.length;s++){let c=o[s],l=n*c/i,y=n*c%i;a.push({share:l,remainder:y,index:s}),m+=l;}let u=n-m;a.sort((s,c)=>c.remainder>s.remainder?1:c.remainder<s.remainder?-1:0);for(let s=0;s<Number(u);s++)a[s].share+=1n;return a.sort((s,c)=>s.index-c.index),a.map(s=>s.share)}function k(n,r){if(/[eE]/.test(n))throw new Error("Scientific notation not supported");if(/[^0-9.-]/.test(n))throw new Error("Invalid characters in amount");if(!/[0-9]/.test(n))throw new Error("Invalid format");let e=n.split(".");if(e.length>2)throw new Error("Invalid format: multiple decimal points");let t=e[0],o=e[1]||"";if(o.length>r.decimals)throw new C(`Precision ${o.length} exceeds currency decimals ${r.decimals}`);let i=o.padEnd(r.decimals,"0"),a=t+i;return BigInt(a)}function z(n,r){let e=r?.locale||n.currency.locale||"en-US",t=r?.symbol??true,o=r?.display||"symbol",i=r?.accounting??false,a=n.currency.decimals,m=n.minor,u=m<0n,s=u?-m:m,c=10n**BigInt(a),l=s/c,b=(s%c).toString().padStart(a,"0"),B=new Intl.NumberFormat(e,{style:"decimal",minimumFractionDigits:1}).formatToParts(1.1).find(N=>N.type==="decimal")?.value||".",O=new Intl.NumberFormat(e,{style:"decimal",useGrouping:true}).format(l),E=a>0?`${O}${B}${b}`:O;if(!t)return u?i?`(${E})`:`-${E}`:E;let S;try{S=new Intl.NumberFormat(e,{style:"currency",currency:n.currency.code,currencyDisplay:o}).formatToParts(u?-1234.5:1234.5);}catch{let I=o==="symbol"?n.currency.symbol:n.currency.code;return u?i?`(${I}${E})`:`-${I}${E}`:`${I}${E}`}let w="",T=false;for(let N of S)switch(N.type){case "minusSign":{i||(w+=N.value);break}case "integer":{T||(w+=E,T=true);break}case "group":case "decimal":case "fraction":break;case "currency":{o==="symbol"&&n.currency.symbol?w+=n.currency.symbol:w+=N.value;break}default:{w+=N.value;break}}return i&&u?`(${w.trim()})`:w}var f=class n{constructor(r,e){this.minor=r,this.currency=e;}static resolveCurrency(r){return typeof r=="string"?F(r):r}static fromMinor(r,e){return new n(BigInt(r),n.resolveCurrency(e))}static fromCents(r,e){return n.fromMinor(r,e)}static fromMajor(r,e){let t=n.resolveCurrency(e),o=k(r,t);return new n(o,t)}static fromDecimal(r,e){return n.fromMajor(r,e)}static fromFloat(r,e,t){!t?.suppressWarning&&process.env.NODE_ENV!=="production"&&console.warn('[monetra] Money.fromFloat() may lose precision. Consider using Money.fromMajor("'+r+'", currency) instead.');let o=n.resolveCurrency(e),i=10**o.decimals,a=Math.round(r*i);return new n(BigInt(a),o)}static min(...r){if(r.length===0)throw new Error("At least one Money value required");return r.reduce((e,t)=>(d(e,t),t.lessThan(e)?t:e))}static max(...r){if(r.length===0)throw new Error("At least one Money value required");return r.reduce((e,t)=>(d(e,t),t.greaterThan(e)?t:e))}static zero(r){return new n(0n,n.resolveCurrency(r))}resolveOther(r){return r instanceof n?r:typeof r=="string"?n.fromMajor(r,this.currency):n.fromMinor(r,this.currency)}add(r){let e=this.resolveOther(r);return d(this,e),new n(V(this.minor,e.minor),this.currency)}subtract(r){let e=this.resolveOther(r);return d(this,e),new n(D(this.minor,e.minor),this.currency)}multiply(r,e){let t=P(this.minor,r,e?.rounding);return new n(t,this.currency)}divide(r,e){if(r===0||r==="0")throw new Error("Division by zero");let t=U(this.minor,r,e?.rounding);return new n(t,this.currency)}abs(){return new n(this.minor<0n?-this.minor:this.minor,this.currency)}negate(){return new n(-this.minor,this.currency)}allocate(r){return $(this.minor,r).map(t=>new n(t,this.currency))}format(r){return z(this,r)}equals(r){let e=this.resolveOther(r);return this.currency.code===e.currency.code&&this.minor===e.minor}greaterThan(r){let e=this.resolveOther(r);return d(this,e),this.minor>e.minor}lessThan(r){let e=this.resolveOther(r);return d(this,e),this.minor<e.minor}greaterThanOrEqual(r){let e=this.resolveOther(r);return d(this,e),this.minor>=e.minor}lessThanOrEqual(r){let e=this.resolveOther(r);return d(this,e),this.minor<=e.minor}isPositive(){return this.minor>0n}compare(r){let e=this.resolveOther(r);return d(this,e),this.minor<e.minor?-1:this.minor>e.minor?1:0}percentage(r,e="HALF_EVEN"){return this.multiply(r/100,{rounding:e})}addPercent(r,e="HALF_EVEN"){return this.add(this.percentage(r,e))}subtractPercent(r,e="HALF_EVEN"){return this.subtract(this.percentage(r,e))}split(r){let e=Array(r).fill(1);return this.allocate(e)}isZero(){return this.minor===0n}isNegative(){return this.minor<0n}clamp(r,e){if(d(this,r),d(this,e),r.greaterThan(e))throw new Error("Clamp min cannot be greater than max");return this.lessThan(r)?new n(r.minor,this.currency):this.greaterThan(e)?new n(e.minor,this.currency):this}toDecimalString(){let r=this.currency.decimals,e=this.minor<0n,t=e?-this.minor:this.minor;if(r===0)return e?`-${t.toString()}`:t.toString();let o=10n**BigInt(r),i=t/o,m=(t%o).toString().padStart(r,"0"),u=`${i}.${m}`;return e?`-${u}`:u}toJSON(){return {amount:this.minor.toString(),currency:this.currency.code,precision:this.currency.decimals}}static reviver(r,e){if(e!==null&&typeof e=="object"&&"amount"in e&&"currency"in e&&"precision"in e&&typeof e.amount=="string"&&typeof e.currency=="string"&&typeof e.precision=="number"){let t=e;return n.fromMinor(BigInt(t.amount),t.currency)}return e}toString(){return this.format()}};function j(n){let{principal:r,annualRate:e,periods:t,periodsPerYear:o=12,rounding:i="HALF_EVEN"}=n,a=e/o,m;if(e===0)m=r.divide(t,{rounding:i});else {let c=a,l=t,y=c*Math.pow(1+c,l),b=Math.pow(1+c,l)-1,L=y/b;m=r.multiply(L,{rounding:i});}let u=[],s=r;for(let c=1;c<=t;c++){let l=e===0?f.zero(r.currency):s.multiply(a,{rounding:i}),y=m.subtract(l);if(s=s.subtract(y),c===t&&!s.isZero()){let b=y.add(s);u.push({period:c,payment:l.add(b),principal:b,interest:l,balance:f.zero(r.currency)});}else u.push({period:c,payment:m,principal:y,interest:l,balance:s.isNegative()?f.zero(r.currency):s});}return u}function W(n){return j(n)[0].payment}function Wr(n){let{principal:r,annualRate:e,periods:t,rounding:o="HALF_EVEN"}=n;return e===0?f.zero(r.currency):W(n).multiply(t,{rounding:o}).subtract(r)}function Zr(n){if(n.length===0)throw new Error("Schedule must have at least one entry");let r=n[0].interest.currency;return n.reduce((e,t)=>e.add(t.interest),f.zero(r))}function Gr(n){let{principal:r,annualRate:e,periodsPerYear:t=12,rounding:o="HALF_EVEN"}=n;if(e===0)return f.zero(r.currency);let i=e/t;return r.multiply(i,{rounding:o})}function Z(n,r){let{rate:e,years:t,compoundingPerYear:o=12,rounding:i="HALF_EVEN"}=r,a=o,m=t,u=Math.pow(1+e/a,a*m);return n.multiply(u,{rounding:i})}function G(n,r){let{rate:e,years:t,compoundingPerYear:o=12,rounding:i="HALF_EVEN"}=r,a=o,m=t,u=Math.pow(1+e/a,a*m);return n.divide(u,{rounding:i})}var Jr=Z,Kr=G;function oe(n,r){if(n.currency.code!==r.currency.code)throw new h(n.currency.code,r.currency.code);let e=r.subtract(n);return Number(e.minor)/Number(n.minor)}function ie(n,r){if(r.length===0)throw new Error("At least one cash flow required");let e=r[0].currency,t=f.zero(e);for(let o=0;o<r.length;o++){let i=Math.pow(1+n,o),a=r[o].divide(i,{rounding:"HALF_EVEN"});t=t.add(a);}return t}function se(n,r=.1){let e=n.map(a=>Number(a.minor)),t=100,o=1e-7,i=r;for(let a=0;a<t;a++){let m=0,u=0;for(let c=0;c<e.length;c++){let l=Math.pow(1+i,c);m+=e[c]/l,c>0&&(u-=c*e[c]/Math.pow(1+i,c+1));}let s=i-m/u;if(Math.abs(s-i)<o)return s;i=s;}throw new Error("IRR calculation did not converge")}function ce(n,r){if(n.currency.code!==r.currency.code)throw new h(n.currency.code,r.currency.code);if(r.minor<=0n)throw new M("Current price must be positive");return Number(n.minor)/Number(r.minor)}var p=class p{constructor(r){this.decimalValue=r;}static percent(r){let e=typeof r=="string"?parseFloat(r):r,t=BigInt(Math.round(e/100*Number(p.SCALE)));return new p(t)}static decimal(r){let e=typeof r=="string"?parseFloat(r):r,t=BigInt(Math.round(e*Number(p.SCALE)));return new p(t)}static zero(){return new p(0n)}toPercent(){return Number(this.decimalValue)/Number(p.SCALE)*100}toDecimal(){return Number(this.decimalValue)/Number(p.SCALE)}add(r){return new p(this.decimalValue+r.decimalValue)}subtract(r){return new p(this.decimalValue-r.decimalValue)}multiply(r){let e=BigInt(Math.round(r*Number(p.SCALE)));return new p(this.decimalValue*e/p.SCALE)}divide(r){let e=BigInt(Math.round(r*Number(p.SCALE)));return new p(this.decimalValue*p.SCALE/e)}equals(r){return this.decimalValue===r.decimalValue}greaterThan(r){return this.decimalValue>r.decimalValue}lessThan(r){return this.decimalValue<r.decimalValue}isZero(){return this.decimalValue===0n}isNegative(){return this.decimalValue<0n}toString(){return `${this.toPercent()}%`}compoundFactor(r){return Math.pow(1+this.toDecimal(),r)}periodic(r){return this.divide(r)}toNominal(r){let e=this.toDecimal(),t=r*(Math.pow(1+e,1/r)-1);return p.decimal(t)}toEffective(r){let e=this.toDecimal(),t=Math.pow(1+e/r,r)-1;return p.decimal(t)}toJSON(){return {percent:this.toPercent(),decimal:this.toDecimal()}}};p.SCALE=10n**18n;var q=p;function pe(n,r){let{rate:e,years:t,rounding:o="HALF_EVEN"}=r;if(e.isZero()||t===0)return f.zero(n.currency);let i=e.toDecimal()*t;return n.multiply(i,{rounding:o})}function de(n,r){let{rate:e,years:t,rounding:o="HALF_EVEN"}=r;if(e.isZero()||t===0)return n;let i=1+e.toDecimal()*t;return n.multiply(i,{rounding:o})}function _(n,r){if(d(n,r),r.isZero())throw new Error("Total equity cannot be zero");return Number(n.minor)/Number(r.minor)}function Q(n,r){if(d(n,r),r.isZero())throw new Error("Total assets cannot be zero");return Number(n.minor)/Number(r.minor)}function J(n,r){return d(n,r),r.isZero()?1/0:Number(n.minor)/Number(r.minor)}function K(n,r){if(d(n,r),r.isZero())throw new Error("Total equity cannot be zero");return Number(n.minor)/Number(r.minor)}function ge(n){return {debtToEquity:_(n.totalDebt,n.totalEquity),debtToAssets:Q(n.totalDebt,n.totalAssets),interestCoverage:J(n.ebit,n.interestExpense),equityMultiplier:K(n.totalAssets,n.totalEquity)}}function we(n){let{cost:r,salvageValue:e,usefulLife:t,rounding:o="HALF_EVEN"}=n;if(r.currency.code!==e.currency.code)throw new h(r.currency.code,e.currency.code);if(t<=0)throw new M("Useful life must be positive");if(e.greaterThan(r))throw new M("Salvage value cannot be greater than cost");let a=r.subtract(e).divide(t,{rounding:o}),m={annualDepreciation:a,bookValueAtYear(u){if(u<0)throw new M("Year must be non-negative");if(u===0)return r;let s=a.multiply(u),c=r.subtract(s);return c.lessThan(e)?e:c},schedule(){let u=[],s=r,c=Math.ceil(t);for(let l=1;l<=c;l++){let y=m.bookValueAtYear(l),b=s.subtract(y);u.push({year:l,depreciation:b,bookValue:y}),s=y;}return u}};return m}exports.Rate=q;exports.compound=Jr;exports.currentYield=ce;exports.debtToAssets=Q;exports.debtToEquity=_;exports.discount=Kr;exports.equityMultiplier=K;exports.futureValue=Z;exports.interestCoverage=J;exports.interestOnlyPayment=Gr;exports.irr=se;exports.leverageRatios=ge;exports.loan=j;exports.npv=ie;exports.pmt=W;exports.presentValue=G;exports.roi=oe;exports.simpleInterest=pe;exports.simpleInterestTotal=de;exports.straightLineDepreciation=we;exports.totalInterest=Wr;exports.totalInterestFromSchedule=Zr;