monetra 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors/BaseError.ts","../src/errors/CurrencyMismatchError.ts","../src/errors/InvalidPrecisionError.ts","../src/errors/RoundingRequiredError.ts","../src/errors/InsufficientFundsError.ts","../src/errors/OverflowError.ts","../src/money/guards.ts","../src/rounding/strategies.ts","../src/rounding/index.ts","../src/money/arithmetic.ts","../src/money/allocation.ts","../src/format/parser.ts","../src/format/formatter.ts","../src/money/Money.ts","../src/currency/iso4217.ts","../src/currency/precision.ts"],"sourcesContent":["export class MonetraError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class CurrencyMismatchError extends MonetraError {\n constructor(expected: string, actual: string) {\n super(`Currency mismatch: expected ${expected}, got ${actual}`);\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class InvalidPrecisionError extends MonetraError {\n constructor(message: string) {\n super(message);\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class RoundingRequiredError extends MonetraError {\n constructor() {\n super('Rounding is required for this operation but was not provided.');\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class InsufficientFundsError extends MonetraError {\n constructor() {\n super('Insufficient funds for this operation.');\n }\n}\n","import { MonetraError } from './BaseError';\n\nexport class OverflowError extends MonetraError {\n constructor(message: string = 'Arithmetic overflow') {\n super(message);\n }\n}\n","import { Money } from './Money';\nimport { CurrencyMismatchError, InsufficientFundsError } from '../errors';\n\nexport function assertSameCurrency(a: Money, b: Money): void {\n if (a.currency.code !== b.currency.code) {\n throw new CurrencyMismatchError(a.currency.code, b.currency.code);\n }\n}\n\nexport function assertNonNegative(money: Money): void {\n if (money.isNegative()) {\n // Spec doesn't explicitly name a NegativeError, but InsufficientFundsError might fit or just a generic error.\n // Spec lists \"InsufficientFundsError\" in 4.7.\n // But \"assertNonNegative\" is a guard.\n // Let's throw an Error or a specific one if defined.\n // \"Any violation is considered a bug\" -> maybe just Error?\n // But for \"InsufficientFunds\", that's usually when subtracting.\n // Let's use a generic Error for now or create a specific one if needed.\n throw new Error('Money value must be non-negative');\n }\n}\n","/**\n * Enumeration of rounding modes for handling fractional minor units.\n */\nexport enum RoundingMode {\n /**\n * Rounds towards the nearest neighbor. If equidistant, rounds away from zero.\n * Example: 2.5 -> 3, -2.5 -> -3\n */\n HALF_UP = 'HALF_UP',\n\n /**\n * Rounds towards the nearest neighbor. If equidistant, rounds towards zero.\n * Example: 2.5 -> 2, -2.5 -> -2\n */\n HALF_DOWN = 'HALF_DOWN',\n\n /**\n * Rounds towards the nearest neighbor. If equidistant, rounds towards the nearest even integer.\n * Also known as Banker's Rounding.\n * Example: 2.5 -> 2, 3.5 -> 4\n */\n HALF_EVEN = 'HALF_EVEN',\n\n /**\n * Rounds towards negative infinity.\n * Example: 2.9 -> 2, -2.1 -> -3\n */\n FLOOR = 'FLOOR',\n\n /**\n * Rounds towards positive infinity.\n * Example: 2.1 -> 3, -2.9 -> -2\n */\n CEIL = 'CEIL',\n}\n","import { RoundingMode } from './strategies';\n\nexport * from './strategies';\n\nexport function divideWithRounding(\n numerator: bigint,\n denominator: bigint,\n mode: RoundingMode\n): bigint {\n if (denominator === 0n) {\n throw new Error('Division by zero');\n }\n\n const quotient = numerator / denominator;\n const remainder = numerator % denominator;\n\n if (remainder === 0n) {\n return quotient;\n }\n\n const sign = (numerator >= 0n ? 1n : -1n) * (denominator >= 0n ? 1n : -1n);\n const absRemainder = remainder < 0n ? -remainder : remainder;\n const absDenominator = denominator < 0n ? -denominator : denominator;\n\n // Check for exact half\n const isHalf = absRemainder * 2n === absDenominator;\n const isMoreThanHalf = absRemainder * 2n > absDenominator;\n\n switch (mode) {\n case RoundingMode.FLOOR:\n // If positive, quotient is already floor (truncation).\n // If negative, quotient is ceil (truncation towards zero), so we need to subtract 1.\n return sign > 0n ? quotient : quotient - 1n;\n case RoundingMode.CEIL:\n // If positive, quotient is floor, so add 1.\n // If negative, quotient is ceil, so keep it.\n return sign > 0n ? quotient + 1n : quotient;\n case RoundingMode.HALF_UP:\n if (isMoreThanHalf || isHalf) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n return quotient;\n case RoundingMode.HALF_DOWN:\n if (isMoreThanHalf) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n return quotient;\n case RoundingMode.HALF_EVEN:\n if (isMoreThanHalf) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n if (isHalf) {\n // If quotient is odd, round up (away from zero? no, to nearest even).\n // If quotient is even, keep it.\n if (quotient % 2n !== 0n) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n }\n return quotient;\n default:\n throw new Error(`Unsupported rounding mode: ${mode}`);\n }\n}\n","import { RoundingMode, divideWithRounding } from '../rounding';\nimport { RoundingRequiredError } from '../errors';\n\n/**\n * Adds two BigInt values.\n */\nexport function add(a: bigint, b: bigint): bigint {\n return a + b;\n}\n\n/**\n * Subtracts two BigInt values.\n */\nexport function subtract(a: bigint, b: bigint): bigint {\n return a - b;\n}\n\n/**\n * Multiplies a BigInt amount by a multiplier.\n * \n * Handles fractional multipliers by converting them to a rational number (numerator/denominator).\n * If the result is not an integer, a rounding mode must be provided.\n * \n * @param amount - The amount to multiply.\n * @param multiplier - The multiplier (number or string).\n * @param rounding - Optional rounding mode.\n * @returns The result as a BigInt.\n * @throws {RoundingRequiredError} If rounding is needed but not provided.\n */\nexport function multiply(\n amount: bigint,\n multiplier: string | number,\n rounding?: RoundingMode\n): bigint {\n const { numerator, denominator } = parseMultiplier(multiplier);\n \n // result = amount * (numerator / denominator)\n // result = (amount * numerator) / denominator\n \n const product = amount * numerator;\n \n if (product % denominator === 0n) {\n return product / denominator;\n }\n \n if (!rounding) {\n throw new RoundingRequiredError();\n }\n \n return divideWithRounding(product, denominator, rounding);\n}\n\nfunction parseMultiplier(multiplier: string | number): { numerator: bigint; denominator: bigint } {\n const s = multiplier.toString();\n \n // Check for scientific notation\n if (/[eE]/.test(s)) {\n throw new Error('Scientific notation not supported');\n }\n \n const parts = s.split('.');\n if (parts.length > 2) {\n throw new Error('Invalid number format');\n }\n \n const integerPart = parts[0];\n const fractionalPart = parts[1] || '';\n \n const denominator = 10n ** BigInt(fractionalPart.length);\n const numerator = BigInt(integerPart + fractionalPart);\n \n return { numerator, denominator };\n}\n","/**\n * Allocates a monetary amount according to a list of ratios.\n * \n * Uses the \"Largest Remainder Method\" to ensure that the sum of the allocated\n * parts equals the original amount. Remainders are distributed to the parts\n * that had the largest fractional remainders during the division.\n * \n * @param amount - The total amount to allocate (in minor units).\n * @param ratios - An array of ratios (e.g., [1, 1] for 50/50 split).\n * @returns An array of allocated amounts (in minor units).\n * @throws {Error} If ratios are empty or total ratio is zero.\n */\nexport function allocate(amount: bigint, ratios: number[]): bigint[] {\n if (ratios.length === 0) {\n throw new Error('Cannot allocate to empty ratios');\n }\n\n // Scale ratios to integers\n const scaledRatios = ratios.map(r => {\n const s = r.toString();\n if (/[eE]/.test(s)) throw new Error('Scientific notation not supported');\n const parts = s.split('.');\n const decimals = parts[1] ? parts[1].length : 0;\n const value = BigInt(parts[0] + (parts[1] || ''));\n return { value, decimals };\n });\n\n const maxDecimals = Math.max(...scaledRatios.map(r => r.decimals));\n\n const normalizedRatios = scaledRatios.map(r => {\n const factor = 10n ** BigInt(maxDecimals - r.decimals);\n return r.value * factor;\n });\n\n const total = normalizedRatios.reduce((sum, r) => sum + r, 0n);\n\n if (total === 0n) {\n throw new Error('Total ratio must be greater than zero');\n }\n\n const results: { share: bigint; remainder: bigint; index: number }[] = [];\n let allocatedTotal = 0n;\n\n for (let i = 0; i < normalizedRatios.length; i++) {\n const ratio = normalizedRatios[i];\n const share = (amount * ratio) / total;\n const remainder = (amount * ratio) % total;\n \n results.push({ share, remainder, index: i });\n allocatedTotal += share;\n }\n\n let leftOver = amount - allocatedTotal;\n\n // Distribute leftover to those with largest remainder\n // Sort by remainder desc\n results.sort((a, b) => {\n if (b.remainder > a.remainder) return 1;\n if (b.remainder < a.remainder) return -1;\n return 0;\n });\n\n for (let i = 0; i < Number(leftOver); i++) {\n results[i].share += 1n;\n }\n\n // Sort back by index\n results.sort((a, b) => a.index - b.index);\n\n return results.map(r => r.share);\n}\n","import { Currency } from '../currency/Currency';\nimport { InvalidPrecisionError } from '../errors';\n\n/**\n * Parses a string representation of a major unit amount into minor units.\n * \n * Validates the input format to ensure it is a valid decimal number without\n * scientific notation or ambiguous characters. Checks that the precision\n * does not exceed the currency's allowed decimals.\n * \n * @param amount - The amount string (e.g., \"10.50\").\n * @param currency - The currency to validate against.\n * @returns The amount in minor units as a BigInt.\n * @throws {Error} If the format is invalid (scientific notation, non-numeric chars).\n * @throws {InvalidPrecisionError} If the precision exceeds the currency's decimals.\n */\nexport function parseToMinor(amount: string, currency: Currency): bigint {\n // Validate format\n if (/[eE]/.test(amount)) {\n throw new Error('Scientific notation not supported');\n }\n \n // Reject ambiguous separators (commas, spaces, etc.)\n if (/[^0-9.-]/.test(amount)) {\n throw new Error('Invalid characters in amount');\n }\n \n const parts = amount.split('.');\n if (parts.length > 2) {\n throw new Error('Invalid format: multiple decimal points');\n }\n \n const integerPart = parts[0];\n const fractionalPart = parts[1] || '';\n \n if (fractionalPart.length > currency.decimals) {\n throw new InvalidPrecisionError(\n `Precision ${fractionalPart.length} exceeds currency decimals ${currency.decimals}`\n );\n }\n \n // Pad fractional part\n const paddedFractional = fractionalPart.padEnd(currency.decimals, '0');\n \n const combined = integerPart + paddedFractional;\n \n // Handle edge case where integer part is just \"-\"\n if (combined === '-' || combined === '') {\n throw new Error('Invalid format');\n }\n \n return BigInt(combined);\n}\n","import { Money } from '../money/Money';\n\n/**\n * Options for formatting Money values.\n */\nexport interface FormatOptions {\n /**\n * The locale to use for formatting (e.g., \"en-US\", \"de-DE\").\n * If not provided, defaults to the currency's default locale or \"en-US\".\n */\n locale?: string;\n\n /**\n * Whether to include the currency symbol in the output.\n * Defaults to true.\n */\n symbol?: boolean;\n}\n\n/**\n * Formats a Money object into a string representation.\n * \n * Uses `Intl.NumberFormat` for locale-aware formatting of numbers and currency symbols.\n * \n * @param money - The Money object to format.\n * @param options - Formatting options.\n * @returns The formatted string.\n */\nexport function format(money: Money, options?: FormatOptions): string {\n const locale = options?.locale || money.currency.locale || 'en-US';\n const showSymbol = options?.symbol ?? true;\n \n const decimals = money.currency.decimals;\n const minor = money.minor;\n const absMinor = minor < 0n ? -minor : minor;\n \n const divisor = 10n ** BigInt(decimals);\n const integerPart = absMinor / divisor;\n const fractionalPart = absMinor % divisor;\n \n // Pad fractional\n const fractionalStr = fractionalPart.toString().padStart(decimals, '0');\n \n // Get separators\n // We use a dummy format to extract the decimal separator for the locale\n const parts = new Intl.NumberFormat(locale, {\n style: 'decimal',\n minimumFractionDigits: 1,\n }).formatToParts(1.1);\n \n const decimalSeparator = parts.find(p => p.type === 'decimal')?.value || '.';\n \n // Format integer part with grouping using Intl (supports BigInt)\n const integerFormatted = new Intl.NumberFormat(locale, {\n style: 'decimal',\n useGrouping: true,\n }).format(integerPart);\n \n const absString = decimals > 0 \n ? `${integerFormatted}${decimalSeparator}${fractionalStr}`\n : integerFormatted;\n \n if (!showSymbol) {\n // If no symbol, we just return the number with sign\n // But we should respect locale sign format? \n // Usually just \"-\" is fine for \"no symbol\" raw format, but spec says \"Locale-aware formatting\".\n // If we disable symbol, we probably just want the number.\n return minor < 0n ? `-${absString}` : absString;\n }\n\n // Use formatToParts to get the template (sign position, currency position)\n // We use 1 or -1 to get the sign/currency pattern\n const templateParts = new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: money.currency.code,\n }).formatToParts(minor < 0n ? -1 : 1);\n \n let result = '';\n let numberInserted = false;\n \n for (const part of templateParts) {\n if (['integer', 'group', 'decimal', 'fraction'].includes(part.type)) {\n if (!numberInserted) {\n result += absString;\n numberInserted = true;\n }\n } else if (part.type === 'currency') {\n // Use the symbol from currency metadata if available, or the one from Intl\n // Spec says \"Configurable grouping and symbol display\".\n // We'll use the one from Intl usually, but Money.currency.symbol is available.\n // Let's use Intl's symbol to match the locale, unless it's wrong?\n // Spec: \"symbol: 'R'\".\n // If Intl returns \"ZAR\", we might want \"R\".\n // Let's prefer the one from Intl for consistency with locale, \n // but if Intl gives code (e.g. \"USD\" instead of \"$\"), maybe we want symbol.\n // Usually Intl gives symbol.\n result += part.value;\n } else {\n result += part.value; // literals, minusSign, parentheses, etc.\n }\n }\n \n return result;\n}\n","import { Currency } from '../currency/Currency';\nimport { RoundingMode } from '../rounding/strategies';\nimport { assertSameCurrency } from './guards';\nimport { add, subtract, multiply } from './arithmetic';\nimport { allocate } from './allocation';\nimport { parseToMinor } from '../format/parser';\nimport { format } from '../format/formatter';\n\n/**\n * Represents a monetary value in a specific currency.\n * \n * Money objects are immutable and store values in minor units (e.g., cents) using BigInt\n * to avoid floating-point precision errors.\n */\nexport class Money {\n /**\n * The amount in minor units (e.g., cents).\n */\n readonly minor: bigint;\n\n /**\n * The currency of this money value.\n */\n readonly currency: Currency;\n\n private constructor(minor: bigint, currency: Currency) {\n this.minor = minor;\n this.currency = currency;\n }\n\n /**\n * Creates a Money instance from minor units (e.g., cents).\n * \n * @param minor - The amount in minor units. Can be a number or BigInt.\n * @param currency - The currency of the money.\n * @returns A new Money instance.\n * @example\n * const m = Money.fromMinor(100, USD); // $1.00\n */\n static fromMinor(minor: bigint | number, currency: Currency): Money {\n return new Money(BigInt(minor), currency);\n }\n\n /**\n * Creates a Money instance from major units (e.g., \"10.50\").\n * \n * @param amount - The amount as a string. Must be a valid decimal number.\n * @param currency - The currency of the money.\n * @returns A new Money instance.\n * @throws {InvalidPrecisionError} If the amount has more decimal places than the currency allows.\n * @example\n * const m = Money.fromMajor(\"10.50\", USD); // $10.50\n */\n static fromMajor(amount: string, currency: Currency): Money {\n const minor = parseToMinor(amount, currency);\n return new Money(minor, currency);\n }\n\n /**\n * Creates a Money instance representing zero in the given currency.\n * \n * @param currency - The currency.\n * @returns A new Money instance with value 0.\n */\n static zero(currency: Currency): Money {\n return new Money(0n, currency);\n }\n\n /**\n * Adds another Money value to this one.\n * \n * @param other - The Money value to add.\n * @returns A new Money instance representing the sum.\n * @throws {CurrencyMismatchError} If the currencies do not match.\n */\n add(other: Money): Money {\n assertSameCurrency(this, other);\n return new Money(add(this.minor, other.minor), this.currency);\n }\n\n /**\n * Subtracts another Money value from this one.\n * \n * @param other - The Money value to subtract.\n * @returns A new Money instance representing the difference.\n * @throws {CurrencyMismatchError} If the currencies do not match.\n */\n subtract(other: Money): Money {\n assertSameCurrency(this, other);\n return new Money(subtract(this.minor, other.minor), this.currency);\n }\n\n /**\n * Multiplies this Money value by a scalar.\n * \n * @param multiplier - The number to multiply by.\n * @param options - Options for rounding if the result is not an integer.\n * @returns A new Money instance representing the product.\n * @throws {RoundingRequiredError} If the result is fractional and no rounding mode is provided.\n */\n multiply(multiplier: string | number, options?: { rounding?: RoundingMode }): Money {\n const result = multiply(this.minor, multiplier, options?.rounding);\n return new Money(result, this.currency);\n }\n\n /**\n * Allocates (splits) this Money value according to a list of ratios.\n * \n * The sum of the parts will always equal the original amount.\n * Remainders are distributed to the parts with the largest fractional remainders.\n * \n * @param ratios - A list of numbers representing the ratios to split by.\n * @returns An array of Money instances.\n */\n allocate(ratios: number[]): Money[] {\n const shares = allocate(this.minor, ratios);\n return shares.map(share => new Money(share, this.currency));\n }\n\n /**\n * Formats this Money value as a string.\n * \n * @param options - Formatting options.\n * @returns The formatted string.\n */\n format(options?: { locale?: string; symbol?: boolean }): string {\n return format(this, options);\n }\n\n /**\n * Checks if this Money value is equal to another.\n * \n * @param other - The other Money value.\n * @returns True if amounts and currencies are equal.\n */\n equals(other: Money): boolean {\n return this.currency.code === other.currency.code && this.minor === other.minor;\n }\n\n /**\n * Checks if this Money value is greater than another.\n * \n * @param other - The other Money value.\n * @returns True if this value is greater.\n * @throws {CurrencyMismatchError} If the currencies do not match.\n */\n greaterThan(other: Money): boolean {\n assertSameCurrency(this, other);\n return this.minor > other.minor;\n }\n\n /**\n * Checks if this Money value is less than another.\n * \n * @param other - The other Money value.\n * @returns True if this value is less.\n * @throws {CurrencyMismatchError} If the currencies do not match.\n */\n lessThan(other: Money): boolean {\n assertSameCurrency(this, other);\n return this.minor < other.minor;\n }\n\n /**\n * Checks if this Money value is zero.\n * \n * @returns True if the amount is zero.\n */\n isZero(): boolean {\n return this.minor === 0n;\n }\n\n /**\n * Checks if this Money value is negative.\n * \n * @returns True if the amount is negative.\n */\n isNegative(): boolean {\n return this.minor < 0n;\n }\n}\n","import { Currency } from './Currency';\n\n/**\n * United States Dollar (USD).\n */\nexport const USD: Currency = {\n code: 'USD',\n decimals: 2,\n symbol: '$',\n locale: 'en-US',\n};\n\n/**\n * Euro (EUR).\n */\nexport const EUR: Currency = {\n code: 'EUR',\n decimals: 2,\n symbol: '€',\n locale: 'de-DE', // Default locale, can be overridden\n};\n\n/**\n * British Pound Sterling (GBP).\n */\nexport const GBP: Currency = {\n code: 'GBP',\n decimals: 2,\n symbol: '£',\n locale: 'en-GB',\n};\n\n/**\n * Japanese Yen (JPY).\n */\nexport const JPY: Currency = {\n code: 'JPY',\n decimals: 0,\n symbol: '¥',\n locale: 'ja-JP',\n};\n\n/**\n * South African Rand (ZAR).\n */\nexport const ZAR: Currency = {\n code: 'ZAR',\n decimals: 2,\n symbol: 'R',\n locale: 'en-ZA',\n};\n\n/**\n * A collection of common currencies.\n */\nexport const CURRENCIES: Record<string, Currency> = {\n USD,\n EUR,\n GBP,\n JPY,\n ZAR,\n};\n\n/**\n * Retrieves a Currency object by its ISO 4217 code.\n * \n * @param code - The currency code (case-insensitive).\n * @returns The Currency object corresponding to the code.\n * @throws {Error} If the currency code is not found in the registry.\n */\nexport function getCurrency(code: string): Currency {\n const currency = CURRENCIES[code.toUpperCase()];\n if (!currency) {\n // Fallback or error? Spec says \"Currency Defines Precision\".\n // If unknown, we might need to throw or allow custom registration.\n // For now, let's throw if not found, or allow creating one on the fly if we want to be flexible.\n // But spec says \"Currency Module... Defines currency metadata\".\n // Let's assume for now we only support known ones or user must provide the object.\n throw new Error(`Unknown currency code: ${code}`);\n }\n return currency;\n}\n","import { Currency } from './Currency';\n\nexport function getMinorUnitExponent(currency: Currency): bigint {\n return 10n ** BigInt(currency.decimals);\n}\n"],"mappings":"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","names":["RoundingMode"]}
1
+ {"version":3,"sources":["../src/currency/registry.ts","../src/rounding/strategies.ts","../src/errors/BaseError.ts","../src/errors/CurrencyMismatchError.ts","../src/errors/InvalidPrecisionError.ts","../src/errors/RoundingRequiredError.ts","../src/errors/InsufficientFundsError.ts","../src/errors/OverflowError.ts","../src/money/guards.ts","../src/rounding/index.ts","../src/money/arithmetic.ts","../src/money/allocation.ts","../src/format/parser.ts","../src/format/formatter.ts","../src/money/Money.ts","../src/money/Converter.ts","../src/money/MoneyBag.ts","../src/currency/iso4217.ts","../src/currency/precision.ts","../src/index.ts"],"sourcesContent":["import { Currency } from \"./Currency\";\n\nconst registry: Map<string, Currency> = new Map();\n\n/**\n * Registers a currency in the global registry.\n * @param currency The currency to register.\n */\nexport function registerCurrency(currency: Currency): void {\n registry.set(currency.code, currency);\n}\n\n/**\n * Retrieves a currency by its code.\n * @param code The currency code (e.g., \"USD\").\n * @returns The Currency object.\n * @throws Error if the currency is not found.\n */\nexport function getCurrency(code: string): Currency {\n const currency = registry.get(code);\n if (!currency) {\n throw new Error(`Currency '${code}' not found in registry.`);\n }\n return currency;\n}\n\n/**\n * Checks if a currency is registered.\n */\nexport function isCurrencyRegistered(code: string): boolean {\n return registry.has(code);\n}\n","/**\n * Enumeration of rounding modes for handling fractional minor units.\n */\nexport enum RoundingMode {\n /**\n * Rounds towards the nearest neighbor. If equidistant, rounds away from zero.\n * Example: 2.5 -> 3, -2.5 -> -3\n */\n HALF_UP = \"HALF_UP\",\n\n /**\n * Rounds towards the nearest neighbor. If equidistant, rounds towards zero.\n * Example: 2.5 -> 2, -2.5 -> -2\n */\n HALF_DOWN = \"HALF_DOWN\",\n\n /**\n * Rounds towards the nearest neighbor. If equidistant, rounds towards the nearest even integer.\n * Also known as Banker's Rounding.\n * Example: 2.5 -> 2, 3.5 -> 4\n */\n HALF_EVEN = \"HALF_EVEN\",\n\n /**\n * Rounds towards negative infinity.\n * Example: 2.9 -> 2, -2.1 -> -3\n */\n FLOOR = \"FLOOR\",\n\n /**\n * Rounds towards positive infinity.\n * Example: 2.1 -> 3, -2.9 -> -2\n */\n CEIL = \"CEIL\",\n}\n","export class MonetraError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { MonetraError } from \"./BaseError\";\n\nexport class CurrencyMismatchError extends MonetraError {\n constructor(expected: string, received: string) {\n super(\n `Currency mismatch: expected ${expected}, received ${received}.\\n` +\n `💡 Tip: Use a Converter to convert between currencies:\\n` +\n ` const converter = new Converter('USD', { ${received}: rate });\\n` +\n ` const converted = converter.convert(money, '${expected}');`\n );\n }\n}\n","import { MonetraError } from \"./BaseError\";\n\nexport class InvalidPrecisionError extends MonetraError {\n constructor(message: string) {\n super(message);\n }\n}\n","import { MonetraError } from \"./BaseError\";\n\nexport class RoundingRequiredError extends MonetraError {\n constructor(operation?: string, result?: number) {\n let message = \"Rounding is required for this operation but was not provided.\";\n if (operation && result !== undefined) {\n message =\n `Rounding required for ${operation}: result ${result} is not an integer.\\n` +\n `💡 Tip: Provide a rounding mode:\\n` +\n ` money.${operation}(value, { rounding: RoundingMode.HALF_UP })\\n` +\n ` Available modes: HALF_UP, HALF_DOWN, HALF_EVEN, FLOOR, CEIL`;\n }\n super(message);\n }\n}\n","import { MonetraError } from \"./BaseError\";\n\nexport class InsufficientFundsError extends MonetraError {\n constructor() {\n super(\"Insufficient funds for this operation.\");\n }\n}\n","import { MonetraError } from \"./BaseError\";\n\nexport class OverflowError extends MonetraError {\n constructor(message: string = \"Arithmetic overflow\") {\n super(message);\n }\n}\n","import { Money } from \"./Money\";\nimport { CurrencyMismatchError, InsufficientFundsError } from \"../errors\";\n\nexport function assertSameCurrency(a: Money, b: Money): void {\n if (a.currency.code !== b.currency.code) {\n throw new CurrencyMismatchError(a.currency.code, b.currency.code);\n }\n}\n\nexport function assertNonNegative(money: Money): void {\n if (money.isNegative()) {\n // Spec doesn't explicitly name a NegativeError, but InsufficientFundsError might fit or just a generic error.\n // Spec lists \"InsufficientFundsError\" in 4.7.\n // But \"assertNonNegative\" is a guard.\n // Let's throw an Error or a specific one if defined.\n // \"Any violation is considered a bug\" -> maybe just Error?\n // But for \"InsufficientFunds\", that's usually when subtracting.\n // Let's use a generic Error for now or create a specific one if needed.\n throw new Error(\"Money value must be non-negative\");\n }\n}\n","import { RoundingMode } from \"./strategies\";\n\nexport * from \"./strategies\";\n\nexport function divideWithRounding(\n numerator: bigint,\n denominator: bigint,\n mode: RoundingMode\n): bigint {\n if (denominator === 0n) {\n throw new Error(\"Division by zero\");\n }\n\n const quotient = numerator / denominator;\n const remainder = numerator % denominator;\n\n if (remainder === 0n) {\n return quotient;\n }\n\n const sign = (numerator >= 0n ? 1n : -1n) * (denominator >= 0n ? 1n : -1n);\n const absRemainder = remainder < 0n ? -remainder : remainder;\n const absDenominator = denominator < 0n ? -denominator : denominator;\n\n // Check for exact half\n const isHalf = absRemainder * 2n === absDenominator;\n const isMoreThanHalf = absRemainder * 2n > absDenominator;\n\n switch (mode) {\n case RoundingMode.FLOOR:\n // If positive, quotient is already floor (truncation).\n // If negative, quotient is ceil (truncation towards zero), so we need to subtract 1.\n return sign > 0n ? quotient : quotient - 1n;\n case RoundingMode.CEIL:\n // If positive, quotient is floor, so add 1.\n // If negative, quotient is ceil, so keep it.\n return sign > 0n ? quotient + 1n : quotient;\n case RoundingMode.HALF_UP:\n if (isMoreThanHalf || isHalf) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n return quotient;\n case RoundingMode.HALF_DOWN:\n if (isMoreThanHalf) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n return quotient;\n case RoundingMode.HALF_EVEN:\n if (isMoreThanHalf) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n if (isHalf) {\n // If quotient is odd, round up (away from zero? no, to nearest even).\n // If quotient is even, keep it.\n if (quotient % 2n !== 0n) {\n return sign > 0n ? quotient + 1n : quotient - 1n;\n }\n }\n return quotient;\n default:\n throw new Error(`Unsupported rounding mode: ${mode}`);\n }\n}\n","import { RoundingMode, divideWithRounding } from \"../rounding\";\nimport { RoundingRequiredError } from \"../errors\";\n\n/**\n * Adds two BigInt values.\n */\nexport function add(a: bigint, b: bigint): bigint {\n return a + b;\n}\n\n/**\n * Subtracts two BigInt values.\n */\nexport function subtract(a: bigint, b: bigint): bigint {\n return a - b;\n}\n\n/**\n * Multiplies a BigInt amount by a multiplier.\n *\n * Handles fractional multipliers by converting them to a rational number (numerator/denominator).\n * If the result is not an integer, a rounding mode must be provided.\n *\n * @param amount - The amount to multiply.\n * @param multiplier - The multiplier (number or string).\n * @param rounding - Optional rounding mode.\n * @returns The result as a BigInt.\n * @throws {RoundingRequiredError} If rounding is needed but not provided.\n */\nexport function multiply(\n amount: bigint,\n multiplier: string | number,\n rounding?: RoundingMode\n): bigint {\n const { numerator, denominator } = parseMultiplier(multiplier);\n\n // result = amount * (numerator / denominator)\n // result = (amount * numerator) / denominator\n\n const product = amount * numerator;\n\n if (product % denominator === 0n) {\n return product / denominator;\n }\n\n if (!rounding) {\n throw new RoundingRequiredError(\n \"multiply\",\n Number(product) / Number(denominator)\n );\n }\n\n return divideWithRounding(product, denominator, rounding);\n}\n\n/**\n * Divides a BigInt amount by a divisor.\n *\n * @param amount - The amount to divide.\n * @param divisor - The divisor (number or string).\n * @param rounding - Optional rounding mode.\n * @returns The result as a BigInt.\n * @throws {RoundingRequiredError} If rounding is needed but not provided.\n */\nexport function divide(\n amount: bigint,\n divisor: string | number,\n rounding?: RoundingMode\n): bigint {\n const { numerator, denominator } = parseMultiplier(divisor);\n\n // result = amount / (numerator / denominator)\n // result = (amount * denominator) / numerator\n\n const product = amount * denominator;\n\n if (product % numerator === 0n) {\n return product / numerator;\n }\n\n if (!rounding) {\n throw new RoundingRequiredError(\"divide\", Number(product) / Number(numerator));\n }\n\n return divideWithRounding(product, numerator, rounding);\n}\n\nfunction parseMultiplier(multiplier: string | number): {\n numerator: bigint;\n denominator: bigint;\n} {\n const s = multiplier.toString();\n\n // Check for scientific notation\n if (/[eE]/.test(s)) {\n throw new Error(\"Scientific notation not supported\");\n }\n\n const parts = s.split(\".\");\n if (parts.length > 2) {\n throw new Error(\"Invalid number format\");\n }\n\n const integerPart = parts[0];\n const fractionalPart = parts[1] || \"\";\n\n const denominator = 10n ** BigInt(fractionalPart.length);\n const numerator = BigInt(integerPart + fractionalPart);\n\n return { numerator, denominator };\n}\n","/**\n * Allocates a monetary amount according to a list of ratios.\n *\n * Uses the \"Largest Remainder Method\" to ensure that the sum of the allocated\n * parts equals the original amount. Remainders are distributed to the parts\n * that had the largest fractional remainders during the division.\n *\n * @param amount - The total amount to allocate (in minor units).\n * @param ratios - An array of ratios (e.g., [1, 1] for 50/50 split).\n * @returns An array of allocated amounts (in minor units).\n * @throws {Error} If ratios are empty or total ratio is zero.\n */\nexport function allocate(amount: bigint, ratios: number[]): bigint[] {\n if (ratios.length === 0) {\n throw new Error(\"Cannot allocate to empty ratios\");\n }\n\n // Scale ratios to integers\n const scaledRatios = ratios.map((r) => {\n const s = r.toString();\n if (/[eE]/.test(s)) throw new Error(\"Scientific notation not supported\");\n const parts = s.split(\".\");\n const decimals = parts[1] ? parts[1].length : 0;\n const value = BigInt(parts[0] + (parts[1] || \"\"));\n return { value, decimals };\n });\n\n const maxDecimals = Math.max(...scaledRatios.map((r) => r.decimals));\n\n const normalizedRatios = scaledRatios.map((r) => {\n const factor = 10n ** BigInt(maxDecimals - r.decimals);\n return r.value * factor;\n });\n\n const total = normalizedRatios.reduce((sum, r) => sum + r, 0n);\n\n if (total === 0n) {\n throw new Error(\"Total ratio must be greater than zero\");\n }\n\n const results: { share: bigint; remainder: bigint; index: number }[] = [];\n let allocatedTotal = 0n;\n\n for (let i = 0; i < normalizedRatios.length; i++) {\n const ratio = normalizedRatios[i];\n const share = (amount * ratio) / total;\n const remainder = (amount * ratio) % total;\n\n results.push({ share, remainder, index: i });\n allocatedTotal += share;\n }\n\n let leftOver = amount - allocatedTotal;\n\n // Distribute leftover to those with largest remainder\n // Sort by remainder desc\n results.sort((a, b) => {\n if (b.remainder > a.remainder) return 1;\n if (b.remainder < a.remainder) return -1;\n return 0;\n });\n\n for (let i = 0; i < Number(leftOver); i++) {\n results[i].share += 1n;\n }\n\n // Sort back by index\n results.sort((a, b) => a.index - b.index);\n\n return results.map((r) => r.share);\n}\n","import { Currency } from \"../currency/Currency\";\nimport { InvalidPrecisionError } from \"../errors\";\n\n/**\n * Parses a string representation of a major unit amount into minor units.\n *\n * Validates the input format to ensure it is a valid decimal number without\n * scientific notation or ambiguous characters. Checks that the precision\n * does not exceed the currency's allowed decimals.\n *\n * @param amount - The amount string (e.g., \"10.50\").\n * @param currency - The currency to validate against.\n * @returns The amount in minor units as a BigInt.\n * @throws {Error} If the format is invalid (scientific notation, non-numeric chars).\n * @throws {InvalidPrecisionError} If the precision exceeds the currency's decimals.\n */\nexport function parseToMinor(amount: string, currency: Currency): bigint {\n // Validate format\n if (/[eE]/.test(amount)) {\n throw new Error(\"Scientific notation not supported\");\n }\n\n // Reject ambiguous separators (commas, spaces, etc.)\n if (/[^0-9.-]/.test(amount)) {\n throw new Error(\"Invalid characters in amount\");\n }\n\n const parts = amount.split(\".\");\n if (parts.length > 2) {\n throw new Error(\"Invalid format: multiple decimal points\");\n }\n\n const integerPart = parts[0];\n const fractionalPart = parts[1] || \"\";\n\n if (fractionalPart.length > currency.decimals) {\n throw new InvalidPrecisionError(\n `Precision ${fractionalPart.length} exceeds currency decimals ${currency.decimals}`\n );\n }\n\n // Pad fractional part\n const paddedFractional = fractionalPart.padEnd(currency.decimals, \"0\");\n\n const combined = integerPart + paddedFractional;\n\n // Handle edge case where integer part is just \"-\"\n if (combined === \"-\" || combined === \"\") {\n throw new Error(\"Invalid format\");\n }\n\n return BigInt(combined);\n}\n","import { Money } from \"../money/Money\";\n\n/**\n * Options for formatting Money values.\n */\nexport interface FormatOptions {\n /**\n * The locale to use for formatting (e.g., \"en-US\", \"de-DE\").\n * If not provided, defaults to the currency's default locale or \"en-US\".\n */\n locale?: string;\n\n /**\n * Whether to include the currency symbol in the output.\n * Defaults to true.\n */\n symbol?: boolean;\n\n /**\n * How to display the currency.\n * 'symbol' (default): \"$1.00\"\n * 'code': \"USD 1.00\"\n * 'name': \"1.00 US dollars\"\n */\n display?: \"symbol\" | \"code\" | \"name\";\n}\n\n/**\n * Formats a Money object into a string representation.\n *\n * Uses `Intl.NumberFormat` for locale-aware formatting of numbers and currency symbols.\n *\n * @param money - The Money object to format.\n * @param options - Formatting options.\n * @returns The formatted string.\n */\nexport function format(money: Money, options?: FormatOptions): string {\n const locale = options?.locale || money.currency.locale || \"en-US\";\n const showSymbol = options?.symbol ?? true;\n const display = options?.display || \"symbol\";\n\n const decimals = money.currency.decimals;\n const minor = money.minor;\n const absMinor = minor < 0n ? -minor : minor;\n\n const divisor = 10n ** BigInt(decimals);\n const integerPart = absMinor / divisor;\n const fractionalPart = absMinor % divisor;\n\n // Pad fractional\n const fractionalStr = fractionalPart.toString().padStart(decimals, \"0\");\n\n // Get separators\n // We use a dummy format to extract the decimal separator for the locale\n const parts = new Intl.NumberFormat(locale, {\n style: \"decimal\",\n minimumFractionDigits: 1,\n }).formatToParts(1.1);\n\n const decimalSeparator =\n parts.find((p) => p.type === \"decimal\")?.value || \".\";\n\n // Format integer part with grouping using Intl (supports BigInt)\n const integerFormatted = new Intl.NumberFormat(locale, {\n style: \"decimal\",\n useGrouping: true,\n }).format(integerPart);\n\n const absString =\n decimals > 0\n ? `${integerFormatted}${decimalSeparator}${fractionalStr}`\n : integerFormatted;\n\n if (!showSymbol) {\n return minor < 0n ? `-${absString}` : absString;\n }\n\n // Use formatToParts to get the template (sign position, currency position)\n const templateParts = new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: money.currency.code,\n currencyDisplay: display,\n }).formatToParts(minor < 0n ? -1 : 1);\n\n let result = \"\";\n let numberInserted = false;\n\n for (const part of templateParts) {\n if ([\"integer\", \"group\", \"decimal\", \"fraction\"].includes(part.type)) {\n if (!numberInserted) {\n result += absString;\n numberInserted = true;\n }\n } else if (part.type === \"currency\") {\n result += part.value;\n } else {\n result += part.value; // literals, minusSign, parentheses, etc.\n }\n }\n\n return result;\n}\n","import { Currency } from \"../currency/Currency\";\nimport { getCurrency } from \"../currency/registry\";\nimport { RoundingMode } from \"../rounding/strategies\";\n\nimport { assertSameCurrency } from \"./guards\";\nimport { add, subtract, multiply, divide } from \"./arithmetic\";\nimport { allocate } from \"./allocation\";\nimport { parseToMinor } from \"../format/parser\";\nimport { format } from \"../format/formatter\";\n\n/**\n * Represents a monetary value in a specific currency.\n *\n * Money objects are immutable and store values in minor units (e.g., cents) using BigInt\n * to avoid floating-point precision errors.\n */\nexport class Money {\n /**\n * The amount in minor units (e.g., cents).\n */\n readonly minor: bigint;\n\n /**\n * The currency of this money value.\n */\n readonly currency: Currency;\n\n private constructor(minor: bigint, currency: Currency) {\n this.minor = minor;\n this.currency = currency;\n }\n\n private static resolveCurrency(currency: Currency | string): Currency {\n if (typeof currency === \"string\") {\n return getCurrency(currency);\n }\n return currency;\n }\n\n /**\n * Creates a Money instance from minor units (e.g., cents).\n *\n * @param minor - The amount in minor units. Can be a number or BigInt.\n * @param currency - The currency of the money (object or code string).\n * @returns A new Money instance.\n * @example\n * const m = Money.fromMinor(100, USD); // $1.00\n * const m2 = Money.fromMinor(100, 'USD'); // $1.00\n */\n static fromMinor(minor: bigint | number, currency: Currency | string): Money {\n return new Money(BigInt(minor), Money.resolveCurrency(currency));\n }\n\n /**\n * Creates a Money instance from major units (e.g., \"10.50\").\n *\n * @param amount - The amount as a string. Must be a valid decimal number.\n * @param currency - The currency of the money (object or code string).\n * @returns A new Money instance.\n * @throws {InvalidPrecisionError} If the amount has more decimal places than the currency allows.\n * @example\n * const m = Money.fromMajor(\"10.50\", USD); // $10.50\n */\n static fromMajor(amount: string, currency: Currency | string): Money {\n const resolvedCurrency = Money.resolveCurrency(currency);\n const minor = parseToMinor(amount, resolvedCurrency);\n return new Money(minor, resolvedCurrency);\n }\n\n /**\n * Creates a Money instance from a floating-point number.\n *\n * ⚠️ WARNING: Floating-point numbers can have precision issues.\n * Prefer `Money.fromMajor(\"10.50\", currency)` for exact values.\n *\n * @param amount - The float amount in major units.\n * @param currency - The currency.\n * @param options - Options for handling precision.\n * @returns A new Money instance.\n */\n static fromFloat(\n amount: number,\n currency: Currency | string,\n options?: {\n rounding?: RoundingMode;\n suppressWarning?: boolean;\n }\n ): Money {\n if (!options?.suppressWarning && process.env.NODE_ENV !== \"production\") {\n console.warn(\n '[monetra] Money.fromFloat() may lose precision. ' +\n 'Consider using Money.fromMajor(\"' +\n amount +\n '\", currency) instead.'\n );\n }\n const resolvedCurrency = Money.resolveCurrency(currency);\n const factor = 10 ** resolvedCurrency.decimals;\n const minorUnits = Math.round(amount * factor);\n return new Money(BigInt(minorUnits), resolvedCurrency);\n }\n\n /**\n * Returns the minimum of the provided Money values.\n * @param values - Money values to compare (must all be same currency).\n * @returns The Money with the smallest amount.\n * @throws {CurrencyMismatchError} If currencies don't match.\n */\n static min(...values: Money[]): Money {\n if (values.length === 0)\n throw new Error(\"At least one Money value required\");\n return values.reduce((min, current) => {\n assertSameCurrency(min, current);\n return current.lessThan(min) ? current : min;\n });\n }\n\n /**\n * Returns the maximum of the provided Money values.\n * @param values - Money values to compare (must all be same currency).\n * @returns The Money with the largest amount.\n * @throws {CurrencyMismatchError} If currencies don't match.\n */\n static max(...values: Money[]): Money {\n if (values.length === 0)\n throw new Error(\"At least one Money value required\");\n return values.reduce((max, current) => {\n assertSameCurrency(max, current);\n return current.greaterThan(max) ? current : max;\n });\n }\n\n /**\n * Creates a Money instance representing zero in the given currency.\n *\n * @param currency - The currency.\n * @returns A new Money instance with value 0.\n */\n static zero(currency: Currency | string): Money {\n return new Money(0n, Money.resolveCurrency(currency));\n }\n\n private resolveOther(other: Money | number | bigint | string): Money {\n if (other instanceof Money) {\n return other;\n }\n if (typeof other === \"string\") {\n return Money.fromMajor(other, this.currency);\n }\n return Money.fromMinor(other, this.currency);\n }\n\n /**\n * Adds another Money value to this one.\n *\n * @param other - The value to add (Money, minor units as number/bigint, or major units as string).\n * @returns A new Money instance representing the sum.\n * @throws {CurrencyMismatchError} If the currencies do not match.\n */\n add(other: Money | number | bigint | string): Money {\n const otherMoney = this.resolveOther(other);\n assertSameCurrency(this, otherMoney);\n return new Money(add(this.minor, otherMoney.minor), this.currency);\n }\n\n /**\n * Subtracts another Money value from this one.\n *\n * @param other - The value to subtract (Money, minor units as number/bigint, or major units as string).\n * @returns A new Money instance representing the difference.\n * @throws {CurrencyMismatchError} If the currencies do not match.\n */\n subtract(other: Money | number | bigint | string): Money {\n const otherMoney = this.resolveOther(other);\n assertSameCurrency(this, otherMoney);\n return new Money(subtract(this.minor, otherMoney.minor), this.currency);\n }\n\n /**\n * Multiplies this Money value by a scalar.\n *\n * @param multiplier - The number to multiply by.\n * @param options - Options for rounding if the result is not an integer.\n * @returns A new Money instance representing the product.\n * @throws {RoundingRequiredError} If the result is fractional and no rounding mode is provided.\n */\n multiply(\n multiplier: string | number,\n options?: { rounding?: RoundingMode }\n ): Money {\n const result = multiply(this.minor, multiplier, options?.rounding);\n return new Money(result, this.currency);\n }\n\n /**\n * Divides this Money value by a divisor.\n *\n * @param divisor - The number to divide by.\n * @param options - Options for rounding if the result is not an integer.\n * @returns A new Money instance representing the quotient.\n * @throws {RoundingRequiredError} If the result is fractional and no rounding mode is provided.\n * @throws {Error} If divisor is zero.\n */\n divide(\n divisor: number | string,\n options?: { rounding?: RoundingMode }\n ): Money {\n if (divisor === 0 || divisor === \"0\") {\n throw new Error(\"Division by zero\");\n }\n const result = divide(this.minor, divisor, options?.rounding);\n return new Money(result, this.currency);\n }\n\n /**\n * Returns the absolute value of this Money.\n * @returns A new Money instance with the absolute value.\n */\n abs(): Money {\n return new Money(\n this.minor < 0n ? -this.minor : this.minor,\n this.currency\n );\n }\n\n /**\n * Returns the negated value of this Money.\n * @returns A new Money instance with the negated value.\n */\n negate(): Money {\n return new Money(-this.minor, this.currency);\n }\n\n /**\n * Allocates (splits) this Money value according to a list of ratios.\n *\n * The sum of the parts will always equal the original amount.\n * Remainders are distributed to the parts with the largest fractional remainders.\n *\n * @param ratios - A list of numbers representing the ratios to split by.\n * @returns An array of Money instances.\n */\n allocate(ratios: number[]): Money[] {\n const shares = allocate(this.minor, ratios);\n return shares.map((share) => new Money(share, this.currency));\n }\n\n /**\n * Formats this Money value as a string.\n *\n * @param options - Formatting options.\n * @returns The formatted string.\n */\n format(options?: {\n locale?: string;\n symbol?: boolean;\n display?: \"symbol\" | \"code\" | \"name\";\n }): string {\n return format(this, options);\n }\n\n /**\n * Checks if this Money value is equal to another.\n *\n * @param other - The other Money value (Money, minor units as number/bigint, or major units as string).\n * @returns True if amounts and currencies are equal.\n */\n equals(other: Money | number | bigint | string): boolean {\n const otherMoney = this.resolveOther(other);\n return (\n this.currency.code === otherMoney.currency.code &&\n this.minor === otherMoney.minor\n );\n }\n\n /**\n * Checks if this Money value is greater than another.\n *\n * @param other - The other Money value (Money, minor units as number/bigint, or major units as string).\n * @returns True if this value is greater.\n * @throws {CurrencyMismatchError} If the currencies do not match.\n */\n greaterThan(other: Money | number | bigint | string): boolean {\n const otherMoney = this.resolveOther(other);\n assertSameCurrency(this, otherMoney);\n return this.minor > otherMoney.minor;\n }\n\n /**\n * Checks if this Money value is less than another.\n *\n * @param other - The other Money value (Money, minor units as number/bigint, or major units as string).\n * @returns True if this value is less.\n * @throws {CurrencyMismatchError} If the currencies do not match.\n */\n lessThan(other: Money | number | bigint | string): boolean {\n const otherMoney = this.resolveOther(other);\n assertSameCurrency(this, otherMoney);\n return this.minor < otherMoney.minor;\n }\n\n /**\n * Checks if this Money value is greater than or equal to another.\n */\n greaterThanOrEqual(other: Money | number | bigint | string): boolean {\n const otherMoney = this.resolveOther(other);\n assertSameCurrency(this, otherMoney);\n return this.minor >= otherMoney.minor;\n }\n\n /**\n * Checks if this Money value is less than or equal to another.\n */\n lessThanOrEqual(other: Money | number | bigint | string): boolean {\n const otherMoney = this.resolveOther(other);\n assertSameCurrency(this, otherMoney);\n return this.minor <= otherMoney.minor;\n }\n\n /**\n * Checks if this Money value is positive (greater than zero).\n */\n isPositive(): boolean {\n return this.minor > 0n;\n }\n\n /**\n * Compares this Money to another, returning -1, 0, or 1.\n * Useful for sorting.\n */\n compare(other: Money | number | bigint | string): -1 | 0 | 1 {\n const otherMoney = this.resolveOther(other);\n assertSameCurrency(this, otherMoney);\n if (this.minor < otherMoney.minor) return -1;\n if (this.minor > otherMoney.minor) return 1;\n return 0;\n }\n\n /**\n * Calculates a percentage of the money.\n * @param percent - The percentage (e.g., 50 for 50%).\n * @param rounding - Rounding mode (defaults to HALF_EVEN).\n */\n percentage(\n percent: number,\n rounding: RoundingMode = RoundingMode.HALF_EVEN\n ): Money {\n return this.multiply(percent / 100, { rounding });\n }\n\n /**\n * Adds a percentage to the money.\n * @param percent - The percentage to add.\n * @param rounding - Rounding mode.\n */\n addPercent(\n percent: number,\n rounding: RoundingMode = RoundingMode.HALF_EVEN\n ): Money {\n return this.add(this.percentage(percent, rounding));\n }\n\n /**\n * Subtracts a percentage from the money.\n * @param percent - The percentage to subtract.\n * @param rounding - Rounding mode.\n */\n subtractPercent(\n percent: number,\n rounding: RoundingMode = RoundingMode.HALF_EVEN\n ): Money {\n return this.subtract(this.percentage(percent, rounding));\n }\n\n /**\n * Splits the money into equal parts.\n * @param parts - Number of parts.\n */\n split(parts: number): Money[] {\n const ratios = Array(parts).fill(1);\n return this.allocate(ratios);\n }\n\n /**\n * Checks if this Money value is zero.\n *\n * @returns True if the amount is zero.\n */\n isZero(): boolean {\n return this.minor === 0n;\n }\n\n /**\n * Checks if this Money value is negative.\n *\n * @returns True if the amount is negative.\n */\n isNegative(): boolean {\n return this.minor < 0n;\n }\n\n /**\n * Returns a JSON representation of the Money object.\n *\n * @returns An object with amount (string), currency (code), and precision.\n */\n toJSON(): { amount: string; currency: string; precision: number } {\n return {\n amount: this.minor.toString(),\n currency: this.currency.code,\n precision: this.currency.decimals,\n };\n }\n\n /**\n * Returns a string representation of the Money object (formatted).\n */\n toString(): string {\n return this.format();\n }\n}\n","import { Money } from \"./Money\";\nimport { Currency } from \"../currency/Currency\";\nimport { getCurrency } from \"../currency/registry\";\nimport { RoundingMode } from \"../rounding\";\n\n/**\n * Handles currency conversion using exchange rates.\n */\nexport class Converter {\n private rates: Record<string, number>;\n private base: string;\n\n /**\n * Creates a new Converter.\n *\n * @param base - The base currency code (e.g., \"USD\").\n * @param rates - A map of currency codes to exchange rates relative to the base.\n * Example: { \"EUR\": 0.85, \"GBP\": 0.75 } (where 1 Base = 0.85 EUR).\n */\n constructor(base: string, rates: Record<string, number>) {\n this.base = base;\n this.rates = { ...rates };\n\n // Ensure base rate is 1\n if (this.rates[base] === undefined) {\n this.rates[base] = 1;\n }\n }\n\n /**\n * Converts a Money object to a target currency.\n *\n * @param money - The Money object to convert.\n * @param toCurrency - The target currency (code or object).\n * @returns A new Money object in the target currency.\n * @throws {Error} If exchange rates are missing.\n */\n convert(money: Money, toCurrency: Currency | string): Money {\n const targetCurrency =\n typeof toCurrency === \"string\" ? getCurrency(toCurrency) : toCurrency;\n\n if (money.currency.code === targetCurrency.code) {\n return money;\n }\n\n const fromRate = this.rates[money.currency.code];\n const toRate = this.rates[targetCurrency.code];\n\n if (fromRate === undefined || toRate === undefined) {\n throw new Error(\n `Exchange rate missing for conversion from ${money.currency.code} to ${targetCurrency.code}`\n );\n }\n\n // Calculate the exchange rate ratio\n const ratio = toRate / fromRate;\n\n // Adjust for difference in decimal places\n // targetMinor = sourceMinor * ratio * 10^(targetDecimals - sourceDecimals)\n const decimalAdjustment =\n 10 ** (targetCurrency.decimals - money.currency.decimals);\n\n // We use the multiply method of Money which handles rounding.\n // Default rounding is usually needed for conversion.\n // We'll use the default rounding (HALF_EVEN is common for money, but Money.multiply requires explicit if fractional).\n // Let's assume standard rounding (HALF_UP) or require options?\n // For ease of use, we should probably default to HALF_UP or similar.\n // But Money.multiply throws if rounding is needed and not provided.\n\n // Let's pass a default rounding mode.\n // We need to import RoundingMode.\n\n const multiplier = ratio * decimalAdjustment;\n\n // We need to access the internal multiply or use the public one.\n // Public one: money.multiply(multiplier, { rounding: 'HALF_EVEN' })\n\n const convertedAmount = money.multiply(multiplier, {\n rounding: RoundingMode.HALF_EVEN,\n });\n return Money.fromMinor(convertedAmount.minor, targetCurrency);\n }\n}\n","import { Money } from \"./Money\";\nimport { Currency } from \"../currency/Currency\";\nimport { Converter } from \"./Converter\";\nimport { getCurrency } from \"../currency/registry\";\n\n/**\n * A collection of Money objects in different currencies.\n * Useful for representing a wallet or portfolio.\n */\nexport class MoneyBag {\n private contents: Map<string, Money> = new Map();\n\n /**\n * Adds a Money amount to the bag.\n * @param money The money to add.\n */\n add(money: Money): void {\n const code = money.currency.code;\n const existing = this.contents.get(code);\n if (existing) {\n this.contents.set(code, existing.add(money));\n } else {\n this.contents.set(code, money);\n }\n }\n\n /**\n * Subtracts a Money amount from the bag.\n * @param money The money to subtract.\n */\n subtract(money: Money): void {\n const code = money.currency.code;\n const existing = this.contents.get(code);\n if (existing) {\n this.contents.set(code, existing.subtract(money));\n } else {\n // If not present, we assume 0 - amount\n const zero = Money.zero(money.currency);\n this.contents.set(code, zero.subtract(money));\n }\n }\n\n /**\n * Gets the amount for a specific currency.\n * @param currency The currency to retrieve.\n * @returns The Money amount in that currency (or zero if not present).\n */\n get(currency: Currency | string): Money {\n const code = typeof currency === \"string\" ? currency : currency.code;\n return (\n this.contents.get(code) ||\n Money.zero(\n typeof currency === \"string\" ? getCurrency(currency) : currency\n )\n );\n }\n\n /**\n * Calculates the total value of the bag in a specific currency.\n *\n * @param targetCurrency The currency to convert everything to.\n * @param converter The converter instance with exchange rates.\n * @returns The total amount in the target currency.\n */\n total(targetCurrency: Currency | string, converter: Converter): Money {\n const target =\n typeof targetCurrency === \"string\"\n ? getCurrency(targetCurrency)\n : targetCurrency;\n let total = Money.zero(target);\n\n for (const money of this.contents.values()) {\n const converted = converter.convert(money, target);\n total = total.add(converted);\n }\n\n return total;\n }\n\n /**\n * Returns a list of all Money objects in the bag.\n */\n getAll(): Money[] {\n return Array.from(this.contents.values());\n }\n\n /**\n * Returns a JSON representation of the bag.\n */\n toJSON(): { amount: string; currency: string; precision: number }[] {\n return this.getAll().map((m) => m.toJSON());\n }\n}\n","import { Currency } from \"./Currency\";\nimport { registerCurrency } from \"./registry\";\n\n/**\n * United States Dollar (USD).\n */\nexport const USD: Currency = {\n code: \"USD\",\n decimals: 2,\n symbol: \"$\",\n locale: \"en-US\",\n};\nregisterCurrency(USD);\n\n/**\n * Euro (EUR).\n */\nexport const EUR: Currency = {\n code: \"EUR\",\n decimals: 2,\n symbol: \"€\",\n locale: \"de-DE\", // Default locale, can be overridden\n};\nregisterCurrency(EUR);\n\n/**\n * British Pound Sterling (GBP).\n */\nexport const GBP: Currency = {\n code: \"GBP\",\n decimals: 2,\n symbol: \"£\",\n locale: \"en-GB\",\n};\nregisterCurrency(GBP);\n\n/**\n * Japanese Yen (JPY).\n */\nexport const JPY: Currency = {\n code: \"JPY\",\n decimals: 0,\n symbol: \"¥\",\n locale: \"ja-JP\",\n};\nregisterCurrency(JPY);\n\n/**\n * South African Rand (ZAR).\n */\nexport const ZAR: Currency = {\n code: \"ZAR\",\n decimals: 2,\n symbol: \"R\",\n locale: \"en-ZA\",\n};\nregisterCurrency(ZAR);\n\n/**\n * A collection of common currencies.\n * @deprecated Use registry instead.\n */\nexport const CURRENCIES: Record<string, Currency> = {\n USD,\n EUR,\n GBP,\n JPY,\n ZAR,\n};\n\n\n","import { Currency } from \"./Currency\";\n\nexport function getMinorUnitExponent(currency: Currency): bigint {\n return 10n ** BigInt(currency.decimals);\n}\n","import { Money } from \"./money\";\nimport { Currency } from \"./currency\";\n\nexport * from \"./money\";\nexport * from \"./currency\";\nexport * from \"./rounding\";\nexport * from \"./errors\";\n\n/**\n * Helper function to create Money instances.\n *\n * If amount is a number or BigInt, it is treated as minor units.\n * If amount is a string, it is treated as major units.\n *\n * @param amount - The amount (minor units if number/bigint, major units if string).\n * @param currency - The currency code or object.\n */\nexport function money(\n amount: number | bigint | string,\n currency: string | Currency\n): Money {\n if (typeof amount === \"string\") {\n return Money.fromMajor(amount, currency);\n }\n return Money.fromMinor(amount, currency);\n}\n"],"mappings":"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","names":["RoundingMode","money","money","money","money"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monetra",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "A currency-aware, integer-based money engine designed for financial correctness.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -16,7 +16,8 @@
16
16
  "build": "tsup",
17
17
  "test": "vitest",
18
18
  "lint": "tsc --noEmit",
19
- "format": "prettier --write \"src/**/*.ts\""
19
+ "format": "prettier --write \"src/**/*.ts\"",
20
+ "prepublishOnly": "npm run build"
20
21
  },
21
22
  "keywords": [
22
23
  "money",
package/dist/index 2.js DELETED
@@ -1,424 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- CURRENCIES: () => CURRENCIES,
24
- CurrencyMismatchError: () => CurrencyMismatchError,
25
- EUR: () => EUR,
26
- GBP: () => GBP,
27
- InsufficientFundsError: () => InsufficientFundsError,
28
- InvalidPrecisionError: () => InvalidPrecisionError,
29
- JPY: () => JPY,
30
- MonetraError: () => MonetraError,
31
- Money: () => Money,
32
- OverflowError: () => OverflowError,
33
- RoundingMode: () => RoundingMode,
34
- RoundingRequiredError: () => RoundingRequiredError,
35
- USD: () => USD,
36
- ZAR: () => ZAR,
37
- divideWithRounding: () => divideWithRounding,
38
- getCurrency: () => getCurrency,
39
- getMinorUnitExponent: () => getMinorUnitExponent
40
- });
41
- module.exports = __toCommonJS(index_exports);
42
-
43
- // src/errors/BaseError.ts
44
- var MonetraError = class extends Error {
45
- constructor(message) {
46
- super(message);
47
- this.name = this.constructor.name;
48
- Object.setPrototypeOf(this, new.target.prototype);
49
- }
50
- };
51
-
52
- // src/errors/CurrencyMismatchError.ts
53
- var CurrencyMismatchError = class extends MonetraError {
54
- constructor(expected, actual) {
55
- super(`Currency mismatch: expected ${expected}, got ${actual}`);
56
- }
57
- };
58
-
59
- // src/errors/InvalidPrecisionError.ts
60
- var InvalidPrecisionError = class extends MonetraError {
61
- constructor(message) {
62
- super(message);
63
- }
64
- };
65
-
66
- // src/errors/RoundingRequiredError.ts
67
- var RoundingRequiredError = class extends MonetraError {
68
- constructor() {
69
- super("Rounding is required for this operation but was not provided.");
70
- }
71
- };
72
-
73
- // src/errors/InsufficientFundsError.ts
74
- var InsufficientFundsError = class extends MonetraError {
75
- constructor() {
76
- super("Insufficient funds for this operation.");
77
- }
78
- };
79
-
80
- // src/errors/OverflowError.ts
81
- var OverflowError = class extends MonetraError {
82
- constructor(message = "Arithmetic overflow") {
83
- super(message);
84
- }
85
- };
86
-
87
- // src/money/guards.ts
88
- function assertSameCurrency(a, b) {
89
- if (a.currency.code !== b.currency.code) {
90
- throw new CurrencyMismatchError(a.currency.code, b.currency.code);
91
- }
92
- }
93
-
94
- // src/rounding/strategies.ts
95
- var RoundingMode = /* @__PURE__ */ ((RoundingMode3) => {
96
- RoundingMode3["HALF_UP"] = "HALF_UP";
97
- RoundingMode3["HALF_DOWN"] = "HALF_DOWN";
98
- RoundingMode3["HALF_EVEN"] = "HALF_EVEN";
99
- RoundingMode3["FLOOR"] = "FLOOR";
100
- RoundingMode3["CEIL"] = "CEIL";
101
- return RoundingMode3;
102
- })(RoundingMode || {});
103
-
104
- // src/rounding/index.ts
105
- function divideWithRounding(numerator, denominator, mode) {
106
- if (denominator === 0n) {
107
- throw new Error("Division by zero");
108
- }
109
- const quotient = numerator / denominator;
110
- const remainder = numerator % denominator;
111
- if (remainder === 0n) {
112
- return quotient;
113
- }
114
- const sign = (numerator >= 0n ? 1n : -1n) * (denominator >= 0n ? 1n : -1n);
115
- const absRemainder = remainder < 0n ? -remainder : remainder;
116
- const absDenominator = denominator < 0n ? -denominator : denominator;
117
- const isHalf = absRemainder * 2n === absDenominator;
118
- const isMoreThanHalf = absRemainder * 2n > absDenominator;
119
- switch (mode) {
120
- case "FLOOR" /* FLOOR */:
121
- return sign > 0n ? quotient : quotient - 1n;
122
- case "CEIL" /* CEIL */:
123
- return sign > 0n ? quotient + 1n : quotient;
124
- case "HALF_UP" /* HALF_UP */:
125
- if (isMoreThanHalf || isHalf) {
126
- return sign > 0n ? quotient + 1n : quotient - 1n;
127
- }
128
- return quotient;
129
- case "HALF_DOWN" /* HALF_DOWN */:
130
- if (isMoreThanHalf) {
131
- return sign > 0n ? quotient + 1n : quotient - 1n;
132
- }
133
- return quotient;
134
- case "HALF_EVEN" /* HALF_EVEN */:
135
- if (isMoreThanHalf) {
136
- return sign > 0n ? quotient + 1n : quotient - 1n;
137
- }
138
- if (isHalf) {
139
- if (quotient % 2n !== 0n) {
140
- return sign > 0n ? quotient + 1n : quotient - 1n;
141
- }
142
- }
143
- return quotient;
144
- default:
145
- throw new Error(`Unsupported rounding mode: ${mode}`);
146
- }
147
- }
148
-
149
- // src/money/arithmetic.ts
150
- function add(a, b) {
151
- return a + b;
152
- }
153
- function subtract(a, b) {
154
- return a - b;
155
- }
156
- function multiply(amount, multiplier, rounding) {
157
- const { numerator, denominator } = parseMultiplier(multiplier);
158
- const product = amount * numerator;
159
- if (product % denominator === 0n) {
160
- return product / denominator;
161
- }
162
- if (!rounding) {
163
- throw new RoundingRequiredError();
164
- }
165
- return divideWithRounding(product, denominator, rounding);
166
- }
167
- function parseMultiplier(multiplier) {
168
- const s = multiplier.toString();
169
- if (/[eE]/.test(s)) {
170
- throw new Error("Scientific notation not supported");
171
- }
172
- const parts = s.split(".");
173
- if (parts.length > 2) {
174
- throw new Error("Invalid number format");
175
- }
176
- const integerPart = parts[0];
177
- const fractionalPart = parts[1] || "";
178
- const denominator = 10n ** BigInt(fractionalPart.length);
179
- const numerator = BigInt(integerPart + fractionalPart);
180
- return { numerator, denominator };
181
- }
182
-
183
- // src/money/allocation.ts
184
- function allocate(amount, ratios) {
185
- if (ratios.length === 0) {
186
- throw new Error("Cannot allocate to empty ratios");
187
- }
188
- const scaledRatios = ratios.map((r) => {
189
- const s = r.toString();
190
- if (/[eE]/.test(s)) throw new Error("Scientific notation not supported");
191
- const parts = s.split(".");
192
- const decimals = parts[1] ? parts[1].length : 0;
193
- const value = BigInt(parts[0] + (parts[1] || ""));
194
- return { value, decimals };
195
- });
196
- const maxDecimals = Math.max(...scaledRatios.map((r) => r.decimals));
197
- const normalizedRatios = scaledRatios.map((r) => {
198
- const factor = 10n ** BigInt(maxDecimals - r.decimals);
199
- return r.value * factor;
200
- });
201
- const total = normalizedRatios.reduce((sum, r) => sum + r, 0n);
202
- if (total === 0n) {
203
- throw new Error("Total ratio must be greater than zero");
204
- }
205
- const results = [];
206
- let allocatedTotal = 0n;
207
- for (let i = 0; i < normalizedRatios.length; i++) {
208
- const ratio = normalizedRatios[i];
209
- const share = amount * ratio / total;
210
- const remainder = amount * ratio % total;
211
- results.push({ share, remainder, index: i });
212
- allocatedTotal += share;
213
- }
214
- let leftOver = amount - allocatedTotal;
215
- results.sort((a, b) => {
216
- if (b.remainder > a.remainder) return 1;
217
- if (b.remainder < a.remainder) return -1;
218
- return 0;
219
- });
220
- for (let i = 0; i < Number(leftOver); i++) {
221
- results[i].share += 1n;
222
- }
223
- results.sort((a, b) => a.index - b.index);
224
- return results.map((r) => r.share);
225
- }
226
-
227
- // src/format/parser.ts
228
- function parseToMinor(amount, currency) {
229
- if (/[eE]/.test(amount)) {
230
- throw new Error("Scientific notation not supported");
231
- }
232
- if (/[^0-9.-]/.test(amount)) {
233
- throw new Error("Invalid characters in amount");
234
- }
235
- const parts = amount.split(".");
236
- if (parts.length > 2) {
237
- throw new Error("Invalid format: multiple decimal points");
238
- }
239
- const integerPart = parts[0];
240
- const fractionalPart = parts[1] || "";
241
- if (fractionalPart.length > currency.decimals) {
242
- throw new InvalidPrecisionError(
243
- `Precision ${fractionalPart.length} exceeds currency decimals ${currency.decimals}`
244
- );
245
- }
246
- const paddedFractional = fractionalPart.padEnd(currency.decimals, "0");
247
- const combined = integerPart + paddedFractional;
248
- if (combined === "-" || combined === "") {
249
- throw new Error("Invalid format");
250
- }
251
- return BigInt(combined);
252
- }
253
-
254
- // src/format/formatter.ts
255
- function format(money, options) {
256
- const locale = options?.locale || money.currency.locale || "en-US";
257
- const showSymbol = options?.symbol ?? true;
258
- const decimals = money.currency.decimals;
259
- const minor = money.minor;
260
- const absMinor = minor < 0n ? -minor : minor;
261
- const divisor = 10n ** BigInt(decimals);
262
- const integerPart = absMinor / divisor;
263
- const fractionalPart = absMinor % divisor;
264
- const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
265
- const parts = new Intl.NumberFormat(locale, {
266
- style: "decimal",
267
- minimumFractionDigits: 1
268
- }).formatToParts(1.1);
269
- const decimalSeparator = parts.find((p) => p.type === "decimal")?.value || ".";
270
- const integerFormatted = new Intl.NumberFormat(locale, {
271
- style: "decimal",
272
- useGrouping: true
273
- }).format(integerPart);
274
- const absString = decimals > 0 ? `${integerFormatted}${decimalSeparator}${fractionalStr}` : integerFormatted;
275
- if (!showSymbol) {
276
- return minor < 0n ? `-${absString}` : absString;
277
- }
278
- const templateParts = new Intl.NumberFormat(locale, {
279
- style: "currency",
280
- currency: money.currency.code
281
- }).formatToParts(minor < 0n ? -1 : 1);
282
- let result = "";
283
- let numberInserted = false;
284
- for (const part of templateParts) {
285
- if (["integer", "group", "decimal", "fraction"].includes(part.type)) {
286
- if (!numberInserted) {
287
- result += absString;
288
- numberInserted = true;
289
- }
290
- } else if (part.type === "currency") {
291
- result += part.value;
292
- } else {
293
- result += part.value;
294
- }
295
- }
296
- return result;
297
- }
298
-
299
- // src/money/Money.ts
300
- var Money = class _Money {
301
- constructor(minor, currency) {
302
- this.minor = minor;
303
- this.currency = currency;
304
- }
305
- static fromMinor(minor, currency) {
306
- return new _Money(BigInt(minor), currency);
307
- }
308
- static fromMajor(amount, currency) {
309
- const minor = parseToMinor(amount, currency);
310
- return new _Money(minor, currency);
311
- }
312
- static zero(currency) {
313
- return new _Money(0n, currency);
314
- }
315
- add(other) {
316
- assertSameCurrency(this, other);
317
- return new _Money(add(this.minor, other.minor), this.currency);
318
- }
319
- subtract(other) {
320
- assertSameCurrency(this, other);
321
- return new _Money(subtract(this.minor, other.minor), this.currency);
322
- }
323
- multiply(multiplier, options) {
324
- const result = multiply(this.minor, multiplier, options?.rounding);
325
- return new _Money(result, this.currency);
326
- }
327
- allocate(ratios) {
328
- const shares = allocate(this.minor, ratios);
329
- return shares.map((share) => new _Money(share, this.currency));
330
- }
331
- format(options) {
332
- return format(this, options);
333
- }
334
- equals(other) {
335
- return this.currency.code === other.currency.code && this.minor === other.minor;
336
- }
337
- greaterThan(other) {
338
- assertSameCurrency(this, other);
339
- return this.minor > other.minor;
340
- }
341
- lessThan(other) {
342
- assertSameCurrency(this, other);
343
- return this.minor < other.minor;
344
- }
345
- isZero() {
346
- return this.minor === 0n;
347
- }
348
- isNegative() {
349
- return this.minor < 0n;
350
- }
351
- };
352
-
353
- // src/currency/iso4217.ts
354
- var USD = {
355
- code: "USD",
356
- decimals: 2,
357
- symbol: "$",
358
- locale: "en-US"
359
- };
360
- var EUR = {
361
- code: "EUR",
362
- decimals: 2,
363
- symbol: "\u20AC",
364
- locale: "de-DE"
365
- // Default locale, can be overridden
366
- };
367
- var GBP = {
368
- code: "GBP",
369
- decimals: 2,
370
- symbol: "\xA3",
371
- locale: "en-GB"
372
- };
373
- var JPY = {
374
- code: "JPY",
375
- decimals: 0,
376
- symbol: "\xA5",
377
- locale: "ja-JP"
378
- };
379
- var ZAR = {
380
- code: "ZAR",
381
- decimals: 2,
382
- symbol: "R",
383
- locale: "en-ZA"
384
- };
385
- var CURRENCIES = {
386
- USD,
387
- EUR,
388
- GBP,
389
- JPY,
390
- ZAR
391
- };
392
- function getCurrency(code) {
393
- const currency = CURRENCIES[code.toUpperCase()];
394
- if (!currency) {
395
- throw new Error(`Unknown currency code: ${code}`);
396
- }
397
- return currency;
398
- }
399
-
400
- // src/currency/precision.ts
401
- function getMinorUnitExponent(currency) {
402
- return 10n ** BigInt(currency.decimals);
403
- }
404
- // Annotate the CommonJS export names for ESM import in node:
405
- 0 && (module.exports = {
406
- CURRENCIES,
407
- CurrencyMismatchError,
408
- EUR,
409
- GBP,
410
- InsufficientFundsError,
411
- InvalidPrecisionError,
412
- JPY,
413
- MonetraError,
414
- Money,
415
- OverflowError,
416
- RoundingMode,
417
- RoundingRequiredError,
418
- USD,
419
- ZAR,
420
- divideWithRounding,
421
- getCurrency,
422
- getMinorUnitExponent
423
- });
424
- //# sourceMappingURL=index.js.map