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/financial/index.js +5 -1321
- package/dist/financial/index.mjs +5 -1273
- package/dist/index.js +5 -2456
- package/dist/index.mjs +5 -2325
- package/dist/ledger/index.js +5 -1052
- package/dist/ledger/index.mjs +5 -1027
- package/dist/tokens/index.js +1 -100
- package/dist/tokens/index.mjs +1 -69
- package/package.json +2 -4
- package/dist/financial/index.js.map +0 -1
- package/dist/financial/index.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/ledger/index.js.map +0 -1
- package/dist/ledger/index.mjs.map +0 -1
- package/dist/tokens/index.js.map +0 -1
- package/dist/tokens/index.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/currency/registry.ts","../../src/errors/BaseError.ts","../../src/errors/CurrencyMismatchError.ts","../../src/errors/InvalidPrecisionError.ts","../../src/errors/RoundingRequiredError.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/ledger/verification.ts","../../src/ledger/Ledger.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/**\n * Returns all registered currencies as a map.\n * @internal Use for testing only.\n */\nexport function getAllCurrencies(): Record<string, Currency> {\n return Object.fromEntries(registry);\n}\n","/**\n * Error codes for programmatic handling of Monetra errors.\n */\nexport enum MonetraErrorCode {\n CURRENCY_MISMATCH = \"MONETRA_CURRENCY_MISMATCH\",\n INSUFFICIENT_FUNDS = \"MONETRA_INSUFFICIENT_FUNDS\",\n INVALID_ARGUMENT = \"MONETRA_INVALID_ARGUMENT\",\n INVALID_PRECISION = \"MONETRA_INVALID_PRECISION\",\n OVERFLOW = \"MONETRA_OVERFLOW\",\n ROUNDING_REQUIRED = \"MONETRA_ROUNDING_REQUIRED\",\n}\n\n/**\n * Base error class for all Monetra errors.\n * All Monetra errors include an error code for programmatic handling.\n */\nexport class MonetraError extends Error {\n /**\n * A unique error code for programmatic handling.\n */\n readonly code: MonetraErrorCode;\n\n constructor(message: string, code: MonetraErrorCode) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { MonetraError, MonetraErrorCode } from \"./BaseError\";\n\nexport class CurrencyMismatchError extends MonetraError {\n /**\n * The expected currency code.\n */\n readonly expected: string;\n\n /**\n * The received currency code.\n */\n readonly received: string;\n\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 MonetraErrorCode.CURRENCY_MISMATCH,\n );\n this.expected = expected;\n this.received = received;\n }\n}\n","import { MonetraError, MonetraErrorCode } from \"./BaseError\";\n\nexport class InvalidPrecisionError extends MonetraError {\n constructor(message: string) {\n super(message, MonetraErrorCode.INVALID_PRECISION);\n }\n}\n","import { MonetraError, MonetraErrorCode } from \"./BaseError\";\n\nexport class RoundingRequiredError extends MonetraError {\n constructor(operation?: string, result?: number) {\n let message =\n \"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, TRUNCATE`;\n }\n super(message, MonetraErrorCode.ROUNDING_REQUIRED);\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 isNegativeResult = numerator < 0n !== denominator < 0n;\n const isPositiveResult = !isNegativeResult;\n\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 isPositiveResult ? 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 isPositiveResult ? quotient + 1n : quotient;\n case RoundingMode.HALF_UP:\n if (isMoreThanHalf || isHalf) {\n return isPositiveResult ? quotient + 1n : quotient - 1n;\n }\n return quotient;\n case RoundingMode.HALF_DOWN:\n if (isMoreThanHalf) {\n return isPositiveResult ? quotient + 1n : quotient - 1n;\n }\n return quotient;\n case RoundingMode.HALF_EVEN:\n if (isMoreThanHalf) {\n return isPositiveResult ? 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 isPositiveResult ? quotient + 1n : quotient - 1n;\n }\n }\n return quotient;\n case RoundingMode.TRUNCATE:\n // Truncate towards zero (simply return the quotient without adjustment)\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(\n \"divide\",\n Number(product) / Number(numerator),\n );\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 * Options for locale-aware parsing.\n */\nexport interface LocaleParseOptions {\n /**\n * The locale to use for parsing (e.g., \"en-US\", \"de-DE\").\n * Used to detect decimal and grouping separators.\n */\n locale: string;\n}\n\n/**\n * Parses a locale-formatted string into a normalized decimal string.\n *\n * Handles locale-specific decimal separators (e.g., comma in German \"1.234,56\")\n * and grouping separators (e.g., period in German \"1.234\").\n *\n * @param amount - The locale-formatted amount string (e.g., \"1,234.56\" or \"1.234,56\").\n * @param options - The locale options.\n * @returns A normalized decimal string (e.g., \"1234.56\").\n * @example\n * parseLocaleString(\"1.234,56\", { locale: \"de-DE\" }); // \"1234.56\"\n * parseLocaleString(\"1,234.56\", { locale: \"en-US\" }); // \"1234.56\"\n */\nexport function parseLocaleString(\n amount: string,\n options: LocaleParseOptions,\n): string {\n // Use Intl.NumberFormat to determine the locale's separators\n const parts = new Intl.NumberFormat(options.locale, {\n style: \"decimal\",\n minimumFractionDigits: 1,\n useGrouping: true,\n }).formatToParts(1234.5);\n\n let decimalSeparator = \".\";\n let groupSeparator = \",\";\n\n for (const part of parts) {\n if (part.type === \"decimal\") {\n decimalSeparator = part.value;\n } else if (part.type === \"group\") {\n groupSeparator = part.value;\n }\n }\n\n // Handle the case where group and decimal separators are the same\n // (shouldn't happen, but be defensive)\n if (groupSeparator === decimalSeparator) {\n throw new Error(\n `Invalid locale configuration: group and decimal separators are the same for locale ${options.locale}`,\n );\n }\n\n // Remove currency symbols and whitespace\n let normalized = amount.replace(/[^\\d.,\\-\\s]/g, \"\").trim();\n\n // Handle negative sign\n const isNegative = normalized.startsWith(\"-\") || amount.includes(\"(\");\n normalized = normalized.replace(/[-()]/g, \"\");\n\n // Remove all grouping separators\n normalized = normalized.split(groupSeparator).join(\"\");\n\n // Replace locale decimal separator with standard period\n normalized = normalized.split(decimalSeparator).join(\".\");\n\n return isNegative ? `-${normalized}` : normalized;\n}\n\n/**\n * Parses a locale-formatted amount string and converts it to Money minor units.\n *\n * This is a convenience function that combines locale parsing with currency validation.\n *\n * @param amount - The locale-formatted amount string.\n * @param currency - The currency to validate against.\n * @param options - The locale options.\n * @returns The amount in minor units as a BigInt.\n * @throws {InvalidPrecisionError} If the precision exceeds the currency's decimals.\n * @example\n * parseLocaleToMinor(\"1.234,56\", EUR, { locale: \"de-DE\" }); // 123456n\n */\nexport function parseLocaleToMinor(\n amount: string,\n currency: Currency,\n options: LocaleParseOptions,\n): bigint {\n const normalized = parseLocaleString(amount, options);\n return parseToMinor(normalized, currency);\n}\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 // Reject inputs with no digits (e.g. \"-\" or \"\")\n if (!/[0-9]/.test(amount)) {\n throw new Error(\"Invalid format\");\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 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 * Whether to use accounting format for negative numbers.\n * When true, negative values are wrapped in parentheses instead of using a minus sign.\n * Defaults to false.\n * @example\n * // accounting: false (default): \"-$1.00\"\n * // accounting: true: \"($1.00)\"\n */\n accounting?: 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 const display = options?.display || \"symbol\";\n const useAccounting = options?.accounting ?? false;\n\n const decimals = money.currency.decimals;\n const minor = money.minor;\n const isNegative = minor < 0n;\n const absMinor = isNegative ? -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 if (isNegative) {\n return useAccounting ? `(${absString})` : `-${absString}`;\n }\n return absString;\n }\n\n // Use formatToParts to get the template (sign position, currency position)\n let templateParts: Intl.NumberFormatPart[];\n try {\n templateParts = new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: money.currency.code,\n currencyDisplay: display,\n }).formatToParts(isNegative ? -1234.5 : 1234.5);\n } catch (e) {\n // Fallback for custom currencies or invalid codes\n const symbol =\n display === \"symbol\" ? money.currency.symbol : money.currency.code;\n if (isNegative) {\n return useAccounting\n ? `(${symbol}${absString})`\n : `-${symbol}${absString}`;\n }\n return `${symbol}${absString}`;\n }\n\n let result = \"\";\n let numberInserted = false;\n\n for (const part of templateParts) {\n switch (part.type) {\n case \"minusSign\": {\n // Skip the minus sign, we'll handle it later for accounting format\n if (!useAccounting) {\n result += part.value;\n }\n break;\n }\n\n case \"integer\": {\n if (!numberInserted) {\n result += absString;\n numberInserted = true;\n }\n break;\n }\n\n // Skip the remaining numeric parts, since we already inserted the full number string.\n case \"group\":\n case \"decimal\":\n case \"fraction\": {\n break;\n }\n\n case \"currency\": {\n if (display === \"symbol\" && money.currency.symbol) {\n result += money.currency.symbol;\n } else {\n result += part.value;\n }\n break;\n }\n\n default: {\n result += part.value; // literals, parentheses, etc.\n break;\n }\n }\n }\n\n // Apply accounting format if negative\n if (useAccounting && isNegative) {\n return `(${result.trim()})`;\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 * Alias for `fromMinor`. Creates a Money instance from cents/minor units.\n *\n * @param cents - The amount in minor units.\n * @param currency - The currency of the money (object or code string).\n * @returns A new Money instance.\n * @example\n * const m = Money.fromCents(100, 'USD'); // $1.00\n */\n static fromCents(cents: bigint | number, currency: Currency | string): Money {\n return Money.fromMinor(cents, 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 * Alias for `fromMajor`. Creates a Money instance from a decimal string.\n *\n * @param amount - The amount as a string (e.g., \"10.50\").\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.fromDecimal(\"10.50\", 'USD'); // $10.50\n */\n static fromDecimal(amount: string, currency: Currency | string): Money {\n return Money.fromMajor(amount, currency);\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(this.minor < 0n ? -this.minor : this.minor, this.currency);\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 * Clamps this Money value between a minimum and maximum.\n *\n * @param min - The minimum Money value.\n * @param max - The maximum Money value.\n * @returns A new Money instance clamped between min and max.\n * @throws {CurrencyMismatchError} If currencies don't match.\n * @throws {Error} If min is greater than max.\n * @example\n * const price = Money.fromMajor(\"150\", 'USD');\n * const clamped = price.clamp(Money.fromMajor(\"50\", 'USD'), Money.fromMajor(\"100\", 'USD'));\n * // clamped is $100.00\n */\n clamp(min: Money, max: Money): Money {\n assertSameCurrency(this, min);\n assertSameCurrency(this, max);\n\n if (min.greaterThan(max)) {\n throw new Error(\"Clamp min cannot be greater than max\");\n }\n\n if (this.lessThan(min)) {\n return new Money(min.minor, this.currency);\n }\n if (this.greaterThan(max)) {\n return new Money(max.minor, this.currency);\n }\n return this;\n }\n\n /**\n * Returns the value as a decimal string without locale formatting.\n *\n * This returns a raw decimal representation suitable for storage or calculations,\n * without any currency symbols, grouping separators, or locale-specific formatting.\n *\n * @returns The decimal string representation (e.g., \"10.50\", \"-5.25\").\n * @example\n * const m = Money.fromMajor(\"1234.56\", 'USD');\n * m.toDecimalString(); // \"1234.56\"\n */\n toDecimalString(): string {\n const decimals = this.currency.decimals;\n const isNegative = this.minor < 0n;\n const absMinor = isNegative ? -this.minor : this.minor;\n\n if (decimals === 0) {\n return isNegative ? `-${absMinor.toString()}` : absMinor.toString();\n }\n\n const divisor = 10n ** BigInt(decimals);\n const integerPart = absMinor / divisor;\n const fractionalPart = absMinor % divisor;\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, \"0\");\n const result = `${integerPart}.${fractionalStr}`;\n\n return isNegative ? `-${result}` : result;\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 * JSON reviver function for deserializing Money objects.\n *\n * Use with `JSON.parse()` to automatically reconstruct Money instances:\n *\n * @param key - The JSON key (unused).\n * @param value - The parsed JSON value.\n * @returns A Money instance if value is a serialized Money object, otherwise the original value.\n * @example\n * const json = '{\"amount\": \"1050\", \"currency\": \"USD\", \"precision\": 2}';\n * const money = JSON.parse(json, Money.reviver);\n * // money is Money instance: $10.50\n */\n static reviver(key: string, value: unknown): unknown {\n if (\n value !== null &&\n typeof value === \"object\" &&\n \"amount\" in value &&\n \"currency\" in value &&\n \"precision\" in value &&\n typeof (value as Record<string, unknown>).amount === \"string\" &&\n typeof (value as Record<string, unknown>).currency === \"string\" &&\n typeof (value as Record<string, unknown>).precision === \"number\"\n ) {\n const obj = value as {\n amount: string;\n currency: string;\n precision: number;\n };\n return Money.fromMinor(BigInt(obj.amount), obj.currency);\n }\n return value;\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 { Entry } from \"./types\";\n\n/**\n * SHA-256 hash function that works in both Node.js and browsers.\n */\nlet hashFunction: ((data: string) => string | Promise<string>) | null = null;\n\n/**\n * Initializes the hash function based on the environment.\n * Uses Node.js crypto if available, falls back to SubtleCrypto for browsers.\n */\nfunction getHashFunction(): (data: string) => string | Promise<string> {\n if (hashFunction) return hashFunction;\n\n // Private escape hatch for tests to force the SubtleCrypto path.\n // This does not affect normal usage unless explicitly set.\n const disableNodeCrypto =\n (globalThis as unknown as { __MONETRA_DISABLE_NODE_CRYPTO__?: boolean })\n .__MONETRA_DISABLE_NODE_CRYPTO__ === true;\n\n // Try Node.js crypto first\n if (!disableNodeCrypto && typeof globalThis !== \"undefined\") {\n try {\n // Dynamic import to avoid bundler issues\n const nodeCrypto = require(\"crypto\");\n if (nodeCrypto && nodeCrypto.createHash) {\n hashFunction = (data: string): string => {\n return nodeCrypto.createHash(\"sha256\").update(data).digest(\"hex\");\n };\n return hashFunction;\n }\n } catch {\n // Node crypto not available\n }\n }\n\n // Fall back to SubtleCrypto (browser)\n if (\n typeof globalThis !== \"undefined\" &&\n globalThis.crypto &&\n globalThis.crypto.subtle\n ) {\n hashFunction = async (data: string): Promise<string> => {\n const encoder = new TextEncoder();\n const dataBuffer = encoder.encode(data);\n const hashBuffer = await globalThis.crypto.subtle.digest(\n \"SHA-256\",\n dataBuffer,\n );\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n return hashArray.map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n };\n return hashFunction;\n }\n\n // No crypto available\n throw new Error(\n \"No cryptographic hash function available. \" +\n \"Ensure you are running in Node.js or a browser with SubtleCrypto support.\",\n );\n}\n\n/**\n * Allows injecting a custom hash function for testing or special environments.\n */\nexport function setHashFunction(\n fn: (data: string) => string | Promise<string>,\n): void {\n hashFunction = fn;\n}\n\nfunction serialize(data: unknown): string {\n return JSON.stringify(data, (_key, value) => {\n if (typeof value === \"bigint\") {\n return value.toString();\n }\n // Handle Money objects if they are passed directly and not via toJSON\n if (\n value &&\n typeof value === \"object\" &&\n \"minor\" in value &&\n \"currency\" in value\n ) {\n return {\n minor: (value as { minor: bigint }).minor.toString(),\n currency: (value as { currency: { code: string } }).currency.code,\n };\n }\n return value;\n });\n}\n\n/**\n * Generates a SHA-256 hash of the provided data.\n * Works in both Node.js and browser environments.\n *\n * @param data - The data to hash.\n * @returns The hash as a hex string (sync in Node.js, may be async in browser).\n */\nexport function generateHash(data: unknown): string | Promise<string> {\n const serialized = serialize(data);\n const fn = getHashFunction();\n return fn(serialized);\n}\n\n/**\n * Synchronous version of generateHash for environments that support it.\n * Throws if only async hashing is available (browser without await).\n *\n * @param data - The data to hash.\n * @returns The hash as a hex string.\n */\nexport function generateHashSync(data: unknown): string {\n const result = generateHash(data);\n if (result instanceof Promise) {\n throw new Error(\n \"Synchronous hashing not available in this environment. \" +\n \"Use generateHash() with await instead.\",\n );\n }\n return result;\n}\n\n/**\n * Verifies the integrity of a chain of ledger entries.\n * Checks that each entry's hash is correct and that previousHash pointers form a valid chain.\n *\n * @param entries - The entries to verify.\n * @returns True if the chain is valid, false if tampered.\n */\nexport async function verifyChain(entries: Entry[]): Promise<boolean> {\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n const previousEntry = i > 0 ? entries[i - 1] : null;\n\n // Check previous hash pointer\n if (previousEntry) {\n if (entry.previousHash !== previousEntry.hash) return false;\n } else {\n if (entry.previousHash !== null) return false;\n }\n\n // Check integrity of current entry\n const { hash, ...content } = entry;\n const calculatedHash = await generateHash(content);\n\n if (hash !== calculatedHash) return false;\n }\n return true;\n}\n\n/**\n * Synchronous version of verifyChain for Node.js environments.\n *\n * @param entries - The entries to verify.\n * @returns True if the chain is valid.\n */\nexport function verifyChainSync(entries: Entry[]): boolean {\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n const previousEntry = i > 0 ? entries[i - 1] : null;\n\n if (previousEntry) {\n if (entry.previousHash !== previousEntry.hash) return false;\n } else {\n if (entry.previousHash !== null) return false;\n }\n\n const { hash, ...content } = entry;\n const calculatedHash = generateHashSync(content);\n\n if (hash !== calculatedHash) return false;\n }\n return true;\n}\n","import { Money } from \"../money/Money\";\nimport { Currency } from \"../currency/Currency\";\nimport { CurrencyMismatchError } from \"../errors/CurrencyMismatchError\";\nimport {\n Entry,\n TransactionMetadata,\n LedgerSnapshot,\n TransactionType,\n} from \"./types\";\nimport {\n generateHash,\n generateHashSync,\n verifyChain,\n verifyChainSync,\n} from \"./verification\";\nimport { randomUUID } from \"crypto\";\n\n/**\n * Current snapshot format version.\n */\nconst SNAPSHOT_VERSION = 2;\n\nexport class Ledger {\n private entries: Entry[] = [];\n private currency: string;\n\n constructor(currency: string | Currency) {\n this.currency = typeof currency === \"string\" ? currency : currency.code;\n }\n\n /**\n * Records a transaction in the ledger.\n * Returns the created entry for reference.\n * Note: This is the synchronous version for Node.js. Use recordAsync for browser support.\n */\n record(money: Money, metadata: TransactionMetadata): Entry {\n if (money.currency.code !== this.currency) {\n throw new CurrencyMismatchError(this.currency, money.currency.code);\n }\n\n const previousHash =\n this.entries.length > 0\n ? this.entries[this.entries.length - 1].hash\n : null;\n\n // Construct the entry content first\n const content = {\n id: randomUUID(),\n money,\n metadata: Object.freeze({\n ...metadata,\n timestamp: metadata.timestamp ?? new Date(),\n }),\n createdAt: new Date(),\n previousHash,\n };\n\n // Calculate hash (sync)\n const hash = generateHashSync(content);\n\n // Create full entry\n const entry: Entry = {\n ...content,\n hash,\n };\n\n this.entries.push(Object.freeze(entry)); // Immutable\n\n return entry;\n }\n\n /**\n * Records a transaction in the ledger (async version for browser support).\n * Returns the created entry for reference.\n */\n async recordAsync(\n money: Money,\n metadata: TransactionMetadata,\n ): Promise<Entry> {\n if (money.currency.code !== this.currency) {\n throw new CurrencyMismatchError(this.currency, money.currency.code);\n }\n\n const previousHash =\n this.entries.length > 0\n ? this.entries[this.entries.length - 1].hash\n : null;\n\n const content = {\n id: randomUUID(),\n money,\n metadata: Object.freeze({\n ...metadata,\n timestamp: metadata.timestamp ?? new Date(),\n }),\n createdAt: new Date(),\n previousHash,\n };\n\n const hash = await generateHash(content);\n\n const entry: Entry = {\n ...content,\n hash,\n };\n\n this.entries.push(Object.freeze(entry));\n\n return entry;\n }\n\n /**\n * Gets the current balance.\n */\n getBalance(): Money {\n return this.entries.reduce(\n (balance, entry) => balance.add(entry.money),\n Money.zero(this.currency),\n );\n }\n\n /**\n * Returns the complete transaction history.\n */\n getHistory(): ReadonlyArray<Entry> {\n return Object.freeze([...this.entries]);\n }\n\n /**\n * Filters entries by criteria.\n */\n query(filter: {\n type?: TransactionType | TransactionType[];\n from?: Date;\n to?: Date;\n reference?: string;\n minAmount?: Money;\n maxAmount?: Money;\n tags?: string[];\n }): Entry[] {\n return this.entries.filter((entry) => {\n if (filter.type) {\n const types = Array.isArray(filter.type) ? filter.type : [filter.type];\n if (!types.includes(entry.metadata.type)) return false;\n }\n if (filter.from && entry.createdAt < filter.from) return false;\n if (filter.to && entry.createdAt > filter.to) return false;\n if (filter.reference && entry.metadata.reference !== filter.reference)\n return false;\n if (filter.minAmount && entry.money.lessThan(filter.minAmount))\n return false;\n if (filter.maxAmount && entry.money.greaterThan(filter.maxAmount))\n return false;\n if (filter.tags && entry.metadata.tags) {\n const hasTag = filter.tags.some((tag) =>\n entry.metadata.tags?.includes(tag),\n );\n if (!hasTag) return false;\n }\n return true;\n });\n }\n\n /**\n * Verifies the integrity of the ledger using hash chain (sync version).\n * @returns true if all hashes are valid and chain is unbroken.\n */\n verify(): boolean {\n return verifyChainSync(this.entries);\n }\n\n /**\n * Verifies the integrity of the ledger using hash chain (async version for browsers).\n * @returns Promise resolving to true if all hashes are valid and chain is unbroken.\n */\n async verifyAsync(): Promise<boolean> {\n return verifyChain(this.entries);\n }\n\n /**\n * Exports a snapshot for backup/audit purposes (sync version).\n */\n snapshot(): LedgerSnapshot {\n return {\n version: SNAPSHOT_VERSION,\n entries: [...this.entries],\n balance: this.getBalance(),\n currency: this.currency,\n createdAt: new Date(),\n checksum: generateHashSync({ entries: this.entries }),\n };\n }\n\n /**\n * Exports a snapshot for backup/audit purposes (async version for browsers).\n */\n async snapshotAsync(): Promise<LedgerSnapshot> {\n const checksum = await generateHash({ entries: this.entries });\n return {\n version: SNAPSHOT_VERSION,\n entries: [...this.entries],\n balance: this.getBalance(),\n currency: this.currency,\n createdAt: new Date(),\n checksum,\n };\n }\n\n /**\n * Restores from a snapshot (sync version).\n */\n static fromSnapshot(snapshot: LedgerSnapshot): Ledger {\n const ledger = new Ledger(snapshot.currency);\n // Verify integrity before restoring\n if (!verifyChainSync(snapshot.entries)) {\n throw new Error(\"Ledger snapshot integrity check failed\");\n }\n ledger.entries = [...snapshot.entries];\n return ledger;\n }\n\n /**\n * Restores from a snapshot (async version for browsers).\n */\n static async fromSnapshotAsync(snapshot: LedgerSnapshot): Promise<Ledger> {\n const ledger = new Ledger(snapshot.currency);\n // Verify integrity before restoring\n const isValid = await verifyChain(snapshot.entries);\n if (!isValid) {\n throw new Error(\"Ledger snapshot integrity check failed\");\n }\n ledger.entries = [...snapshot.entries];\n return ledger;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/tokens/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tokens/index.ts","../../src/currency/registry.ts","../../src/tokens/defineToken.ts"],"sourcesContent":["export * from \"./types\";\nexport * from \"./defineToken\";\n","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/**\n * Returns all registered currencies as a map.\n * @internal Use for testing only.\n */\nexport function getAllCurrencies(): Record<string, Currency> {\n return Object.fromEntries(registry);\n}\n","import { TokenDefinition } from \"./types\";\nimport { registerCurrency } from \"../currency/registry\";\n\n/**\n * Defines a custom token that can be used with Money.\n *\n * @example\n * const ETH = defineToken({\n * code: 'ETH',\n * symbol: 'Ξ',\n * decimals: 18,\n * type: 'crypto',\n * chainId: 1,\n * });\n *\n * const balance = money('1.5', ETH);\n */\nexport function defineToken(definition: {\n code: string;\n symbol: string;\n decimals: number;\n type?: \"fiat\" | \"crypto\" | \"commodity\" | \"custom\";\n locale?: string;\n chainId?: number;\n contractAddress?: string;\n standard?: string;\n coingeckoId?: string;\n}): TokenDefinition {\n if (!definition.code) throw new Error(\"Token definition requires a code\");\n if (!definition.symbol) throw new Error(\"Token definition requires a symbol\");\n if (definition.decimals === undefined)\n throw new Error(\"Token definition requires decimals\");\n\n const token: TokenDefinition = {\n code: definition.code.toUpperCase(),\n symbol: definition.symbol,\n decimals: definition.decimals,\n locale: definition.locale,\n type: definition.type ?? \"custom\",\n chainId: definition.chainId,\n contractAddress: definition.contractAddress,\n standard: definition.standard,\n coingeckoId: definition.coingeckoId,\n };\n\n // Register so it can be used with currency codes\n registerCurrency(token);\n\n return Object.freeze(token);\n}\n\n// Pre-defined popular crypto tokens\nexport const ETH = defineToken({\n code: \"ETH\",\n symbol: \"Ξ\",\n decimals: 18,\n type: \"crypto\",\n chainId: 1,\n coingeckoId: \"ethereum\",\n});\n\nexport const BTC = defineToken({\n code: \"BTC\",\n symbol: \"₿\",\n decimals: 8,\n type: \"crypto\",\n coingeckoId: \"bitcoin\",\n});\n\nexport const USDC = defineToken({\n code: \"USDC\",\n symbol: \"USDC\",\n decimals: 6,\n type: \"crypto\",\n chainId: 1,\n contractAddress: \"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48\",\n standard: \"ERC-20\",\n coingeckoId: \"usd-coin\",\n});\n\nexport const USDT = defineToken({\n code: \"USDT\",\n symbol: \"₮\",\n decimals: 6,\n type: \"crypto\",\n chainId: 1,\n contractAddress: \"0xdac17f958d2ee523a2206206994597c13d831ec7\",\n standard: \"ERC-20\",\n coingeckoId: \"tether\",\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,IAAM,WAAkC,oBAAI,IAAI;AAMzC,SAAS,iBAAiB,UAA0B;AACzD,WAAS,IAAI,SAAS,MAAM,QAAQ;AACtC;;;ACOO,SAAS,YAAY,YAUR;AAClB,MAAI,CAAC,WAAW,KAAM,OAAM,IAAI,MAAM,kCAAkC;AACxE,MAAI,CAAC,WAAW,OAAQ,OAAM,IAAI,MAAM,oCAAoC;AAC5E,MAAI,WAAW,aAAa;AAC1B,UAAM,IAAI,MAAM,oCAAoC;AAEtD,QAAM,QAAyB;AAAA,IAC7B,MAAM,WAAW,KAAK,YAAY;AAAA,IAClC,QAAQ,WAAW;AAAA,IACnB,UAAU,WAAW;AAAA,IACrB,QAAQ,WAAW;AAAA,IACnB,MAAM,WAAW,QAAQ;AAAA,IACzB,SAAS,WAAW;AAAA,IACpB,iBAAiB,WAAW;AAAA,IAC5B,UAAU,WAAW;AAAA,IACrB,aAAa,WAAW;AAAA,EAC1B;AAGA,mBAAiB,KAAK;AAEtB,SAAO,OAAO,OAAO,KAAK;AAC5B;AAGO,IAAM,MAAM,YAAY;AAAA,EAC7B,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,SAAS;AAAA,EACT,aAAa;AACf,CAAC;AAEM,IAAM,MAAM,YAAY;AAAA,EAC7B,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,aAAa;AACf,CAAC;AAEM,IAAM,OAAO,YAAY;AAAA,EAC9B,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,aAAa;AACf,CAAC;AAEM,IAAM,OAAO,YAAY;AAAA,EAC9B,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,aAAa;AACf,CAAC;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/currency/registry.ts","../../src/tokens/defineToken.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/**\n * Returns all registered currencies as a map.\n * @internal Use for testing only.\n */\nexport function getAllCurrencies(): Record<string, Currency> {\n return Object.fromEntries(registry);\n}\n","import { TokenDefinition } from \"./types\";\nimport { registerCurrency } from \"../currency/registry\";\n\n/**\n * Defines a custom token that can be used with Money.\n *\n * @example\n * const ETH = defineToken({\n * code: 'ETH',\n * symbol: 'Ξ',\n * decimals: 18,\n * type: 'crypto',\n * chainId: 1,\n * });\n *\n * const balance = money('1.5', ETH);\n */\nexport function defineToken(definition: {\n code: string;\n symbol: string;\n decimals: number;\n type?: \"fiat\" | \"crypto\" | \"commodity\" | \"custom\";\n locale?: string;\n chainId?: number;\n contractAddress?: string;\n standard?: string;\n coingeckoId?: string;\n}): TokenDefinition {\n if (!definition.code) throw new Error(\"Token definition requires a code\");\n if (!definition.symbol) throw new Error(\"Token definition requires a symbol\");\n if (definition.decimals === undefined)\n throw new Error(\"Token definition requires decimals\");\n\n const token: TokenDefinition = {\n code: definition.code.toUpperCase(),\n symbol: definition.symbol,\n decimals: definition.decimals,\n locale: definition.locale,\n type: definition.type ?? \"custom\",\n chainId: definition.chainId,\n contractAddress: definition.contractAddress,\n standard: definition.standard,\n coingeckoId: definition.coingeckoId,\n };\n\n // Register so it can be used with currency codes\n registerCurrency(token);\n\n return Object.freeze(token);\n}\n\n// Pre-defined popular crypto tokens\nexport const ETH = defineToken({\n code: \"ETH\",\n symbol: \"Ξ\",\n decimals: 18,\n type: \"crypto\",\n chainId: 1,\n coingeckoId: \"ethereum\",\n});\n\nexport const BTC = defineToken({\n code: \"BTC\",\n symbol: \"₿\",\n decimals: 8,\n type: \"crypto\",\n coingeckoId: \"bitcoin\",\n});\n\nexport const USDC = defineToken({\n code: \"USDC\",\n symbol: \"USDC\",\n decimals: 6,\n type: \"crypto\",\n chainId: 1,\n contractAddress: \"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48\",\n standard: \"ERC-20\",\n coingeckoId: \"usd-coin\",\n});\n\nexport const USDT = defineToken({\n code: \"USDT\",\n symbol: \"₮\",\n decimals: 6,\n type: \"crypto\",\n chainId: 1,\n contractAddress: \"0xdac17f958d2ee523a2206206994597c13d831ec7\",\n standard: \"ERC-20\",\n coingeckoId: \"tether\",\n});\n"],"mappings":";AAEA,IAAM,WAAkC,oBAAI,IAAI;AAMzC,SAAS,iBAAiB,UAA0B;AACzD,WAAS,IAAI,SAAS,MAAM,QAAQ;AACtC;;;ACOO,SAAS,YAAY,YAUR;AAClB,MAAI,CAAC,WAAW,KAAM,OAAM,IAAI,MAAM,kCAAkC;AACxE,MAAI,CAAC,WAAW,OAAQ,OAAM,IAAI,MAAM,oCAAoC;AAC5E,MAAI,WAAW,aAAa;AAC1B,UAAM,IAAI,MAAM,oCAAoC;AAEtD,QAAM,QAAyB;AAAA,IAC7B,MAAM,WAAW,KAAK,YAAY;AAAA,IAClC,QAAQ,WAAW;AAAA,IACnB,UAAU,WAAW;AAAA,IACrB,QAAQ,WAAW;AAAA,IACnB,MAAM,WAAW,QAAQ;AAAA,IACzB,SAAS,WAAW;AAAA,IACpB,iBAAiB,WAAW;AAAA,IAC5B,UAAU,WAAW;AAAA,IACrB,aAAa,WAAW;AAAA,EAC1B;AAGA,mBAAiB,KAAK;AAEtB,SAAO,OAAO,OAAO,KAAK;AAC5B;AAGO,IAAM,MAAM,YAAY;AAAA,EAC7B,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,SAAS;AAAA,EACT,aAAa;AACf,CAAC;AAEM,IAAM,MAAM,YAAY;AAAA,EAC7B,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,aAAa;AACf,CAAC;AAEM,IAAM,OAAO,YAAY;AAAA,EAC9B,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,aAAa;AACf,CAAC;AAEM,IAAM,OAAO,YAAY;AAAA,EAC9B,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,aAAa;AACf,CAAC;","names":[]}
|