{"version":3,"file":"numeric-quantity.production.mjs","names":[],"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts","../src/isNumericQuantity.ts"],"sourcesContent":["import type {\n  NumericQuantityOptions,\n  RomanNumeralAscii,\n  RomanNumeralUnicode,\n  SubscriptDigit,\n  SuperscriptDigit,\n  VulgarFraction,\n} from './types';\n\n// #region Decimal_Number Unicode category\n\n/**\n * Unicode decimal digit block start code points.\n * Each block contains 10 contiguous digits (0-9).\n * This list covers all \\p{Nd} (Decimal_Number) blocks through Unicode 17.0.\n * The drift test in index.test.ts validates completeness against the JS engine.\n */\nconst decimalDigitBlockStarts = [\n  0x0030, // ASCII (0-9)\n  0x0660, // Arabic-Indic\n  0x06f0, // Extended Arabic-Indic (Persian/Urdu)\n  0x07c0, // NKo\n  0x0966, // Devanagari\n  0x09e6, // Bengali\n  0x0a66, // Gurmukhi\n  0x0ae6, // Gujarati\n  0x0b66, // Oriya\n  0x0be6, // Tamil\n  0x0c66, // Telugu\n  0x0ce6, // Kannada\n  0x0d66, // Malayalam\n  0x0de6, // Sinhala Lith\n  0x0e50, // Thai\n  0x0ed0, // Lao\n  0x0f20, // Tibetan\n  0x1040, // Myanmar\n  0x1090, // Myanmar Shan\n  0x17e0, // Khmer\n  0x1810, // Mongolian\n  0x1946, // Limbu\n  0x19d0, // New Tai Lue\n  0x1a80, // Tai Tham Hora\n  0x1a90, // Tai Tham Tham\n  0x1b50, // Balinese\n  0x1bb0, // Sundanese\n  0x1c40, // Lepcha\n  0x1c50, // Ol Chiki\n  0xa620, // Vai\n  0xa8d0, // Saurashtra\n  0xa900, // Kayah Li\n  0xa9d0, // Javanese\n  0xa9f0, // Myanmar Tai Laing\n  0xaa50, // Cham\n  0xabf0, // Meetei Mayek\n  0xff10, // Fullwidth\n  0x104a0, // Osmanya\n  0x10d30, // Hanifi Rohingya\n  0x10d40, // Garay\n  0x11066, // Brahmi\n  0x110f0, // Sora Sompeng\n  0x11136, // Chakma\n  0x111d0, // Sharada\n  0x112f0, // Khudawadi\n  0x11450, // Newa\n  0x114d0, // Tirhuta\n  0x11650, // Modi\n  0x116c0, // Takri\n  0x116d0, // Myanmar Pao\n  0x116da, // Myanmar Eastern Pwo Karen\n  0x11730, // Ahom\n  0x118e0, // Warang Citi\n  0x11950, // Dives Akuru\n  0x11bf0, // Sunuwar\n  0x11c50, // Bhaiksuki\n  0x11d50, // Masaram Gondi\n  0x11da0, // Gunjala Gondi\n  0x11de0, // Tolong Siki\n  0x11f50, // Kawi\n  0x16130, // Gurung Khema\n  0x16a60, // Mro\n  0x16ac0, // Tangsa\n  0x16b50, // Pahawh Hmong\n  0x16d70, // Kirat Rai\n  0x1ccf0, // Outlined Digits\n  0x1d7ce, // Mathematical Bold\n  0x1d7d8, // Mathematical Double-Struck\n  0x1d7e2, // Mathematical Sans-Serif\n  0x1d7ec, // Mathematical Sans-Serif Bold\n  0x1d7f6, // Mathematical Monospace\n  0x1e140, // Nyiakeng Puachue Hmong\n  0x1e2f0, // Wancho\n  0x1e4f0, // Nag Mundari\n  0x1e5f1, // Ol Onal\n  0x1e950, // Adlam\n  0x1fbf0, // Segmented Digits\n] as const;\n\n/**\n * Normalizes non-ASCII decimal digits to ASCII digits.\n * Converts characters from Unicode decimal digit blocks (e.g., Arabic-Indic,\n * Devanagari, Bengali) to their ASCII equivalents (0-9).\n *\n * All current Unicode \\p{Nd} blocks are included in decimalDigitBlockStarts.\n */\nexport const normalizeDigits = (str: string): string =>\n  str.replace(/\\p{Nd}/gu, ch => {\n    const cp = ch.codePointAt(0)!;\n    // ASCII digits (0x0030-0x0039) don't need conversion\n    if (cp <= 0x39) return ch;\n    // Binary search for the largest block start ≤ cp\n    let lo = 0;\n    let hi = decimalDigitBlockStarts.length - 1;\n    while (lo < hi) {\n      const mid = (lo + hi + 1) >>> 1;\n      if (decimalDigitBlockStarts[mid] <= cp) {\n        lo = mid;\n      } else {\n        hi = mid - 1;\n      }\n    }\n    return String(cp - decimalDigitBlockStarts[lo]!);\n  });\n\n/**\n * Map of Unicode superscript and subscript digit code points to ASCII digits.\n */\nexport const superSubDigitToAsciiMap: Record<SuperscriptDigit | SubscriptDigit, string> = {\n  '⁰': '0',\n  '¹': '1',\n  '²': '2',\n  '³': '3',\n  '⁴': '4',\n  '⁵': '5',\n  '⁶': '6',\n  '⁷': '7',\n  '⁸': '8',\n  '⁹': '9',\n  '₀': '0',\n  '₁': '1',\n  '₂': '2',\n  '₃': '3',\n  '₄': '4',\n  '₅': '5',\n  '₆': '6',\n  '₇': '7',\n  '₈': '8',\n  '₉': '9',\n} as const;\n\n/**\n * Captures Unicode superscript and subscript digits.\n */\nexport const superSubDigitsRegex: RegExp = /[⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉]/g;\n\n/**\n * Map of Unicode fraction code points to their ASCII equivalents.\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, `${number}/${number | ''}`> = {\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. Commas and underscores are allowed\n * as separators, as long as they appear between digits and are not consecutive.\n *\n * Capture groups:\n *\n * |  #  |    Description                                   |        Example(s)                                                   |\n * | --- | ------------------------------------------------ | ------------------------------------------------------------------- |\n * | `0` | entire string                                    | `\"2 1/3\"` from `\"2 1/3\"`                                            |\n * | `1` | sign (`-` or `+`)                                | `\"-\"` 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 * Capture group 7 contains the trailing invalid portion.\n */\nexport const numericRegexWithTrailingInvalid: 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)*)?(\\s*[^.\\d/].*)?/;\n\n/**\n * Captures any Unicode vulgar fractions.\n */\nexport const vulgarFractionsRegex: RegExp = /([¼½¾⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟}])/g;\n\n// #endregion\n\n// #region Roman numerals\n\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 = /([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/gi;\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\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  decimalSeparator: '.',\n  allowCurrency: false,\n  percentage: false,\n  verbose: 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) => 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  normalizeDigits,\n  numericRegexWithTrailingInvalid,\n  superSubDigitToAsciiMap,\n  superSubDigitsRegex,\n  vulgarFractionToAsciiMap,\n  vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\nimport type {\n  NumericQuantityOptions,\n  NumericQuantityReturnType,\n  NumericQuantityVerboseResult,\n} from './types';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\nconst currencyPrefixRegex = /^([-+]?)\\s*(\\p{Sc}+)\\s*/u;\nconst currencySuffixRegex = /\\s*(\\p{Sc}+)$/u;\nconst percentageSuffixRegex = /%$/;\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 * Input is expected to be a `string`, but will be coerced to `string` if necessary.\n *\n * @param quantity - The value to parse as a numeric quantity.\n * @param options - Optional settings to control parsing behavior.\n */\nfunction numericQuantity(quantity: unknown): number;\nfunction numericQuantity<T extends NumericQuantityOptions>(\n  quantity: unknown,\n  options: T\n): NumericQuantityReturnType<T>;\nfunction numericQuantity(quantity: unknown, options?: NumericQuantityOptions): number;\nfunction numericQuantity(\n  quantity: unknown,\n  options: NumericQuantityOptions = defaultOptions\n): number | bigint | NumericQuantityVerboseResult {\n  const opts: Required<NumericQuantityOptions> = {\n    ...defaultOptions,\n    ...options,\n  };\n\n  // oxlint-disable-next-line typescript/restrict-template-expressions\n  const originalInput = typeof quantity === 'string' ? quantity : `${quantity}`;\n\n  // Metadata for verbose output\n  let currencyPrefix: string | undefined;\n  let currencySuffix: string | undefined;\n  let percentageSuffix: boolean | undefined;\n  let trailingInvalid: string | undefined;\n  let parsedSign: '-' | '+' | undefined;\n  let parsedWhole: number | undefined;\n  let parsedNumerator: number | undefined;\n  let parsedDenominator: number | undefined;\n\n  const buildVerboseResult = (value: number | bigint): NumericQuantityVerboseResult => {\n    const result: NumericQuantityVerboseResult = {\n      value,\n      input: originalInput,\n    };\n    if (currencyPrefix) result.currencyPrefix = currencyPrefix;\n    if (currencySuffix) result.currencySuffix = currencySuffix;\n    if (percentageSuffix) result.percentageSuffix = percentageSuffix;\n    if (trailingInvalid) result.trailingInvalid = trailingInvalid;\n    if (parsedSign) result.sign = parsedSign;\n    if (parsedWhole !== undefined) result.whole = parsedWhole;\n    if (parsedNumerator !== undefined) result.numerator = parsedNumerator;\n    if (parsedDenominator !== undefined) result.denominator = parsedDenominator;\n    return result;\n  };\n\n  const returnValue = (value: number | bigint) =>\n    opts.verbose ? buildVerboseResult(value) : value;\n\n  if (typeof quantity === 'number' || typeof quantity === 'bigint') {\n    return returnValue(quantity);\n  }\n\n  let finalResult = NaN;\n  let workingString = originalInput;\n\n  // Strip currency prefix if allowed (preserving leading dash for negatives)\n  if (opts.allowCurrency) {\n    const prefixMatch = currencyPrefixRegex.exec(workingString);\n    if (prefixMatch && prefixMatch[2]) {\n      currencyPrefix = prefixMatch[2];\n      // Keep the dash if present, remove currency symbol\n      workingString = (prefixMatch[1] || '') + workingString.slice(prefixMatch[0].length);\n    }\n  }\n\n  // Strip currency suffix if allowed (before percentage check)\n  if (opts.allowCurrency) {\n    const suffixMatch = currencySuffixRegex.exec(workingString);\n    if (suffixMatch) {\n      currencySuffix = suffixMatch[1];\n      workingString = workingString.slice(0, -suffixMatch[0].length);\n    }\n  }\n\n  // Strip percentage suffix if option is set\n  if (opts.percentage && percentageSuffixRegex.test(workingString)) {\n    percentageSuffix = true;\n    workingString = workingString.slice(0, -1);\n  }\n\n  // Coerce to string and normalize\n  const quantityAsString = normalizeDigits(\n    workingString\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) => ` ${vulgarFractionToAsciiMap[vf]}`\n      )\n      // Convert superscript/subscript digits to ASCII\n      .replace(\n        superSubDigitsRegex,\n        ch => superSubDigitToAsciiMap[ch as keyof typeof superSubDigitToAsciiMap]\n      )\n      // Convert fraction slash to standard slash\n      .replace('⁄', '/')\n      .trim()\n  );\n\n  // Bail out if the string was only white space\n  if (quantityAsString.length === 0) {\n    return returnValue(NaN);\n  }\n\n  let normalizedString = quantityAsString;\n\n  if (opts.decimalSeparator === ',') {\n    const commaCount = (quantityAsString.match(/,/g) || []).length;\n    if (commaCount === 1) {\n      // Treat lone comma as decimal separator; remove all \".\" since they represent\n      // thousands/whatever separators\n      normalizedString = quantityAsString.replaceAll('.', '_').replace(',', '.');\n    } else if (commaCount > 1) {\n      // The second comma and everything after is \"trailing invalid\"\n      if (!opts.allowTrailingInvalid) {\n        // Bail out if trailing invalid is not allowed\n        return returnValue(NaN);\n      }\n\n      const firstCommaIndex = quantityAsString.indexOf(',');\n      const secondCommaIndex = quantityAsString.indexOf(',', firstCommaIndex + 1);\n      const beforeSecondComma = quantityAsString\n        .substring(0, secondCommaIndex)\n        .replaceAll('.', '_')\n        .replace(',', '.');\n      const afterSecondComma = quantityAsString.substring(secondCommaIndex + 1);\n      normalizedString = opts.allowTrailingInvalid\n        ? beforeSecondComma + '&' + afterSecondComma\n        : beforeSecondComma;\n    } else {\n      // No comma as decimal separator, so remove all \".\" since they represent\n      // thousands/whatever separators\n      normalizedString = quantityAsString.replaceAll('.', '_');\n    }\n  }\n\n  const regexResult = numericRegexWithTrailingInvalid.exec(normalizedString);\n\n  // If the Arabic numeral regex fails, try Roman numerals\n  if (!regexResult) {\n    return returnValue(opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN);\n  }\n\n  // Capture trailing invalid characters: group 7 catches chars starting with\n  // [^.\\d/], but the regex (which lacks a $ anchor) may also leave unconsumed\n  // input starting with \".\", \"/\", or digits (e.g. \"0.1.2\" or \"1/\").\n  const rawTrailing = (regexResult[7] || normalizedString.slice(regexResult[0].length)).trim();\n  if (rawTrailing) {\n    trailingInvalid = rawTrailing;\n    if (!opts.allowTrailingInvalid) {\n      return returnValue(NaN);\n    }\n  }\n\n  const [, sign, ng1temp, ng2temp] = regexResult;\n  if (sign === '-' || sign === '+') parsedSign = sign;\n  const numberGroup1 = ng1temp.replaceAll(',', '').replaceAll('_', '');\n  const numberGroup2 = ng2temp?.replaceAll(',', '').replaceAll('_', '');\n\n  // Numerify capture group 1\n  if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n    finalResult = 0;\n  } else {\n    if (opts.bigIntOnOverflow) {\n      const asBigInt = sign === '-' ? BigInt(`-${numberGroup1}`) : BigInt(numberGroup1);\n      if (\n        asBigInt > BigInt(Number.MAX_SAFE_INTEGER) ||\n        asBigInt < BigInt(Number.MIN_SAFE_INTEGER)\n      ) {\n        // Note: percentage division not applied to bigint overflow\n        return returnValue(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    finalResult = sign === '-' ? finalResult * -1 : finalResult;\n    if (percentageSuffix && opts.percentage !== 'number') {\n      finalResult = finalResult / 100;\n    }\n    return returnValue(finalResult);\n  }\n\n  const roundingFactor =\n    opts.round === false ? NaN : 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    parsedNumerator = numerator;\n    parsedDenominator = denominator;\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    parsedWhole = finalResult;\n    parsedNumerator = numerator;\n    parsedDenominator = denominator;\n    finalResult += isNaN(roundingFactor)\n      ? numerator / denominator\n      : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\n  }\n\n  finalResult = sign === '-' ? finalResult * -1 : finalResult;\n\n  // Apply percentage division if needed\n  if (percentageSuffix && opts.percentage !== 'number') {\n    finalResult = isNaN(roundingFactor)\n      ? finalResult / 100\n      : Math.round((finalResult / 100) * roundingFactor) / roundingFactor;\n  }\n\n  return returnValue(finalResult);\n}\n\nexport { numericQuantity };\n","import { numericQuantity } from './numericQuantity';\nimport type { NumericQuantityOptions } from './types';\n\n/**\n * Checks if a string represents a valid numeric quantity.\n *\n * Returns `true` if the string can be parsed as a number, `false` otherwise.\n * Accepts the same options as `numericQuantity`.\n */\nexport const isNumericQuantity = (\n  quantity: string | number,\n  options?: NumericQuantityOptions\n): boolean => {\n  const result = numericQuantity(quantity, { ...options, verbose: false });\n  return typeof result === 'bigint' || !isNaN(result);\n};\n"],"mappings":"AAiBA,MAAM,EAA0B,CAC9B,GACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,OACA,OACA,OACA,OACA,OACA,OACA,OACA,OACA,OACA,OACA,MACF,EASa,EAAmB,GAC9B,EAAI,QAAQ,WAAY,GAAM,CAC5B,IAAM,EAAK,EAAG,YAAY,CAAC,EAE3B,GAAI,GAAM,GAAM,OAAO,EAEvB,IAAI,EAAK,EACL,EAAK,EAAwB,OAAS,EAC1C,KAAO,EAAK,GAAI,CACd,IAAM,EAAO,EAAK,EAAK,IAAO,EAC1B,EAAwB,IAAQ,EAClC,EAAK,EAEL,EAAK,EAAM,CAEf,CACA,OAAO,OAAO,EAAK,EAAwB,EAAI,CACjD,CAAC,EAKU,EAA6E,CACxF,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,GACP,EAKa,EAA8B,0BAK9B,EAA+E,CAC1F,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,OACL,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,MACL,IAAK,IACP,EA2Ba,EACX,iMAKW,EACX,+MAKW,EAA+B,4BAe/B,EAET,CACF,IAAK,IACL,GAAI,IACJ,EAAG,IACH,GA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"}