{"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n  NumericQuantityOptions,\n  RomanNumeralAscii,\n  RomanNumeralUnicode,\n  VulgarFraction,\n} from './types';\n\n// #region Arabic numerals\n/**\n * Map of Unicode fraction code points to their ASCII equivalents.\n */\nexport const vulgarFractionToAsciiMap: Record<\n  VulgarFraction,\n  `${number}/${number | ''}`\n> = {\n  '¼': '1/4',\n  '½': '1/2',\n  '¾': '3/4',\n  '⅐': '1/7',\n  '⅑': '1/9',\n  '⅒': '1/10',\n  '⅓': '1/3',\n  '⅔': '2/3',\n  '⅕': '1/5',\n  '⅖': '2/5',\n  '⅗': '3/5',\n  '⅘': '4/5',\n  '⅙': '1/6',\n  '⅚': '5/6',\n  '⅛': '1/8',\n  '⅜': '3/8',\n  '⅝': '5/8',\n  '⅞': '7/8',\n  '⅟': '1/',\n} as const;\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * |  #  |    Description                                   |        Example(s)                                                   |\n * | --- | ------------------------------------------------ | ------------------------------------------------------------------- |\n * | `0` | entire string                                    | `\"2 1/3\"` from `\"2 1/3\"`                                            |\n * | `1` | \"negative\" dash                                  | `\"-\"` from `\"-2 1/3\"`                                               |\n * | `2` | whole number or numerator                        | `\"2\"` from `\"2 1/3\"`; `\"1\"` from `\"1/3\"`                            |\n * | `3` | entire fraction, decimal portion, or denominator | `\" 1/3\"` from `\"2 1/3\"`; `\".33\"` from `\"2.33\"`; `\"/3\"` from `\"1/3\"` |\n *\n * _Capture group 2 may include comma/underscore separators._\n *\n * @example\n *\n * ```ts\n * numericRegex.exec(\"1\")     // [ \"1\",     \"1\", null,   null ]\n * numericRegex.exec(\"1.23\")  // [ \"1.23\",  \"1\", \".23\",  null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", \"1\", \" 2/3\", \" 2\" ]\n * numericRegex.exec(\"2/3\")   // [ \"2/3\",   \"2\", \"/3\",   null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", \"2\", \"/ 3\",  null ]\n * ```\n */\nexport const numericRegex: RegExp =\n  /^(?=-?\\s*\\.\\d|-?\\s*\\d)(-)?\\s*((?:\\d(?:[\\d,_]*\\d)?)*)(([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|\\.\\d(?:[\\d,_]*\\d)?([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|(\\s+\\d(?:[\\d,_]*\\d)?\\s*)?\\s*\\/\\s*\\d(?:[\\d,_]*\\d)?)?$/;\n/**\n * Same as {@link numericRegex}, but allows (and ignores) trailing invalid characters.\n */\nexport const numericRegexWithTrailingInvalid: RegExp = new RegExp(\n  numericRegex.source.replace(/\\$$/, '(?:\\\\s*[^\\\\.\\\\d\\\\/].*)?')\n);\n\n/**\n * Captures any Unicode vulgar fractions.\n */\nexport const vulgarFractionsRegex: RegExp = new RegExp(\n  `(${Object.keys(vulgarFractionToAsciiMap).join('|')})`\n);\n// #endregion\n\n// #region Roman numerals\ntype RomanNumeralSequenceFragment =\n  | `${RomanNumeralAscii}`\n  | `${RomanNumeralAscii}${RomanNumeralAscii}`\n  | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`\n  | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`;\n\n/**\n * Map of Roman numeral sequences to their decimal equivalents.\n */\nexport const romanNumeralValues: {\n  [k in RomanNumeralSequenceFragment]?: number;\n} = {\n  MMM: 3000,\n  MM: 2000,\n  M: 1000,\n  CM: 900,\n  DCCC: 800,\n  DCC: 700,\n  DC: 600,\n  D: 500,\n  CD: 400,\n  CCC: 300,\n  CC: 200,\n  C: 100,\n  XC: 90,\n  LXXX: 80,\n  LXX: 70,\n  LX: 60,\n  L: 50,\n  XL: 40,\n  XXX: 30,\n  XX: 20,\n  XII: 12, // only here for tests; not used in practice\n  XI: 11, // only here for tests; not used in practice\n  X: 10,\n  IX: 9,\n  VIII: 8,\n  VII: 7,\n  VI: 6,\n  V: 5,\n  IV: 4,\n  III: 3,\n  II: 2,\n  I: 1,\n} as const;\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents.\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n  RomanNumeralUnicode,\n  keyof typeof romanNumeralValues\n> = {\n  // Roman Numeral One (U+2160)\n  Ⅰ: 'I',\n  // Roman Numeral Two (U+2161)\n  Ⅱ: 'II',\n  // Roman Numeral Three (U+2162)\n  Ⅲ: 'III',\n  // Roman Numeral Four (U+2163)\n  Ⅳ: 'IV',\n  // Roman Numeral Five (U+2164)\n  Ⅴ: 'V',\n  // Roman Numeral Six (U+2165)\n  Ⅵ: 'VI',\n  // Roman Numeral Seven (U+2166)\n  Ⅶ: 'VII',\n  // Roman Numeral Eight (U+2167)\n  Ⅷ: 'VIII',\n  // Roman Numeral Nine (U+2168)\n  Ⅸ: 'IX',\n  // Roman Numeral Ten (U+2169)\n  Ⅹ: 'X',\n  // Roman Numeral Eleven (U+216A)\n  Ⅺ: 'XI',\n  // Roman Numeral Twelve (U+216B)\n  Ⅻ: 'XII',\n  // Roman Numeral Fifty (U+216C)\n  Ⅼ: 'L',\n  // Roman Numeral One Hundred (U+216D)\n  Ⅽ: 'C',\n  // Roman Numeral Five Hundred (U+216E)\n  Ⅾ: 'D',\n  // Roman Numeral One Thousand (U+216F)\n  Ⅿ: 'M',\n  // Small Roman Numeral One (U+2170)\n  ⅰ: 'I',\n  // Small Roman Numeral Two (U+2171)\n  ⅱ: 'II',\n  // Small Roman Numeral Three (U+2172)\n  ⅲ: 'III',\n  // Small Roman Numeral Four (U+2173)\n  ⅳ: 'IV',\n  // Small Roman Numeral Five (U+2174)\n  ⅴ: 'V',\n  // Small Roman Numeral Six (U+2175)\n  ⅵ: 'VI',\n  // Small Roman Numeral Seven (U+2176)\n  ⅶ: 'VII',\n  // Small Roman Numeral Eight (U+2177)\n  ⅷ: 'VIII',\n  // Small Roman Numeral Nine (U+2178)\n  ⅸ: 'IX',\n  // Small Roman Numeral Ten (U+2179)\n  ⅹ: 'X',\n  // Small Roman Numeral Eleven (U+217A)\n  ⅺ: 'XI',\n  // Small Roman Numeral Twelve (U+217B)\n  ⅻ: 'XII',\n  // Small Roman Numeral Fifty (U+217C)\n  ⅼ: 'L',\n  // Small Roman Numeral One Hundred (U+217D)\n  ⅽ: 'C',\n  // Small Roman Numeral Five Hundred (U+217E)\n  ⅾ: 'D',\n  // Small Roman Numeral One Thousand (U+217F)\n  ⅿ: 'M',\n} as const;\n\n/**\n * Captures all Unicode Roman numeral code points.\n */\nexport const romanNumeralUnicodeRegex: RegExp = new RegExp(\n  `(${Object.keys(romanNumeralUnicodeToAsciiMap).join('|')})`,\n  'gi'\n);\n\n/**\n * Captures a valid Roman numeral sequence.\n *\n * Capture groups:\n *\n * |  #  |  Description    |         Example          |\n * | --- | --------------- | ------------------------ |\n * | `0` |  Entire string  |  \"MCCXIV\" from \"MCCXIV\"  |\n * | `1` |  Thousands      |  \"M\" from \"MCCXIV\"       |\n * | `2` |  Hundreds       |  \"CC\" from \"MCCXIV\"      |\n * | `3` |  Tens           |  \"X\" from \"MCCXIV\"       |\n * | `4` |  Ones           |  \"IV\" from \"MCCXIV\"      |\n *\n * @example\n *\n * ```ts\n * romanNumeralRegex.exec(\"M\")      // [      \"M\", \"M\",   \"\",  \"\",   \"\" ]\n * romanNumeralRegex.exec(\"XII\")    // [    \"XII\",  \"\",   \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n * ```\n */\nexport const romanNumeralRegex: RegExp =\n  /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;\n// #endregion\n\n/**\n * Default options for {@link numericQuantity}.\n */\nexport const defaultOptions: Required<NumericQuantityOptions> = {\n  round: 3,\n  allowTrailingInvalid: false,\n  romanNumerals: false,\n  bigIntOnOverflow: false,\n} as const;\n","import {\n  romanNumeralRegex,\n  romanNumeralUnicodeRegex,\n  romanNumeralUnicodeToAsciiMap,\n  romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\n/**\n * Converts a string of Roman numerals to a number, like `parseInt`\n * for Roman numerals. Uses modern, strict rules (only 1 to 3999).\n *\n * The string can include ASCII representations of Roman numerals\n * or Unicode Roman numeral code points (`U+2160` through `U+217F`).\n */\nexport const parseRomanNumerals = (romanNumerals: string): number => {\n  const normalized = `${romanNumerals}`\n    // Convert Unicode Roman numerals to ASCII\n    .replace(\n      romanNumeralUnicodeRegex,\n      (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n        romanNumeralUnicodeToAsciiMap[rn]\n    )\n    // Normalize to uppercase (more common for Roman numerals)\n    .toUpperCase();\n\n  const regexResult = romanNumeralRegex.exec(normalized);\n\n  if (!regexResult) {\n    return NaN;\n  }\n\n  const [, thousands, hundreds, tens, ones] = regexResult;\n\n  return (\n    (romanNumeralValues[thousands as RNV] ?? 0) +\n    (romanNumeralValues[hundreds as RNV] ?? 0) +\n    (romanNumeralValues[tens as RNV] ?? 0) +\n    (romanNumeralValues[ones as RNV] ?? 0)\n  );\n};\n","import {\n  defaultOptions,\n  numericRegex,\n  numericRegexWithTrailingInvalid,\n  vulgarFractionToAsciiMap,\n  vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\nimport type { NumericQuantityOptions } from './types';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nfunction numericQuantity(quantity: string | number): number;\nfunction numericQuantity(quantity: string | number): number;\nfunction numericQuantity(\n  quantity: string | number,\n  options: NumericQuantityOptions & { bigIntOnOverflow: true }\n): number | bigint;\nfunction numericQuantity(\n  quantity: string | number,\n  options?: NumericQuantityOptions\n): number;\nfunction numericQuantity(\n  quantity: string | number,\n  options: NumericQuantityOptions = defaultOptions\n) {\n  if (typeof quantity === 'number' || typeof quantity === 'bigint') {\n    return quantity;\n  }\n\n  let finalResult = NaN;\n\n  // Coerce to string in case qty is a number\n  const quantityAsString = `${quantity}`\n    // Convert vulgar fractions to ASCII, with a leading space\n    // to keep the whole number and the fraction separate\n    .replace(\n      vulgarFractionsRegex,\n      (_m, vf: keyof typeof vulgarFractionToAsciiMap) =>\n        ` ${vulgarFractionToAsciiMap[vf]}`\n    )\n    // Convert fraction slash to standard slash\n    .replace('⁄', '/')\n    .trim();\n\n  // Bail out if the string was only white space\n  if (quantityAsString.length === 0) {\n    return NaN;\n  }\n\n  const opts: Required<NumericQuantityOptions> = {\n    ...defaultOptions,\n    ...options,\n  };\n\n  const regexResult = (\n    opts.allowTrailingInvalid ? numericRegexWithTrailingInvalid : numericRegex\n  ).exec(quantityAsString);\n\n  // If the Arabic numeral regex fails, try Roman numerals\n  if (!regexResult) {\n    return opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN;\n  }\n\n  const [, dash, ng1temp, ng2temp] = regexResult;\n  const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n  const numberGroup2 = ng2temp?.replace(/[,_]/g, '');\n\n  // Numerify capture group 1\n  if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n    finalResult = 0;\n  } else {\n    if (opts.bigIntOnOverflow) {\n      const asBigInt = dash ? BigInt(`-${numberGroup1}`) : BigInt(numberGroup1);\n      if (\n        asBigInt > BigInt(Number.MAX_SAFE_INTEGER) ||\n        asBigInt < BigInt(Number.MIN_SAFE_INTEGER)\n      ) {\n        return asBigInt;\n      }\n    }\n\n    finalResult = parseInt(numberGroup1);\n  }\n\n  // If capture group 2 is null, then we're dealing with an integer\n  // and there is nothing left to process\n  if (!numberGroup2) {\n    return dash ? finalResult * -1 : finalResult;\n  }\n\n  const roundingFactor =\n    opts.round === false\n      ? NaN\n      : parseFloat(`1e${Math.floor(Math.max(0, opts.round))}`);\n\n  if (\n    numberGroup2.startsWith('.') ||\n    numberGroup2.startsWith('e') ||\n    numberGroup2.startsWith('E')\n  ) {\n    // If first char of `numberGroup2` is \".\" or \"e\"/\"E\", it's a decimal\n    const decimalValue = parseFloat(`${finalResult}${numberGroup2}`);\n    finalResult = isNaN(roundingFactor)\n      ? decimalValue\n      : Math.round(decimalValue * roundingFactor) / roundingFactor;\n  } else if (spaceThenSlashRegex.test(numberGroup2)) {\n    // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n    const numerator = parseInt(numberGroup1);\n    const denominator = parseInt(numberGroup2.replace('/', ''));\n    finalResult = isNaN(roundingFactor)\n      ? numerator / denominator\n      : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\n  } else {\n    // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n    const fractionArray = numberGroup2.split('/');\n    const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n    finalResult += isNaN(roundingFactor)\n      ? numerator / denominator\n      : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\n  }\n\n  return dash ? finalResult * -1 : finalResult;\n}\n\nexport { numericQuantity };\n"],"mappings":"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","names":["vulgarFractionToAsciiMap","numericRegex","numericRegexWithTrailingInvalid","vulgarFractionsRegex","romanNumeralValues","romanNumeralUnicodeToAsciiMap","romanNumeralUnicodeRegex","romanNumeralRegex","defaultOptions","parseRomanNumerals","romanNumerals","normalized","romanNumeralUnicodeRegex","_m","rn","romanNumeralUnicodeToAsciiMap","regexResult","romanNumeralRegex","thousands","hundreds","tens","ones","romanNumeralValues","spaceThenSlashRegex","numericQuantity","quantity","options","defaultOptions","finalResult","quantityAsString","vulgarFractionsRegex","_m","vf","vulgarFractionToAsciiMap","opts","regexResult","numericRegexWithTrailingInvalid","numericRegex","parseRomanNumerals","dash","ng1temp","ng2temp","numberGroup1","numberGroup2","asBigInt","roundingFactor","decimalValue","numerator","denominator","fractionArray","v"]}