numeric-quantity 3.2.0 → 3.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.
@@ -91,6 +91,14 @@ interface NumericQuantityVerboseResult {
91
91
  */
92
92
  type VulgarFraction = "¼" | "½" | "¾" | "⅐" | "⅑" | "⅒" | "⅓" | "⅔" | "⅕" | "⅖" | "⅗" | "⅘" | "⅙" | "⅚" | "⅛" | "⅜" | "⅝" | "⅞" | "⅟";
93
93
  /**
94
+ * Unicode superscript digit code points.
95
+ */
96
+ type SuperscriptDigit = "⁰" | "¹" | "²" | "³" | "⁴" | "⁵" | "⁶" | "⁷" | "⁸" | "⁹";
97
+ /**
98
+ * Unicode subscript digit code points.
99
+ */
100
+ type SubscriptDigit = "₀" | "₁" | "₂" | "₃" | "₄" | "₅" | "₆" | "₇" | "₈" | "₉";
101
+ /**
94
102
  * Allowable Roman numeral characters (ASCII, uppercase only).
95
103
  */
96
104
  type RomanNumeralAscii = "I" | "V" | "X" | "L" | "C" | "D" | "M";
@@ -114,6 +122,14 @@ type RomanNumeral = RomanNumeralAscii | RomanNumeralUnicode;
114
122
  */
115
123
  declare const normalizeDigits: (str: string) => string;
116
124
  /**
125
+ * Map of Unicode superscript and subscript digit code points to ASCII digits.
126
+ */
127
+ declare const superSubDigitToAsciiMap: Record<SuperscriptDigit | SubscriptDigit, string>;
128
+ /**
129
+ * Captures Unicode superscript and subscript digits.
130
+ */
131
+ declare const superSubDigitsRegex: RegExp;
132
+ /**
117
133
  * Map of Unicode fraction code points to their ASCII equivalents.
118
134
  */
119
135
  declare const vulgarFractionToAsciiMap: Record<VulgarFraction, `${number}/${number | ""}`>;
@@ -221,5 +237,5 @@ declare function numericQuantity(quantity: string | number, options?: NumericQua
221
237
  */
222
238
  declare const parseRomanNumerals: (romanNumerals: string) => number;
223
239
  //#endregion
224
- export { NumericQuantityOptions, NumericQuantityReturnType, NumericQuantityVerboseResult, RomanNumeral, RomanNumeralAscii, RomanNumeralUnicode, VulgarFraction, defaultOptions, isNumericQuantity, normalizeDigits, numericQuantity, numericRegex, numericRegexWithTrailingInvalid, parseRomanNumerals, romanNumeralRegex, romanNumeralUnicodeRegex, romanNumeralUnicodeToAsciiMap, romanNumeralValues, vulgarFractionToAsciiMap, vulgarFractionsRegex };
240
+ export { NumericQuantityOptions, NumericQuantityReturnType, NumericQuantityVerboseResult, RomanNumeral, RomanNumeralAscii, RomanNumeralUnicode, SubscriptDigit, SuperscriptDigit, VulgarFraction, defaultOptions, isNumericQuantity, normalizeDigits, numericQuantity, numericRegex, numericRegexWithTrailingInvalid, parseRomanNumerals, romanNumeralRegex, romanNumeralUnicodeRegex, romanNumeralUnicodeToAsciiMap, romanNumeralValues, superSubDigitToAsciiMap, superSubDigitsRegex, vulgarFractionToAsciiMap, vulgarFractionsRegex };
225
241
  //# sourceMappingURL=numeric-quantity.cjs.development.d.ts.map
@@ -106,6 +106,35 @@ const normalizeDigits = (str) => str.replace(/\p{Nd}/gu, (ch) => {
106
106
  return String(cp - decimalDigitBlockStarts[lo]);
107
107
  });
108
108
  /**
109
+ * Map of Unicode superscript and subscript digit code points to ASCII digits.
110
+ */
111
+ const superSubDigitToAsciiMap = {
112
+ "⁰": "0",
113
+ "¹": "1",
114
+ "²": "2",
115
+ "³": "3",
116
+ "⁴": "4",
117
+ "⁵": "5",
118
+ "⁶": "6",
119
+ "⁷": "7",
120
+ "⁸": "8",
121
+ "⁹": "9",
122
+ "₀": "0",
123
+ "₁": "1",
124
+ "₂": "2",
125
+ "₃": "3",
126
+ "₄": "4",
127
+ "₅": "5",
128
+ "₆": "6",
129
+ "₇": "7",
130
+ "₈": "8",
131
+ "₉": "9"
132
+ };
133
+ /**
134
+ * Captures Unicode superscript and subscript digits.
135
+ */
136
+ const superSubDigitsRegex = /[⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉]/g;
137
+ /**
109
138
  * Map of Unicode fraction code points to their ASCII equivalents.
110
139
  */
111
140
  const vulgarFractionToAsciiMap = {
@@ -352,7 +381,7 @@ function numericQuantity(quantity, options = defaultOptions) {
352
381
  percentageSuffix = true;
353
382
  workingString = workingString.slice(0, -1);
354
383
  }
355
- const quantityAsString = normalizeDigits(workingString.replace(vulgarFractionsRegex, (_m, vf) => ` ${vulgarFractionToAsciiMap[vf]}`).replace("⁄", "/").trim());
384
+ const quantityAsString = normalizeDigits(workingString.replace(vulgarFractionsRegex, (_m, vf) => ` ${vulgarFractionToAsciiMap[vf]}`).replace(superSubDigitsRegex, (ch) => superSubDigitToAsciiMap[ch]).replace("⁄", "/").trim());
356
385
  if (quantityAsString.length === 0) return returnValue(NaN);
357
386
  let normalizedString = quantityAsString;
358
387
  if (opts.decimalSeparator === ",") {
@@ -441,6 +470,8 @@ exports.romanNumeralRegex = romanNumeralRegex;
441
470
  exports.romanNumeralUnicodeRegex = romanNumeralUnicodeRegex;
442
471
  exports.romanNumeralUnicodeToAsciiMap = romanNumeralUnicodeToAsciiMap;
443
472
  exports.romanNumeralValues = romanNumeralValues;
473
+ exports.superSubDigitToAsciiMap = superSubDigitToAsciiMap;
474
+ exports.superSubDigitsRegex = superSubDigitsRegex;
444
475
  exports.vulgarFractionToAsciiMap = vulgarFractionToAsciiMap;
445
476
  exports.vulgarFractionsRegex = vulgarFractionsRegex;
446
477
  //# sourceMappingURL=numeric-quantity.cjs.development.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"numeric-quantity.cjs.development.js","names":[],"sources":["../../src/constants.ts","../../src/parseRomanNumerals.ts","../../src/numericQuantity.ts","../../src/isNumericQuantity.ts"],"sourcesContent":["import type {\n NumericQuantityOptions,\n RomanNumeralAscii,\n RomanNumeralUnicode,\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 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. 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 =\n /([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/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) =>\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 normalizeDigits,\n numericRegexWithTrailingInvalid,\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 */\nfunction numericQuantity(quantity: string | number): number;\nfunction numericQuantity<T extends NumericQuantityOptions>(\n quantity: string | number,\n options: T\n): NumericQuantityReturnType<T>;\nfunction numericQuantity(\n quantity: string | number,\n options?: NumericQuantityOptions\n): number;\nfunction numericQuantity(\n quantity: string | number,\n options: NumericQuantityOptions = defaultOptions\n): number | bigint | NumericQuantityVerboseResult {\n const opts: Required<NumericQuantityOptions> = {\n ...defaultOptions,\n ...options,\n };\n\n // Metadata for verbose output\n const originalInput = typeof quantity === 'string' ? quantity : `${quantity}`;\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 = (\n value: number | bigint\n ): NumericQuantityVerboseResult => {\n const result: NumericQuantityVerboseResult = { value, input: originalInput };\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 = `${quantity}`;\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 =\n (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) =>\n ` ${vulgarFractionToAsciiMap[vf]}`\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\n .replaceAll('.', '_')\n .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(\n ',',\n firstCommaIndex + 1\n );\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(\n 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 = (\n regexResult[7] || normalizedString.slice(regexResult[0].length)\n ).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\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 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":";;;;;;;;;AAeA,MAAM,0BAA0B;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;AASD,MAAa,mBAAmB,QAC9B,IAAI,QAAQ,aAAY,OAAM;CAC5B,MAAM,KAAK,GAAG,YAAY,EAAE;AAE5B,KAAI,MAAM,GAAM,QAAO;CAEvB,IAAI,KAAK;CACT,IAAI,KAAK,wBAAwB,SAAS;AAC1C,QAAO,KAAK,IAAI;EACd,MAAM,MAAO,KAAK,KAAK,MAAO;AAC9B,MAAI,wBAAwB,QAAQ,GAClC,MAAK;MAEL,MAAK,MAAM;;AAGf,QAAO,OAAO,KAAK,wBAAwB,IAAK;EAChD;;;;AAKJ,MAAa,2BAGT;CACF,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BD,MAAa,eACX;;;;;AAKF,MAAa,kCACX;;;;AAKF,MAAa,uBAA+B;;;;AAe5C,MAAa,qBAET;CACF,KAAK;CACL,IAAI;CACJ,GAAG;CACH,IAAI;CACJ,MAAM;CACN,KAAK;CACL,IAAI;CACJ,GAAG;CACH,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,GAAG;CACH,IAAI;CACJ,MAAM;CACN,KAAK;CACL,IAAI;CACJ,GAAG;CACH,IAAI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"}
1
+ {"version":3,"file":"numeric-quantity.cjs.development.js","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<\n SuperscriptDigit | SubscriptDigit,\n string\n> = {\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<\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. 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 =\n /([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/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) =>\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 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 */\nfunction numericQuantity(quantity: string | number): number;\nfunction numericQuantity<T extends NumericQuantityOptions>(\n quantity: string | number,\n options: T\n): NumericQuantityReturnType<T>;\nfunction numericQuantity(\n quantity: string | number,\n options?: NumericQuantityOptions\n): number;\nfunction numericQuantity(\n quantity: string | number,\n options: NumericQuantityOptions = defaultOptions\n): number | bigint | NumericQuantityVerboseResult {\n const opts: Required<NumericQuantityOptions> = {\n ...defaultOptions,\n ...options,\n };\n\n // Metadata for verbose output\n const originalInput = typeof quantity === 'string' ? quantity : `${quantity}`;\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 = (\n value: number | bigint\n ): NumericQuantityVerboseResult => {\n const result: NumericQuantityVerboseResult = { value, input: originalInput };\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 = `${quantity}`;\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 =\n (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) =>\n ` ${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\n .replaceAll('.', '_')\n .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(\n ',',\n firstCommaIndex + 1\n );\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(\n 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 = (\n regexResult[7] || normalizedString.slice(regexResult[0].length)\n ).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\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 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,0BAA0B;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;AASD,MAAa,mBAAmB,QAC9B,IAAI,QAAQ,aAAY,OAAM;CAC5B,MAAM,KAAK,GAAG,YAAY,EAAE;AAE5B,KAAI,MAAM,GAAM,QAAO;CAEvB,IAAI,KAAK;CACT,IAAI,KAAK,wBAAwB,SAAS;AAC1C,QAAO,KAAK,IAAI;EACd,MAAM,MAAO,KAAK,KAAK,MAAO;AAC9B,MAAI,wBAAwB,QAAQ,GAClC,MAAK;MAEL,MAAK,MAAM;;AAGf,QAAO,OAAO,KAAK,wBAAwB,IAAK;EAChD;;;;AAKJ,MAAa,0BAGT;CACF,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;;;;AAKD,MAAa,sBAA8B;;;;AAK3C,MAAa,2BAGT;CACF,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BD,MAA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"}
@@ -91,6 +91,14 @@ interface NumericQuantityVerboseResult {
91
91
  */
92
92
  type VulgarFraction = "¼" | "½" | "¾" | "⅐" | "⅑" | "⅒" | "⅓" | "⅔" | "⅕" | "⅖" | "⅗" | "⅘" | "⅙" | "⅚" | "⅛" | "⅜" | "⅝" | "⅞" | "⅟";
93
93
  /**
94
+ * Unicode superscript digit code points.
95
+ */
96
+ type SuperscriptDigit = "⁰" | "¹" | "²" | "³" | "⁴" | "⁵" | "⁶" | "⁷" | "⁸" | "⁹";
97
+ /**
98
+ * Unicode subscript digit code points.
99
+ */
100
+ type SubscriptDigit = "₀" | "₁" | "₂" | "₃" | "₄" | "₅" | "₆" | "₇" | "₈" | "₉";
101
+ /**
94
102
  * Allowable Roman numeral characters (ASCII, uppercase only).
95
103
  */
96
104
  type RomanNumeralAscii = "I" | "V" | "X" | "L" | "C" | "D" | "M";
@@ -114,6 +122,14 @@ type RomanNumeral = RomanNumeralAscii | RomanNumeralUnicode;
114
122
  */
115
123
  declare const normalizeDigits: (str: string) => string;
116
124
  /**
125
+ * Map of Unicode superscript and subscript digit code points to ASCII digits.
126
+ */
127
+ declare const superSubDigitToAsciiMap: Record<SuperscriptDigit | SubscriptDigit, string>;
128
+ /**
129
+ * Captures Unicode superscript and subscript digits.
130
+ */
131
+ declare const superSubDigitsRegex: RegExp;
132
+ /**
117
133
  * Map of Unicode fraction code points to their ASCII equivalents.
118
134
  */
119
135
  declare const vulgarFractionToAsciiMap: Record<VulgarFraction, `${number}/${number | ""}`>;
@@ -221,5 +237,5 @@ declare function numericQuantity(quantity: string | number, options?: NumericQua
221
237
  */
222
238
  declare const parseRomanNumerals: (romanNumerals: string) => number;
223
239
  //#endregion
224
- export { NumericQuantityOptions, NumericQuantityReturnType, NumericQuantityVerboseResult, RomanNumeral, RomanNumeralAscii, RomanNumeralUnicode, VulgarFraction, defaultOptions, isNumericQuantity, normalizeDigits, numericQuantity, numericRegex, numericRegexWithTrailingInvalid, parseRomanNumerals, romanNumeralRegex, romanNumeralUnicodeRegex, romanNumeralUnicodeToAsciiMap, romanNumeralValues, vulgarFractionToAsciiMap, vulgarFractionsRegex };
240
+ export { NumericQuantityOptions, NumericQuantityReturnType, NumericQuantityVerboseResult, RomanNumeral, RomanNumeralAscii, RomanNumeralUnicode, SubscriptDigit, SuperscriptDigit, VulgarFraction, defaultOptions, isNumericQuantity, normalizeDigits, numericQuantity, numericRegex, numericRegexWithTrailingInvalid, parseRomanNumerals, romanNumeralRegex, romanNumeralUnicodeRegex, romanNumeralUnicodeToAsciiMap, romanNumeralValues, superSubDigitToAsciiMap, superSubDigitsRegex, vulgarFractionToAsciiMap, vulgarFractionsRegex };
225
241
  //# sourceMappingURL=numeric-quantity.cjs.production.d.ts.map
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=[48,1632,1776,1984,2406,2534,2662,2790,2918,3046,3174,3302,3430,3558,3664,3792,3872,4160,4240,6112,6160,6470,6608,6784,6800,6992,7088,7232,7248,42528,43216,43264,43472,43504,43600,44016,65296,66720,68912,68928,69734,69872,69942,70096,70384,70736,70864,71248,71360,71376,71386,71472,71904,72016,72688,72784,73040,73120,73184,73552,90416,92768,92864,93008,93552,118e3,120782,120792,120802,120812,120822,123200,123632,124144,124401,125264,130032],t=t=>t.replace(/\p{Nd}/gu,t=>{let n=t.codePointAt(0);if(n<=57)return t;let r=0,i=e.length-1;for(;r<i;){let t=r+i+1>>>1;e[t]<=n?r=t:i=t-1}return String(n-e[r])}),n={"¼":`1/4`,"½":`1/2`,"¾":`3/4`,"⅐":`1/7`,"⅑":`1/9`,"⅒":`1/10`,"⅓":`1/3`,"⅔":`2/3`,"⅕":`1/5`,"⅖":`2/5`,"⅗":`3/5`,"⅘":`4/5`,"⅙":`1/6`,"⅚":`5/6`,"⅛":`1/8`,"⅜":`3/8`,"⅝":`5/8`,"⅞":`7/8`,"⅟":`1/`},r=/^(?=[-+]?\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)*)?$/,i=/^(?=[-+]?\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/].*)?/,a=/([¼½¾⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟}])/g,o={MMM:3e3,MM:2e3,M:1e3,CM:900,DCCC:800,DCC:700,DC:600,D:500,CD:400,CCC:300,CC:200,C:100,XC:90,LXXX:80,LXX:70,LX:60,L:50,XL:40,XXX:30,XX:20,XII:12,XI:11,X:10,IX:9,VIII:8,VII:7,VI:6,V:5,IV:4,III:3,II:2,I:1},s={Ⅰ:`I`,Ⅱ:`II`,Ⅲ:`III`,Ⅳ:`IV`,Ⅴ:`V`,Ⅵ:`VI`,Ⅶ:`VII`,Ⅷ:`VIII`,Ⅸ:`IX`,Ⅹ:`X`,Ⅺ:`XI`,Ⅻ:`XII`,Ⅼ:`L`,Ⅽ:`C`,Ⅾ:`D`,Ⅿ:`M`,ⅰ:`I`,ⅱ:`II`,ⅲ:`III`,ⅳ:`IV`,ⅴ:`V`,ⅵ:`VI`,ⅶ:`VII`,ⅷ:`VIII`,ⅸ:`IX`,ⅹ:`X`,ⅺ:`XI`,ⅻ:`XII`,ⅼ:`L`,ⅽ:`C`,ⅾ:`D`,ⅿ:`M`},c=/([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/gi,l=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i,u={round:3,allowTrailingInvalid:!1,romanNumerals:!1,bigIntOnOverflow:!1,decimalSeparator:`.`,allowCurrency:!1,percentage:!1,verbose:!1},d=e=>{let t=`${e}`.replace(c,(e,t)=>s[t]).toUpperCase(),n=l.exec(t);if(!n)return NaN;let[,r,i,a,u]=n;return(o[r]??0)+(o[i]??0)+(o[a]??0)+(o[u]??0)},f=/^\s*\//,p=/^([-+]?)\s*(\p{Sc}+)\s*/u,m=/\s*(\p{Sc}+)$/u,h=/%$/;function g(e,r=u){let o={...u,...r},s=typeof e==`string`?e:`${e}`,c,l,g,_,v,y,b,x,S=e=>{let t={value:e,input:s};return c&&(t.currencyPrefix=c),l&&(t.currencySuffix=l),g&&(t.percentageSuffix=g),_&&(t.trailingInvalid=_),v&&(t.sign=v),y!==void 0&&(t.whole=y),b!==void 0&&(t.numerator=b),x!==void 0&&(t.denominator=x),t},C=e=>o.verbose?S(e):e;if(typeof e==`number`||typeof e==`bigint`)return C(e);let w=NaN,T=`${e}`;if(o.allowCurrency){let e=p.exec(T);e&&e[2]&&(c=e[2],T=(e[1]||``)+T.slice(e[0].length))}if(o.allowCurrency){let e=m.exec(T);e&&(l=e[1],T=T.slice(0,-e[0].length))}o.percentage&&h.test(T)&&(g=!0,T=T.slice(0,-1));let E=t(T.replace(a,(e,t)=>` ${n[t]}`).replace(`⁄`,`/`).trim());if(E.length===0)return C(NaN);let D=E;if(o.decimalSeparator===`,`){let e=(E.match(/,/g)||[]).length;if(e===1)D=E.replaceAll(`.`,`_`).replace(`,`,`.`);else if(e>1){if(!o.allowTrailingInvalid)return C(NaN);let e=E.indexOf(`,`),t=E.indexOf(`,`,e+1),n=E.substring(0,t).replaceAll(`.`,`_`).replace(`,`,`.`),r=E.substring(t+1);D=o.allowTrailingInvalid?n+`&`+r:n}else D=E.replaceAll(`.`,`_`)}let O=i.exec(D);if(!O)return C(o.romanNumerals?d(E):NaN);let k=(O[7]||D.slice(O[0].length)).trim();if(k&&(_=k,!o.allowTrailingInvalid))return C(NaN);let[,A,j,M]=O;(A===`-`||A===`+`)&&(v=A);let N=j.replaceAll(`,`,``).replaceAll(`_`,``),P=M==null?void 0:M.replaceAll(`,`,``).replaceAll(`_`,``);if(!N&&P&&P.startsWith(`.`))w=0;else{if(o.bigIntOnOverflow){let e=A===`-`?BigInt(`-${N}`):BigInt(N);if(e>BigInt(2**53-1)||e<BigInt(-(2**53-1)))return C(e)}w=parseInt(N)}if(!P)return w=A===`-`?w*-1:w,g&&o.percentage!==`number`&&(w/=100),C(w);let F=o.round===!1?NaN:parseFloat(`1e${Math.floor(Math.max(0,o.round))}`);if(P.startsWith(`.`)||P.startsWith(`e`)||P.startsWith(`E`)){let e=parseFloat(`${w}${P}`);w=isNaN(F)?e:Math.round(e*F)/F}else if(f.test(P)){let e=parseInt(N),t=parseInt(P.replace(`/`,``));b=e,x=t,w=isNaN(F)?e/t:Math.round(e*F/t)/F}else{let[e,t]=P.split(`/`).map(e=>parseInt(e));y=w,b=e,x=t,w+=isNaN(F)?e/t:Math.round(e*F/t)/F}return w=A===`-`?w*-1:w,g&&o.percentage!==`number`&&(w=isNaN(F)?w/100:Math.round(w/100*F)/F),C(w)}const _=(e,t)=>{let n=g(e,{...t,verbose:!1});return typeof n==`bigint`||!isNaN(n)};exports.defaultOptions=u,exports.isNumericQuantity=_,exports.normalizeDigits=t,exports.numericQuantity=g,exports.numericRegex=r,exports.numericRegexWithTrailingInvalid=i,exports.parseRomanNumerals=d,exports.romanNumeralRegex=l,exports.romanNumeralUnicodeRegex=c,exports.romanNumeralUnicodeToAsciiMap=s,exports.romanNumeralValues=o,exports.vulgarFractionToAsciiMap=n,exports.vulgarFractionsRegex=a;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=[48,1632,1776,1984,2406,2534,2662,2790,2918,3046,3174,3302,3430,3558,3664,3792,3872,4160,4240,6112,6160,6470,6608,6784,6800,6992,7088,7232,7248,42528,43216,43264,43472,43504,43600,44016,65296,66720,68912,68928,69734,69872,69942,70096,70384,70736,70864,71248,71360,71376,71386,71472,71904,72016,72688,72784,73040,73120,73184,73552,90416,92768,92864,93008,93552,118e3,120782,120792,120802,120812,120822,123200,123632,124144,124401,125264,130032],t=t=>t.replace(/\p{Nd}/gu,t=>{let n=t.codePointAt(0);if(n<=57)return t;let r=0,i=e.length-1;for(;r<i;){let t=r+i+1>>>1;e[t]<=n?r=t:i=t-1}return String(n-e[r])}),n={"⁰":`0`,"¹":`1`,"²":`2`,"³":`3`,"⁴":`4`,"⁵":`5`,"⁶":`6`,"⁷":`7`,"⁸":`8`,"⁹":`9`,"₀":`0`,"₁":`1`,"₂":`2`,"₃":`3`,"₄":`4`,"₅":`5`,"₆":`6`,"₇":`7`,"₈":`8`,"₉":`9`},r=/[⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉]/g,i={"¼":`1/4`,"½":`1/2`,"¾":`3/4`,"⅐":`1/7`,"⅑":`1/9`,"⅒":`1/10`,"⅓":`1/3`,"⅔":`2/3`,"⅕":`1/5`,"⅖":`2/5`,"⅗":`3/5`,"⅘":`4/5`,"⅙":`1/6`,"⅚":`5/6`,"⅛":`1/8`,"⅜":`3/8`,"⅝":`5/8`,"⅞":`7/8`,"⅟":`1/`},a=/^(?=[-+]?\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)*)?$/,o=/^(?=[-+]?\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/].*)?/,s=/([¼½¾⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟}])/g,c={MMM:3e3,MM:2e3,M:1e3,CM:900,DCCC:800,DCC:700,DC:600,D:500,CD:400,CCC:300,CC:200,C:100,XC:90,LXXX:80,LXX:70,LX:60,L:50,XL:40,XXX:30,XX:20,XII:12,XI:11,X:10,IX:9,VIII:8,VII:7,VI:6,V:5,IV:4,III:3,II:2,I:1},l={Ⅰ:`I`,Ⅱ:`II`,Ⅲ:`III`,Ⅳ:`IV`,Ⅴ:`V`,Ⅵ:`VI`,Ⅶ:`VII`,Ⅷ:`VIII`,Ⅸ:`IX`,Ⅹ:`X`,Ⅺ:`XI`,Ⅻ:`XII`,Ⅼ:`L`,Ⅽ:`C`,Ⅾ:`D`,Ⅿ:`M`,ⅰ:`I`,ⅱ:`II`,ⅲ:`III`,ⅳ:`IV`,ⅴ:`V`,ⅵ:`VI`,ⅶ:`VII`,ⅷ:`VIII`,ⅸ:`IX`,ⅹ:`X`,ⅺ:`XI`,ⅻ:`XII`,ⅼ:`L`,ⅽ:`C`,ⅾ:`D`,ⅿ:`M`},u=/([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/gi,d=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i,f={round:3,allowTrailingInvalid:!1,romanNumerals:!1,bigIntOnOverflow:!1,decimalSeparator:`.`,allowCurrency:!1,percentage:!1,verbose:!1},p=e=>{let t=`${e}`.replace(u,(e,t)=>l[t]).toUpperCase(),n=d.exec(t);if(!n)return NaN;let[,r,i,a,o]=n;return(c[r]??0)+(c[i]??0)+(c[a]??0)+(c[o]??0)},m=/^\s*\//,h=/^([-+]?)\s*(\p{Sc}+)\s*/u,g=/\s*(\p{Sc}+)$/u,_=/%$/;function v(e,a=f){let c={...f,...a},l=typeof e==`string`?e:`${e}`,u,d,v,y,b,x,S,C,w=e=>{let t={value:e,input:l};return u&&(t.currencyPrefix=u),d&&(t.currencySuffix=d),v&&(t.percentageSuffix=v),y&&(t.trailingInvalid=y),b&&(t.sign=b),x!==void 0&&(t.whole=x),S!==void 0&&(t.numerator=S),C!==void 0&&(t.denominator=C),t},T=e=>c.verbose?w(e):e;if(typeof e==`number`||typeof e==`bigint`)return T(e);let E=NaN,D=`${e}`;if(c.allowCurrency){let e=h.exec(D);e&&e[2]&&(u=e[2],D=(e[1]||``)+D.slice(e[0].length))}if(c.allowCurrency){let e=g.exec(D);e&&(d=e[1],D=D.slice(0,-e[0].length))}c.percentage&&_.test(D)&&(v=!0,D=D.slice(0,-1));let O=t(D.replace(s,(e,t)=>` ${i[t]}`).replace(r,e=>n[e]).replace(`⁄`,`/`).trim());if(O.length===0)return T(NaN);let k=O;if(c.decimalSeparator===`,`){let e=(O.match(/,/g)||[]).length;if(e===1)k=O.replaceAll(`.`,`_`).replace(`,`,`.`);else if(e>1){if(!c.allowTrailingInvalid)return T(NaN);let e=O.indexOf(`,`),t=O.indexOf(`,`,e+1),n=O.substring(0,t).replaceAll(`.`,`_`).replace(`,`,`.`),r=O.substring(t+1);k=c.allowTrailingInvalid?n+`&`+r:n}else k=O.replaceAll(`.`,`_`)}let A=o.exec(k);if(!A)return T(c.romanNumerals?p(O):NaN);let j=(A[7]||k.slice(A[0].length)).trim();if(j&&(y=j,!c.allowTrailingInvalid))return T(NaN);let[,M,N,P]=A;(M===`-`||M===`+`)&&(b=M);let F=N.replaceAll(`,`,``).replaceAll(`_`,``),I=P==null?void 0:P.replaceAll(`,`,``).replaceAll(`_`,``);if(!F&&I&&I.startsWith(`.`))E=0;else{if(c.bigIntOnOverflow){let e=M===`-`?BigInt(`-${F}`):BigInt(F);if(e>BigInt(2**53-1)||e<BigInt(-(2**53-1)))return T(e)}E=parseInt(F)}if(!I)return E=M===`-`?E*-1:E,v&&c.percentage!==`number`&&(E/=100),T(E);let L=c.round===!1?NaN:parseFloat(`1e${Math.floor(Math.max(0,c.round))}`);if(I.startsWith(`.`)||I.startsWith(`e`)||I.startsWith(`E`)){let e=parseFloat(`${E}${I}`);E=isNaN(L)?e:Math.round(e*L)/L}else if(m.test(I)){let e=parseInt(F),t=parseInt(I.replace(`/`,``));S=e,C=t,E=isNaN(L)?e/t:Math.round(e*L/t)/L}else{let[e,t]=I.split(`/`).map(e=>parseInt(e));x=E,S=e,C=t,E+=isNaN(L)?e/t:Math.round(e*L/t)/L}return E=M===`-`?E*-1:E,v&&c.percentage!==`number`&&(E=isNaN(L)?E/100:Math.round(E/100*L)/L),T(E)}const y=(e,t)=>{let n=v(e,{...t,verbose:!1});return typeof n==`bigint`||!isNaN(n)};exports.defaultOptions=f,exports.isNumericQuantity=y,exports.normalizeDigits=t,exports.numericQuantity=v,exports.numericRegex=a,exports.numericRegexWithTrailingInvalid=o,exports.parseRomanNumerals=p,exports.romanNumeralRegex=d,exports.romanNumeralUnicodeRegex=u,exports.romanNumeralUnicodeToAsciiMap=l,exports.romanNumeralValues=c,exports.superSubDigitToAsciiMap=n,exports.superSubDigitsRegex=r,exports.vulgarFractionToAsciiMap=i,exports.vulgarFractionsRegex=s;
2
2
  //# sourceMappingURL=numeric-quantity.cjs.production.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"numeric-quantity.cjs.production.js","names":[],"sources":["../../src/constants.ts","../../src/parseRomanNumerals.ts","../../src/numericQuantity.ts","../../src/isNumericQuantity.ts"],"sourcesContent":["import type {\n NumericQuantityOptions,\n RomanNumeralAscii,\n RomanNumeralUnicode,\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 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. 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 =\n /([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/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) =>\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 normalizeDigits,\n numericRegexWithTrailingInvalid,\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 */\nfunction numericQuantity(quantity: string | number): number;\nfunction numericQuantity<T extends NumericQuantityOptions>(\n quantity: string | number,\n options: T\n): NumericQuantityReturnType<T>;\nfunction numericQuantity(\n quantity: string | number,\n options?: NumericQuantityOptions\n): number;\nfunction numericQuantity(\n quantity: string | number,\n options: NumericQuantityOptions = defaultOptions\n): number | bigint | NumericQuantityVerboseResult {\n const opts: Required<NumericQuantityOptions> = {\n ...defaultOptions,\n ...options,\n };\n\n // Metadata for verbose output\n const originalInput = typeof quantity === 'string' ? quantity : `${quantity}`;\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 = (\n value: number | bigint\n ): NumericQuantityVerboseResult => {\n const result: NumericQuantityVerboseResult = { value, input: originalInput };\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 = `${quantity}`;\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 =\n (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) =>\n ` ${vulgarFractionToAsciiMap[vf]}`\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\n .replaceAll('.', '_')\n .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(\n ',',\n firstCommaIndex + 1\n );\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(\n 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 = (\n regexResult[7] || normalizedString.slice(regexResult[0].length)\n ).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\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 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":"mEAeA,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,OACD,CASY,EAAmB,GAC9B,EAAI,QAAQ,WAAY,GAAM,CAC5B,IAAM,EAAK,EAAG,YAAY,EAAE,CAE5B,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,EAGf,OAAO,OAAO,EAAK,EAAwB,GAAK,EAChD,CAKS,EAGT,CACF,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,KACN,CA2BY,EACX,iMAKW,EACX,+MAKW,EAA+B,4BAe/B,EAET,CACF,IAAK,IACL,GAAI,IACJ,EAAG,IACH,GAAI,IACJ,KAAM,IACN,IAAK,IACL,GAAI,IACJ,EAAG,IACH,GAAI,IACJ,IAAK,IACL,GAAI,IACJ,EAAG,IACH,GAAI,GACJ,KAAM,GACN,IAAK,GACL,GAAI,GACJ,EAAG,GACH,GAAI,GACJ,IAAK,GACL,GAAI,GACJ,IAAK,GACL,GAAI,GACJ,EAAG,GACH,GAAI,EACJ,KAAM,EACN,IAAK,EACL,GAAI,EACJ,EAAG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"}
1
+ {"version":3,"file":"numeric-quantity.cjs.production.js","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<\n SuperscriptDigit | SubscriptDigit,\n string\n> = {\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<\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. 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 =\n /([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/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) =>\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 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 */\nfunction numericQuantity(quantity: string | number): number;\nfunction numericQuantity<T extends NumericQuantityOptions>(\n quantity: string | number,\n options: T\n): NumericQuantityReturnType<T>;\nfunction numericQuantity(\n quantity: string | number,\n options?: NumericQuantityOptions\n): number;\nfunction numericQuantity(\n quantity: string | number,\n options: NumericQuantityOptions = defaultOptions\n): number | bigint | NumericQuantityVerboseResult {\n const opts: Required<NumericQuantityOptions> = {\n ...defaultOptions,\n ...options,\n };\n\n // Metadata for verbose output\n const originalInput = typeof quantity === 'string' ? quantity : `${quantity}`;\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 = (\n value: number | bigint\n ): NumericQuantityVerboseResult => {\n const result: NumericQuantityVerboseResult = { value, input: originalInput };\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 = `${quantity}`;\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 =\n (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) =>\n ` ${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\n .replaceAll('.', '_')\n .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(\n ',',\n firstCommaIndex + 1\n );\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(\n 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 = (\n regexResult[7] || normalizedString.slice(regexResult[0].length)\n ).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\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 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":"mEAiBA,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,OACD,CASY,EAAmB,GAC9B,EAAI,QAAQ,WAAY,GAAM,CAC5B,IAAM,EAAK,EAAG,YAAY,EAAE,CAE5B,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,EAGf,OAAO,OAAO,EAAK,EAAwB,GAAK,EAChD,CAKS,EAGT,CACF,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,IACN,CAKY,EAA8B,0BAK9B,EAGT,CACF,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,KACN,CA2BY,EACX,iMAKW,EACX,+MAKW,EAA+B,4BAe/B,EAET,CACF,IAAK,IACL,GAAI,IACJ,EAAG,IACH,GAAI,IACJ,KAAM,IACN,IAAK,IACL,GAAI,IACJ,EAAG,IACH,GAAI,IACJ,IAAK,IACL,GAAI,IACJ,EAAG,IACH,GAAI,GACJ,KAAM,GACN,IAAK,GACL,GAAI,GACJ,EAAG,GACH,GAAI,GACJ,IAAK,GACL,GAAI,GACJ,IAAK,GACL,GAAI,GACJ,EAAG,GACH,GAAI,EACJ,KAAM,EACN,IAAK,EACL,GAAI,EACJ,EAAG,EACH,GAAI,EACJ,IAAK,EACL,GAAI,EACJ,EAAG,EACJ,CAKY,EAGT,CAEF,EAAG,IAEH,EAAG,KAEH,EAAG,MAEH,EAAG,KAEH,EAAG,IAEH,EAAG,KAEH,EAAG,MAEH,EAAG,OAEH,EAAG,KAEH,EAAG,IAEH,EAAG,KAEH,EAAG,MAEH,EAAG,IAEH,EAAG,IAEH,EAAG,IAEH,EAAG,IAEH,EAAG,IAEH,EAAG,KAEH,EAAG,MAEH,EAAG,KAEH,EAAG,IAEH,EAAG,KAEH,EAAG,MAEH,EAAG,OAEH,EAAG,KAEH,EAAG,IAEH,EAAG,KAEH,EAAG,MAEH,EAAG,IAEH,EAAG,IAEH,EAAG,IAEH,EAAG,IACJ,CAKY,EACX,yCAuBW,EACX,2EAOW,EAAmD,CAC9D,MAAO,EACP,qBAAsB,GACtB,cAAe,GACf,iBAAkB,GAClB,iBAAkB,IAClB,cAAe,GACf,WAAY,GACZ,QAAS,GACV,CCtXY,EAAsB,GAAkC,CACnE,IAAM,EAAa,GAAG,IAEnB,QACC,GACC,EAAI,IACH,EAA8B,GACjC,CAEA,aAAa,CAEV,EAAc,EAAkB,KAAK,EAAW,CAEtD,GAAI,CAAC,EACH,MAAO,KAGT,GAAM,EAAG,EAAW,EAAU,EAAM,GAAQ,EAE5C,OACG,EAAmB,IAAqB,IACxC,EAAmB,IAAoB,IACvC,EAAmB,IAAgB,IACnC,EAAmB,IAAgB,ICxBlC,EAAsB,SACtB,EAAsB,2BACtB,EAAsB,iBACtB,EAAwB,KAgB9B,SAAS,EACP,EACA,EAAkC,EACc,CAChD,IAAM,EAAyC,CAC7C,GAAG,EACH,GAAG,EACJ,CAGK,EAAgB,OAAO,GAAa,SAAW,EAAW,GAAG,IAC/D,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EAEE,EACJ,GACiC,CACjC,IAAM,EAAuC,CAAE,QAAO,MAAO,EAAe,CAS5E,OARI,IAAgB,EAAO,eAAiB,GACxC,IAAgB,EAAO,eAAiB,GACxC,IAAkB,EAAO,iBAAmB,GAC5C,IAAiB,EAAO,gBAAkB,GAC1C,IAAY,EAAO,KAAO,GAC1B,IAAgB,IAAA,KAAW,EAAO,MAAQ,GAC1C,IAAoB,IAAA,KAAW,EAAO,UAAY,GAClD,IAAsB,IAAA,KAAW,EAAO,YAAc,GACnD,GAGH,EAAe,GACnB,EAAK,QAAU,EAAmB,EAAM,CAAG,EAE7C,GAAI,OAAO,GAAa,UAAY,OAAO,GAAa,SACtD,OAAO,EAAY,EAAS,CAG9B,IAAI,EAAc,IACd,EAAgB,GAAG,IAGvB,GAAI,EAAK,cAAe,CACtB,IAAM,EAAc,EAAoB,KAAK,EAAc,CACvD,GAAe,EAAY,KAC7B,EAAiB,EAAY,GAE7B,GACG,EAAY,IAAM,IAAM,EAAc,MAAM,EAAY,GAAG,OAAO,EAKzE,GAAI,EAAK,cAAe,CACtB,IAAM,EAAc,EAAoB,KAAK,EAAc,CACvD,IACF,EAAiB,EAAY,GAC7B,EAAgB,EAAc,MAAM,EAAG,CAAC,EAAY,GAAG,OAAO,EAK9D,EAAK,YAAc,EAAsB,KAAK,EAAc,GAC9D,EAAmB,GACnB,EAAgB,EAAc,MAAM,EAAG,GAAG,EAI5C,IAAM,EAAmB,EACvB,EAGG,QACC,GACC,EAAI,IACH,IAAI,EAAyB,KAChC,CAEA,QACC,EACA,GAAM,EAAwB,GAC/B,CAEA,QAAQ,IAAK,IAAI,CACjB,MAAM,CACV,CAGD,GAAI,EAAiB,SAAW,EAC9B,OAAO,EAAY,IAAI,CAGzB,IAAI,EAAmB,EAEvB,GAAI,EAAK,mBAAqB,IAAK,CACjC,IAAM,GAAc,EAAiB,MAAM,KAAK,EAAI,EAAE,EAAE,OACxD,GAAI,IAAe,EAGjB,EAAmB,EAChB,WAAW,IAAK,IAAI,CACpB,QAAQ,IAAK,IAAI,SACX,EAAa,EAAG,CAEzB,GAAI,CAAC,EAAK,qBAER,OAAO,EAAY,IAAI,CAGzB,IAAM,EAAkB,EAAiB,QAAQ,IAAI,CAC/C,EAAmB,EAAiB,QACxC,IACA,EAAkB,EACnB,CACK,EAAoB,EACvB,UAAU,EAAG,EAAiB,CAC9B,WAAW,IAAK,IAAI,CACpB,QAAQ,IAAK,IAAI,CACd,EAAmB,EAAiB,UAAU,EAAmB,EAAE,CACzE,EAAmB,EAAK,qBACpB,EAAoB,IAAM,EAC1B,OAIJ,EAAmB,EAAiB,WAAW,IAAK,IAAI,CAI5D,IAAM,EAAc,EAAgC,KAAK,EAAiB,CAG1E,GAAI,CAAC,EACH,OAAO,EACL,EAAK,cAAgB,EAAmB,EAAiB,CAAG,IAAI,CAMpE,IAAM,GACJ,EAAY,IAAM,EAAiB,MAAM,EAAY,GAAG,OAAO,EAC/D,MAAM,CACR,GAAI,IACF,EAAkB,EACd,CAAC,EAAK,sBACR,OAAO,EAAY,IAAI,CAI3B,GAAM,EAAG,EAAM,EAAS,GAAW,GAC/B,IAAS,KAAO,IAAS,OAAK,EAAa,GAC/C,IAAM,EAAe,EAAQ,WAAW,IAAK,GAAG,CAAC,WAAW,IAAK,GAAG,CAC9D,EAAA,GAAA,KAAA,IAAA,GAAe,EAAS,WAAW,IAAK,GAAG,CAAC,WAAW,IAAK,GAAG,CAGrE,GAAI,CAAC,GAAgB,GAAgB,EAAa,WAAW,IAAI,CAC/D,EAAc,MACT,CACL,GAAI,EAAK,iBAAkB,CACzB,IAAM,EAAW,IAAS,IAAM,OAAO,IAAI,IAAe,CAAG,OAAO,EAAa,CACjF,GACE,EAAW,eAA+B,EAC1C,EAAW,OAAO,WAAwB,CAG1C,OAAO,EAAY,EAAS,CAIhC,EAAc,SAAS,EAAa,CAKtC,GAAI,CAAC,EAKH,MAJA,GAAc,IAAS,IAAM,EAAc,GAAK,EAC5C,GAAoB,EAAK,aAAe,WAC1C,GAA4B,KAEvB,EAAY,EAAY,CAGjC,IAAM,EACJ,EAAK,QAAU,GACX,IACA,WAAW,KAAK,KAAK,MAAM,KAAK,IAAI,EAAG,EAAK,MAAM,CAAC,GAAG,CAE5D,GACE,EAAa,WAAW,IAAI,EAC5B,EAAa,WAAW,IAAI,EAC5B,EAAa,WAAW,IAAI,CAC5B,CAEA,IAAM,EAAe,WAAW,GAAG,IAAc,IAAe,CAChE,EAAc,MAAM,EAAe,CAC/B,EACA,KAAK,MAAM,EAAe,EAAe,CAAG,UACvC,EAAoB,KAAK,EAAa,CAAE,CAEjD,IAAM,EAAY,SAAS,EAAa,CAClC,EAAc,SAAS,EAAa,QAAQ,IAAK,GAAG,CAAC,CAC3D,EAAkB,EAClB,EAAoB,EACpB,EAAc,MAAM,EAAe,CAC/B,EAAY,EACZ,KAAK,MAAO,EAAY,EAAkB,EAAY,CAAG,MACxD,CAGL,GAAM,CAAC,EAAW,GADI,EAAa,MAAM,IAAI,CACE,IAAI,GAAK,SAAS,EAAE,CAAC,CACpE,EAAc,EACd,EAAkB,EAClB,EAAoB,EACpB,GAAe,MAAM,EAAe,CAChC,EAAY,EACZ,KAAK,MAAO,EAAY,EAAkB,EAAY,CAAG,EAY/D,MATA,GAAc,IAAS,IAAM,EAAc,GAAK,EAG5C,GAAoB,EAAK,aAAe,WAC1C,EAAc,MAAM,EAAe,CAC/B,EAAc,IACd,KAAK,MAAO,EAAc,IAAO,EAAe,CAAG,GAGlD,EAAY,EAAY,CClQjC,MAAa,GACX,EACA,IACY,CACZ,IAAM,EAAS,EAAgB,EAAU,CAAE,GAAG,EAAS,QAAS,GAAO,CAAC,CACxE,OAAO,OAAO,GAAW,UAAY,CAAC,MAAM,EAAO"}
@@ -91,6 +91,14 @@ interface NumericQuantityVerboseResult {
91
91
  */
92
92
  type VulgarFraction = "¼" | "½" | "¾" | "⅐" | "⅑" | "⅒" | "⅓" | "⅔" | "⅕" | "⅖" | "⅗" | "⅘" | "⅙" | "⅚" | "⅛" | "⅜" | "⅝" | "⅞" | "⅟";
93
93
  /**
94
+ * Unicode superscript digit code points.
95
+ */
96
+ type SuperscriptDigit = "⁰" | "¹" | "²" | "³" | "⁴" | "⁵" | "⁶" | "⁷" | "⁸" | "⁹";
97
+ /**
98
+ * Unicode subscript digit code points.
99
+ */
100
+ type SubscriptDigit = "₀" | "₁" | "₂" | "₃" | "₄" | "₅" | "₆" | "₇" | "₈" | "₉";
101
+ /**
94
102
  * Allowable Roman numeral characters (ASCII, uppercase only).
95
103
  */
96
104
  type RomanNumeralAscii = "I" | "V" | "X" | "L" | "C" | "D" | "M";
@@ -114,6 +122,14 @@ type RomanNumeral = RomanNumeralAscii | RomanNumeralUnicode;
114
122
  */
115
123
  declare const normalizeDigits: (str: string) => string;
116
124
  /**
125
+ * Map of Unicode superscript and subscript digit code points to ASCII digits.
126
+ */
127
+ declare const superSubDigitToAsciiMap: Record<SuperscriptDigit | SubscriptDigit, string>;
128
+ /**
129
+ * Captures Unicode superscript and subscript digits.
130
+ */
131
+ declare const superSubDigitsRegex: RegExp;
132
+ /**
117
133
  * Map of Unicode fraction code points to their ASCII equivalents.
118
134
  */
119
135
  declare const vulgarFractionToAsciiMap: Record<VulgarFraction, `${number}/${number | ""}`>;
@@ -221,5 +237,5 @@ declare function numericQuantity(quantity: string | number, options?: NumericQua
221
237
  */
222
238
  declare const parseRomanNumerals: (romanNumerals: string) => number;
223
239
  //#endregion
224
- export { NumericQuantityOptions, NumericQuantityReturnType, NumericQuantityVerboseResult, RomanNumeral, RomanNumeralAscii, RomanNumeralUnicode, VulgarFraction, defaultOptions, isNumericQuantity, normalizeDigits, numericQuantity, numericRegex, numericRegexWithTrailingInvalid, parseRomanNumerals, romanNumeralRegex, romanNumeralUnicodeRegex, romanNumeralUnicodeToAsciiMap, romanNumeralValues, vulgarFractionToAsciiMap, vulgarFractionsRegex };
240
+ export { NumericQuantityOptions, NumericQuantityReturnType, NumericQuantityVerboseResult, RomanNumeral, RomanNumeralAscii, RomanNumeralUnicode, SubscriptDigit, SuperscriptDigit, VulgarFraction, defaultOptions, isNumericQuantity, normalizeDigits, numericQuantity, numericRegex, numericRegexWithTrailingInvalid, parseRomanNumerals, romanNumeralRegex, romanNumeralUnicodeRegex, romanNumeralUnicodeToAsciiMap, romanNumeralValues, superSubDigitToAsciiMap, superSubDigitsRegex, vulgarFractionToAsciiMap, vulgarFractionsRegex };
225
241
  //# sourceMappingURL=numeric-quantity.d.mts.map
@@ -1,2 +1,2 @@
1
- var NumericQuantity=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});let t=[48,1632,1776,1984,2406,2534,2662,2790,2918,3046,3174,3302,3430,3558,3664,3792,3872,4160,4240,6112,6160,6470,6608,6784,6800,6992,7088,7232,7248,42528,43216,43264,43472,43504,43600,44016,65296,66720,68912,68928,69734,69872,69942,70096,70384,70736,70864,71248,71360,71376,71386,71472,71904,72016,72688,72784,73040,73120,73184,73552,90416,92768,92864,93008,93552,118e3,120782,120792,120802,120812,120822,123200,123632,124144,124401,125264,130032],n=e=>e.replace(/\p{Nd}/gu,e=>{let n=e.codePointAt(0);if(n<=57)return e;let r=0,i=t.length-1;for(;r<i;){let e=r+i+1>>>1;t[e]<=n?r=e:i=e-1}return String(n-t[r])}),r={"¼":`1/4`,"½":`1/2`,"¾":`3/4`,"⅐":`1/7`,"⅑":`1/9`,"⅒":`1/10`,"⅓":`1/3`,"⅔":`2/3`,"⅕":`1/5`,"⅖":`2/5`,"⅗":`3/5`,"⅘":`4/5`,"⅙":`1/6`,"⅚":`5/6`,"⅛":`1/8`,"⅜":`3/8`,"⅝":`5/8`,"⅞":`7/8`,"⅟":`1/`},i=/^(?=[-+]?\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)*)?$/,a=/^(?=[-+]?\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/].*)?/,o=/([¼½¾⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟}])/g,s={MMM:3e3,MM:2e3,M:1e3,CM:900,DCCC:800,DCC:700,DC:600,D:500,CD:400,CCC:300,CC:200,C:100,XC:90,LXXX:80,LXX:70,LX:60,L:50,XL:40,XXX:30,XX:20,XII:12,XI:11,X:10,IX:9,VIII:8,VII:7,VI:6,V:5,IV:4,III:3,II:2,I:1},c={Ⅰ:`I`,Ⅱ:`II`,Ⅲ:`III`,Ⅳ:`IV`,Ⅴ:`V`,Ⅵ:`VI`,Ⅶ:`VII`,Ⅷ:`VIII`,Ⅸ:`IX`,Ⅹ:`X`,Ⅺ:`XI`,Ⅻ:`XII`,Ⅼ:`L`,Ⅽ:`C`,Ⅾ:`D`,Ⅿ:`M`,ⅰ:`I`,ⅱ:`II`,ⅲ:`III`,ⅳ:`IV`,ⅴ:`V`,ⅵ:`VI`,ⅶ:`VII`,ⅷ:`VIII`,ⅸ:`IX`,ⅹ:`X`,ⅺ:`XI`,ⅻ:`XII`,ⅼ:`L`,ⅽ:`C`,ⅾ:`D`,ⅿ:`M`},l=/([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/gi,u=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i,d={round:3,allowTrailingInvalid:!1,romanNumerals:!1,bigIntOnOverflow:!1,decimalSeparator:`.`,allowCurrency:!1,percentage:!1,verbose:!1},f=e=>{let t=`${e}`.replace(l,(e,t)=>c[t]).toUpperCase(),n=u.exec(t);if(!n)return NaN;let[,r,i,a,o]=n;return(s[r]??0)+(s[i]??0)+(s[a]??0)+(s[o]??0)},p=/^\s*\//,m=/^([-+]?)\s*(\p{Sc}+)\s*/u,h=/\s*(\p{Sc}+)$/u,g=/%$/;function _(e,t=d){let i={...d,...t},s=typeof e==`string`?e:`${e}`,c,l,u,_,v,y,b,x,S=e=>{let t={value:e,input:s};return c&&(t.currencyPrefix=c),l&&(t.currencySuffix=l),u&&(t.percentageSuffix=u),_&&(t.trailingInvalid=_),v&&(t.sign=v),y!==void 0&&(t.whole=y),b!==void 0&&(t.numerator=b),x!==void 0&&(t.denominator=x),t},C=e=>i.verbose?S(e):e;if(typeof e==`number`||typeof e==`bigint`)return C(e);let w=NaN,T=`${e}`;if(i.allowCurrency){let e=m.exec(T);e&&e[2]&&(c=e[2],T=(e[1]||``)+T.slice(e[0].length))}if(i.allowCurrency){let e=h.exec(T);e&&(l=e[1],T=T.slice(0,-e[0].length))}i.percentage&&g.test(T)&&(u=!0,T=T.slice(0,-1));let E=n(T.replace(o,(e,t)=>` ${r[t]}`).replace(`⁄`,`/`).trim());if(E.length===0)return C(NaN);let D=E;if(i.decimalSeparator===`,`){let e=(E.match(/,/g)||[]).length;if(e===1)D=E.replaceAll(`.`,`_`).replace(`,`,`.`);else if(e>1){if(!i.allowTrailingInvalid)return C(NaN);let e=E.indexOf(`,`),t=E.indexOf(`,`,e+1),n=E.substring(0,t).replaceAll(`.`,`_`).replace(`,`,`.`),r=E.substring(t+1);D=i.allowTrailingInvalid?n+`&`+r:n}else D=E.replaceAll(`.`,`_`)}let O=a.exec(D);if(!O)return C(i.romanNumerals?f(E):NaN);let k=(O[7]||D.slice(O[0].length)).trim();if(k&&(_=k,!i.allowTrailingInvalid))return C(NaN);let[,A,j,M]=O;(A===`-`||A===`+`)&&(v=A);let N=j.replaceAll(`,`,``).replaceAll(`_`,``),P=M==null?void 0:M.replaceAll(`,`,``).replaceAll(`_`,``);if(!N&&P&&P.startsWith(`.`))w=0;else{if(i.bigIntOnOverflow){let e=A===`-`?BigInt(`-${N}`):BigInt(N);if(e>BigInt(2**53-1)||e<BigInt(-(2**53-1)))return C(e)}w=parseInt(N)}if(!P)return w=A===`-`?w*-1:w,u&&i.percentage!==`number`&&(w/=100),C(w);let F=i.round===!1?NaN:parseFloat(`1e${Math.floor(Math.max(0,i.round))}`);if(P.startsWith(`.`)||P.startsWith(`e`)||P.startsWith(`E`)){let e=parseFloat(`${w}${P}`);w=isNaN(F)?e:Math.round(e*F)/F}else if(p.test(P)){let e=parseInt(N),t=parseInt(P.replace(`/`,``));b=e,x=t,w=isNaN(F)?e/t:Math.round(e*F/t)/F}else{let[e,t]=P.split(`/`).map(e=>parseInt(e));y=w,b=e,x=t,w+=isNaN(F)?e/t:Math.round(e*F/t)/F}return w=A===`-`?w*-1:w,u&&i.percentage!==`number`&&(w=isNaN(F)?w/100:Math.round(w/100*F)/F),C(w)}return e.defaultOptions=d,e.isNumericQuantity=(e,t)=>{let n=_(e,{...t,verbose:!1});return typeof n==`bigint`||!isNaN(n)},e.normalizeDigits=n,e.numericQuantity=_,e.numericRegex=i,e.numericRegexWithTrailingInvalid=a,e.parseRomanNumerals=f,e.romanNumeralRegex=u,e.romanNumeralUnicodeRegex=l,e.romanNumeralUnicodeToAsciiMap=c,e.romanNumeralValues=s,e.vulgarFractionToAsciiMap=r,e.vulgarFractionsRegex=o,e})({});
1
+ var NumericQuantity=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});let t=[48,1632,1776,1984,2406,2534,2662,2790,2918,3046,3174,3302,3430,3558,3664,3792,3872,4160,4240,6112,6160,6470,6608,6784,6800,6992,7088,7232,7248,42528,43216,43264,43472,43504,43600,44016,65296,66720,68912,68928,69734,69872,69942,70096,70384,70736,70864,71248,71360,71376,71386,71472,71904,72016,72688,72784,73040,73120,73184,73552,90416,92768,92864,93008,93552,118e3,120782,120792,120802,120812,120822,123200,123632,124144,124401,125264,130032],n=e=>e.replace(/\p{Nd}/gu,e=>{let n=e.codePointAt(0);if(n<=57)return e;let r=0,i=t.length-1;for(;r<i;){let e=r+i+1>>>1;t[e]<=n?r=e:i=e-1}return String(n-t[r])}),r={"⁰":`0`,"¹":`1`,"²":`2`,"³":`3`,"⁴":`4`,"⁵":`5`,"⁶":`6`,"⁷":`7`,"⁸":`8`,"⁹":`9`,"₀":`0`,"₁":`1`,"₂":`2`,"₃":`3`,"₄":`4`,"₅":`5`,"₆":`6`,"₇":`7`,"₈":`8`,"₉":`9`},i=/[⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉]/g,a={"¼":`1/4`,"½":`1/2`,"¾":`3/4`,"⅐":`1/7`,"⅑":`1/9`,"⅒":`1/10`,"⅓":`1/3`,"⅔":`2/3`,"⅕":`1/5`,"⅖":`2/5`,"⅗":`3/5`,"⅘":`4/5`,"⅙":`1/6`,"⅚":`5/6`,"⅛":`1/8`,"⅜":`3/8`,"⅝":`5/8`,"⅞":`7/8`,"⅟":`1/`},o=/^(?=[-+]?\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=/^(?=[-+]?\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/].*)?/,c=/([¼½¾⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟}])/g,l={MMM:3e3,MM:2e3,M:1e3,CM:900,DCCC:800,DCC:700,DC:600,D:500,CD:400,CCC:300,CC:200,C:100,XC:90,LXXX:80,LXX:70,LX:60,L:50,XL:40,XXX:30,XX:20,XII:12,XI:11,X:10,IX:9,VIII:8,VII:7,VI:6,V:5,IV:4,III:3,II:2,I:1},u={Ⅰ:`I`,Ⅱ:`II`,Ⅲ:`III`,Ⅳ:`IV`,Ⅴ:`V`,Ⅵ:`VI`,Ⅶ:`VII`,Ⅷ:`VIII`,Ⅸ:`IX`,Ⅹ:`X`,Ⅺ:`XI`,Ⅻ:`XII`,Ⅼ:`L`,Ⅽ:`C`,Ⅾ:`D`,Ⅿ:`M`,ⅰ:`I`,ⅱ:`II`,ⅲ:`III`,ⅳ:`IV`,ⅴ:`V`,ⅵ:`VI`,ⅶ:`VII`,ⅷ:`VIII`,ⅸ:`IX`,ⅹ:`X`,ⅺ:`XI`,ⅻ:`XII`,ⅼ:`L`,ⅽ:`C`,ⅾ:`D`,ⅿ:`M`},d=/([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/gi,f=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i,p={round:3,allowTrailingInvalid:!1,romanNumerals:!1,bigIntOnOverflow:!1,decimalSeparator:`.`,allowCurrency:!1,percentage:!1,verbose:!1},m=e=>{let t=`${e}`.replace(d,(e,t)=>u[t]).toUpperCase(),n=f.exec(t);if(!n)return NaN;let[,r,i,a,o]=n;return(l[r]??0)+(l[i]??0)+(l[a]??0)+(l[o]??0)},h=/^\s*\//,g=/^([-+]?)\s*(\p{Sc}+)\s*/u,_=/\s*(\p{Sc}+)$/u,v=/%$/;function y(e,t=p){let o={...p,...t},l=typeof e==`string`?e:`${e}`,u,d,f,y,b,x,S,C,w=e=>{let t={value:e,input:l};return u&&(t.currencyPrefix=u),d&&(t.currencySuffix=d),f&&(t.percentageSuffix=f),y&&(t.trailingInvalid=y),b&&(t.sign=b),x!==void 0&&(t.whole=x),S!==void 0&&(t.numerator=S),C!==void 0&&(t.denominator=C),t},T=e=>o.verbose?w(e):e;if(typeof e==`number`||typeof e==`bigint`)return T(e);let E=NaN,D=`${e}`;if(o.allowCurrency){let e=g.exec(D);e&&e[2]&&(u=e[2],D=(e[1]||``)+D.slice(e[0].length))}if(o.allowCurrency){let e=_.exec(D);e&&(d=e[1],D=D.slice(0,-e[0].length))}o.percentage&&v.test(D)&&(f=!0,D=D.slice(0,-1));let O=n(D.replace(c,(e,t)=>` ${a[t]}`).replace(i,e=>r[e]).replace(`⁄`,`/`).trim());if(O.length===0)return T(NaN);let k=O;if(o.decimalSeparator===`,`){let e=(O.match(/,/g)||[]).length;if(e===1)k=O.replaceAll(`.`,`_`).replace(`,`,`.`);else if(e>1){if(!o.allowTrailingInvalid)return T(NaN);let e=O.indexOf(`,`),t=O.indexOf(`,`,e+1),n=O.substring(0,t).replaceAll(`.`,`_`).replace(`,`,`.`),r=O.substring(t+1);k=o.allowTrailingInvalid?n+`&`+r:n}else k=O.replaceAll(`.`,`_`)}let A=s.exec(k);if(!A)return T(o.romanNumerals?m(O):NaN);let j=(A[7]||k.slice(A[0].length)).trim();if(j&&(y=j,!o.allowTrailingInvalid))return T(NaN);let[,M,N,P]=A;(M===`-`||M===`+`)&&(b=M);let F=N.replaceAll(`,`,``).replaceAll(`_`,``),I=P==null?void 0:P.replaceAll(`,`,``).replaceAll(`_`,``);if(!F&&I&&I.startsWith(`.`))E=0;else{if(o.bigIntOnOverflow){let e=M===`-`?BigInt(`-${F}`):BigInt(F);if(e>BigInt(2**53-1)||e<BigInt(-(2**53-1)))return T(e)}E=parseInt(F)}if(!I)return E=M===`-`?E*-1:E,f&&o.percentage!==`number`&&(E/=100),T(E);let L=o.round===!1?NaN:parseFloat(`1e${Math.floor(Math.max(0,o.round))}`);if(I.startsWith(`.`)||I.startsWith(`e`)||I.startsWith(`E`)){let e=parseFloat(`${E}${I}`);E=isNaN(L)?e:Math.round(e*L)/L}else if(h.test(I)){let e=parseInt(F),t=parseInt(I.replace(`/`,``));S=e,C=t,E=isNaN(L)?e/t:Math.round(e*L/t)/L}else{let[e,t]=I.split(`/`).map(e=>parseInt(e));x=E,S=e,C=t,E+=isNaN(L)?e/t:Math.round(e*L/t)/L}return E=M===`-`?E*-1:E,f&&o.percentage!==`number`&&(E=isNaN(L)?E/100:Math.round(E/100*L)/L),T(E)}return e.defaultOptions=p,e.isNumericQuantity=(e,t)=>{let n=y(e,{...t,verbose:!1});return typeof n==`bigint`||!isNaN(n)},e.normalizeDigits=n,e.numericQuantity=y,e.numericRegex=o,e.numericRegexWithTrailingInvalid=s,e.parseRomanNumerals=m,e.romanNumeralRegex=f,e.romanNumeralUnicodeRegex=d,e.romanNumeralUnicodeToAsciiMap=u,e.romanNumeralValues=l,e.superSubDigitToAsciiMap=r,e.superSubDigitsRegex=i,e.vulgarFractionToAsciiMap=a,e.vulgarFractionsRegex=c,e})({});
2
2
  //# sourceMappingURL=numeric-quantity.iife.umd.min.js.map