{"version":3,"sources":["../src/index.ts","../src/parser.ts","../src/formatter.ts","../src/helpers.ts"],"sourcesContent":["// Types\nexport type { ParseOk, ParseErr, ParseOutput, FormatOptions, ParseOptions } from './types.js';\n\n// Main functions\nexport { parseINR } from './parser.js';\nexport { formatINR } from './formatter.js';\n\n// Helper functions\nexport { toIndianCompact, fromIndianCompact } from './helpers.js';","import { ParseOptions, ParseOutput } from './types.js';\n\nconst RX = {\n  currency: /^(₹|rs\\.?|inr|रु)/i,\n  sep: /[\\s\\u00A0,]+/g,\n};\n\nconst UNIT_MULT: Record<string, number> = {\n  cr: 1e7,\n  crs: 1e7,\n  crore: 1e7,\n  crores: 1e7,\n  l: 1e5,\n  lac: 1e5,\n  lacs: 1e5,\n  lakh: 1e5,\n  lakhs: 1e5,\n  // Metric units (optional)\n  k: 1e3,\n  m: 1e6,\n  b: 1e9,\n};\n\nfunction parseNumberLoose(s: string): number {\n  const cleaned = s.replace(/[\\s,\\u00A0]+/g, '');\n  return Number(cleaned);\n}\n\nexport function parseINR(\n  input: string,\n  opts: ParseOptions = {}\n): ParseOutput {\n  let s = input?.trim() ?? '';\n  if (!s) return { kind: 'err', reason: 'empty' };\n\n  const maxLen = opts.maxLen ?? 200;\n  if (s.length > maxLen) {\n    return { kind: 'err', reason: 'invalid' };\n  }\n\n  let negative = false;\n\n  // Check for accounting parentheses\n  if (/^\\(/.test(s) && /\\)$/.test(s)) {\n    negative = true;\n    s = s.slice(1, -1).trim();\n  }\n\n  // Check for leading sign\n  if (/^[-+]/.test(s)) {\n    negative = s[0] === '-' || negative;\n    s = s.slice(1).trim();\n  }\n\n  let detectedCurrency: 'INR' | undefined;\n  // Strip currency marker\n  s = s\n    .replace(RX.currency, () => {\n      detectedCurrency = 'INR';\n      return '';\n    })\n    .trim();\n\n  // Normalize separators: replace commas with spaces, collapse whitespace\n  s = s.replace(/,/g, ' ').replace(/[\\s\\u00A0]+/g, ' ').trim();\n\n  // Try mixed units first: \"X cr Y lakh\"\n  let m = s.match(\n    /^([0-9]+(?:\\s[0-9]+)*(?:\\.[0-9]+)?(?:e[+-]?[0-9]+)?)\\s*(cr(?:s)?|crores?)\\s+([0-9]+(?:\\s[0-9]+)*(?:\\.[0-9]+)?(?:e[+-]?[0-9]+)?)\\s*(l(?:acs?)?|lakhs?)$/i\n  );\n  if (m) {\n    const x = parseNumberLoose(m[1]!);\n    const y = parseNumberLoose(m[3]!);\n    const v = x * 1e7 + y * 1e5;\n\n    if (!Number.isFinite(v)) {\n      return { kind: 'err', reason: 'nonfinite' };\n    }\n    if (Math.abs(v) > Number.MAX_SAFE_INTEGER) {\n      return { kind: 'err', reason: 'overflow' };\n    }\n\n    return {\n      kind: 'ok',\n      value: negative ? -v : v,\n      negative,\n      detected: {\n        currency: detectedCurrency,\n        unit: 'crore',\n        raw: input,\n      },\n    };\n  }\n\n  // Try scalar × unit: \"X cr|lakh|lac|l\" (and optionally k/m/b)\n  const unitPattern = opts.allowMetricUnits\n    ? /^([0-9]+(?:\\s[0-9]+)*(?:\\.[0-9]+)?(?:e[+-]?[0-9]+)?)\\s*(cr(?:s)?|crores?|l(?:acs?)?|lakhs?|k|m|b)$/i\n    : /^([0-9]+(?:\\s[0-9]+)*(?:\\.[0-9]+)?(?:e[+-]?[0-9]+)?)\\s*(cr(?:s)?|crores?|l(?:acs?)?|lakhs?)$/i;\n\n  m = s.match(unitPattern);\n  if (m) {\n    const x = parseNumberLoose(m[1]!);\n    const u = m[2]!.toLowerCase();\n\n    // Determine unit key\n    let unitKey: string;\n    let detectedUnit: 'lakh' | 'crore' | null = null;\n\n    if (u.startsWith('cr') || u === 'crore' || u === 'crores') {\n      unitKey = 'crore';\n      detectedUnit = 'crore';\n    } else if (u === 'l' || u.startsWith('lac') || u.startsWith('lak')) {\n      unitKey = u.startsWith('lak') ? 'lakh' : u.startsWith('lac') ? 'lac' : 'l';\n      detectedUnit = 'lakh';\n    } else if (opts.allowMetricUnits && (u === 'k' || u === 'm' || u === 'b')) {\n      unitKey = u;\n      detectedUnit = null; // Metric units don't map to Indian units\n    } else {\n      return { kind: 'err', reason: 'invalid' };\n    }\n\n    const mult = UNIT_MULT[unitKey];\n    if (!mult) {\n      return { kind: 'err', reason: 'invalid' };\n    }\n\n    const v = x * mult;\n\n    if (!Number.isFinite(v)) {\n      return { kind: 'err', reason: 'nonfinite' };\n    }\n    if (Math.abs(v) > Number.MAX_SAFE_INTEGER) {\n      return { kind: 'err', reason: 'overflow' };\n    }\n\n    return {\n      kind: 'ok',\n      value: negative ? -v : v,\n      negative,\n      detected: {\n        currency: detectedCurrency,\n        unit: detectedUnit,\n        raw: input,\n      },\n    };\n  }\n\n  // Try plain number\n  const v = parseNumberLoose(s);\n  if (!Number.isFinite(v)) {\n    return { kind: 'err', reason: 'invalid' };\n  }\n\n  return {\n    kind: 'ok',\n    value: negative ? -v : v,\n    negative,\n    detected: {\n      currency: detectedCurrency,\n      unit: null,\n      raw: input,\n    },\n  };\n}","import { FormatOptions } from './types.js';\n\nfunction fallbackIndianGrouping(num: number, decimals: number): string {\n  const parts = num.toString().split('.');\n  const integerPart = parts[0] || '0';\n  const fractionalPart = parts[1];\n  \n  // Group the integer part using Indian numbering system\n  let grouped = '';\n  const digits = integerPart.split('').reverse();\n  \n  for (let i = 0; i < digits.length; i++) {\n    if (i === 3) {\n      grouped = ',' + grouped;\n    } else if (i > 3 && (i - 3) % 2 === 0) {\n      grouped = ',' + grouped;\n    }\n    grouped = digits[i] + grouped;\n  }\n  \n  // Add fractional part\n  if (fractionalPart !== undefined || decimals > 0) {\n    const fraction = fractionalPart || '';\n    const paddedFraction = fraction.padEnd(decimals, '0').slice(0, decimals);\n    if (paddedFraction) {\n      grouped += '.' + paddedFraction;\n    }\n  }\n  \n  return grouped;\n}\n\nfunction formatWithIntl(num: number, decimals: number): string {\n  try {\n    return new Intl.NumberFormat('en-IN', {\n      minimumFractionDigits: decimals,\n      maximumFractionDigits: decimals,\n    }).format(num);\n  } catch {\n    // Fallback if Intl is not available\n    return fallbackIndianGrouping(Math.abs(num), decimals);\n  }\n}\n\nexport function formatINR(n: number, opts: FormatOptions = {}): string {\n  const {\n    currency = 'INR',\n    ascii = false,\n    decimals = 2,\n    style = 'standard',\n    minCompact = 1e5,\n    spaceBetween = false,\n    sign = 'auto',\n  } = opts;\n\n  if (!Number.isFinite(n)) {\n    throw new Error('Cannot format non-finite number');\n  }\n\n  const isNegative = n < 0;\n  const absN = Math.abs(n);\n\n  let formatted = '';\n\n  // Handle compact styles\n  if (style === 'compact-indian' && absN >= minCompact) {\n    if (absN >= 1e7) {\n      // Crore\n      const croreValue = absN / 1e7;\n      const croreFormatted = croreValue.toFixed(decimals).replace(/\\.?0+$/, '');\n      formatted = croreFormatted + (spaceBetween ? ' ' : '') + 'Cr';\n    } else if (absN >= 1e5) {\n      // Lakh\n      const lakhValue = absN / 1e5;\n      const lakhFormatted = lakhValue.toFixed(decimals).replace(/\\.?0+$/, '');\n      formatted = lakhFormatted + (spaceBetween ? ' ' : '') + 'L';\n    } else {\n      formatted = formatWithIntl(absN, decimals);\n    }\n  } else if (style === 'compact-metric') {\n    // Compact metric style (like Intl.NumberFormat compact)\n    if (absN >= 1e9) {\n      const bValue = absN / 1e9;\n      formatted = bValue.toFixed(1).replace(/\\.?0+$/, '') + 'b';\n    } else if (absN >= 1e6) {\n      const mValue = absN / 1e6;\n      formatted = mValue.toFixed(1).replace(/\\.?0+$/, '') + 'm';\n    } else if (absN >= 1e3) {\n      const kValue = absN / 1e3;\n      formatted = kValue.toFixed(1).replace(/\\.?0+$/, '') + 'k';\n    } else {\n      formatted = formatWithIntl(absN, decimals);\n    }\n  } else {\n    // Standard formatting\n    formatted = formatWithIntl(absN, decimals);\n  }\n\n  // Apply currency prefix\n  let result = formatted;\n  if (currency === 'INR') {\n    const currencySymbol = ascii ? 'Rs ' : '₹';\n    result = currencySymbol + formatted;\n  }\n\n  // Apply sign\n  if (isNegative) {\n    if (sign === 'accounting') {\n      result = `(${result})`;\n    } else {\n      result = '-' + result;\n    }\n  } else if (sign === 'always') {\n    result = '+' + result;\n  }\n\n  return result;\n}","import { ParseOutput } from './types.js';\nimport { parseINR } from './parser.js';\n\nexport function toIndianCompact(\n  n: number,\n  opts?: { decimals?: number; spaceBetween?: boolean }\n): string {\n  const { decimals = 1, spaceBetween = false } = opts || {};\n\n  if (!Number.isFinite(n)) {\n    throw new Error('Cannot format non-finite number');\n  }\n\n  const absN = Math.abs(n);\n  let result = '';\n\n  if (absN >= 1e7) {\n    // Crore\n    const croreValue = absN / 1e7;\n    const croreFormatted = croreValue.toFixed(decimals).replace(/\\.?0+$/, '');\n    result = croreFormatted + (spaceBetween ? ' ' : '') + 'Cr';\n  } else if (absN >= 1e5) {\n    // Lakh\n    const lakhValue = absN / 1e5;\n    const lakhFormatted = lakhValue.toFixed(decimals).replace(/\\.?0+$/, '');\n    result = lakhFormatted + (spaceBetween ? ' ' : '') + 'L';\n  } else {\n    // Below 1 lakh, return as-is with specified decimals\n    result = absN.toFixed(decimals).replace(/\\.?0+$/, '');\n  }\n\n  return n < 0 ? '-' + result : result;\n}\n\nexport function fromIndianCompact(input: string): ParseOutput {\n  // This is essentially an alias to parseINR with focus on compact forms\n  return parseINR(input);\n}"],"mappings":"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","names":["index_exports","__export","formatINR","fromIndianCompact","parseINR","toIndianCompact","__toCommonJS","RX","UNIT_MULT","parseNumberLoose","s","cleaned","parseINR","input","opts","maxLen","negative","detectedCurrency","m","x","y","v","unitPattern","u","unitKey","detectedUnit","mult","fallbackIndianGrouping","num","decimals","parts","integerPart","fractionalPart","grouped","digits","i","paddedFraction","formatWithIntl","formatINR","n","opts","currency","ascii","style","minCompact","spaceBetween","sign","isNegative","absN","formatted","result","toIndianCompact","n","opts","decimals","spaceBetween","absN","result","fromIndianCompact","input","parseINR"]}