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.
package/dist/index.mjs CHANGED
@@ -1,2327 +1,7 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
- // src/currency/registry.ts
9
- var registry = /* @__PURE__ */ new Map();
10
- function registerCurrency(currency) {
11
- registry.set(currency.code, currency);
12
- }
13
- function getCurrency(code) {
14
- const currency = registry.get(code);
15
- if (!currency) {
16
- throw new Error(`Currency '${code}' not found in registry.`);
17
- }
18
- return currency;
19
- }
20
- function isCurrencyRegistered(code) {
21
- return registry.has(code);
22
- }
23
- function getAllCurrencies() {
24
- return Object.fromEntries(registry);
25
- }
26
-
27
- // src/rounding/strategies.ts
28
- var RoundingMode = /* @__PURE__ */ ((RoundingMode3) => {
29
- RoundingMode3["HALF_UP"] = "HALF_UP";
30
- RoundingMode3["HALF_DOWN"] = "HALF_DOWN";
31
- RoundingMode3["HALF_EVEN"] = "HALF_EVEN";
32
- RoundingMode3["FLOOR"] = "FLOOR";
33
- RoundingMode3["CEIL"] = "CEIL";
34
- RoundingMode3["TRUNCATE"] = "TRUNCATE";
35
- return RoundingMode3;
36
- })(RoundingMode || {});
37
-
38
- // src/errors/BaseError.ts
39
- var MonetraErrorCode = /* @__PURE__ */ ((MonetraErrorCode2) => {
40
- MonetraErrorCode2["CURRENCY_MISMATCH"] = "MONETRA_CURRENCY_MISMATCH";
41
- MonetraErrorCode2["INSUFFICIENT_FUNDS"] = "MONETRA_INSUFFICIENT_FUNDS";
42
- MonetraErrorCode2["INVALID_ARGUMENT"] = "MONETRA_INVALID_ARGUMENT";
43
- MonetraErrorCode2["INVALID_PRECISION"] = "MONETRA_INVALID_PRECISION";
44
- MonetraErrorCode2["OVERFLOW"] = "MONETRA_OVERFLOW";
45
- MonetraErrorCode2["ROUNDING_REQUIRED"] = "MONETRA_ROUNDING_REQUIRED";
46
- return MonetraErrorCode2;
47
- })(MonetraErrorCode || {});
48
- var MonetraError = class extends Error {
49
- constructor(message, code) {
50
- super(message);
51
- this.name = this.constructor.name;
52
- this.code = code;
53
- Object.setPrototypeOf(this, new.target.prototype);
54
- }
55
- };
56
-
57
- // src/errors/CurrencyMismatchError.ts
58
- var CurrencyMismatchError = class extends MonetraError {
59
- constructor(expected, received) {
60
- super(
61
- `Currency mismatch: expected ${expected}, received ${received}.
1
+ import {randomUUID}from'crypto';var ar=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,r)=>(typeof require<"u"?require:e)[r]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var D=new Map;function c(t){D.set(t.code,t);}function N(t){let e=D.get(t);if(!e)throw new Error(`Currency '${t}' not found in registry.`);return e}function xr(t){return D.has(t)}function Er(){return Object.fromEntries(D)}var C=(s=>(s.HALF_UP="HALF_UP",s.HALF_DOWN="HALF_DOWN",s.HALF_EVEN="HALF_EVEN",s.FLOOR="FLOOR",s.CEIL="CEIL",s.TRUNCATE="TRUNCATE",s))(C||{});var A=(s=>(s.CURRENCY_MISMATCH="MONETRA_CURRENCY_MISMATCH",s.INSUFFICIENT_FUNDS="MONETRA_INSUFFICIENT_FUNDS",s.INVALID_ARGUMENT="MONETRA_INVALID_ARGUMENT",s.INVALID_PRECISION="MONETRA_INVALID_PRECISION",s.OVERFLOW="MONETRA_OVERFLOW",s.ROUNDING_REQUIRED="MONETRA_ROUNDING_REQUIRED",s))(A||{}),h=class extends Error{constructor(e,r){super(e),this.name=this.constructor.name,this.code=r,Object.setPrototypeOf(this,new.target.prototype);}};var b=class extends h{constructor(e,r){super(`Currency mismatch: expected ${e}, received ${r}.
62
2
  \u{1F4A1} Tip: Use a Converter to convert between currencies:
63
- const converter = new Converter('USD', { ${received}: rate });
64
- const converted = converter.convert(money, '${expected}');`,
65
- "MONETRA_CURRENCY_MISMATCH" /* CURRENCY_MISMATCH */
66
- );
67
- this.expected = expected;
68
- this.received = received;
69
- }
70
- };
71
-
72
- // src/errors/InvalidPrecisionError.ts
73
- var InvalidPrecisionError = class extends MonetraError {
74
- constructor(message) {
75
- super(message, "MONETRA_INVALID_PRECISION" /* INVALID_PRECISION */);
76
- }
77
- };
78
-
79
- // src/errors/RoundingRequiredError.ts
80
- var RoundingRequiredError = class extends MonetraError {
81
- constructor(operation, result) {
82
- let message = "Rounding is required for this operation but was not provided.";
83
- if (operation && result !== void 0) {
84
- message = `Rounding required for ${operation}: result ${result} is not an integer.
3
+ const converter = new Converter('USD', { ${r}: rate });
4
+ const converted = converter.convert(money, '${e}');`,"MONETRA_CURRENCY_MISMATCH"),this.expected=e,this.received=r;}};var I=class extends h{constructor(e){super(e,"MONETRA_INVALID_PRECISION");}};var S=class extends h{constructor(e,r){let n="Rounding is required for this operation but was not provided.";e&&r!==void 0&&(n=`Rounding required for ${e}: result ${r} is not an integer.
85
5
  \u{1F4A1} Tip: Provide a rounding mode:
86
- money.${operation}(value, { rounding: RoundingMode.HALF_UP })
87
- Available modes: HALF_UP, HALF_DOWN, HALF_EVEN, FLOOR, CEIL, TRUNCATE`;
88
- }
89
- super(message, "MONETRA_ROUNDING_REQUIRED" /* ROUNDING_REQUIRED */);
90
- }
91
- };
92
-
93
- // src/errors/InsufficientFundsError.ts
94
- var InsufficientFundsError = class extends MonetraError {
95
- constructor(message) {
96
- super(
97
- message || "Insufficient funds for this operation.",
98
- "MONETRA_INSUFFICIENT_FUNDS" /* INSUFFICIENT_FUNDS */
99
- );
100
- }
101
- };
102
-
103
- // src/errors/InvalidArgumentError.ts
104
- var InvalidArgumentError = class extends MonetraError {
105
- constructor(message) {
106
- super(message, "MONETRA_INVALID_ARGUMENT" /* INVALID_ARGUMENT */);
107
- }
108
- };
109
-
110
- // src/errors/OverflowError.ts
111
- var OverflowError = class extends MonetraError {
112
- constructor(message = "Arithmetic overflow") {
113
- super(message, "MONETRA_OVERFLOW" /* OVERFLOW */);
114
- }
115
- };
116
-
117
- // src/money/guards.ts
118
- function assertSameCurrency(a, b) {
119
- if (a.currency.code !== b.currency.code) {
120
- throw new CurrencyMismatchError(a.currency.code, b.currency.code);
121
- }
122
- }
123
- function assertNonNegative(money2) {
124
- if (money2.isNegative()) {
125
- throw new Error("Money value must be non-negative");
126
- }
127
- }
128
-
129
- // src/rounding/index.ts
130
- function divideWithRounding(numerator, denominator, mode) {
131
- if (denominator === 0n) {
132
- throw new Error("Division by zero");
133
- }
134
- const quotient = numerator / denominator;
135
- const remainder = numerator % denominator;
136
- if (remainder === 0n) {
137
- return quotient;
138
- }
139
- const isNegativeResult = numerator < 0n !== denominator < 0n;
140
- const isPositiveResult = !isNegativeResult;
141
- const absRemainder = remainder < 0n ? -remainder : remainder;
142
- const absDenominator = denominator < 0n ? -denominator : denominator;
143
- const isHalf = absRemainder * 2n === absDenominator;
144
- const isMoreThanHalf = absRemainder * 2n > absDenominator;
145
- switch (mode) {
146
- case "FLOOR" /* FLOOR */:
147
- return isPositiveResult ? quotient : quotient - 1n;
148
- case "CEIL" /* CEIL */:
149
- return isPositiveResult ? quotient + 1n : quotient;
150
- case "HALF_UP" /* HALF_UP */:
151
- if (isMoreThanHalf || isHalf) {
152
- return isPositiveResult ? quotient + 1n : quotient - 1n;
153
- }
154
- return quotient;
155
- case "HALF_DOWN" /* HALF_DOWN */:
156
- if (isMoreThanHalf) {
157
- return isPositiveResult ? quotient + 1n : quotient - 1n;
158
- }
159
- return quotient;
160
- case "HALF_EVEN" /* HALF_EVEN */:
161
- if (isMoreThanHalf) {
162
- return isPositiveResult ? quotient + 1n : quotient - 1n;
163
- }
164
- if (isHalf) {
165
- if (quotient % 2n !== 0n) {
166
- return isPositiveResult ? quotient + 1n : quotient - 1n;
167
- }
168
- }
169
- return quotient;
170
- case "TRUNCATE" /* TRUNCATE */:
171
- return quotient;
172
- default:
173
- throw new Error(`Unsupported rounding mode: ${mode}`);
174
- }
175
- }
176
-
177
- // src/money/arithmetic.ts
178
- function add(a, b) {
179
- return a + b;
180
- }
181
- function subtract(a, b) {
182
- return a - b;
183
- }
184
- function multiply(amount, multiplier, rounding) {
185
- const { numerator, denominator } = parseMultiplier(multiplier);
186
- const product = amount * numerator;
187
- if (product % denominator === 0n) {
188
- return product / denominator;
189
- }
190
- if (!rounding) {
191
- throw new RoundingRequiredError(
192
- "multiply",
193
- Number(product) / Number(denominator)
194
- );
195
- }
196
- return divideWithRounding(product, denominator, rounding);
197
- }
198
- function divide(amount, divisor, rounding) {
199
- const { numerator, denominator } = parseMultiplier(divisor);
200
- const product = amount * denominator;
201
- if (product % numerator === 0n) {
202
- return product / numerator;
203
- }
204
- if (!rounding) {
205
- throw new RoundingRequiredError(
206
- "divide",
207
- Number(product) / Number(numerator)
208
- );
209
- }
210
- return divideWithRounding(product, numerator, rounding);
211
- }
212
- function parseMultiplier(multiplier) {
213
- const s = multiplier.toString();
214
- if (/[eE]/.test(s)) {
215
- throw new Error("Scientific notation not supported");
216
- }
217
- const parts = s.split(".");
218
- if (parts.length > 2) {
219
- throw new Error("Invalid number format");
220
- }
221
- const integerPart = parts[0];
222
- const fractionalPart = parts[1] || "";
223
- const denominator = 10n ** BigInt(fractionalPart.length);
224
- const numerator = BigInt(integerPart + fractionalPart);
225
- return { numerator, denominator };
226
- }
227
-
228
- // src/money/allocation.ts
229
- function allocate(amount, ratios) {
230
- if (ratios.length === 0) {
231
- throw new Error("Cannot allocate to empty ratios");
232
- }
233
- const scaledRatios = ratios.map((r) => {
234
- const s = r.toString();
235
- if (/[eE]/.test(s)) throw new Error("Scientific notation not supported");
236
- const parts = s.split(".");
237
- const decimals = parts[1] ? parts[1].length : 0;
238
- const value = BigInt(parts[0] + (parts[1] || ""));
239
- return { value, decimals };
240
- });
241
- const maxDecimals = Math.max(...scaledRatios.map((r) => r.decimals));
242
- const normalizedRatios = scaledRatios.map((r) => {
243
- const factor = 10n ** BigInt(maxDecimals - r.decimals);
244
- return r.value * factor;
245
- });
246
- const total = normalizedRatios.reduce((sum, r) => sum + r, 0n);
247
- if (total === 0n) {
248
- throw new Error("Total ratio must be greater than zero");
249
- }
250
- const results = [];
251
- let allocatedTotal = 0n;
252
- for (let i = 0; i < normalizedRatios.length; i++) {
253
- const ratio = normalizedRatios[i];
254
- const share = amount * ratio / total;
255
- const remainder = amount * ratio % total;
256
- results.push({ share, remainder, index: i });
257
- allocatedTotal += share;
258
- }
259
- let leftOver = amount - allocatedTotal;
260
- results.sort((a, b) => {
261
- if (b.remainder > a.remainder) return 1;
262
- if (b.remainder < a.remainder) return -1;
263
- return 0;
264
- });
265
- for (let i = 0; i < Number(leftOver); i++) {
266
- results[i].share += 1n;
267
- }
268
- results.sort((a, b) => a.index - b.index);
269
- return results.map((r) => r.share);
270
- }
271
-
272
- // src/format/parser.ts
273
- function parseLocaleString(amount, options) {
274
- const parts = new Intl.NumberFormat(options.locale, {
275
- style: "decimal",
276
- minimumFractionDigits: 1,
277
- useGrouping: true
278
- }).formatToParts(1234.5);
279
- let decimalSeparator = ".";
280
- let groupSeparator = ",";
281
- for (const part of parts) {
282
- if (part.type === "decimal") {
283
- decimalSeparator = part.value;
284
- } else if (part.type === "group") {
285
- groupSeparator = part.value;
286
- }
287
- }
288
- if (groupSeparator === decimalSeparator) {
289
- throw new Error(
290
- `Invalid locale configuration: group and decimal separators are the same for locale ${options.locale}`
291
- );
292
- }
293
- let normalized = amount.replace(/[^\d.,\-\s]/g, "").trim();
294
- const isNegative = normalized.startsWith("-") || amount.includes("(");
295
- normalized = normalized.replace(/[-()]/g, "");
296
- normalized = normalized.split(groupSeparator).join("");
297
- normalized = normalized.split(decimalSeparator).join(".");
298
- return isNegative ? `-${normalized}` : normalized;
299
- }
300
- function parseLocaleToMinor(amount, currency, options) {
301
- const normalized = parseLocaleString(amount, options);
302
- return parseToMinor(normalized, currency);
303
- }
304
- function parseToMinor(amount, currency) {
305
- if (/[eE]/.test(amount)) {
306
- throw new Error("Scientific notation not supported");
307
- }
308
- if (/[^0-9.-]/.test(amount)) {
309
- throw new Error("Invalid characters in amount");
310
- }
311
- if (!/[0-9]/.test(amount)) {
312
- throw new Error("Invalid format");
313
- }
314
- const parts = amount.split(".");
315
- if (parts.length > 2) {
316
- throw new Error("Invalid format: multiple decimal points");
317
- }
318
- const integerPart = parts[0];
319
- const fractionalPart = parts[1] || "";
320
- if (fractionalPart.length > currency.decimals) {
321
- throw new InvalidPrecisionError(
322
- `Precision ${fractionalPart.length} exceeds currency decimals ${currency.decimals}`
323
- );
324
- }
325
- const paddedFractional = fractionalPart.padEnd(currency.decimals, "0");
326
- const combined = integerPart + paddedFractional;
327
- return BigInt(combined);
328
- }
329
-
330
- // src/format/formatter.ts
331
- function format(money2, options) {
332
- const locale = options?.locale || money2.currency.locale || "en-US";
333
- const showSymbol = options?.symbol ?? true;
334
- const display = options?.display || "symbol";
335
- const useAccounting = options?.accounting ?? false;
336
- const decimals = money2.currency.decimals;
337
- const minor = money2.minor;
338
- const isNegative = minor < 0n;
339
- const absMinor = isNegative ? -minor : minor;
340
- const divisor = 10n ** BigInt(decimals);
341
- const integerPart = absMinor / divisor;
342
- const fractionalPart = absMinor % divisor;
343
- const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
344
- const parts = new Intl.NumberFormat(locale, {
345
- style: "decimal",
346
- minimumFractionDigits: 1
347
- }).formatToParts(1.1);
348
- const decimalSeparator = parts.find((p) => p.type === "decimal")?.value || ".";
349
- const integerFormatted = new Intl.NumberFormat(locale, {
350
- style: "decimal",
351
- useGrouping: true
352
- }).format(integerPart);
353
- const absString = decimals > 0 ? `${integerFormatted}${decimalSeparator}${fractionalStr}` : integerFormatted;
354
- if (!showSymbol) {
355
- if (isNegative) {
356
- return useAccounting ? `(${absString})` : `-${absString}`;
357
- }
358
- return absString;
359
- }
360
- let templateParts;
361
- try {
362
- templateParts = new Intl.NumberFormat(locale, {
363
- style: "currency",
364
- currency: money2.currency.code,
365
- currencyDisplay: display
366
- }).formatToParts(isNegative ? -1234.5 : 1234.5);
367
- } catch (e) {
368
- const symbol = display === "symbol" ? money2.currency.symbol : money2.currency.code;
369
- if (isNegative) {
370
- return useAccounting ? `(${symbol}${absString})` : `-${symbol}${absString}`;
371
- }
372
- return `${symbol}${absString}`;
373
- }
374
- let result = "";
375
- let numberInserted = false;
376
- for (const part of templateParts) {
377
- switch (part.type) {
378
- case "minusSign": {
379
- if (!useAccounting) {
380
- result += part.value;
381
- }
382
- break;
383
- }
384
- case "integer": {
385
- if (!numberInserted) {
386
- result += absString;
387
- numberInserted = true;
388
- }
389
- break;
390
- }
391
- // Skip the remaining numeric parts, since we already inserted the full number string.
392
- case "group":
393
- case "decimal":
394
- case "fraction": {
395
- break;
396
- }
397
- case "currency": {
398
- if (display === "symbol" && money2.currency.symbol) {
399
- result += money2.currency.symbol;
400
- } else {
401
- result += part.value;
402
- }
403
- break;
404
- }
405
- default: {
406
- result += part.value;
407
- break;
408
- }
409
- }
410
- }
411
- if (useAccounting && isNegative) {
412
- return `(${result.trim()})`;
413
- }
414
- return result;
415
- }
416
-
417
- // src/money/Money.ts
418
- var Money = class _Money {
419
- constructor(minor, currency) {
420
- this.minor = minor;
421
- this.currency = currency;
422
- }
423
- static resolveCurrency(currency) {
424
- if (typeof currency === "string") {
425
- return getCurrency(currency);
426
- }
427
- return currency;
428
- }
429
- /**
430
- * Creates a Money instance from minor units (e.g., cents).
431
- *
432
- * @param minor - The amount in minor units. Can be a number or BigInt.
433
- * @param currency - The currency of the money (object or code string).
434
- * @returns A new Money instance.
435
- * @example
436
- * const m = Money.fromMinor(100, USD); // $1.00
437
- * const m2 = Money.fromMinor(100, 'USD'); // $1.00
438
- */
439
- static fromMinor(minor, currency) {
440
- return new _Money(BigInt(minor), _Money.resolveCurrency(currency));
441
- }
442
- /**
443
- * Alias for `fromMinor`. Creates a Money instance from cents/minor units.
444
- *
445
- * @param cents - The amount in minor units.
446
- * @param currency - The currency of the money (object or code string).
447
- * @returns A new Money instance.
448
- * @example
449
- * const m = Money.fromCents(100, 'USD'); // $1.00
450
- */
451
- static fromCents(cents, currency) {
452
- return _Money.fromMinor(cents, currency);
453
- }
454
- /**
455
- * Creates a Money instance from major units (e.g., "10.50").
456
- *
457
- * @param amount - The amount as a string. Must be a valid decimal number.
458
- * @param currency - The currency of the money (object or code string).
459
- * @returns A new Money instance.
460
- * @throws {InvalidPrecisionError} If the amount has more decimal places than the currency allows.
461
- * @example
462
- * const m = Money.fromMajor("10.50", USD); // $10.50
463
- */
464
- static fromMajor(amount, currency) {
465
- const resolvedCurrency = _Money.resolveCurrency(currency);
466
- const minor = parseToMinor(amount, resolvedCurrency);
467
- return new _Money(minor, resolvedCurrency);
468
- }
469
- /**
470
- * Alias for `fromMajor`. Creates a Money instance from a decimal string.
471
- *
472
- * @param amount - The amount as a string (e.g., "10.50").
473
- * @param currency - The currency of the money (object or code string).
474
- * @returns A new Money instance.
475
- * @throws {InvalidPrecisionError} If the amount has more decimal places than the currency allows.
476
- * @example
477
- * const m = Money.fromDecimal("10.50", 'USD'); // $10.50
478
- */
479
- static fromDecimal(amount, currency) {
480
- return _Money.fromMajor(amount, currency);
481
- }
482
- /**
483
- * Creates a Money instance from a floating-point number.
484
- *
485
- * ⚠️ WARNING: Floating-point numbers can have precision issues.
486
- * Prefer `Money.fromMajor("10.50", currency)` for exact values.
487
- *
488
- * @param amount - The float amount in major units.
489
- * @param currency - The currency.
490
- * @param options - Options for handling precision.
491
- * @returns A new Money instance.
492
- */
493
- static fromFloat(amount, currency, options) {
494
- if (!options?.suppressWarning && process.env.NODE_ENV !== "production") {
495
- console.warn(
496
- '[monetra] Money.fromFloat() may lose precision. Consider using Money.fromMajor("' + amount + '", currency) instead.'
497
- );
498
- }
499
- const resolvedCurrency = _Money.resolveCurrency(currency);
500
- const factor = 10 ** resolvedCurrency.decimals;
501
- const minorUnits = Math.round(amount * factor);
502
- return new _Money(BigInt(minorUnits), resolvedCurrency);
503
- }
504
- /**
505
- * Returns the minimum of the provided Money values.
506
- * @param values - Money values to compare (must all be same currency).
507
- * @returns The Money with the smallest amount.
508
- * @throws {CurrencyMismatchError} If currencies don't match.
509
- */
510
- static min(...values) {
511
- if (values.length === 0)
512
- throw new Error("At least one Money value required");
513
- return values.reduce((min, current) => {
514
- assertSameCurrency(min, current);
515
- return current.lessThan(min) ? current : min;
516
- });
517
- }
518
- /**
519
- * Returns the maximum of the provided Money values.
520
- * @param values - Money values to compare (must all be same currency).
521
- * @returns The Money with the largest amount.
522
- * @throws {CurrencyMismatchError} If currencies don't match.
523
- */
524
- static max(...values) {
525
- if (values.length === 0)
526
- throw new Error("At least one Money value required");
527
- return values.reduce((max, current) => {
528
- assertSameCurrency(max, current);
529
- return current.greaterThan(max) ? current : max;
530
- });
531
- }
532
- /**
533
- * Creates a Money instance representing zero in the given currency.
534
- *
535
- * @param currency - The currency.
536
- * @returns A new Money instance with value 0.
537
- */
538
- static zero(currency) {
539
- return new _Money(0n, _Money.resolveCurrency(currency));
540
- }
541
- resolveOther(other) {
542
- if (other instanceof _Money) {
543
- return other;
544
- }
545
- if (typeof other === "string") {
546
- return _Money.fromMajor(other, this.currency);
547
- }
548
- return _Money.fromMinor(other, this.currency);
549
- }
550
- /**
551
- * Adds another Money value to this one.
552
- *
553
- * @param other - The value to add (Money, minor units as number/bigint, or major units as string).
554
- * @returns A new Money instance representing the sum.
555
- * @throws {CurrencyMismatchError} If the currencies do not match.
556
- */
557
- add(other) {
558
- const otherMoney = this.resolveOther(other);
559
- assertSameCurrency(this, otherMoney);
560
- return new _Money(add(this.minor, otherMoney.minor), this.currency);
561
- }
562
- /**
563
- * Subtracts another Money value from this one.
564
- *
565
- * @param other - The value to subtract (Money, minor units as number/bigint, or major units as string).
566
- * @returns A new Money instance representing the difference.
567
- * @throws {CurrencyMismatchError} If the currencies do not match.
568
- */
569
- subtract(other) {
570
- const otherMoney = this.resolveOther(other);
571
- assertSameCurrency(this, otherMoney);
572
- return new _Money(subtract(this.minor, otherMoney.minor), this.currency);
573
- }
574
- /**
575
- * Multiplies this Money value by a scalar.
576
- *
577
- * @param multiplier - The number to multiply by.
578
- * @param options - Options for rounding if the result is not an integer.
579
- * @returns A new Money instance representing the product.
580
- * @throws {RoundingRequiredError} If the result is fractional and no rounding mode is provided.
581
- */
582
- multiply(multiplier, options) {
583
- const result = multiply(this.minor, multiplier, options?.rounding);
584
- return new _Money(result, this.currency);
585
- }
586
- /**
587
- * Divides this Money value by a divisor.
588
- *
589
- * @param divisor - The number to divide by.
590
- * @param options - Options for rounding if the result is not an integer.
591
- * @returns A new Money instance representing the quotient.
592
- * @throws {RoundingRequiredError} If the result is fractional and no rounding mode is provided.
593
- * @throws {Error} If divisor is zero.
594
- */
595
- divide(divisor, options) {
596
- if (divisor === 0 || divisor === "0") {
597
- throw new Error("Division by zero");
598
- }
599
- const result = divide(this.minor, divisor, options?.rounding);
600
- return new _Money(result, this.currency);
601
- }
602
- /**
603
- * Returns the absolute value of this Money.
604
- * @returns A new Money instance with the absolute value.
605
- */
606
- abs() {
607
- return new _Money(this.minor < 0n ? -this.minor : this.minor, this.currency);
608
- }
609
- /**
610
- * Returns the negated value of this Money.
611
- * @returns A new Money instance with the negated value.
612
- */
613
- negate() {
614
- return new _Money(-this.minor, this.currency);
615
- }
616
- /**
617
- * Allocates (splits) this Money value according to a list of ratios.
618
- *
619
- * The sum of the parts will always equal the original amount.
620
- * Remainders are distributed to the parts with the largest fractional remainders.
621
- *
622
- * @param ratios - A list of numbers representing the ratios to split by.
623
- * @returns An array of Money instances.
624
- */
625
- allocate(ratios) {
626
- const shares = allocate(this.minor, ratios);
627
- return shares.map((share) => new _Money(share, this.currency));
628
- }
629
- /**
630
- * Formats this Money value as a string.
631
- *
632
- * @param options - Formatting options.
633
- * @returns The formatted string.
634
- */
635
- format(options) {
636
- return format(this, options);
637
- }
638
- /**
639
- * Checks if this Money value is equal to another.
640
- *
641
- * @param other - The other Money value (Money, minor units as number/bigint, or major units as string).
642
- * @returns True if amounts and currencies are equal.
643
- */
644
- equals(other) {
645
- const otherMoney = this.resolveOther(other);
646
- return this.currency.code === otherMoney.currency.code && this.minor === otherMoney.minor;
647
- }
648
- /**
649
- * Checks if this Money value is greater than another.
650
- *
651
- * @param other - The other Money value (Money, minor units as number/bigint, or major units as string).
652
- * @returns True if this value is greater.
653
- * @throws {CurrencyMismatchError} If the currencies do not match.
654
- */
655
- greaterThan(other) {
656
- const otherMoney = this.resolveOther(other);
657
- assertSameCurrency(this, otherMoney);
658
- return this.minor > otherMoney.minor;
659
- }
660
- /**
661
- * Checks if this Money value is less than another.
662
- *
663
- * @param other - The other Money value (Money, minor units as number/bigint, or major units as string).
664
- * @returns True if this value is less.
665
- * @throws {CurrencyMismatchError} If the currencies do not match.
666
- */
667
- lessThan(other) {
668
- const otherMoney = this.resolveOther(other);
669
- assertSameCurrency(this, otherMoney);
670
- return this.minor < otherMoney.minor;
671
- }
672
- /**
673
- * Checks if this Money value is greater than or equal to another.
674
- */
675
- greaterThanOrEqual(other) {
676
- const otherMoney = this.resolveOther(other);
677
- assertSameCurrency(this, otherMoney);
678
- return this.minor >= otherMoney.minor;
679
- }
680
- /**
681
- * Checks if this Money value is less than or equal to another.
682
- */
683
- lessThanOrEqual(other) {
684
- const otherMoney = this.resolveOther(other);
685
- assertSameCurrency(this, otherMoney);
686
- return this.minor <= otherMoney.minor;
687
- }
688
- /**
689
- * Checks if this Money value is positive (greater than zero).
690
- */
691
- isPositive() {
692
- return this.minor > 0n;
693
- }
694
- /**
695
- * Compares this Money to another, returning -1, 0, or 1.
696
- * Useful for sorting.
697
- */
698
- compare(other) {
699
- const otherMoney = this.resolveOther(other);
700
- assertSameCurrency(this, otherMoney);
701
- if (this.minor < otherMoney.minor) return -1;
702
- if (this.minor > otherMoney.minor) return 1;
703
- return 0;
704
- }
705
- /**
706
- * Calculates a percentage of the money.
707
- * @param percent - The percentage (e.g., 50 for 50%).
708
- * @param rounding - Rounding mode (defaults to HALF_EVEN).
709
- */
710
- percentage(percent, rounding = "HALF_EVEN" /* HALF_EVEN */) {
711
- return this.multiply(percent / 100, { rounding });
712
- }
713
- /**
714
- * Adds a percentage to the money.
715
- * @param percent - The percentage to add.
716
- * @param rounding - Rounding mode.
717
- */
718
- addPercent(percent, rounding = "HALF_EVEN" /* HALF_EVEN */) {
719
- return this.add(this.percentage(percent, rounding));
720
- }
721
- /**
722
- * Subtracts a percentage from the money.
723
- * @param percent - The percentage to subtract.
724
- * @param rounding - Rounding mode.
725
- */
726
- subtractPercent(percent, rounding = "HALF_EVEN" /* HALF_EVEN */) {
727
- return this.subtract(this.percentage(percent, rounding));
728
- }
729
- /**
730
- * Splits the money into equal parts.
731
- * @param parts - Number of parts.
732
- */
733
- split(parts) {
734
- const ratios = Array(parts).fill(1);
735
- return this.allocate(ratios);
736
- }
737
- /**
738
- * Checks if this Money value is zero.
739
- *
740
- * @returns True if the amount is zero.
741
- */
742
- isZero() {
743
- return this.minor === 0n;
744
- }
745
- /**
746
- * Checks if this Money value is negative.
747
- *
748
- * @returns True if the amount is negative.
749
- */
750
- isNegative() {
751
- return this.minor < 0n;
752
- }
753
- /**
754
- * Clamps this Money value between a minimum and maximum.
755
- *
756
- * @param min - The minimum Money value.
757
- * @param max - The maximum Money value.
758
- * @returns A new Money instance clamped between min and max.
759
- * @throws {CurrencyMismatchError} If currencies don't match.
760
- * @throws {Error} If min is greater than max.
761
- * @example
762
- * const price = Money.fromMajor("150", 'USD');
763
- * const clamped = price.clamp(Money.fromMajor("50", 'USD'), Money.fromMajor("100", 'USD'));
764
- * // clamped is $100.00
765
- */
766
- clamp(min, max) {
767
- assertSameCurrency(this, min);
768
- assertSameCurrency(this, max);
769
- if (min.greaterThan(max)) {
770
- throw new Error("Clamp min cannot be greater than max");
771
- }
772
- if (this.lessThan(min)) {
773
- return new _Money(min.minor, this.currency);
774
- }
775
- if (this.greaterThan(max)) {
776
- return new _Money(max.minor, this.currency);
777
- }
778
- return this;
779
- }
780
- /**
781
- * Returns the value as a decimal string without locale formatting.
782
- *
783
- * This returns a raw decimal representation suitable for storage or calculations,
784
- * without any currency symbols, grouping separators, or locale-specific formatting.
785
- *
786
- * @returns The decimal string representation (e.g., "10.50", "-5.25").
787
- * @example
788
- * const m = Money.fromMajor("1234.56", 'USD');
789
- * m.toDecimalString(); // "1234.56"
790
- */
791
- toDecimalString() {
792
- const decimals = this.currency.decimals;
793
- const isNegative = this.minor < 0n;
794
- const absMinor = isNegative ? -this.minor : this.minor;
795
- if (decimals === 0) {
796
- return isNegative ? `-${absMinor.toString()}` : absMinor.toString();
797
- }
798
- const divisor = 10n ** BigInt(decimals);
799
- const integerPart = absMinor / divisor;
800
- const fractionalPart = absMinor % divisor;
801
- const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
802
- const result = `${integerPart}.${fractionalStr}`;
803
- return isNegative ? `-${result}` : result;
804
- }
805
- /**
806
- * Returns a JSON representation of the Money object.
807
- *
808
- * @returns An object with amount (string), currency (code), and precision.
809
- */
810
- toJSON() {
811
- return {
812
- amount: this.minor.toString(),
813
- currency: this.currency.code,
814
- precision: this.currency.decimals
815
- };
816
- }
817
- /**
818
- * JSON reviver function for deserializing Money objects.
819
- *
820
- * Use with `JSON.parse()` to automatically reconstruct Money instances:
821
- *
822
- * @param key - The JSON key (unused).
823
- * @param value - The parsed JSON value.
824
- * @returns A Money instance if value is a serialized Money object, otherwise the original value.
825
- * @example
826
- * const json = '{"amount": "1050", "currency": "USD", "precision": 2}';
827
- * const money = JSON.parse(json, Money.reviver);
828
- * // money is Money instance: $10.50
829
- */
830
- static reviver(key, value) {
831
- 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") {
832
- const obj = value;
833
- return _Money.fromMinor(BigInt(obj.amount), obj.currency);
834
- }
835
- return value;
836
- }
837
- /**
838
- * Returns a string representation of the Money object (formatted).
839
- */
840
- toString() {
841
- return this.format();
842
- }
843
- };
844
-
845
- // src/money/Converter.ts
846
- var Converter = class {
847
- /**
848
- * Creates a new Converter.
849
- *
850
- * @param base - The base currency code (e.g., "USD").
851
- * @param rates - A map of currency codes to exchange rates relative to the base.
852
- * Example: { "EUR": 0.85, "GBP": 0.75 } (where 1 Base = 0.85 EUR).
853
- */
854
- constructor(base, rates) {
855
- this.base = base;
856
- this.rates = { ...rates };
857
- if (this.rates[base] === void 0) {
858
- this.rates[base] = 1;
859
- }
860
- }
861
- /**
862
- * Converts a Money object to a target currency.
863
- *
864
- * @param money - The Money object to convert.
865
- * @param toCurrency - The target currency (code or object).
866
- * @returns A new Money object in the target currency.
867
- * @throws {Error} If exchange rates are missing.
868
- */
869
- convert(money2, toCurrency) {
870
- const targetCurrency = typeof toCurrency === "string" ? getCurrency(toCurrency) : toCurrency;
871
- if (money2.currency.code === targetCurrency.code) {
872
- return money2;
873
- }
874
- const fromRate = this.rates[money2.currency.code];
875
- const toRate = this.rates[targetCurrency.code];
876
- if (fromRate === void 0 || toRate === void 0) {
877
- throw new Error(
878
- `Exchange rate missing for conversion from ${money2.currency.code} to ${targetCurrency.code}`
879
- );
880
- }
881
- const ratio = toRate / fromRate;
882
- const decimalAdjustment = 10 ** (targetCurrency.decimals - money2.currency.decimals);
883
- const multiplier = ratio * decimalAdjustment;
884
- const convertedAmount = money2.multiply(multiplier, {
885
- rounding: "HALF_EVEN" /* HALF_EVEN */
886
- });
887
- return Money.fromMinor(convertedAmount.minor, targetCurrency);
888
- }
889
- };
890
-
891
- // src/money/MoneyBag.ts
892
- var MoneyBag = class {
893
- constructor() {
894
- this.contents = /* @__PURE__ */ new Map();
895
- }
896
- /**
897
- * Adds a Money amount to the bag.
898
- * @param money The money to add.
899
- */
900
- add(money2) {
901
- const code = money2.currency.code;
902
- const existing = this.contents.get(code);
903
- if (existing) {
904
- this.contents.set(code, existing.add(money2));
905
- } else {
906
- this.contents.set(code, money2);
907
- }
908
- }
909
- /**
910
- * Subtracts a Money amount from the bag.
911
- * @param money The money to subtract.
912
- */
913
- subtract(money2) {
914
- const code = money2.currency.code;
915
- const existing = this.contents.get(code);
916
- if (existing) {
917
- this.contents.set(code, existing.subtract(money2));
918
- } else {
919
- const zero = Money.zero(money2.currency);
920
- this.contents.set(code, zero.subtract(money2));
921
- }
922
- }
923
- /**
924
- * Gets the amount for a specific currency.
925
- * @param currency The currency to retrieve.
926
- * @returns The Money amount in that currency (or zero if not present).
927
- */
928
- get(currency) {
929
- const code = typeof currency === "string" ? currency : currency.code;
930
- return this.contents.get(code) || Money.zero(
931
- typeof currency === "string" ? getCurrency(currency) : currency
932
- );
933
- }
934
- /**
935
- * Calculates the total value of the bag in a specific currency.
936
- *
937
- * @param targetCurrency The currency to convert everything to.
938
- * @param converter The converter instance with exchange rates.
939
- * @returns The total amount in the target currency.
940
- */
941
- total(targetCurrency, converter) {
942
- const target = typeof targetCurrency === "string" ? getCurrency(targetCurrency) : targetCurrency;
943
- let total = Money.zero(target);
944
- for (const money2 of this.contents.values()) {
945
- const converted = converter.convert(money2, target);
946
- total = total.add(converted);
947
- }
948
- return total;
949
- }
950
- /**
951
- * Returns a list of all Money objects in the bag.
952
- */
953
- getAll() {
954
- return Array.from(this.contents.values());
955
- }
956
- /**
957
- * Returns a JSON representation of the bag.
958
- */
959
- toJSON() {
960
- return this.getAll().map((m) => m.toJSON());
961
- }
962
- };
963
-
964
- // src/currency/iso4217.ts
965
- var USD = {
966
- code: "USD",
967
- decimals: 2,
968
- symbol: "$",
969
- locale: "en-US"
970
- };
971
- registerCurrency(USD);
972
- var EUR = {
973
- code: "EUR",
974
- decimals: 2,
975
- symbol: "\u20AC",
976
- locale: "de-DE"
977
- };
978
- registerCurrency(EUR);
979
- var GBP = {
980
- code: "GBP",
981
- decimals: 2,
982
- symbol: "\xA3",
983
- locale: "en-GB"
984
- };
985
- registerCurrency(GBP);
986
- var JPY = {
987
- code: "JPY",
988
- decimals: 0,
989
- symbol: "\xA5",
990
- locale: "ja-JP"
991
- };
992
- registerCurrency(JPY);
993
- var CHF = {
994
- code: "CHF",
995
- decimals: 2,
996
- symbol: "CHF",
997
- locale: "de-CH"
998
- };
999
- registerCurrency(CHF);
1000
- var CAD = {
1001
- code: "CAD",
1002
- decimals: 2,
1003
- symbol: "CA$",
1004
- locale: "en-CA"
1005
- };
1006
- registerCurrency(CAD);
1007
- var AUD = {
1008
- code: "AUD",
1009
- decimals: 2,
1010
- symbol: "A$",
1011
- locale: "en-AU"
1012
- };
1013
- registerCurrency(AUD);
1014
- var NZD = {
1015
- code: "NZD",
1016
- decimals: 2,
1017
- symbol: "NZ$",
1018
- locale: "en-NZ"
1019
- };
1020
- registerCurrency(NZD);
1021
- var CNY = {
1022
- code: "CNY",
1023
- decimals: 2,
1024
- symbol: "\xA5",
1025
- locale: "zh-CN"
1026
- };
1027
- registerCurrency(CNY);
1028
- var HKD = {
1029
- code: "HKD",
1030
- decimals: 2,
1031
- symbol: "HK$",
1032
- locale: "zh-HK"
1033
- };
1034
- registerCurrency(HKD);
1035
- var SGD = {
1036
- code: "SGD",
1037
- decimals: 2,
1038
- symbol: "S$",
1039
- locale: "en-SG"
1040
- };
1041
- registerCurrency(SGD);
1042
- var KRW = {
1043
- code: "KRW",
1044
- decimals: 0,
1045
- symbol: "\u20A9",
1046
- locale: "ko-KR"
1047
- };
1048
- registerCurrency(KRW);
1049
- var INR = {
1050
- code: "INR",
1051
- decimals: 2,
1052
- symbol: "\u20B9",
1053
- locale: "en-IN"
1054
- };
1055
- registerCurrency(INR);
1056
- var SEK = {
1057
- code: "SEK",
1058
- decimals: 2,
1059
- symbol: "kr",
1060
- locale: "sv-SE"
1061
- };
1062
- registerCurrency(SEK);
1063
- var NOK = {
1064
- code: "NOK",
1065
- decimals: 2,
1066
- symbol: "kr",
1067
- locale: "nb-NO"
1068
- };
1069
- registerCurrency(NOK);
1070
- var DKK = {
1071
- code: "DKK",
1072
- decimals: 2,
1073
- symbol: "kr",
1074
- locale: "da-DK"
1075
- };
1076
- registerCurrency(DKK);
1077
- var PLN = {
1078
- code: "PLN",
1079
- decimals: 2,
1080
- symbol: "z\u0142",
1081
- locale: "pl-PL"
1082
- };
1083
- registerCurrency(PLN);
1084
- var CZK = {
1085
- code: "CZK",
1086
- decimals: 2,
1087
- symbol: "K\u010D",
1088
- locale: "cs-CZ"
1089
- };
1090
- registerCurrency(CZK);
1091
- var HUF = {
1092
- code: "HUF",
1093
- decimals: 2,
1094
- symbol: "Ft",
1095
- locale: "hu-HU"
1096
- };
1097
- registerCurrency(HUF);
1098
- var RON = {
1099
- code: "RON",
1100
- decimals: 2,
1101
- symbol: "lei",
1102
- locale: "ro-RO"
1103
- };
1104
- registerCurrency(RON);
1105
- var BGN = {
1106
- code: "BGN",
1107
- decimals: 2,
1108
- symbol: "\u043B\u0432",
1109
- locale: "bg-BG"
1110
- };
1111
- registerCurrency(BGN);
1112
- var HRK = {
1113
- code: "HRK",
1114
- decimals: 2,
1115
- symbol: "kn",
1116
- locale: "hr-HR"
1117
- };
1118
- registerCurrency(HRK);
1119
- var TRY = {
1120
- code: "TRY",
1121
- decimals: 2,
1122
- symbol: "\u20BA",
1123
- locale: "tr-TR"
1124
- };
1125
- registerCurrency(TRY);
1126
- var RUB = {
1127
- code: "RUB",
1128
- decimals: 2,
1129
- symbol: "\u20BD",
1130
- locale: "ru-RU"
1131
- };
1132
- registerCurrency(RUB);
1133
- var UAH = {
1134
- code: "UAH",
1135
- decimals: 2,
1136
- symbol: "\u20B4",
1137
- locale: "uk-UA"
1138
- };
1139
- registerCurrency(UAH);
1140
- var ILS = {
1141
- code: "ILS",
1142
- decimals: 2,
1143
- symbol: "\u20AA",
1144
- locale: "he-IL"
1145
- };
1146
- registerCurrency(ILS);
1147
- var MXN = {
1148
- code: "MXN",
1149
- decimals: 2,
1150
- symbol: "MX$",
1151
- locale: "es-MX"
1152
- };
1153
- registerCurrency(MXN);
1154
- var BRL = {
1155
- code: "BRL",
1156
- decimals: 2,
1157
- symbol: "R$",
1158
- locale: "pt-BR"
1159
- };
1160
- registerCurrency(BRL);
1161
- var ARS = {
1162
- code: "ARS",
1163
- decimals: 2,
1164
- symbol: "AR$",
1165
- locale: "es-AR"
1166
- };
1167
- registerCurrency(ARS);
1168
- var CLP = {
1169
- code: "CLP",
1170
- decimals: 0,
1171
- symbol: "CL$",
1172
- locale: "es-CL"
1173
- };
1174
- registerCurrency(CLP);
1175
- var COP = {
1176
- code: "COP",
1177
- decimals: 2,
1178
- symbol: "CO$",
1179
- locale: "es-CO"
1180
- };
1181
- registerCurrency(COP);
1182
- var PEN = {
1183
- code: "PEN",
1184
- decimals: 2,
1185
- symbol: "S/",
1186
- locale: "es-PE"
1187
- };
1188
- registerCurrency(PEN);
1189
- var ZAR = {
1190
- code: "ZAR",
1191
- decimals: 2,
1192
- symbol: "R",
1193
- locale: "en-ZA"
1194
- };
1195
- registerCurrency(ZAR);
1196
- var NGN = {
1197
- code: "NGN",
1198
- decimals: 2,
1199
- symbol: "\u20A6",
1200
- locale: "en-NG"
1201
- };
1202
- registerCurrency(NGN);
1203
- var KES = {
1204
- code: "KES",
1205
- decimals: 2,
1206
- symbol: "KSh",
1207
- locale: "en-KE"
1208
- };
1209
- registerCurrency(KES);
1210
- var EGP = {
1211
- code: "EGP",
1212
- decimals: 2,
1213
- symbol: "E\xA3",
1214
- locale: "ar-EG"
1215
- };
1216
- registerCurrency(EGP);
1217
- var MAD = {
1218
- code: "MAD",
1219
- decimals: 2,
1220
- symbol: "\u062F.\u0645.",
1221
- locale: "ar-MA"
1222
- };
1223
- registerCurrency(MAD);
1224
- var GHS = {
1225
- code: "GHS",
1226
- decimals: 2,
1227
- symbol: "GH\u20B5",
1228
- locale: "en-GH"
1229
- };
1230
- registerCurrency(GHS);
1231
- var TZS = {
1232
- code: "TZS",
1233
- decimals: 2,
1234
- symbol: "TSh",
1235
- locale: "sw-TZ"
1236
- };
1237
- registerCurrency(TZS);
1238
- var UGX = {
1239
- code: "UGX",
1240
- decimals: 0,
1241
- symbol: "USh",
1242
- locale: "en-UG"
1243
- };
1244
- registerCurrency(UGX);
1245
- var THB = {
1246
- code: "THB",
1247
- decimals: 2,
1248
- symbol: "\u0E3F",
1249
- locale: "th-TH"
1250
- };
1251
- registerCurrency(THB);
1252
- var MYR = {
1253
- code: "MYR",
1254
- decimals: 2,
1255
- symbol: "RM",
1256
- locale: "ms-MY"
1257
- };
1258
- registerCurrency(MYR);
1259
- var IDR = {
1260
- code: "IDR",
1261
- decimals: 2,
1262
- symbol: "Rp",
1263
- locale: "id-ID"
1264
- };
1265
- registerCurrency(IDR);
1266
- var PHP = {
1267
- code: "PHP",
1268
- decimals: 2,
1269
- symbol: "\u20B1",
1270
- locale: "en-PH"
1271
- };
1272
- registerCurrency(PHP);
1273
- var VND = {
1274
- code: "VND",
1275
- decimals: 0,
1276
- symbol: "\u20AB",
1277
- locale: "vi-VN"
1278
- };
1279
- registerCurrency(VND);
1280
- var TWD = {
1281
- code: "TWD",
1282
- decimals: 2,
1283
- symbol: "NT$",
1284
- locale: "zh-TW"
1285
- };
1286
- registerCurrency(TWD);
1287
- var PKR = {
1288
- code: "PKR",
1289
- decimals: 2,
1290
- symbol: "\u20A8",
1291
- locale: "ur-PK"
1292
- };
1293
- registerCurrency(PKR);
1294
- var BDT = {
1295
- code: "BDT",
1296
- decimals: 2,
1297
- symbol: "\u09F3",
1298
- locale: "bn-BD"
1299
- };
1300
- registerCurrency(BDT);
1301
- var LKR = {
1302
- code: "LKR",
1303
- decimals: 2,
1304
- symbol: "Rs",
1305
- locale: "si-LK"
1306
- };
1307
- registerCurrency(LKR);
1308
- var AED = {
1309
- code: "AED",
1310
- decimals: 2,
1311
- symbol: "\u062F.\u0625",
1312
- locale: "ar-AE"
1313
- };
1314
- registerCurrency(AED);
1315
- var SAR = {
1316
- code: "SAR",
1317
- decimals: 2,
1318
- symbol: "\uFDFC",
1319
- locale: "ar-SA"
1320
- };
1321
- registerCurrency(SAR);
1322
- var QAR = {
1323
- code: "QAR",
1324
- decimals: 2,
1325
- symbol: "\uFDFC",
1326
- locale: "ar-QA"
1327
- };
1328
- registerCurrency(QAR);
1329
- var KWD = {
1330
- code: "KWD",
1331
- decimals: 3,
1332
- symbol: "\u062F.\u0643",
1333
- locale: "ar-KW"
1334
- };
1335
- registerCurrency(KWD);
1336
- var BHD = {
1337
- code: "BHD",
1338
- decimals: 3,
1339
- symbol: "\u062F.\u0628",
1340
- locale: "ar-BH"
1341
- };
1342
- registerCurrency(BHD);
1343
- var OMR = {
1344
- code: "OMR",
1345
- decimals: 3,
1346
- symbol: "\uFDFC",
1347
- locale: "ar-OM"
1348
- };
1349
- registerCurrency(OMR);
1350
- var JOD = {
1351
- code: "JOD",
1352
- decimals: 3,
1353
- symbol: "\u062F.\u0627",
1354
- locale: "ar-JO"
1355
- };
1356
- registerCurrency(JOD);
1357
- var ISK = {
1358
- code: "ISK",
1359
- decimals: 0,
1360
- symbol: "kr",
1361
- locale: "is-IS"
1362
- };
1363
- registerCurrency(ISK);
1364
- var MRU = {
1365
- code: "MRU",
1366
- decimals: 2,
1367
- symbol: "UM",
1368
- locale: "ar-MR"
1369
- };
1370
- registerCurrency(MRU);
1371
- var CURRENCIES = {
1372
- // Major world currencies
1373
- USD,
1374
- EUR,
1375
- GBP,
1376
- JPY,
1377
- CHF,
1378
- CAD,
1379
- AUD,
1380
- NZD,
1381
- CNY,
1382
- HKD,
1383
- SGD,
1384
- KRW,
1385
- INR,
1386
- // European
1387
- SEK,
1388
- NOK,
1389
- DKK,
1390
- PLN,
1391
- CZK,
1392
- HUF,
1393
- RON,
1394
- BGN,
1395
- HRK,
1396
- TRY,
1397
- RUB,
1398
- UAH,
1399
- ILS,
1400
- // Americas
1401
- MXN,
1402
- BRL,
1403
- ARS,
1404
- CLP,
1405
- COP,
1406
- PEN,
1407
- // Africa
1408
- ZAR,
1409
- NGN,
1410
- KES,
1411
- EGP,
1412
- MAD,
1413
- GHS,
1414
- TZS,
1415
- UGX,
1416
- // Asia-Pacific
1417
- THB,
1418
- MYR,
1419
- IDR,
1420
- PHP,
1421
- VND,
1422
- TWD,
1423
- PKR,
1424
- BDT,
1425
- LKR,
1426
- // Middle East
1427
- AED,
1428
- SAR,
1429
- QAR,
1430
- KWD,
1431
- BHD,
1432
- OMR,
1433
- JOD,
1434
- // Special
1435
- ISK,
1436
- MRU
1437
- };
1438
-
1439
- // src/currency/precision.ts
1440
- function getMinorUnitExponent(currency) {
1441
- return 10n ** BigInt(currency.decimals);
1442
- }
1443
-
1444
- // src/financial/loan.ts
1445
- function loan(options) {
1446
- const {
1447
- principal,
1448
- annualRate,
1449
- periods,
1450
- periodsPerYear = 12,
1451
- rounding = "HALF_EVEN" /* HALF_EVEN */
1452
- } = options;
1453
- const periodicRate = annualRate / periodsPerYear;
1454
- let payment;
1455
- if (annualRate === 0) {
1456
- payment = principal.divide(periods, { rounding });
1457
- } else {
1458
- const r = periodicRate;
1459
- const n = periods;
1460
- const numerator = r * Math.pow(1 + r, n);
1461
- const denominator = Math.pow(1 + r, n) - 1;
1462
- const pmtMultiplier = numerator / denominator;
1463
- payment = principal.multiply(pmtMultiplier, { rounding });
1464
- }
1465
- const schedule = [];
1466
- let balance = principal;
1467
- for (let period = 1; period <= periods; period++) {
1468
- const interest = annualRate === 0 ? Money.zero(principal.currency) : balance.multiply(periodicRate, { rounding });
1469
- let principalPayment = payment.subtract(interest);
1470
- balance = balance.subtract(principalPayment);
1471
- if (period === periods && !balance.isZero()) {
1472
- const adjustedPrincipal = principalPayment.add(balance);
1473
- schedule.push({
1474
- period,
1475
- payment: interest.add(adjustedPrincipal),
1476
- principal: adjustedPrincipal,
1477
- interest,
1478
- balance: Money.zero(principal.currency)
1479
- });
1480
- } else {
1481
- schedule.push({
1482
- period,
1483
- payment,
1484
- principal: principalPayment,
1485
- interest,
1486
- balance: balance.isNegative() ? Money.zero(principal.currency) : balance
1487
- });
1488
- }
1489
- }
1490
- return schedule;
1491
- }
1492
- function pmt(options) {
1493
- const schedule = loan(options);
1494
- return schedule[0].payment;
1495
- }
1496
- function totalInterest(options) {
1497
- const {
1498
- principal,
1499
- annualRate,
1500
- periods,
1501
- rounding = "HALF_EVEN" /* HALF_EVEN */
1502
- } = options;
1503
- if (annualRate === 0) {
1504
- return Money.zero(principal.currency);
1505
- }
1506
- const payment = pmt(options);
1507
- const totalPaid = payment.multiply(periods, { rounding });
1508
- return totalPaid.subtract(principal);
1509
- }
1510
- function totalInterestFromSchedule(schedule) {
1511
- if (schedule.length === 0) {
1512
- throw new Error("Schedule must have at least one entry");
1513
- }
1514
- const currency = schedule[0].interest.currency;
1515
- return schedule.reduce(
1516
- (sum, entry) => sum.add(entry.interest),
1517
- Money.zero(currency)
1518
- );
1519
- }
1520
- function interestOnlyPayment(options) {
1521
- const {
1522
- principal,
1523
- annualRate,
1524
- periodsPerYear = 12,
1525
- rounding = "HALF_EVEN" /* HALF_EVEN */
1526
- } = options;
1527
- if (annualRate === 0) {
1528
- return Money.zero(principal.currency);
1529
- }
1530
- const periodicRate = annualRate / periodsPerYear;
1531
- return principal.multiply(periodicRate, { rounding });
1532
- }
1533
-
1534
- // src/financial/compound.ts
1535
- function futureValue(presentValue2, options) {
1536
- const {
1537
- rate,
1538
- years,
1539
- compoundingPerYear = 12,
1540
- rounding = "HALF_EVEN" /* HALF_EVEN */
1541
- } = options;
1542
- const n = compoundingPerYear;
1543
- const t = years;
1544
- const multiplier = Math.pow(1 + rate / n, n * t);
1545
- return presentValue2.multiply(multiplier, { rounding });
1546
- }
1547
- function presentValue(futureVal, options) {
1548
- const {
1549
- rate,
1550
- years,
1551
- compoundingPerYear = 12,
1552
- rounding = "HALF_EVEN" /* HALF_EVEN */
1553
- } = options;
1554
- const n = compoundingPerYear;
1555
- const t = years;
1556
- const divisor = Math.pow(1 + rate / n, n * t);
1557
- return futureVal.divide(divisor, { rounding });
1558
- }
1559
- var compound = futureValue;
1560
- var discount = presentValue;
1561
-
1562
- // src/financial/investment.ts
1563
- function roi(initialValue, finalValue) {
1564
- if (initialValue.currency.code !== finalValue.currency.code) {
1565
- throw new CurrencyMismatchError(
1566
- initialValue.currency.code,
1567
- finalValue.currency.code
1568
- );
1569
- }
1570
- const gain = finalValue.subtract(initialValue);
1571
- return Number(gain.minor) / Number(initialValue.minor);
1572
- }
1573
- function npv(discountRate, cashFlows) {
1574
- if (cashFlows.length === 0) {
1575
- throw new Error("At least one cash flow required");
1576
- }
1577
- const currency = cashFlows[0].currency;
1578
- let total = Money.zero(currency);
1579
- for (let i = 0; i < cashFlows.length; i++) {
1580
- const discountFactor = Math.pow(1 + discountRate, i);
1581
- const discounted = cashFlows[i].divide(discountFactor, {
1582
- rounding: "HALF_EVEN" /* HALF_EVEN */
1583
- });
1584
- total = total.add(discounted);
1585
- }
1586
- return total;
1587
- }
1588
- function irr(cashFlows, guess = 0.1) {
1589
- const values = cashFlows.map((cf) => Number(cf.minor));
1590
- const maxIterations = 100;
1591
- const tolerance = 1e-7;
1592
- let rate = guess;
1593
- for (let i = 0; i < maxIterations; i++) {
1594
- let npvValue = 0;
1595
- let derivative = 0;
1596
- for (let j = 0; j < values.length; j++) {
1597
- const denominator = Math.pow(1 + rate, j);
1598
- npvValue += values[j] / denominator;
1599
- if (j > 0) {
1600
- derivative -= j * values[j] / Math.pow(1 + rate, j + 1);
1601
- }
1602
- }
1603
- const newRate = rate - npvValue / derivative;
1604
- if (Math.abs(newRate - rate) < tolerance) {
1605
- return newRate;
1606
- }
1607
- rate = newRate;
1608
- }
1609
- throw new Error("IRR calculation did not converge");
1610
- }
1611
- function currentYield(annualCoupon, currentPrice) {
1612
- if (annualCoupon.currency.code !== currentPrice.currency.code) {
1613
- throw new CurrencyMismatchError(
1614
- annualCoupon.currency.code,
1615
- currentPrice.currency.code
1616
- );
1617
- }
1618
- if (currentPrice.minor <= 0n) {
1619
- throw new InvalidArgumentError("Current price must be positive");
1620
- }
1621
- return Number(annualCoupon.minor) / Number(currentPrice.minor);
1622
- }
1623
-
1624
- // src/financial/rate.ts
1625
- var _Rate = class _Rate {
1626
- constructor(decimalValue) {
1627
- this.decimalValue = decimalValue;
1628
- }
1629
- /**
1630
- * Creates a Rate from a percentage value (e.g., 5 for 5%).
1631
- *
1632
- * @param percent - The percentage value.
1633
- * @returns A new Rate instance.
1634
- * @example
1635
- * const rate = Rate.percent(5.5); // 5.5%
1636
- */
1637
- static percent(percent) {
1638
- const decimal = typeof percent === "string" ? parseFloat(percent) : percent;
1639
- const scaled = BigInt(Math.round(decimal / 100 * Number(_Rate.SCALE)));
1640
- return new _Rate(scaled);
1641
- }
1642
- /**
1643
- * Creates a Rate from a decimal value (e.g., 0.05 for 5%).
1644
- *
1645
- * @param decimal - The decimal value.
1646
- * @returns A new Rate instance.
1647
- * @example
1648
- * const rate = Rate.decimal(0.055); // 5.5%
1649
- */
1650
- static decimal(decimal) {
1651
- const value = typeof decimal === "string" ? parseFloat(decimal) : decimal;
1652
- const scaled = BigInt(Math.round(value * Number(_Rate.SCALE)));
1653
- return new _Rate(scaled);
1654
- }
1655
- /**
1656
- * Creates a Rate representing zero.
1657
- *
1658
- * @returns A Rate of 0%.
1659
- */
1660
- static zero() {
1661
- return new _Rate(0n);
1662
- }
1663
- /**
1664
- * Returns the rate as a percentage (e.g., 5 for 5%).
1665
- *
1666
- * @returns The percentage value as a number.
1667
- */
1668
- toPercent() {
1669
- return Number(this.decimalValue) / Number(_Rate.SCALE) * 100;
1670
- }
1671
- /**
1672
- * Returns the rate as a decimal (e.g., 0.05 for 5%).
1673
- *
1674
- * @returns The decimal value as a number.
1675
- */
1676
- toDecimal() {
1677
- return Number(this.decimalValue) / Number(_Rate.SCALE);
1678
- }
1679
- /**
1680
- * Adds another rate to this one.
1681
- *
1682
- * @param other - The rate to add.
1683
- * @returns A new Rate representing the sum.
1684
- */
1685
- add(other) {
1686
- return new _Rate(this.decimalValue + other.decimalValue);
1687
- }
1688
- /**
1689
- * Subtracts another rate from this one.
1690
- *
1691
- * @param other - The rate to subtract.
1692
- * @returns A new Rate representing the difference.
1693
- */
1694
- subtract(other) {
1695
- return new _Rate(this.decimalValue - other.decimalValue);
1696
- }
1697
- /**
1698
- * Multiplies this rate by a scalar.
1699
- *
1700
- * @param multiplier - The number to multiply by.
1701
- * @returns A new Rate representing the product.
1702
- */
1703
- multiply(multiplier) {
1704
- const scaled = BigInt(Math.round(multiplier * Number(_Rate.SCALE)));
1705
- return new _Rate(this.decimalValue * scaled / _Rate.SCALE);
1706
- }
1707
- /**
1708
- * Divides this rate by a divisor.
1709
- *
1710
- * @param divisor - The number to divide by.
1711
- * @returns A new Rate representing the quotient.
1712
- */
1713
- divide(divisor) {
1714
- const scaled = BigInt(Math.round(divisor * Number(_Rate.SCALE)));
1715
- return new _Rate(this.decimalValue * _Rate.SCALE / scaled);
1716
- }
1717
- /**
1718
- * Checks if this rate equals another.
1719
- *
1720
- * @param other - The rate to compare.
1721
- * @returns True if the rates are equal.
1722
- */
1723
- equals(other) {
1724
- return this.decimalValue === other.decimalValue;
1725
- }
1726
- /**
1727
- * Checks if this rate is greater than another.
1728
- *
1729
- * @param other - The rate to compare.
1730
- * @returns True if this rate is greater.
1731
- */
1732
- greaterThan(other) {
1733
- return this.decimalValue > other.decimalValue;
1734
- }
1735
- /**
1736
- * Checks if this rate is less than another.
1737
- *
1738
- * @param other - The rate to compare.
1739
- * @returns True if this rate is less.
1740
- */
1741
- lessThan(other) {
1742
- return this.decimalValue < other.decimalValue;
1743
- }
1744
- /**
1745
- * Checks if this rate is zero.
1746
- *
1747
- * @returns True if the rate is 0%.
1748
- */
1749
- isZero() {
1750
- return this.decimalValue === 0n;
1751
- }
1752
- /**
1753
- * Checks if this rate is negative.
1754
- *
1755
- * @returns True if the rate is negative.
1756
- */
1757
- isNegative() {
1758
- return this.decimalValue < 0n;
1759
- }
1760
- /**
1761
- * Returns a string representation of the rate as a percentage.
1762
- *
1763
- * @returns The rate as a percentage string (e.g., "5.5%").
1764
- */
1765
- toString() {
1766
- return `${this.toPercent()}%`;
1767
- }
1768
- /**
1769
- * Computes compound factor: (1 + rate)^periods.
1770
- *
1771
- * @param periods - The number of compounding periods.
1772
- * @returns The compound factor as a number.
1773
- * @example
1774
- * Rate.percent(10).compoundFactor(3); // (1.10)^3 = 1.331
1775
- */
1776
- compoundFactor(periods) {
1777
- return Math.pow(1 + this.toDecimal(), periods);
1778
- }
1779
- /**
1780
- * Returns the periodic rate for a given frequency.
1781
- *
1782
- * @param periodsPerYear - Number of periods per year (e.g., 12 for monthly).
1783
- * @returns A new Rate representing the periodic rate.
1784
- * @example
1785
- * Rate.percent(12).periodic(12); // 1% monthly
1786
- */
1787
- periodic(periodsPerYear) {
1788
- return this.divide(periodsPerYear);
1789
- }
1790
- /**
1791
- * Converts an effective rate to a nominal rate.
1792
- *
1793
- * @param periodsPerYear - Number of compounding periods per year.
1794
- * @returns The nominal annual rate.
1795
- */
1796
- toNominal(periodsPerYear) {
1797
- const effectiveDecimal = this.toDecimal();
1798
- const nominalDecimal = periodsPerYear * (Math.pow(1 + effectiveDecimal, 1 / periodsPerYear) - 1);
1799
- return _Rate.decimal(nominalDecimal);
1800
- }
1801
- /**
1802
- * Converts a nominal rate to an effective annual rate.
1803
- *
1804
- * @param periodsPerYear - Number of compounding periods per year.
1805
- * @returns The effective annual rate.
1806
- */
1807
- toEffective(periodsPerYear) {
1808
- const nominalDecimal = this.toDecimal();
1809
- const effectiveDecimal = Math.pow(1 + nominalDecimal / periodsPerYear, periodsPerYear) - 1;
1810
- return _Rate.decimal(effectiveDecimal);
1811
- }
1812
- /**
1813
- * Returns a JSON representation.
1814
- */
1815
- toJSON() {
1816
- return {
1817
- percent: this.toPercent(),
1818
- decimal: this.toDecimal()
1819
- };
1820
- }
1821
- };
1822
- /**
1823
- * Scale factor for internal precision (1e18 for 18 decimal places).
1824
- */
1825
- _Rate.SCALE = 10n ** 18n;
1826
- var Rate = _Rate;
1827
-
1828
- // src/financial/simple.ts
1829
- function simpleInterest(principal, options) {
1830
- const { rate, years, rounding = "HALF_EVEN" /* HALF_EVEN */ } = options;
1831
- if (rate.isZero() || years === 0) {
1832
- return Money.zero(principal.currency);
1833
- }
1834
- const interestMultiplier = rate.toDecimal() * years;
1835
- return principal.multiply(interestMultiplier, { rounding });
1836
- }
1837
- function simpleInterestTotal(principal, options) {
1838
- const { rate, years, rounding = "HALF_EVEN" /* HALF_EVEN */ } = options;
1839
- if (rate.isZero() || years === 0) {
1840
- return principal;
1841
- }
1842
- const totalMultiplier = 1 + rate.toDecimal() * years;
1843
- return principal.multiply(totalMultiplier, { rounding });
1844
- }
1845
-
1846
- // src/financial/leverage.ts
1847
- function debtToEquity(totalDebt, totalEquity) {
1848
- assertSameCurrency(totalDebt, totalEquity);
1849
- if (totalEquity.isZero()) {
1850
- throw new Error("Total equity cannot be zero");
1851
- }
1852
- return Number(totalDebt.minor) / Number(totalEquity.minor);
1853
- }
1854
- function debtToAssets(totalDebt, totalAssets) {
1855
- assertSameCurrency(totalDebt, totalAssets);
1856
- if (totalAssets.isZero()) {
1857
- throw new Error("Total assets cannot be zero");
1858
- }
1859
- return Number(totalDebt.minor) / Number(totalAssets.minor);
1860
- }
1861
- function interestCoverage(ebit, interestExpense) {
1862
- assertSameCurrency(ebit, interestExpense);
1863
- if (interestExpense.isZero()) {
1864
- return Infinity;
1865
- }
1866
- return Number(ebit.minor) / Number(interestExpense.minor);
1867
- }
1868
- function equityMultiplier(totalAssets, totalEquity) {
1869
- assertSameCurrency(totalAssets, totalEquity);
1870
- if (totalEquity.isZero()) {
1871
- throw new Error("Total equity cannot be zero");
1872
- }
1873
- return Number(totalAssets.minor) / Number(totalEquity.minor);
1874
- }
1875
- function leverageRatios(inputs) {
1876
- return {
1877
- debtToEquity: debtToEquity(inputs.totalDebt, inputs.totalEquity),
1878
- debtToAssets: debtToAssets(inputs.totalDebt, inputs.totalAssets),
1879
- interestCoverage: interestCoverage(inputs.ebit, inputs.interestExpense),
1880
- equityMultiplier: equityMultiplier(inputs.totalAssets, inputs.totalEquity)
1881
- };
1882
- }
1883
-
1884
- // src/financial/depreciation.ts
1885
- function straightLineDepreciation(options) {
1886
- const {
1887
- cost,
1888
- salvageValue,
1889
- usefulLife,
1890
- rounding = "HALF_EVEN" /* HALF_EVEN */
1891
- } = options;
1892
- if (cost.currency.code !== salvageValue.currency.code) {
1893
- throw new CurrencyMismatchError(
1894
- cost.currency.code,
1895
- salvageValue.currency.code
1896
- );
1897
- }
1898
- if (usefulLife <= 0) {
1899
- throw new InvalidArgumentError("Useful life must be positive");
1900
- }
1901
- if (salvageValue.greaterThan(cost)) {
1902
- throw new InvalidArgumentError("Salvage value cannot be greater than cost");
1903
- }
1904
- const depreciableAmount = cost.subtract(salvageValue);
1905
- const annualDepreciation = depreciableAmount.divide(usefulLife, { rounding });
1906
- const result = {
1907
- annualDepreciation,
1908
- bookValueAtYear(year) {
1909
- if (year < 0) {
1910
- throw new InvalidArgumentError("Year must be non-negative");
1911
- }
1912
- if (year === 0) return cost;
1913
- const totalDepreciation = annualDepreciation.multiply(year);
1914
- const bookValue = cost.subtract(totalDepreciation);
1915
- if (bookValue.lessThan(salvageValue)) {
1916
- return salvageValue;
1917
- }
1918
- return bookValue;
1919
- },
1920
- schedule() {
1921
- const schedule = [];
1922
- let previousBookValue = cost;
1923
- const maxYears = Math.ceil(usefulLife);
1924
- for (let year = 1; year <= maxYears; year++) {
1925
- const currentBookValue = result.bookValueAtYear(year);
1926
- const depreciation = previousBookValue.subtract(currentBookValue);
1927
- schedule.push({
1928
- year,
1929
- depreciation,
1930
- bookValue: currentBookValue
1931
- });
1932
- previousBookValue = currentBookValue;
1933
- }
1934
- return schedule;
1935
- }
1936
- };
1937
- return result;
1938
- }
1939
-
1940
- // src/ledger/verification.ts
1941
- var hashFunction = null;
1942
- function getHashFunction() {
1943
- if (hashFunction) return hashFunction;
1944
- const disableNodeCrypto = globalThis.__MONETRA_DISABLE_NODE_CRYPTO__ === true;
1945
- if (!disableNodeCrypto && typeof globalThis !== "undefined") {
1946
- try {
1947
- const nodeCrypto = __require("crypto");
1948
- if (nodeCrypto && nodeCrypto.createHash) {
1949
- hashFunction = (data) => {
1950
- return nodeCrypto.createHash("sha256").update(data).digest("hex");
1951
- };
1952
- return hashFunction;
1953
- }
1954
- } catch {
1955
- }
1956
- }
1957
- if (typeof globalThis !== "undefined" && globalThis.crypto && globalThis.crypto.subtle) {
1958
- hashFunction = async (data) => {
1959
- const encoder = new TextEncoder();
1960
- const dataBuffer = encoder.encode(data);
1961
- const hashBuffer = await globalThis.crypto.subtle.digest(
1962
- "SHA-256",
1963
- dataBuffer
1964
- );
1965
- const hashArray = Array.from(new Uint8Array(hashBuffer));
1966
- return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
1967
- };
1968
- return hashFunction;
1969
- }
1970
- throw new Error(
1971
- "No cryptographic hash function available. Ensure you are running in Node.js or a browser with SubtleCrypto support."
1972
- );
1973
- }
1974
- function setHashFunction(fn) {
1975
- hashFunction = fn;
1976
- }
1977
- function serialize(data) {
1978
- return JSON.stringify(data, (_key, value) => {
1979
- if (typeof value === "bigint") {
1980
- return value.toString();
1981
- }
1982
- if (value && typeof value === "object" && "minor" in value && "currency" in value) {
1983
- return {
1984
- minor: value.minor.toString(),
1985
- currency: value.currency.code
1986
- };
1987
- }
1988
- return value;
1989
- });
1990
- }
1991
- function generateHash(data) {
1992
- const serialized = serialize(data);
1993
- const fn = getHashFunction();
1994
- return fn(serialized);
1995
- }
1996
- function generateHashSync(data) {
1997
- const result = generateHash(data);
1998
- if (result instanceof Promise) {
1999
- throw new Error(
2000
- "Synchronous hashing not available in this environment. Use generateHash() with await instead."
2001
- );
2002
- }
2003
- return result;
2004
- }
2005
- async function verifyChain(entries) {
2006
- for (let i = 0; i < entries.length; i++) {
2007
- const entry = entries[i];
2008
- const previousEntry = i > 0 ? entries[i - 1] : null;
2009
- if (previousEntry) {
2010
- if (entry.previousHash !== previousEntry.hash) return false;
2011
- } else {
2012
- if (entry.previousHash !== null) return false;
2013
- }
2014
- const { hash, ...content } = entry;
2015
- const calculatedHash = await generateHash(content);
2016
- if (hash !== calculatedHash) return false;
2017
- }
2018
- return true;
2019
- }
2020
- function verifyChainSync(entries) {
2021
- for (let i = 0; i < entries.length; i++) {
2022
- const entry = entries[i];
2023
- const previousEntry = i > 0 ? entries[i - 1] : null;
2024
- if (previousEntry) {
2025
- if (entry.previousHash !== previousEntry.hash) return false;
2026
- } else {
2027
- if (entry.previousHash !== null) return false;
2028
- }
2029
- const { hash, ...content } = entry;
2030
- const calculatedHash = generateHashSync(content);
2031
- if (hash !== calculatedHash) return false;
2032
- }
2033
- return true;
2034
- }
2035
-
2036
- // src/ledger/Ledger.ts
2037
- import { randomUUID } from "crypto";
2038
- var SNAPSHOT_VERSION = 2;
2039
- var Ledger = class _Ledger {
2040
- constructor(currency) {
2041
- this.entries = [];
2042
- this.currency = typeof currency === "string" ? currency : currency.code;
2043
- }
2044
- /**
2045
- * Records a transaction in the ledger.
2046
- * Returns the created entry for reference.
2047
- * Note: This is the synchronous version for Node.js. Use recordAsync for browser support.
2048
- */
2049
- record(money2, metadata) {
2050
- if (money2.currency.code !== this.currency) {
2051
- throw new CurrencyMismatchError(this.currency, money2.currency.code);
2052
- }
2053
- const previousHash = this.entries.length > 0 ? this.entries[this.entries.length - 1].hash : null;
2054
- const content = {
2055
- id: randomUUID(),
2056
- money: money2,
2057
- metadata: Object.freeze({
2058
- ...metadata,
2059
- timestamp: metadata.timestamp ?? /* @__PURE__ */ new Date()
2060
- }),
2061
- createdAt: /* @__PURE__ */ new Date(),
2062
- previousHash
2063
- };
2064
- const hash = generateHashSync(content);
2065
- const entry = {
2066
- ...content,
2067
- hash
2068
- };
2069
- this.entries.push(Object.freeze(entry));
2070
- return entry;
2071
- }
2072
- /**
2073
- * Records a transaction in the ledger (async version for browser support).
2074
- * Returns the created entry for reference.
2075
- */
2076
- async recordAsync(money2, metadata) {
2077
- if (money2.currency.code !== this.currency) {
2078
- throw new CurrencyMismatchError(this.currency, money2.currency.code);
2079
- }
2080
- const previousHash = this.entries.length > 0 ? this.entries[this.entries.length - 1].hash : null;
2081
- const content = {
2082
- id: randomUUID(),
2083
- money: money2,
2084
- metadata: Object.freeze({
2085
- ...metadata,
2086
- timestamp: metadata.timestamp ?? /* @__PURE__ */ new Date()
2087
- }),
2088
- createdAt: /* @__PURE__ */ new Date(),
2089
- previousHash
2090
- };
2091
- const hash = await generateHash(content);
2092
- const entry = {
2093
- ...content,
2094
- hash
2095
- };
2096
- this.entries.push(Object.freeze(entry));
2097
- return entry;
2098
- }
2099
- /**
2100
- * Gets the current balance.
2101
- */
2102
- getBalance() {
2103
- return this.entries.reduce(
2104
- (balance, entry) => balance.add(entry.money),
2105
- Money.zero(this.currency)
2106
- );
2107
- }
2108
- /**
2109
- * Returns the complete transaction history.
2110
- */
2111
- getHistory() {
2112
- return Object.freeze([...this.entries]);
2113
- }
2114
- /**
2115
- * Filters entries by criteria.
2116
- */
2117
- query(filter) {
2118
- return this.entries.filter((entry) => {
2119
- if (filter.type) {
2120
- const types = Array.isArray(filter.type) ? filter.type : [filter.type];
2121
- if (!types.includes(entry.metadata.type)) return false;
2122
- }
2123
- if (filter.from && entry.createdAt < filter.from) return false;
2124
- if (filter.to && entry.createdAt > filter.to) return false;
2125
- if (filter.reference && entry.metadata.reference !== filter.reference)
2126
- return false;
2127
- if (filter.minAmount && entry.money.lessThan(filter.minAmount))
2128
- return false;
2129
- if (filter.maxAmount && entry.money.greaterThan(filter.maxAmount))
2130
- return false;
2131
- if (filter.tags && entry.metadata.tags) {
2132
- const hasTag = filter.tags.some(
2133
- (tag) => entry.metadata.tags?.includes(tag)
2134
- );
2135
- if (!hasTag) return false;
2136
- }
2137
- return true;
2138
- });
2139
- }
2140
- /**
2141
- * Verifies the integrity of the ledger using hash chain (sync version).
2142
- * @returns true if all hashes are valid and chain is unbroken.
2143
- */
2144
- verify() {
2145
- return verifyChainSync(this.entries);
2146
- }
2147
- /**
2148
- * Verifies the integrity of the ledger using hash chain (async version for browsers).
2149
- * @returns Promise resolving to true if all hashes are valid and chain is unbroken.
2150
- */
2151
- async verifyAsync() {
2152
- return verifyChain(this.entries);
2153
- }
2154
- /**
2155
- * Exports a snapshot for backup/audit purposes (sync version).
2156
- */
2157
- snapshot() {
2158
- return {
2159
- version: SNAPSHOT_VERSION,
2160
- entries: [...this.entries],
2161
- balance: this.getBalance(),
2162
- currency: this.currency,
2163
- createdAt: /* @__PURE__ */ new Date(),
2164
- checksum: generateHashSync({ entries: this.entries })
2165
- };
2166
- }
2167
- /**
2168
- * Exports a snapshot for backup/audit purposes (async version for browsers).
2169
- */
2170
- async snapshotAsync() {
2171
- const checksum = await generateHash({ entries: this.entries });
2172
- return {
2173
- version: SNAPSHOT_VERSION,
2174
- entries: [...this.entries],
2175
- balance: this.getBalance(),
2176
- currency: this.currency,
2177
- createdAt: /* @__PURE__ */ new Date(),
2178
- checksum
2179
- };
2180
- }
2181
- /**
2182
- * Restores from a snapshot (sync version).
2183
- */
2184
- static fromSnapshot(snapshot) {
2185
- const ledger = new _Ledger(snapshot.currency);
2186
- if (!verifyChainSync(snapshot.entries)) {
2187
- throw new Error("Ledger snapshot integrity check failed");
2188
- }
2189
- ledger.entries = [...snapshot.entries];
2190
- return ledger;
2191
- }
2192
- /**
2193
- * Restores from a snapshot (async version for browsers).
2194
- */
2195
- static async fromSnapshotAsync(snapshot) {
2196
- const ledger = new _Ledger(snapshot.currency);
2197
- const isValid = await verifyChain(snapshot.entries);
2198
- if (!isValid) {
2199
- throw new Error("Ledger snapshot integrity check failed");
2200
- }
2201
- ledger.entries = [...snapshot.entries];
2202
- return ledger;
2203
- }
2204
- };
2205
-
2206
- // src/index.ts
2207
- function money(amount, currency) {
2208
- if (typeof amount === "string") {
2209
- return Money.fromMajor(amount, currency);
2210
- }
2211
- return Money.fromMinor(amount, currency);
2212
- }
2213
- export {
2214
- AED,
2215
- ARS,
2216
- AUD,
2217
- BDT,
2218
- BGN,
2219
- BHD,
2220
- BRL,
2221
- CAD,
2222
- CHF,
2223
- CLP,
2224
- CNY,
2225
- COP,
2226
- CURRENCIES,
2227
- CZK,
2228
- Converter,
2229
- CurrencyMismatchError,
2230
- DKK,
2231
- EGP,
2232
- EUR,
2233
- GBP,
2234
- GHS,
2235
- HKD,
2236
- HRK,
2237
- HUF,
2238
- IDR,
2239
- ILS,
2240
- INR,
2241
- ISK,
2242
- InsufficientFundsError,
2243
- InvalidArgumentError,
2244
- InvalidPrecisionError,
2245
- JOD,
2246
- JPY,
2247
- KES,
2248
- KRW,
2249
- KWD,
2250
- LKR,
2251
- Ledger,
2252
- MAD,
2253
- MRU,
2254
- MXN,
2255
- MYR,
2256
- MonetraError,
2257
- MonetraErrorCode,
2258
- Money,
2259
- MoneyBag,
2260
- NGN,
2261
- NOK,
2262
- NZD,
2263
- OMR,
2264
- OverflowError,
2265
- PEN,
2266
- PHP,
2267
- PKR,
2268
- PLN,
2269
- QAR,
2270
- RON,
2271
- RUB,
2272
- Rate,
2273
- RoundingMode,
2274
- RoundingRequiredError,
2275
- SAR,
2276
- SEK,
2277
- SGD,
2278
- THB,
2279
- TRY,
2280
- TWD,
2281
- TZS,
2282
- UAH,
2283
- UGX,
2284
- USD,
2285
- VND,
2286
- ZAR,
2287
- assertNonNegative,
2288
- assertSameCurrency,
2289
- compound,
2290
- currentYield,
2291
- debtToAssets,
2292
- debtToEquity,
2293
- discount,
2294
- divideWithRounding,
2295
- equityMultiplier,
2296
- format,
2297
- futureValue,
2298
- generateHash,
2299
- generateHashSync,
2300
- getAllCurrencies,
2301
- getCurrency,
2302
- getMinorUnitExponent,
2303
- interestCoverage,
2304
- interestOnlyPayment,
2305
- irr,
2306
- isCurrencyRegistered,
2307
- leverageRatios,
2308
- loan,
2309
- money,
2310
- npv,
2311
- parseLocaleString,
2312
- parseLocaleToMinor,
2313
- parseToMinor,
2314
- pmt,
2315
- presentValue,
2316
- registerCurrency,
2317
- roi,
2318
- setHashFunction,
2319
- simpleInterest,
2320
- simpleInterestTotal,
2321
- straightLineDepreciation,
2322
- totalInterest,
2323
- totalInterestFromSchedule,
2324
- verifyChain,
2325
- verifyChainSync
2326
- };
2327
- //# sourceMappingURL=index.mjs.map
6
+ money.${e}(value, { rounding: RoundingMode.HALF_UP })
7
+ Available modes: HALF_UP, HALF_DOWN, HALF_EVEN, FLOOR, CEIL, TRUNCATE`),super(n,"MONETRA_ROUNDING_REQUIRED");}};var K=class extends h{constructor(e){super(e||"Insufficient funds for this operation.","MONETRA_INSUFFICIENT_FUNDS");}};var x=class extends h{constructor(e){super(e,"MONETRA_INVALID_ARGUMENT");}};var z=class extends h{constructor(e="Arithmetic overflow"){super(e,"MONETRA_OVERFLOW");}};function f(t,e){if(t.currency.code!==e.currency.code)throw new b(t.currency.code,e.currency.code)}function _r(t){if(t.isNegative())throw new Error("Money value must be non-negative")}function P(t,e,r){if(e===0n)throw new Error("Division by zero");let n=t/e,o=t%e;if(o===0n)return n;let s=!(t<0n!=e<0n),l=o<0n?-o:o,m=e<0n?-e:e,a=l*2n===m,u=l*2n>m;switch(r){case "FLOOR":return s?n:n-1n;case "CEIL":return s?n+1n:n;case "HALF_UP":return u||a?s?n+1n:n-1n:n;case "HALF_DOWN":return u?s?n+1n:n-1n:n;case "HALF_EVEN":return u||a&&n%2n!==0n?s?n+1n:n-1n:n;case "TRUNCATE":return n;default:throw new Error(`Unsupported rounding mode: ${r}`)}}function G(t,e){return t+e}function Y(t,e){return t-e}function j(t,e,r){let{numerator:n,denominator:o}=_(e),i=t*n;if(i%o===0n)return i/o;if(!r)throw new S("multiply",Number(i)/Number(o));return P(i,o,r)}function Z(t,e,r){let{numerator:n,denominator:o}=_(e),i=t*o;if(i%n===0n)return i/n;if(!r)throw new S("divide",Number(i)/Number(n));return P(i,n,r)}function _(t){let e=t.toString();if(/[eE]/.test(e))throw new Error("Scientific notation not supported");let r=e.split(".");if(r.length>2)throw new Error("Invalid number format");let n=r[0],o=r[1]||"",i=10n**BigInt(o.length);return {numerator:BigInt(n+o),denominator:i}}function W(t,e){if(e.length===0)throw new Error("Cannot allocate to empty ratios");let r=e.map(a=>{let u=a.toString();if(/[eE]/.test(u))throw new Error("Scientific notation not supported");let d=u.split("."),g=d[1]?d[1].length:0;return {value:BigInt(d[0]+(d[1]||"")),decimals:g}}),n=Math.max(...r.map(a=>a.decimals)),o=r.map(a=>{let u=10n**BigInt(n-a.decimals);return a.value*u}),i=o.reduce((a,u)=>a+u,0n);if(i===0n)throw new Error("Total ratio must be greater than zero");let s=[],l=0n;for(let a=0;a<o.length;a++){let u=o[a],d=t*u/i,g=t*u%i;s.push({share:d,remainder:g,index:a}),l+=d;}let m=t-l;s.sort((a,u)=>u.remainder>a.remainder?1:u.remainder<a.remainder?-1:0);for(let a=0;a<Number(m);a++)s[a].share+=1n;return s.sort((a,u)=>a.index-u.index),s.map(a=>a.share)}function ur(t,e){let r=new Intl.NumberFormat(e.locale,{style:"decimal",minimumFractionDigits:1,useGrouping:true}).formatToParts(1234.5),n=".",o=",";for(let l of r)l.type==="decimal"?n=l.value:l.type==="group"&&(o=l.value);if(o===n)throw new Error(`Invalid locale configuration: group and decimal separators are the same for locale ${e.locale}`);let i=t.replace(/[^\d.,\-\s]/g,"").trim(),s=i.startsWith("-")||t.includes("(");return i=i.replace(/[-()]/g,""),i=i.split(o).join(""),i=i.split(n).join("."),s?`-${i}`:i}function it(t,e,r){let n=ur(t,r);return H(n,e)}function H(t,e){if(/[eE]/.test(t))throw new Error("Scientific notation not supported");if(/[^0-9.-]/.test(t))throw new Error("Invalid characters in amount");if(!/[0-9]/.test(t))throw new Error("Invalid format");let r=t.split(".");if(r.length>2)throw new Error("Invalid format: multiple decimal points");let n=r[0],o=r[1]||"";if(o.length>e.decimals)throw new I(`Precision ${o.length} exceeds currency decimals ${e.decimals}`);let i=o.padEnd(e.decimals,"0"),s=n+i;return BigInt(s)}function q(t,e){let r=e?.locale||t.currency.locale||"en-US",n=e?.symbol??true,o=e?.display||"symbol",i=e?.accounting??false,s=t.currency.decimals,l=t.minor,m=l<0n,a=m?-l:l,u=10n**BigInt(s),d=a/u,M=(a%u).toString().padStart(s,"0"),cr=new Intl.NumberFormat(r,{style:"decimal",minimumFractionDigits:1}).formatToParts(1.1).find(w=>w.type==="decimal")?.value||".",$=new Intl.NumberFormat(r,{style:"decimal",useGrouping:true}).format(d),E=s>0?`${$}${cr}${M}`:$;if(!n)return m?i?`(${E})`:`-${E}`:E;let B;try{B=new Intl.NumberFormat(r,{style:"currency",currency:t.currency.code,currencyDisplay:o}).formatToParts(m?-1234.5:1234.5);}catch{let L=o==="symbol"?t.currency.symbol:t.currency.code;return m?i?`(${L}${E})`:`-${L}${E}`:`${L}${E}`}let R="",k=false;for(let w of B)switch(w.type){case "minusSign":{i||(R+=w.value);break}case "integer":{k||(R+=E,k=true);break}case "group":case "decimal":case "fraction":break;case "currency":{o==="symbol"&&t.currency.symbol?R+=t.currency.symbol:R+=w.value;break}default:{R+=w.value;break}}return i&&m?`(${R.trim()})`:R}var y=class t{constructor(e,r){this.minor=e,this.currency=r;}static resolveCurrency(e){return typeof e=="string"?N(e):e}static fromMinor(e,r){return new t(BigInt(e),t.resolveCurrency(r))}static fromCents(e,r){return t.fromMinor(e,r)}static fromMajor(e,r){let n=t.resolveCurrency(r),o=H(e,n);return new t(o,n)}static fromDecimal(e,r){return t.fromMajor(e,r)}static fromFloat(e,r,n){!n?.suppressWarning&&process.env.NODE_ENV!=="production"&&console.warn('[monetra] Money.fromFloat() may lose precision. Consider using Money.fromMajor("'+e+'", currency) instead.');let o=t.resolveCurrency(r),i=10**o.decimals,s=Math.round(e*i);return new t(BigInt(s),o)}static min(...e){if(e.length===0)throw new Error("At least one Money value required");return e.reduce((r,n)=>(f(r,n),n.lessThan(r)?n:r))}static max(...e){if(e.length===0)throw new Error("At least one Money value required");return e.reduce((r,n)=>(f(r,n),n.greaterThan(r)?n:r))}static zero(e){return new t(0n,t.resolveCurrency(e))}resolveOther(e){return e instanceof t?e:typeof e=="string"?t.fromMajor(e,this.currency):t.fromMinor(e,this.currency)}add(e){let r=this.resolveOther(e);return f(this,r),new t(G(this.minor,r.minor),this.currency)}subtract(e){let r=this.resolveOther(e);return f(this,r),new t(Y(this.minor,r.minor),this.currency)}multiply(e,r){let n=j(this.minor,e,r?.rounding);return new t(n,this.currency)}divide(e,r){if(e===0||e==="0")throw new Error("Division by zero");let n=Z(this.minor,e,r?.rounding);return new t(n,this.currency)}abs(){return new t(this.minor<0n?-this.minor:this.minor,this.currency)}negate(){return new t(-this.minor,this.currency)}allocate(e){return W(this.minor,e).map(n=>new t(n,this.currency))}format(e){return q(this,e)}equals(e){let r=this.resolveOther(e);return this.currency.code===r.currency.code&&this.minor===r.minor}greaterThan(e){let r=this.resolveOther(e);return f(this,r),this.minor>r.minor}lessThan(e){let r=this.resolveOther(e);return f(this,r),this.minor<r.minor}greaterThanOrEqual(e){let r=this.resolveOther(e);return f(this,r),this.minor>=r.minor}lessThanOrEqual(e){let r=this.resolveOther(e);return f(this,r),this.minor<=r.minor}isPositive(){return this.minor>0n}compare(e){let r=this.resolveOther(e);return f(this,r),this.minor<r.minor?-1:this.minor>r.minor?1:0}percentage(e,r="HALF_EVEN"){return this.multiply(e/100,{rounding:r})}addPercent(e,r="HALF_EVEN"){return this.add(this.percentage(e,r))}subtractPercent(e,r="HALF_EVEN"){return this.subtract(this.percentage(e,r))}split(e){let r=Array(e).fill(1);return this.allocate(r)}isZero(){return this.minor===0n}isNegative(){return this.minor<0n}clamp(e,r){if(f(this,e),f(this,r),e.greaterThan(r))throw new Error("Clamp min cannot be greater than max");return this.lessThan(e)?new t(e.minor,this.currency):this.greaterThan(r)?new t(r.minor,this.currency):this}toDecimalString(){let e=this.currency.decimals,r=this.minor<0n,n=r?-this.minor:this.minor;if(e===0)return r?`-${n.toString()}`:n.toString();let o=10n**BigInt(e),i=n/o,l=(n%o).toString().padStart(e,"0"),m=`${i}.${l}`;return r?`-${m}`:m}toJSON(){return {amount:this.minor.toString(),currency:this.currency.code,precision:this.currency.decimals}}static reviver(e,r){if(r!==null&&typeof r=="object"&&"amount"in r&&"currency"in r&&"precision"in r&&typeof r.amount=="string"&&typeof r.currency=="string"&&typeof r.precision=="number"){let n=r;return t.fromMinor(BigInt(n.amount),n.currency)}return r}toString(){return this.format()}};var J=class{constructor(e,r){this.base=e,this.rates={...r},this.rates[e]===void 0&&(this.rates[e]=1);}convert(e,r){let n=typeof r=="string"?N(r):r;if(e.currency.code===n.code)return e;let o=this.rates[e.currency.code],i=this.rates[n.code];if(o===void 0||i===void 0)throw new Error(`Exchange rate missing for conversion from ${e.currency.code} to ${n.code}`);let s=i/o,l=10**(n.decimals-e.currency.decimals),m=s*l,a=e.multiply(m,{rounding:"HALF_EVEN"});return y.fromMinor(a.minor,n)}};var Q=class{constructor(){this.contents=new Map;}add(e){let r=e.currency.code,n=this.contents.get(r);n?this.contents.set(r,n.add(e)):this.contents.set(r,e);}subtract(e){let r=e.currency.code,n=this.contents.get(r);if(n)this.contents.set(r,n.subtract(e));else {let o=y.zero(e.currency);this.contents.set(r,o.subtract(e));}}get(e){let r=typeof e=="string"?e:e.code;return this.contents.get(r)||y.zero(typeof e=="string"?N(e):e)}total(e,r){let n=typeof e=="string"?N(e):e,o=y.zero(n);for(let i of this.contents.values()){let s=r.convert(i,n);o=o.add(s);}return o}getAll(){return Array.from(this.contents.values())}toJSON(){return this.getAll().map(e=>e.toJSON())}};var X={code:"USD",decimals:2,symbol:"$",locale:"en-US"};c(X);var ee={code:"EUR",decimals:2,symbol:"\u20AC",locale:"de-DE"};c(ee);var re={code:"GBP",decimals:2,symbol:"\xA3",locale:"en-GB"};c(re);var te={code:"JPY",decimals:0,symbol:"\xA5",locale:"ja-JP"};c(te);var ne={code:"CHF",decimals:2,symbol:"CHF",locale:"de-CH"};c(ne);var oe={code:"CAD",decimals:2,symbol:"CA$",locale:"en-CA"};c(oe);var ie={code:"AUD",decimals:2,symbol:"A$",locale:"en-AU"};c(ie);var se={code:"NZD",decimals:2,symbol:"NZ$",locale:"en-NZ"};c(se);var ce={code:"CNY",decimals:2,symbol:"\xA5",locale:"zh-CN"};c(ce);var ae={code:"HKD",decimals:2,symbol:"HK$",locale:"zh-HK"};c(ae);var ue={code:"SGD",decimals:2,symbol:"S$",locale:"en-SG"};c(ue);var le={code:"KRW",decimals:0,symbol:"\u20A9",locale:"ko-KR"};c(le);var me={code:"INR",decimals:2,symbol:"\u20B9",locale:"en-IN"};c(me);var de={code:"SEK",decimals:2,symbol:"kr",locale:"sv-SE"};c(de);var ye={code:"NOK",decimals:2,symbol:"kr",locale:"nb-NO"};c(ye);var pe={code:"DKK",decimals:2,symbol:"kr",locale:"da-DK"};c(pe);var fe={code:"PLN",decimals:2,symbol:"z\u0142",locale:"pl-PL"};c(fe);var ge={code:"CZK",decimals:2,symbol:"K\u010D",locale:"cs-CZ"};c(ge);var he={code:"HUF",decimals:2,symbol:"Ft",locale:"hu-HU"};c(he);var be={code:"RON",decimals:2,symbol:"lei",locale:"ro-RO"};c(be);var Me={code:"BGN",decimals:2,symbol:"\u043B\u0432",locale:"bg-BG"};c(Me);var Ce={code:"HRK",decimals:2,symbol:"kn",locale:"hr-HR"};c(Ce);var xe={code:"TRY",decimals:2,symbol:"\u20BA",locale:"tr-TR"};c(xe);var Ee={code:"RUB",decimals:2,symbol:"\u20BD",locale:"ru-RU"};c(Ee);var Re={code:"UAH",decimals:2,symbol:"\u20B4",locale:"uk-UA"};c(Re);var we={code:"ILS",decimals:2,symbol:"\u20AA",locale:"he-IL"};c(we);var Ne={code:"MXN",decimals:2,symbol:"MX$",locale:"es-MX"};c(Ne);var Ae={code:"BRL",decimals:2,symbol:"R$",locale:"pt-BR"};c(Ae);var ve={code:"ARS",decimals:2,symbol:"AR$",locale:"es-AR"};c(ve);var Se={code:"CLP",decimals:0,symbol:"CL$",locale:"es-CL"};c(Se);var Oe={code:"COP",decimals:2,symbol:"CO$",locale:"es-CO"};c(Oe);var De={code:"PEN",decimals:2,symbol:"S/",locale:"es-PE"};c(De);var Ie={code:"ZAR",decimals:2,symbol:"R",locale:"en-ZA"};c(Ie);var Te={code:"NGN",decimals:2,symbol:"\u20A6",locale:"en-NG"};c(Te);var Le={code:"KES",decimals:2,symbol:"KSh",locale:"en-KE"};c(Le);var Pe={code:"EGP",decimals:2,symbol:"E\xA3",locale:"ar-EG"};c(Pe);var He={code:"MAD",decimals:2,symbol:"\u062F.\u0645.",locale:"ar-MA"};c(He);var Fe={code:"GHS",decimals:2,symbol:"GH\u20B5",locale:"en-GH"};c(Fe);var Ue={code:"TZS",decimals:2,symbol:"TSh",locale:"sw-TZ"};c(Ue);var Ve={code:"UGX",decimals:0,symbol:"USh",locale:"en-UG"};c(Ve);var $e={code:"THB",decimals:2,symbol:"\u0E3F",locale:"th-TH"};c($e);var Be={code:"MYR",decimals:2,symbol:"RM",locale:"ms-MY"};c(Be);var ke={code:"IDR",decimals:2,symbol:"Rp",locale:"id-ID"};c(ke);var Ke={code:"PHP",decimals:2,symbol:"\u20B1",locale:"en-PH"};c(Ke);var ze={code:"VND",decimals:0,symbol:"\u20AB",locale:"vi-VN"};c(ze);var Ge={code:"TWD",decimals:2,symbol:"NT$",locale:"zh-TW"};c(Ge);var Ye={code:"PKR",decimals:2,symbol:"\u20A8",locale:"ur-PK"};c(Ye);var je={code:"BDT",decimals:2,symbol:"\u09F3",locale:"bn-BD"};c(je);var Ze={code:"LKR",decimals:2,symbol:"Rs",locale:"si-LK"};c(Ze);var _e={code:"AED",decimals:2,symbol:"\u062F.\u0625",locale:"ar-AE"};c(_e);var We={code:"SAR",decimals:2,symbol:"\uFDFC",locale:"ar-SA"};c(We);var qe={code:"QAR",decimals:2,symbol:"\uFDFC",locale:"ar-QA"};c(qe);var Je={code:"KWD",decimals:3,symbol:"\u062F.\u0643",locale:"ar-KW"};c(Je);var Qe={code:"BHD",decimals:3,symbol:"\u062F.\u0628",locale:"ar-BH"};c(Qe);var Xe={code:"OMR",decimals:3,symbol:"\uFDFC",locale:"ar-OM"};c(Xe);var er={code:"JOD",decimals:3,symbol:"\u062F.\u0627",locale:"ar-JO"};c(er);var rr={code:"ISK",decimals:0,symbol:"kr",locale:"is-IS"};c(rr);var tr={code:"MRU",decimals:2,symbol:"UM",locale:"ar-MR"};c(tr);var Ot={USD:X,EUR:ee,GBP:re,JPY:te,CHF:ne,CAD:oe,AUD:ie,NZD:se,CNY:ce,HKD:ae,SGD:ue,KRW:le,INR:me,SEK:de,NOK:ye,DKK:pe,PLN:fe,CZK:ge,HUF:he,RON:be,BGN:Me,HRK:Ce,TRY:xe,RUB:Ee,UAH:Re,ILS:we,MXN:Ne,BRL:Ae,ARS:ve,CLP:Se,COP:Oe,PEN:De,ZAR:Ie,NGN:Te,KES:Le,EGP:Pe,MAD:He,GHS:Fe,TZS:Ue,UGX:Ve,THB:$e,MYR:Be,IDR:ke,PHP:Ke,VND:ze,TWD:Ge,PKR:Ye,BDT:je,LKR:Ze,AED:_e,SAR:We,QAR:qe,KWD:Je,BHD:Qe,OMR:Xe,JOD:er,ISK:rr,MRU:tr};function It(t){return 10n**BigInt(t.decimals)}function lr(t){let{principal:e,annualRate:r,periods:n,periodsPerYear:o=12,rounding:i="HALF_EVEN"}=t,s=r/o,l;if(r===0)l=e.divide(n,{rounding:i});else {let u=s,d=n,g=u*Math.pow(1+u,d),M=Math.pow(1+u,d)-1,V=g/M;l=e.multiply(V,{rounding:i});}let m=[],a=e;for(let u=1;u<=n;u++){let d=r===0?y.zero(e.currency):a.multiply(s,{rounding:i}),g=l.subtract(d);if(a=a.subtract(g),u===n&&!a.isZero()){let M=g.add(a);m.push({period:u,payment:d.add(M),principal:M,interest:d,balance:y.zero(e.currency)});}else m.push({period:u,payment:l,principal:g,interest:d,balance:a.isNegative()?y.zero(e.currency):a});}return m}function mr(t){return lr(t)[0].payment}function Bt(t){let{principal:e,annualRate:r,periods:n,rounding:o="HALF_EVEN"}=t;return r===0?y.zero(e.currency):mr(t).multiply(n,{rounding:o}).subtract(e)}function kt(t){if(t.length===0)throw new Error("Schedule must have at least one entry");let e=t[0].interest.currency;return t.reduce((r,n)=>r.add(n.interest),y.zero(e))}function Kt(t){let{principal:e,annualRate:r,periodsPerYear:n=12,rounding:o="HALF_EVEN"}=t;if(r===0)return y.zero(e.currency);let i=r/n;return e.multiply(i,{rounding:o})}function dr(t,e){let{rate:r,years:n,compoundingPerYear:o=12,rounding:i="HALF_EVEN"}=e,s=o,l=n,m=Math.pow(1+r/s,s*l);return t.multiply(m,{rounding:i})}function yr(t,e){let{rate:r,years:n,compoundingPerYear:o=12,rounding:i="HALF_EVEN"}=e,s=o,l=n,m=Math.pow(1+r/s,s*l);return t.divide(m,{rounding:i})}var Yt=dr,jt=yr;function Qt(t,e){if(t.currency.code!==e.currency.code)throw new b(t.currency.code,e.currency.code);let r=e.subtract(t);return Number(r.minor)/Number(t.minor)}function Xt(t,e){if(e.length===0)throw new Error("At least one cash flow required");let r=e[0].currency,n=y.zero(r);for(let o=0;o<e.length;o++){let i=Math.pow(1+t,o),s=e[o].divide(i,{rounding:"HALF_EVEN"});n=n.add(s);}return n}function en(t,e=.1){let r=t.map(s=>Number(s.minor)),n=100,o=1e-7,i=e;for(let s=0;s<n;s++){let l=0,m=0;for(let u=0;u<r.length;u++){let d=Math.pow(1+i,u);l+=r[u]/d,u>0&&(m-=u*r[u]/Math.pow(1+i,u+1));}let a=i-l/m;if(Math.abs(a-i)<o)return a;i=a;}throw new Error("IRR calculation did not converge")}function rn(t,e){if(t.currency.code!==e.currency.code)throw new b(t.currency.code,e.currency.code);if(e.minor<=0n)throw new x("Current price must be positive");return Number(t.minor)/Number(e.minor)}var p=class p{constructor(e){this.decimalValue=e;}static percent(e){let r=typeof e=="string"?parseFloat(e):e,n=BigInt(Math.round(r/100*Number(p.SCALE)));return new p(n)}static decimal(e){let r=typeof e=="string"?parseFloat(e):e,n=BigInt(Math.round(r*Number(p.SCALE)));return new p(n)}static zero(){return new p(0n)}toPercent(){return Number(this.decimalValue)/Number(p.SCALE)*100}toDecimal(){return Number(this.decimalValue)/Number(p.SCALE)}add(e){return new p(this.decimalValue+e.decimalValue)}subtract(e){return new p(this.decimalValue-e.decimalValue)}multiply(e){let r=BigInt(Math.round(e*Number(p.SCALE)));return new p(this.decimalValue*r/p.SCALE)}divide(e){let r=BigInt(Math.round(e*Number(p.SCALE)));return new p(this.decimalValue*p.SCALE/r)}equals(e){return this.decimalValue===e.decimalValue}greaterThan(e){return this.decimalValue>e.decimalValue}lessThan(e){return this.decimalValue<e.decimalValue}isZero(){return this.decimalValue===0n}isNegative(){return this.decimalValue<0n}toString(){return `${this.toPercent()}%`}compoundFactor(e){return Math.pow(1+this.toDecimal(),e)}periodic(e){return this.divide(e)}toNominal(e){let r=this.toDecimal(),n=e*(Math.pow(1+r,1/e)-1);return p.decimal(n)}toEffective(e){let r=this.toDecimal(),n=Math.pow(1+r/e,e)-1;return p.decimal(n)}toJSON(){return {percent:this.toPercent(),decimal:this.toDecimal()}}};p.SCALE=10n**18n;var nr=p;function cn(t,e){let{rate:r,years:n,rounding:o="HALF_EVEN"}=e;if(r.isZero()||n===0)return y.zero(t.currency);let i=r.toDecimal()*n;return t.multiply(i,{rounding:o})}function an(t,e){let{rate:r,years:n,rounding:o="HALF_EVEN"}=e;if(r.isZero()||n===0)return t;let i=1+r.toDecimal()*n;return t.multiply(i,{rounding:o})}function pr(t,e){if(f(t,e),e.isZero())throw new Error("Total equity cannot be zero");return Number(t.minor)/Number(e.minor)}function fr(t,e){if(f(t,e),e.isZero())throw new Error("Total assets cannot be zero");return Number(t.minor)/Number(e.minor)}function gr(t,e){return f(t,e),e.isZero()?1/0:Number(t.minor)/Number(e.minor)}function hr(t,e){if(f(t,e),e.isZero())throw new Error("Total equity cannot be zero");return Number(t.minor)/Number(e.minor)}function mn(t){return {debtToEquity:pr(t.totalDebt,t.totalEquity),debtToAssets:fr(t.totalDebt,t.totalAssets),interestCoverage:gr(t.ebit,t.interestExpense),equityMultiplier:hr(t.totalAssets,t.totalEquity)}}function gn(t){let{cost:e,salvageValue:r,usefulLife:n,rounding:o="HALF_EVEN"}=t;if(e.currency.code!==r.currency.code)throw new b(e.currency.code,r.currency.code);if(n<=0)throw new x("Useful life must be positive");if(r.greaterThan(e))throw new x("Salvage value cannot be greater than cost");let s=e.subtract(r).divide(n,{rounding:o}),l={annualDepreciation:s,bookValueAtYear(m){if(m<0)throw new x("Year must be non-negative");if(m===0)return e;let a=s.multiply(m),u=e.subtract(a);return u.lessThan(r)?r:u},schedule(){let m=[],a=e,u=Math.ceil(n);for(let d=1;d<=u;d++){let g=l.bookValueAtYear(d),M=a.subtract(g);m.push({year:d,depreciation:M,bookValue:g}),a=g;}return m}};return l}var v=null;function br(){if(v)return v;if(!(globalThis.__MONETRA_DISABLE_NODE_CRYPTO__===true)&&typeof globalThis<"u")try{let e=ar("crypto");if(e&&e.createHash)return v=r=>e.createHash("sha256").update(r).digest("hex"),v}catch{}if(typeof globalThis<"u"&&globalThis.crypto&&globalThis.crypto.subtle)return v=async e=>{let n=new TextEncoder().encode(e),o=await globalThis.crypto.subtle.digest("SHA-256",n);return Array.from(new Uint8Array(o)).map(s=>s.toString(16).padStart(2,"0")).join("")},v;throw new Error("No cryptographic hash function available. Ensure you are running in Node.js or a browser with SubtleCrypto support.")}function An(t){v=t;}function Mr(t){return JSON.stringify(t,(e,r)=>typeof r=="bigint"?r.toString():r&&typeof r=="object"&&"minor"in r&&"currency"in r?{minor:r.minor.toString(),currency:r.currency.code}:r)}function O(t){let e=Mr(t);return br()(e)}function T(t){let e=O(t);if(e instanceof Promise)throw new Error("Synchronous hashing not available in this environment. Use generateHash() with await instead.");return e}async function F(t){for(let e=0;e<t.length;e++){let r=t[e],n=e>0?t[e-1]:null;if(n){if(r.previousHash!==n.hash)return false}else if(r.previousHash!==null)return false;let{hash:o,...i}=r,s=await O(i);if(o!==s)return false}return true}function U(t){for(let e=0;e<t.length;e++){let r=t[e],n=e>0?t[e-1]:null;if(n){if(r.previousHash!==n.hash)return false}else if(r.previousHash!==null)return false;let{hash:o,...i}=r,s=T(i);if(o!==s)return false}return true}var ir=2,sr=class t{constructor(e){this.entries=[];this.currency=typeof e=="string"?e:e.code;}record(e,r){if(e.currency.code!==this.currency)throw new b(this.currency,e.currency.code);let n=this.entries.length>0?this.entries[this.entries.length-1].hash:null,o={id:randomUUID(),money:e,metadata:Object.freeze({...r,timestamp:r.timestamp??new Date}),createdAt:new Date,previousHash:n},i=T(o),s={...o,hash:i};return this.entries.push(Object.freeze(s)),s}async recordAsync(e,r){if(e.currency.code!==this.currency)throw new b(this.currency,e.currency.code);let n=this.entries.length>0?this.entries[this.entries.length-1].hash:null,o={id:randomUUID(),money:e,metadata:Object.freeze({...r,timestamp:r.timestamp??new Date}),createdAt:new Date,previousHash:n},i=await O(o),s={...o,hash:i};return this.entries.push(Object.freeze(s)),s}getBalance(){return this.entries.reduce((e,r)=>e.add(r.money),y.zero(this.currency))}getHistory(){return Object.freeze([...this.entries])}query(e){return this.entries.filter(r=>!(e.type&&!(Array.isArray(e.type)?e.type:[e.type]).includes(r.metadata.type)||e.from&&r.createdAt<e.from||e.to&&r.createdAt>e.to||e.reference&&r.metadata.reference!==e.reference||e.minAmount&&r.money.lessThan(e.minAmount)||e.maxAmount&&r.money.greaterThan(e.maxAmount)||e.tags&&r.metadata.tags&&!e.tags.some(o=>r.metadata.tags?.includes(o))))}verify(){return U(this.entries)}async verifyAsync(){return F(this.entries)}snapshot(){return {version:ir,entries:[...this.entries],balance:this.getBalance(),currency:this.currency,createdAt:new Date,checksum:T({entries:this.entries})}}async snapshotAsync(){let e=await O({entries:this.entries});return {version:ir,entries:[...this.entries],balance:this.getBalance(),currency:this.currency,createdAt:new Date,checksum:e}}static fromSnapshot(e){let r=new t(e.currency);if(!U(e.entries))throw new Error("Ledger snapshot integrity check failed");return r.entries=[...e.entries],r}static async fromSnapshotAsync(e){let r=new t(e.currency);if(!await F(e.entries))throw new Error("Ledger snapshot integrity check failed");return r.entries=[...e.entries],r}};function Vn(t,e){return typeof t=="string"?y.fromMajor(t,e):y.fromMinor(t,e)}export{_e as AED,ve as ARS,ie as AUD,je as BDT,Me as BGN,Qe as BHD,Ae as BRL,oe as CAD,ne as CHF,Se as CLP,ce as CNY,Oe as COP,Ot as CURRENCIES,ge as CZK,J as Converter,b as CurrencyMismatchError,pe as DKK,Pe as EGP,ee as EUR,re as GBP,Fe as GHS,ae as HKD,Ce as HRK,he as HUF,ke as IDR,we as ILS,me as INR,rr as ISK,K as InsufficientFundsError,x as InvalidArgumentError,I as InvalidPrecisionError,er as JOD,te as JPY,Le as KES,le as KRW,Je as KWD,Ze as LKR,sr as Ledger,He as MAD,tr as MRU,Ne as MXN,Be as MYR,h as MonetraError,A as MonetraErrorCode,y as Money,Q as MoneyBag,Te as NGN,ye as NOK,se as NZD,Xe as OMR,z as OverflowError,De as PEN,Ke as PHP,Ye as PKR,fe as PLN,qe as QAR,be as RON,Ee as RUB,nr as Rate,C as RoundingMode,S as RoundingRequiredError,We as SAR,de as SEK,ue as SGD,$e as THB,xe as TRY,Ge as TWD,Ue as TZS,Re as UAH,Ve as UGX,X as USD,ze as VND,Ie as ZAR,_r as assertNonNegative,f as assertSameCurrency,Yt as compound,rn as currentYield,fr as debtToAssets,pr as debtToEquity,jt as discount,P as divideWithRounding,hr as equityMultiplier,q as format,dr as futureValue,O as generateHash,T as generateHashSync,Er as getAllCurrencies,N as getCurrency,It as getMinorUnitExponent,gr as interestCoverage,Kt as interestOnlyPayment,en as irr,xr as isCurrencyRegistered,mn as leverageRatios,lr as loan,Vn as money,Xt as npv,ur as parseLocaleString,it as parseLocaleToMinor,H as parseToMinor,mr as pmt,yr as presentValue,c as registerCurrency,Qt as roi,An as setHashFunction,cn as simpleInterest,an as simpleInterestTotal,gn as straightLineDepreciation,Bt as totalInterest,kt as totalInterestFromSchedule,F as verifyChain,U as verifyChainSync};