numeric-quantity 3.0.0 → 3.2.0

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.
Files changed (32) hide show
  1. package/README.md +173 -16
  2. package/dist/cjs/index.d.ts +1 -0
  3. package/dist/cjs/numeric-quantity.cjs.development.d.ts +225 -0
  4. package/dist/cjs/numeric-quantity.cjs.development.js +208 -18
  5. package/dist/cjs/numeric-quantity.cjs.development.js.map +1 -1
  6. package/dist/cjs/numeric-quantity.cjs.production.d.ts +225 -0
  7. package/dist/cjs/numeric-quantity.cjs.production.js +1 -1
  8. package/dist/cjs/numeric-quantity.cjs.production.js.map +1 -1
  9. package/dist/numeric-quantity.d.mts +225 -0
  10. package/dist/numeric-quantity.iife.umd.min.js +1 -1
  11. package/dist/numeric-quantity.iife.umd.min.js.map +1 -1
  12. package/dist/numeric-quantity.legacy-esm.d.ts +225 -0
  13. package/dist/numeric-quantity.legacy-esm.js +221 -37
  14. package/dist/numeric-quantity.legacy-esm.js.map +1 -1
  15. package/dist/numeric-quantity.mjs +206 -19
  16. package/dist/numeric-quantity.mjs.map +1 -1
  17. package/dist/numeric-quantity.production.d.mts +225 -0
  18. package/dist/numeric-quantity.production.mjs +1 -1
  19. package/dist/numeric-quantity.production.mjs.map +1 -1
  20. package/package.json +13 -14
  21. package/dist/types/constants.d.ts +0 -79
  22. package/dist/types/dev.d.ts +0 -1
  23. package/dist/types/index.d.ts +0 -4
  24. package/dist/types/numericQuantity.d.ts +0 -12
  25. package/dist/types/parseRomanNumerals.d.ts +0 -8
  26. package/dist/types/types.d.ts +0 -49
  27. package/dist/types-esm/constants.d.mts +0 -79
  28. package/dist/types-esm/dev.d.mts +0 -1
  29. package/dist/types-esm/index.d.mts +0 -4
  30. package/dist/types-esm/numericQuantity.d.mts +0 -12
  31. package/dist/types-esm/parseRomanNumerals.d.mts +0 -8
  32. package/dist/types-esm/types.d.mts +0 -49
@@ -0,0 +1,225 @@
1
+ //#region src/types.d.ts
2
+ interface NumericQuantityOptions {
3
+ /**
4
+ * Round the result to this many decimal places. Defaults to 3; must
5
+ * be greater than or equal to zero.
6
+ *
7
+ * @default 3
8
+ */
9
+ round?: number | false;
10
+ /**
11
+ * Allow and ignore trailing invalid characters _à la_ `parseFloat`.
12
+ *
13
+ * @default false
14
+ */
15
+ allowTrailingInvalid?: boolean;
16
+ /**
17
+ * Attempt to parse Roman numerals if Arabic numeral parsing fails.
18
+ *
19
+ * @default false
20
+ */
21
+ romanNumerals?: boolean;
22
+ /**
23
+ * Generates a `bigint` value if the string represents
24
+ * a valid integer too large for the `number` type.
25
+ */
26
+ bigIntOnOverflow?: boolean;
27
+ /**
28
+ * Specifies which character ("." or ",") to treat as the decimal separator.
29
+ *
30
+ * @default "."
31
+ */
32
+ decimalSeparator?: "," | ".";
33
+ /**
34
+ * Allow and strip currency symbols (Unicode `\p{Sc}` category) from the
35
+ * start and/or end of the string.
36
+ *
37
+ * @default false
38
+ */
39
+ allowCurrency?: boolean;
40
+ /**
41
+ * Parse percentage strings by stripping the `%` suffix.
42
+ * - `'decimal'` or `true`: `"50%"` → `0.5` (divide by 100)
43
+ * - `'number'`: `"50%"` → `50` (strip `%`, keep value)
44
+ * - `false` or omitted: `"50%"` → `NaN` (default behavior)
45
+ *
46
+ * @default false
47
+ */
48
+ percentage?: "decimal" | "number" | boolean;
49
+ /**
50
+ * Return a verbose result object with additional parsing metadata.
51
+ *
52
+ * @default false
53
+ */
54
+ verbose?: boolean;
55
+ }
56
+ /**
57
+ * Resolves the return type of {@link numericQuantity} based on the options provided.
58
+ */
59
+ type NumericQuantityReturnType<T extends NumericQuantityOptions | undefined = undefined> = T extends {
60
+ verbose: true;
61
+ } ? NumericQuantityVerboseResult : T extends {
62
+ bigIntOnOverflow: true;
63
+ } ? number | bigint : number;
64
+ /**
65
+ * Verbose result returned when `verbose: true` is set.
66
+ */
67
+ interface NumericQuantityVerboseResult {
68
+ /** The parsed numeric value (NaN if invalid). */
69
+ value: number | bigint;
70
+ /** The original input string. */
71
+ input: string;
72
+ /** Currency symbol(s) stripped from the start, if any. */
73
+ currencyPrefix?: string;
74
+ /** Currency symbol(s) stripped from the end, if any. */
75
+ currencySuffix?: string;
76
+ /** True if a `%` suffix was stripped. */
77
+ percentageSuffix?: boolean;
78
+ /** Trailing invalid (usually non-numeric) characters detected in the input, if any. Populated even when `allowTrailingInvalid` is `false`. */
79
+ trailingInvalid?: string;
80
+ /** The leading sign character (`'-'` or `'+'`), if present. Omitted when no explicit sign was in the input. */
81
+ sign?: "-" | "+";
82
+ /** The whole-number part of a mixed fraction (e.g. `1` from `"1 2/3"`). Omitted for pure fractions, decimals, and integers. */
83
+ whole?: number;
84
+ /** The numerator of a fraction (e.g. `2` from `"1 2/3"`, or `1` from `"1/2"`). Always unsigned. */
85
+ numerator?: number;
86
+ /** The denominator of a fraction (e.g. `3` from `"1 2/3"`, or `2` from `"1/2"`). Always unsigned. */
87
+ denominator?: number;
88
+ }
89
+ /**
90
+ * Unicode vulgar fraction code points.
91
+ */
92
+ type VulgarFraction = "¼" | "½" | "¾" | "⅐" | "⅑" | "⅒" | "⅓" | "⅔" | "⅕" | "⅖" | "⅗" | "⅘" | "⅙" | "⅚" | "⅛" | "⅜" | "⅝" | "⅞" | "⅟";
93
+ /**
94
+ * Allowable Roman numeral characters (ASCII, uppercase only).
95
+ */
96
+ type RomanNumeralAscii = "I" | "V" | "X" | "L" | "C" | "D" | "M";
97
+ /**
98
+ * Unicode Roman numeral code points (uppercase and lowercase,
99
+ * representing 1-12, 50, 100, 500, and 1000).
100
+ */
101
+ type RomanNumeralUnicode = "Ⅰ" | "Ⅱ" | "Ⅲ" | "Ⅳ" | "Ⅴ" | "Ⅵ" | "Ⅶ" | "Ⅷ" | "Ⅸ" | "Ⅹ" | "Ⅺ" | "Ⅻ" | "Ⅼ" | "Ⅽ" | "Ⅾ" | "Ⅿ" | "ⅰ" | "ⅱ" | "ⅲ" | "ⅳ" | "ⅴ" | "ⅵ" | "ⅶ" | "ⅷ" | "ⅸ" | "ⅹ" | "ⅺ" | "ⅻ" | "ⅼ" | "ⅽ" | "ⅾ" | "ⅿ";
102
+ /**
103
+ * Union of ASCII and Unicode Roman numeral characters/code points.
104
+ */
105
+ type RomanNumeral = RomanNumeralAscii | RomanNumeralUnicode;
106
+ //#endregion
107
+ //#region src/constants.d.ts
108
+ /**
109
+ * Normalizes non-ASCII decimal digits to ASCII digits.
110
+ * Converts characters from Unicode decimal digit blocks (e.g., Arabic-Indic,
111
+ * Devanagari, Bengali) to their ASCII equivalents (0-9).
112
+ *
113
+ * All current Unicode \p{Nd} blocks are included in decimalDigitBlockStarts.
114
+ */
115
+ declare const normalizeDigits: (str: string) => string;
116
+ /**
117
+ * Map of Unicode fraction code points to their ASCII equivalents.
118
+ */
119
+ declare const vulgarFractionToAsciiMap: Record<VulgarFraction, `${number}/${number | ""}`>;
120
+ /**
121
+ * Captures the individual elements of a numeric string. Commas and underscores are allowed
122
+ * as separators, as long as they appear between digits and are not consecutive.
123
+ *
124
+ * Capture groups:
125
+ *
126
+ * | # | Description | Example(s) |
127
+ * | --- | ------------------------------------------------ | ------------------------------------------------------------------- |
128
+ * | `0` | entire string | `"2 1/3"` from `"2 1/3"` |
129
+ * | `1` | sign (`-` or `+`) | `"-"` from `"-2 1/3"` |
130
+ * | `2` | whole number or numerator | `"2"` from `"2 1/3"`; `"1"` from `"1/3"` |
131
+ * | `3` | entire fraction, decimal portion, or denominator | `" 1/3"` from `"2 1/3"`; `".33"` from `"2.33"`; `"/3"` from `"1/3"` |
132
+ *
133
+ * _Capture group 2 may include comma/underscore separators._
134
+ *
135
+ * @example
136
+ *
137
+ * ```ts
138
+ * numericRegex.exec("1") // [ "1", "1", null, null ]
139
+ * numericRegex.exec("1.23") // [ "1.23", "1", ".23", null ]
140
+ * numericRegex.exec("1 2/3") // [ "1 2/3", "1", " 2/3", " 2" ]
141
+ * numericRegex.exec("2/3") // [ "2/3", "2", "/3", null ]
142
+ * numericRegex.exec("2 / 3") // [ "2 / 3", "2", "/ 3", null ]
143
+ * ```
144
+ */
145
+ declare const numericRegex: RegExp;
146
+ /**
147
+ * Same as {@link numericRegex}, but allows (and ignores) trailing invalid characters.
148
+ * Capture group 7 contains the trailing invalid portion.
149
+ */
150
+ declare const numericRegexWithTrailingInvalid: RegExp;
151
+ /**
152
+ * Captures any Unicode vulgar fractions.
153
+ */
154
+ declare const vulgarFractionsRegex: RegExp;
155
+ type RomanNumeralSequenceFragment = `${RomanNumeralAscii}` | `${RomanNumeralAscii}${RomanNumeralAscii}` | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}` | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`;
156
+ /**
157
+ * Map of Roman numeral sequences to their decimal equivalents.
158
+ */
159
+ declare const romanNumeralValues: { [k in RomanNumeralSequenceFragment]?: number };
160
+ /**
161
+ * Map of Unicode Roman numeral code points to their ASCII equivalents.
162
+ */
163
+ declare const romanNumeralUnicodeToAsciiMap: Record<RomanNumeralUnicode, keyof typeof romanNumeralValues>;
164
+ /**
165
+ * Captures all Unicode Roman numeral code points.
166
+ */
167
+ declare const romanNumeralUnicodeRegex: RegExp;
168
+ /**
169
+ * Captures a valid Roman numeral sequence.
170
+ *
171
+ * Capture groups:
172
+ *
173
+ * | # | Description | Example |
174
+ * | --- | --------------- | ------------------------ |
175
+ * | `0` | Entire string | "MCCXIV" from "MCCXIV" |
176
+ * | `1` | Thousands | "M" from "MCCXIV" |
177
+ * | `2` | Hundreds | "CC" from "MCCXIV" |
178
+ * | `3` | Tens | "X" from "MCCXIV" |
179
+ * | `4` | Ones | "IV" from "MCCXIV" |
180
+ *
181
+ * @example
182
+ *
183
+ * ```ts
184
+ * romanNumeralRegex.exec("M") // [ "M", "M", "", "", "" ]
185
+ * romanNumeralRegex.exec("XII") // [ "XII", "", "", "X", "II" ]
186
+ * romanNumeralRegex.exec("MCCXIV") // [ "MCCXIV", "M", "CC", "X", "IV" ]
187
+ * ```
188
+ */
189
+ declare const romanNumeralRegex: RegExp;
190
+ /**
191
+ * Default options for {@link numericQuantity}.
192
+ */
193
+ declare const defaultOptions: Required<NumericQuantityOptions>;
194
+ //#endregion
195
+ //#region src/isNumericQuantity.d.ts
196
+ /**
197
+ * Checks if a string represents a valid numeric quantity.
198
+ *
199
+ * Returns `true` if the string can be parsed as a number, `false` otherwise.
200
+ * Accepts the same options as `numericQuantity`.
201
+ */
202
+ declare const isNumericQuantity: (quantity: string | number, options?: NumericQuantityOptions) => boolean;
203
+ //#endregion
204
+ //#region src/numericQuantity.d.ts
205
+ /**
206
+ * Converts a string to a number, like an enhanced version of `parseFloat`.
207
+ *
208
+ * The string can include mixed numbers, vulgar fractions, or Roman numerals.
209
+ */
210
+ declare function numericQuantity(quantity: string | number): number;
211
+ declare function numericQuantity<T extends NumericQuantityOptions>(quantity: string | number, options: T): NumericQuantityReturnType<T>;
212
+ declare function numericQuantity(quantity: string | number, options?: NumericQuantityOptions): number;
213
+ //#endregion
214
+ //#region src/parseRomanNumerals.d.ts
215
+ /**
216
+ * Converts a string of Roman numerals to a number, like `parseInt`
217
+ * for Roman numerals. Uses modern, strict rules (only 1 to 3999).
218
+ *
219
+ * The string can include ASCII representations of Roman numerals
220
+ * or Unicode Roman numeral code points (`U+2160` through `U+217F`).
221
+ */
222
+ declare const parseRomanNumerals: (romanNumerals: string) => number;
223
+ //#endregion
224
+ export { NumericQuantityOptions, NumericQuantityReturnType, NumericQuantityVerboseResult, RomanNumeral, RomanNumeralAscii, RomanNumeralUnicode, VulgarFraction, defaultOptions, isNumericQuantity, normalizeDigits, numericQuantity, numericRegex, numericRegexWithTrailingInvalid, parseRomanNumerals, romanNumeralRegex, romanNumeralUnicodeRegex, romanNumeralUnicodeToAsciiMap, romanNumeralValues, vulgarFractionToAsciiMap, vulgarFractionsRegex };
225
+ //# sourceMappingURL=numeric-quantity.legacy-esm.d.ts.map
@@ -1,5 +1,109 @@
1
1
  //#region src/constants.ts
2
2
  /**
3
+ * Unicode decimal digit block start code points.
4
+ * Each block contains 10 contiguous digits (0-9).
5
+ * This list covers all \p{Nd} (Decimal_Number) blocks through Unicode 17.0.
6
+ * The drift test in index.test.ts validates completeness against the JS engine.
7
+ */
8
+ const decimalDigitBlockStarts = [
9
+ 48,
10
+ 1632,
11
+ 1776,
12
+ 1984,
13
+ 2406,
14
+ 2534,
15
+ 2662,
16
+ 2790,
17
+ 2918,
18
+ 3046,
19
+ 3174,
20
+ 3302,
21
+ 3430,
22
+ 3558,
23
+ 3664,
24
+ 3792,
25
+ 3872,
26
+ 4160,
27
+ 4240,
28
+ 6112,
29
+ 6160,
30
+ 6470,
31
+ 6608,
32
+ 6784,
33
+ 6800,
34
+ 6992,
35
+ 7088,
36
+ 7232,
37
+ 7248,
38
+ 42528,
39
+ 43216,
40
+ 43264,
41
+ 43472,
42
+ 43504,
43
+ 43600,
44
+ 44016,
45
+ 65296,
46
+ 66720,
47
+ 68912,
48
+ 68928,
49
+ 69734,
50
+ 69872,
51
+ 69942,
52
+ 70096,
53
+ 70384,
54
+ 70736,
55
+ 70864,
56
+ 71248,
57
+ 71360,
58
+ 71376,
59
+ 71386,
60
+ 71472,
61
+ 71904,
62
+ 72016,
63
+ 72688,
64
+ 72784,
65
+ 73040,
66
+ 73120,
67
+ 73184,
68
+ 73552,
69
+ 90416,
70
+ 92768,
71
+ 92864,
72
+ 93008,
73
+ 93552,
74
+ 118e3,
75
+ 120782,
76
+ 120792,
77
+ 120802,
78
+ 120812,
79
+ 120822,
80
+ 123200,
81
+ 123632,
82
+ 124144,
83
+ 124401,
84
+ 125264,
85
+ 130032
86
+ ];
87
+ /**
88
+ * Normalizes non-ASCII decimal digits to ASCII digits.
89
+ * Converts characters from Unicode decimal digit blocks (e.g., Arabic-Indic,
90
+ * Devanagari, Bengali) to their ASCII equivalents (0-9).
91
+ *
92
+ * All current Unicode \p{Nd} blocks are included in decimalDigitBlockStarts.
93
+ */
94
+ const normalizeDigits = (str) => str.replace(/* @__PURE__ */ new RegExp("\\p{Nd}", "gu"), (ch) => {
95
+ const cp = ch.codePointAt(0);
96
+ if (cp <= 57) return ch;
97
+ let lo = 0;
98
+ let hi = decimalDigitBlockStarts.length - 1;
99
+ while (lo < hi) {
100
+ const mid = lo + hi + 1 >>> 1;
101
+ if (decimalDigitBlockStarts[mid] <= cp) lo = mid;
102
+ else hi = mid - 1;
103
+ }
104
+ return String(cp - decimalDigitBlockStarts[lo]);
105
+ });
106
+ /**
3
107
  * Map of Unicode fraction code points to their ASCII equivalents.
4
108
  */
5
109
  const vulgarFractionToAsciiMap = {
@@ -32,7 +136,7 @@ const vulgarFractionToAsciiMap = {
32
136
  * | # | Description | Example(s) |
33
137
  * | --- | ------------------------------------------------ | ------------------------------------------------------------------- |
34
138
  * | `0` | entire string | `"2 1/3"` from `"2 1/3"` |
35
- * | `1` | "negative" dash | `"-"` from `"-2 1/3"` |
139
+ * | `1` | sign (`-` or `+`) | `"-"` from `"-2 1/3"` |
36
140
  * | `2` | whole number or numerator | `"2"` from `"2 1/3"`; `"1"` from `"1/3"` |
37
141
  * | `3` | entire fraction, decimal portion, or denominator | `" 1/3"` from `"2 1/3"`; `".33"` from `"2.33"`; `"/3"` from `"1/3"` |
38
142
  *
@@ -48,15 +152,16 @@ const vulgarFractionToAsciiMap = {
48
152
  * numericRegex.exec("2 / 3") // [ "2 / 3", "2", "/ 3", null ]
49
153
  * ```
50
154
  */
51
- const numericRegex = new RegExp("^(?=-?\\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)*)?$", "");
155
+ const numericRegex = /^(?=[-+]?\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)*)?$/;
52
156
  /**
53
157
  * Same as {@link numericRegex}, but allows (and ignores) trailing invalid characters.
158
+ * Capture group 7 contains the trailing invalid portion.
54
159
  */
55
- const numericRegexWithTrailingInvalid = new RegExp("^(?=-?\\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/].*)?", "");
160
+ const numericRegexWithTrailingInvalid = /^(?=[-+]?\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/].*)?/;
56
161
  /**
57
162
  * Captures any Unicode vulgar fractions.
58
163
  */
59
- const vulgarFractionsRegex = new RegExp("([¼½¾⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟}])", "g");
164
+ const vulgarFractionsRegex = /([¼½¾⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟}])/g;
60
165
  /**
61
166
  * Map of Roman numeral sequences to their decimal equivalents.
62
167
  */
@@ -134,7 +239,7 @@ const romanNumeralUnicodeToAsciiMap = {
134
239
  /**
135
240
  * Captures all Unicode Roman numeral code points.
136
241
  */
137
- const romanNumeralUnicodeRegex = new RegExp("([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])", "gi");
242
+ const romanNumeralUnicodeRegex = /([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/gi;
138
243
  /**
139
244
  * Captures a valid Roman numeral sequence.
140
245
  *
@@ -156,7 +261,7 @@ const romanNumeralUnicodeRegex = new RegExp("([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪ
156
261
  * romanNumeralRegex.exec("MCCXIV") // [ "MCCXIV", "M", "CC", "X", "IV" ]
157
262
  * ```
158
263
  */
159
- const romanNumeralRegex = new RegExp("^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$", "i");
264
+ const romanNumeralRegex = /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;
160
265
  /**
161
266
  * Default options for {@link numericQuantity}.
162
267
  */
@@ -165,7 +270,10 @@ const defaultOptions = {
165
270
  allowTrailingInvalid: false,
166
271
  romanNumerals: false,
167
272
  bigIntOnOverflow: false,
168
- decimalSeparator: "."
273
+ decimalSeparator: ".",
274
+ allowCurrency: false,
275
+ percentage: false,
276
+ verbose: false
169
277
  };
170
278
 
171
279
  //#endregion
@@ -187,18 +295,18 @@ const parseRomanNumerals = (romanNumerals) => {
187
295
  };
188
296
 
189
297
  //#endregion
190
- //#region node_modules/@oxc-project/runtime/src/helpers/esm/typeof.js
298
+ //#region \0@oxc-project+runtime@0.112.0/helpers/typeof.js
191
299
  function _typeof(o) {
192
300
  "@babel/helpers - typeof";
193
- return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
194
- return typeof o$1;
195
- } : function(o$1) {
196
- return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
301
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
302
+ return typeof o;
303
+ } : function(o) {
304
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
197
305
  }, _typeof(o);
198
306
  }
199
307
 
200
308
  //#endregion
201
- //#region node_modules/@oxc-project/runtime/src/helpers/esm/toPrimitive.js
309
+ //#region \0@oxc-project+runtime@0.112.0/helpers/toPrimitive.js
202
310
  function toPrimitive(t, r) {
203
311
  if ("object" != _typeof(t) || !t) return t;
204
312
  var e = t[Symbol.toPrimitive];
@@ -211,14 +319,14 @@ function toPrimitive(t, r) {
211
319
  }
212
320
 
213
321
  //#endregion
214
- //#region node_modules/@oxc-project/runtime/src/helpers/esm/toPropertyKey.js
322
+ //#region \0@oxc-project+runtime@0.112.0/helpers/toPropertyKey.js
215
323
  function toPropertyKey(t) {
216
324
  var i = toPrimitive(t, "string");
217
325
  return "symbol" == _typeof(i) ? i : i + "";
218
326
  }
219
327
 
220
328
  //#endregion
221
- //#region node_modules/@oxc-project/runtime/src/helpers/esm/defineProperty.js
329
+ //#region \0@oxc-project+runtime@0.112.0/helpers/defineProperty.js
222
330
  function _defineProperty(e, r, t) {
223
331
  return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
224
332
  value: t,
@@ -229,13 +337,13 @@ function _defineProperty(e, r, t) {
229
337
  }
230
338
 
231
339
  //#endregion
232
- //#region node_modules/@oxc-project/runtime/src/helpers/esm/objectSpread2.js
340
+ //#region \0@oxc-project+runtime@0.112.0/helpers/objectSpread2.js
233
341
  function ownKeys(e, r) {
234
342
  var t = Object.keys(e);
235
343
  if (Object.getOwnPropertySymbols) {
236
344
  var o = Object.getOwnPropertySymbols(e);
237
- r && (o = o.filter(function(r$1) {
238
- return Object.getOwnPropertyDescriptor(e, r$1).enumerable;
345
+ r && (o = o.filter(function(r) {
346
+ return Object.getOwnPropertyDescriptor(e, r).enumerable;
239
347
  })), t.push.apply(t, o);
240
348
  }
241
349
  return t;
@@ -243,10 +351,10 @@ function ownKeys(e, r) {
243
351
  function _objectSpread2(e) {
244
352
  for (var r = 1; r < arguments.length; r++) {
245
353
  var t = null != arguments[r] ? arguments[r] : {};
246
- r % 2 ? ownKeys(Object(t), !0).forEach(function(r$1) {
247
- _defineProperty(e, r$1, t[r$1]);
248
- }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r$1) {
249
- Object.defineProperty(e, r$1, Object.getOwnPropertyDescriptor(t, r$1));
354
+ r % 2 ? ownKeys(Object(t), !0).forEach(function(r) {
355
+ _defineProperty(e, r, t[r]);
356
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r) {
357
+ Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
250
358
  });
251
359
  }
252
360
  return e;
@@ -255,18 +363,65 @@ function _objectSpread2(e) {
255
363
  //#endregion
256
364
  //#region src/numericQuantity.ts
257
365
  const spaceThenSlashRegex = /^\s*\//;
366
+ const currencyPrefixRegex = /* @__PURE__ */ new RegExp("^([-+]?)\\s*(\\p{Sc}+)\\s*", "u");
367
+ const currencySuffixRegex = /* @__PURE__ */ new RegExp("\\s*(\\p{Sc}+)$", "u");
368
+ const percentageSuffixRegex = /%$/;
258
369
  function numericQuantity(quantity, options = defaultOptions) {
259
- if (typeof quantity === "number" || typeof quantity === "bigint") return quantity;
260
- let finalResult = NaN;
261
- const quantityAsString = `${quantity}`.replace(vulgarFractionsRegex, (_m, vf) => ` ${vulgarFractionToAsciiMap[vf]}`).replace("⁄", "/").trim();
262
- if (quantityAsString.length === 0) return NaN;
263
370
  const opts = _objectSpread2(_objectSpread2({}, defaultOptions), options);
371
+ const originalInput = typeof quantity === "string" ? quantity : `${quantity}`;
372
+ let currencyPrefix;
373
+ let currencySuffix;
374
+ let percentageSuffix;
375
+ let trailingInvalid;
376
+ let parsedSign;
377
+ let parsedWhole;
378
+ let parsedNumerator;
379
+ let parsedDenominator;
380
+ const buildVerboseResult = (value) => {
381
+ const result = {
382
+ value,
383
+ input: originalInput
384
+ };
385
+ if (currencyPrefix) result.currencyPrefix = currencyPrefix;
386
+ if (currencySuffix) result.currencySuffix = currencySuffix;
387
+ if (percentageSuffix) result.percentageSuffix = percentageSuffix;
388
+ if (trailingInvalid) result.trailingInvalid = trailingInvalid;
389
+ if (parsedSign) result.sign = parsedSign;
390
+ if (parsedWhole !== void 0) result.whole = parsedWhole;
391
+ if (parsedNumerator !== void 0) result.numerator = parsedNumerator;
392
+ if (parsedDenominator !== void 0) result.denominator = parsedDenominator;
393
+ return result;
394
+ };
395
+ const returnValue = (value) => opts.verbose ? buildVerboseResult(value) : value;
396
+ if (typeof quantity === "number" || typeof quantity === "bigint") return returnValue(quantity);
397
+ let finalResult = NaN;
398
+ let workingString = `${quantity}`;
399
+ if (opts.allowCurrency) {
400
+ const prefixMatch = currencyPrefixRegex.exec(workingString);
401
+ if (prefixMatch && prefixMatch[2]) {
402
+ currencyPrefix = prefixMatch[2];
403
+ workingString = (prefixMatch[1] || "") + workingString.slice(prefixMatch[0].length);
404
+ }
405
+ }
406
+ if (opts.allowCurrency) {
407
+ const suffixMatch = currencySuffixRegex.exec(workingString);
408
+ if (suffixMatch) {
409
+ currencySuffix = suffixMatch[1];
410
+ workingString = workingString.slice(0, -suffixMatch[0].length);
411
+ }
412
+ }
413
+ if (opts.percentage && percentageSuffixRegex.test(workingString)) {
414
+ percentageSuffix = true;
415
+ workingString = workingString.slice(0, -1);
416
+ }
417
+ const quantityAsString = normalizeDigits(workingString.replace(vulgarFractionsRegex, (_m, vf) => ` ${vulgarFractionToAsciiMap[vf]}`).replace("⁄", "/").trim());
418
+ if (quantityAsString.length === 0) return returnValue(NaN);
264
419
  let normalizedString = quantityAsString;
265
420
  if (opts.decimalSeparator === ",") {
266
421
  const commaCount = (quantityAsString.match(/,/g) || []).length;
267
422
  if (commaCount === 1) normalizedString = quantityAsString.replaceAll(".", "_").replace(",", ".");
268
423
  else if (commaCount > 1) {
269
- if (!opts.allowTrailingInvalid) return NaN;
424
+ if (!opts.allowTrailingInvalid) return returnValue(NaN);
270
425
  const firstCommaIndex = quantityAsString.indexOf(",");
271
426
  const secondCommaIndex = quantityAsString.indexOf(",", firstCommaIndex + 1);
272
427
  const beforeSecondComma = quantityAsString.substring(0, secondCommaIndex).replaceAll(".", "_").replace(",", ".");
@@ -274,20 +429,30 @@ function numericQuantity(quantity, options = defaultOptions) {
274
429
  normalizedString = opts.allowTrailingInvalid ? beforeSecondComma + "&" + afterSecondComma : beforeSecondComma;
275
430
  } else normalizedString = quantityAsString.replaceAll(".", "_");
276
431
  }
277
- const regexResult = (opts.allowTrailingInvalid ? numericRegexWithTrailingInvalid : numericRegex).exec(normalizedString);
278
- if (!regexResult) return opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN;
279
- const [, dash, ng1temp, ng2temp] = regexResult;
432
+ const regexResult = numericRegexWithTrailingInvalid.exec(normalizedString);
433
+ if (!regexResult) return returnValue(opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN);
434
+ const rawTrailing = (regexResult[7] || normalizedString.slice(regexResult[0].length)).trim();
435
+ if (rawTrailing) {
436
+ trailingInvalid = rawTrailing;
437
+ if (!opts.allowTrailingInvalid) return returnValue(NaN);
438
+ }
439
+ const [, sign, ng1temp, ng2temp] = regexResult;
440
+ if (sign === "-" || sign === "+") parsedSign = sign;
280
441
  const numberGroup1 = ng1temp.replaceAll(",", "").replaceAll("_", "");
281
442
  const numberGroup2 = ng2temp === null || ng2temp === void 0 ? void 0 : ng2temp.replaceAll(",", "").replaceAll("_", "");
282
443
  if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith(".")) finalResult = 0;
283
444
  else {
284
445
  if (opts.bigIntOnOverflow) {
285
- const asBigInt = dash ? BigInt(`-${numberGroup1}`) : BigInt(numberGroup1);
286
- if (asBigInt > BigInt(Number.MAX_SAFE_INTEGER) || asBigInt < BigInt(Number.MIN_SAFE_INTEGER)) return asBigInt;
446
+ const asBigInt = sign === "-" ? BigInt(`-${numberGroup1}`) : BigInt(numberGroup1);
447
+ if (asBigInt > BigInt(Number.MAX_SAFE_INTEGER) || asBigInt < BigInt(Number.MIN_SAFE_INTEGER)) return returnValue(asBigInt);
287
448
  }
288
449
  finalResult = parseInt(numberGroup1);
289
450
  }
290
- if (!numberGroup2) return dash ? finalResult * -1 : finalResult;
451
+ if (!numberGroup2) {
452
+ finalResult = sign === "-" ? finalResult * -1 : finalResult;
453
+ if (percentageSuffix && opts.percentage !== "number") finalResult = finalResult / 100;
454
+ return returnValue(finalResult);
455
+ }
291
456
  const roundingFactor = opts.round === false ? NaN : parseFloat(`1e${Math.floor(Math.max(0, opts.round))}`);
292
457
  if (numberGroup2.startsWith(".") || numberGroup2.startsWith("e") || numberGroup2.startsWith("E")) {
293
458
  const decimalValue = parseFloat(`${finalResult}${numberGroup2}`);
@@ -295,15 +460,34 @@ function numericQuantity(quantity, options = defaultOptions) {
295
460
  } else if (spaceThenSlashRegex.test(numberGroup2)) {
296
461
  const numerator = parseInt(numberGroup1);
297
462
  const denominator = parseInt(numberGroup2.replace("/", ""));
463
+ parsedNumerator = numerator;
464
+ parsedDenominator = denominator;
298
465
  finalResult = isNaN(roundingFactor) ? numerator / denominator : Math.round(numerator * roundingFactor / denominator) / roundingFactor;
299
466
  } else {
300
- const fractionArray = numberGroup2.split("/");
301
- const [numerator, denominator] = fractionArray.map((v) => parseInt(v));
467
+ const [numerator, denominator] = numberGroup2.split("/").map((v) => parseInt(v));
468
+ parsedWhole = finalResult;
469
+ parsedNumerator = numerator;
470
+ parsedDenominator = denominator;
302
471
  finalResult += isNaN(roundingFactor) ? numerator / denominator : Math.round(numerator * roundingFactor / denominator) / roundingFactor;
303
472
  }
304
- return dash ? finalResult * -1 : finalResult;
473
+ finalResult = sign === "-" ? finalResult * -1 : finalResult;
474
+ if (percentageSuffix && opts.percentage !== "number") finalResult = isNaN(roundingFactor) ? finalResult / 100 : Math.round(finalResult / 100 * roundingFactor) / roundingFactor;
475
+ return returnValue(finalResult);
305
476
  }
306
477
 
307
478
  //#endregion
308
- export { defaultOptions, numericQuantity, numericRegex, numericRegexWithTrailingInvalid, parseRomanNumerals, romanNumeralRegex, romanNumeralUnicodeRegex, romanNumeralUnicodeToAsciiMap, romanNumeralValues, vulgarFractionToAsciiMap, vulgarFractionsRegex };
479
+ //#region src/isNumericQuantity.ts
480
+ /**
481
+ * Checks if a string represents a valid numeric quantity.
482
+ *
483
+ * Returns `true` if the string can be parsed as a number, `false` otherwise.
484
+ * Accepts the same options as `numericQuantity`.
485
+ */
486
+ const isNumericQuantity = (quantity, options) => {
487
+ const result = numericQuantity(quantity, _objectSpread2(_objectSpread2({}, options), {}, { verbose: false }));
488
+ return typeof result === "bigint" || !isNaN(result);
489
+ };
490
+
491
+ //#endregion
492
+ export { defaultOptions, isNumericQuantity, normalizeDigits, numericQuantity, numericRegex, numericRegexWithTrailingInvalid, parseRomanNumerals, romanNumeralRegex, romanNumeralUnicodeRegex, romanNumeralUnicodeToAsciiMap, romanNumeralValues, vulgarFractionToAsciiMap, vulgarFractionsRegex };
309
493
  //# sourceMappingURL=numeric-quantity.legacy-esm.js.map