@optique/core 1.0.0-dev.693 → 1.0.0-dev.705
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.
- package/dist/dependency.cjs +71 -17
- package/dist/dependency.js +71 -17
- package/dist/valueparser.cjs +104 -18
- package/dist/valueparser.js +104 -18
- package/package.json +1 -1
package/dist/dependency.cjs
CHANGED
|
@@ -86,7 +86,7 @@ function dependency(parser) {
|
|
|
86
86
|
return createSyncDerivedParser(id, options);
|
|
87
87
|
},
|
|
88
88
|
deriveAsync(options) {
|
|
89
|
-
return
|
|
89
|
+
return createAsyncDerivedParserFromAsyncFactory(id, options);
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
92
|
return result;
|
|
@@ -212,8 +212,17 @@ function createSyncDerivedFromParser(sourceId, options) {
|
|
|
212
212
|
[dependencyIds]: alldependencyIds,
|
|
213
213
|
[defaultValues]: options.defaultValues,
|
|
214
214
|
parse(input) {
|
|
215
|
-
|
|
216
|
-
|
|
215
|
+
let derivedParser;
|
|
216
|
+
try {
|
|
217
|
+
const sourceValues = options.defaultValues();
|
|
218
|
+
derivedParser = options.factory(...sourceValues);
|
|
219
|
+
} catch (e) {
|
|
220
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
221
|
+
return {
|
|
222
|
+
success: false,
|
|
223
|
+
error: require_message.message`Derived parser error: ${msg}`
|
|
224
|
+
};
|
|
225
|
+
}
|
|
217
226
|
if (isAsyncModeParser(derivedParser)) return {
|
|
218
227
|
success: false,
|
|
219
228
|
error: require_message.message`Factory returned an async parser where a sync parser is required.`
|
|
@@ -277,9 +286,18 @@ function createAsyncDerivedFromParserFromAsyncFactory(sourceId, options) {
|
|
|
277
286
|
[dependencyIds]: alldependencyIds,
|
|
278
287
|
[defaultValues]: options.defaultValues,
|
|
279
288
|
parse(input) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
289
|
+
let derivedParser;
|
|
290
|
+
try {
|
|
291
|
+
const sourceValues = options.defaultValues();
|
|
292
|
+
derivedParser = options.factory(...sourceValues);
|
|
293
|
+
} catch (e) {
|
|
294
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
295
|
+
return Promise.resolve({
|
|
296
|
+
success: false,
|
|
297
|
+
error: require_message.message`Derived parser error: ${msg}`
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return Promise.resolve(derivedParser.parse(input));
|
|
283
301
|
},
|
|
284
302
|
[parseWithDependency](input, dependencyValue) {
|
|
285
303
|
let derivedParser;
|
|
@@ -292,7 +310,7 @@ function createAsyncDerivedFromParserFromAsyncFactory(sourceId, options) {
|
|
|
292
310
|
error: require_message.message`Factory error: ${msg}`
|
|
293
311
|
});
|
|
294
312
|
}
|
|
295
|
-
return derivedParser.parse(input);
|
|
313
|
+
return Promise.resolve(derivedParser.parse(input));
|
|
296
314
|
},
|
|
297
315
|
format(value) {
|
|
298
316
|
const sourceValues = options.defaultValues();
|
|
@@ -334,8 +352,17 @@ function createAsyncDerivedFromParserFromSyncFactory(sourceId, options) {
|
|
|
334
352
|
[dependencyIds]: alldependencyIds,
|
|
335
353
|
[defaultValues]: options.defaultValues,
|
|
336
354
|
parse(input) {
|
|
337
|
-
|
|
338
|
-
|
|
355
|
+
let derivedParser;
|
|
356
|
+
try {
|
|
357
|
+
const sourceValues = options.defaultValues();
|
|
358
|
+
derivedParser = options.factory(...sourceValues);
|
|
359
|
+
} catch (e) {
|
|
360
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
361
|
+
return Promise.resolve({
|
|
362
|
+
success: false,
|
|
363
|
+
error: require_message.message`Derived parser error: ${msg}`
|
|
364
|
+
});
|
|
365
|
+
}
|
|
339
366
|
return Promise.resolve(derivedParser.parse(input));
|
|
340
367
|
},
|
|
341
368
|
[parseWithDependency](input, dependencyValue) {
|
|
@@ -402,8 +429,17 @@ function createSyncDerivedParser(sourceId, options) {
|
|
|
402
429
|
[derivedValueParserMarker]: true,
|
|
403
430
|
[dependencyId]: sourceId,
|
|
404
431
|
parse(input) {
|
|
405
|
-
|
|
406
|
-
|
|
432
|
+
let derivedParser;
|
|
433
|
+
try {
|
|
434
|
+
const sourceValue = options.defaultValue();
|
|
435
|
+
derivedParser = options.factory(sourceValue);
|
|
436
|
+
} catch (e) {
|
|
437
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
438
|
+
return {
|
|
439
|
+
success: false,
|
|
440
|
+
error: require_message.message`Derived parser error: ${msg}`
|
|
441
|
+
};
|
|
442
|
+
}
|
|
407
443
|
if (isAsyncModeParser(derivedParser)) return {
|
|
408
444
|
success: false,
|
|
409
445
|
error: require_message.message`Factory returned an async parser where a sync parser is required.`
|
|
@@ -463,9 +499,18 @@ function createAsyncDerivedParserFromAsyncFactory(sourceId, options) {
|
|
|
463
499
|
[derivedValueParserMarker]: true,
|
|
464
500
|
[dependencyId]: sourceId,
|
|
465
501
|
parse(input) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
502
|
+
let derivedParser;
|
|
503
|
+
try {
|
|
504
|
+
const sourceValue = options.defaultValue();
|
|
505
|
+
derivedParser = options.factory(sourceValue);
|
|
506
|
+
} catch (e) {
|
|
507
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
508
|
+
return Promise.resolve({
|
|
509
|
+
success: false,
|
|
510
|
+
error: require_message.message`Derived parser error: ${msg}`
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
return Promise.resolve(derivedParser.parse(input));
|
|
469
514
|
},
|
|
470
515
|
[parseWithDependency](input, dependencyValue) {
|
|
471
516
|
let derivedParser;
|
|
@@ -478,7 +523,7 @@ function createAsyncDerivedParserFromAsyncFactory(sourceId, options) {
|
|
|
478
523
|
error: require_message.message`Factory error: ${msg}`
|
|
479
524
|
});
|
|
480
525
|
}
|
|
481
|
-
return derivedParser.parse(input);
|
|
526
|
+
return Promise.resolve(derivedParser.parse(input));
|
|
482
527
|
},
|
|
483
528
|
format(value) {
|
|
484
529
|
const sourceValue = options.defaultValue();
|
|
@@ -516,8 +561,17 @@ function createAsyncDerivedParserFromSyncFactory(sourceId, options) {
|
|
|
516
561
|
[derivedValueParserMarker]: true,
|
|
517
562
|
[dependencyId]: sourceId,
|
|
518
563
|
parse(input) {
|
|
519
|
-
|
|
520
|
-
|
|
564
|
+
let derivedParser;
|
|
565
|
+
try {
|
|
566
|
+
const sourceValue = options.defaultValue();
|
|
567
|
+
derivedParser = options.factory(sourceValue);
|
|
568
|
+
} catch (e) {
|
|
569
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
570
|
+
return Promise.resolve({
|
|
571
|
+
success: false,
|
|
572
|
+
error: require_message.message`Derived parser error: ${msg}`
|
|
573
|
+
});
|
|
574
|
+
}
|
|
521
575
|
return Promise.resolve(derivedParser.parse(input));
|
|
522
576
|
},
|
|
523
577
|
[parseWithDependency](input, dependencyValue) {
|
package/dist/dependency.js
CHANGED
|
@@ -86,7 +86,7 @@ function dependency(parser) {
|
|
|
86
86
|
return createSyncDerivedParser(id, options);
|
|
87
87
|
},
|
|
88
88
|
deriveAsync(options) {
|
|
89
|
-
return
|
|
89
|
+
return createAsyncDerivedParserFromAsyncFactory(id, options);
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
92
|
return result;
|
|
@@ -212,8 +212,17 @@ function createSyncDerivedFromParser(sourceId, options) {
|
|
|
212
212
|
[dependencyIds]: alldependencyIds,
|
|
213
213
|
[defaultValues]: options.defaultValues,
|
|
214
214
|
parse(input) {
|
|
215
|
-
|
|
216
|
-
|
|
215
|
+
let derivedParser;
|
|
216
|
+
try {
|
|
217
|
+
const sourceValues = options.defaultValues();
|
|
218
|
+
derivedParser = options.factory(...sourceValues);
|
|
219
|
+
} catch (e) {
|
|
220
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
221
|
+
return {
|
|
222
|
+
success: false,
|
|
223
|
+
error: message`Derived parser error: ${msg}`
|
|
224
|
+
};
|
|
225
|
+
}
|
|
217
226
|
if (isAsyncModeParser(derivedParser)) return {
|
|
218
227
|
success: false,
|
|
219
228
|
error: message`Factory returned an async parser where a sync parser is required.`
|
|
@@ -277,9 +286,18 @@ function createAsyncDerivedFromParserFromAsyncFactory(sourceId, options) {
|
|
|
277
286
|
[dependencyIds]: alldependencyIds,
|
|
278
287
|
[defaultValues]: options.defaultValues,
|
|
279
288
|
parse(input) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
289
|
+
let derivedParser;
|
|
290
|
+
try {
|
|
291
|
+
const sourceValues = options.defaultValues();
|
|
292
|
+
derivedParser = options.factory(...sourceValues);
|
|
293
|
+
} catch (e) {
|
|
294
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
295
|
+
return Promise.resolve({
|
|
296
|
+
success: false,
|
|
297
|
+
error: message`Derived parser error: ${msg}`
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return Promise.resolve(derivedParser.parse(input));
|
|
283
301
|
},
|
|
284
302
|
[parseWithDependency](input, dependencyValue) {
|
|
285
303
|
let derivedParser;
|
|
@@ -292,7 +310,7 @@ function createAsyncDerivedFromParserFromAsyncFactory(sourceId, options) {
|
|
|
292
310
|
error: message`Factory error: ${msg}`
|
|
293
311
|
});
|
|
294
312
|
}
|
|
295
|
-
return derivedParser.parse(input);
|
|
313
|
+
return Promise.resolve(derivedParser.parse(input));
|
|
296
314
|
},
|
|
297
315
|
format(value) {
|
|
298
316
|
const sourceValues = options.defaultValues();
|
|
@@ -334,8 +352,17 @@ function createAsyncDerivedFromParserFromSyncFactory(sourceId, options) {
|
|
|
334
352
|
[dependencyIds]: alldependencyIds,
|
|
335
353
|
[defaultValues]: options.defaultValues,
|
|
336
354
|
parse(input) {
|
|
337
|
-
|
|
338
|
-
|
|
355
|
+
let derivedParser;
|
|
356
|
+
try {
|
|
357
|
+
const sourceValues = options.defaultValues();
|
|
358
|
+
derivedParser = options.factory(...sourceValues);
|
|
359
|
+
} catch (e) {
|
|
360
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
361
|
+
return Promise.resolve({
|
|
362
|
+
success: false,
|
|
363
|
+
error: message`Derived parser error: ${msg}`
|
|
364
|
+
});
|
|
365
|
+
}
|
|
339
366
|
return Promise.resolve(derivedParser.parse(input));
|
|
340
367
|
},
|
|
341
368
|
[parseWithDependency](input, dependencyValue) {
|
|
@@ -402,8 +429,17 @@ function createSyncDerivedParser(sourceId, options) {
|
|
|
402
429
|
[derivedValueParserMarker]: true,
|
|
403
430
|
[dependencyId]: sourceId,
|
|
404
431
|
parse(input) {
|
|
405
|
-
|
|
406
|
-
|
|
432
|
+
let derivedParser;
|
|
433
|
+
try {
|
|
434
|
+
const sourceValue = options.defaultValue();
|
|
435
|
+
derivedParser = options.factory(sourceValue);
|
|
436
|
+
} catch (e) {
|
|
437
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
438
|
+
return {
|
|
439
|
+
success: false,
|
|
440
|
+
error: message`Derived parser error: ${msg}`
|
|
441
|
+
};
|
|
442
|
+
}
|
|
407
443
|
if (isAsyncModeParser(derivedParser)) return {
|
|
408
444
|
success: false,
|
|
409
445
|
error: message`Factory returned an async parser where a sync parser is required.`
|
|
@@ -463,9 +499,18 @@ function createAsyncDerivedParserFromAsyncFactory(sourceId, options) {
|
|
|
463
499
|
[derivedValueParserMarker]: true,
|
|
464
500
|
[dependencyId]: sourceId,
|
|
465
501
|
parse(input) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
502
|
+
let derivedParser;
|
|
503
|
+
try {
|
|
504
|
+
const sourceValue = options.defaultValue();
|
|
505
|
+
derivedParser = options.factory(sourceValue);
|
|
506
|
+
} catch (e) {
|
|
507
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
508
|
+
return Promise.resolve({
|
|
509
|
+
success: false,
|
|
510
|
+
error: message`Derived parser error: ${msg}`
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
return Promise.resolve(derivedParser.parse(input));
|
|
469
514
|
},
|
|
470
515
|
[parseWithDependency](input, dependencyValue) {
|
|
471
516
|
let derivedParser;
|
|
@@ -478,7 +523,7 @@ function createAsyncDerivedParserFromAsyncFactory(sourceId, options) {
|
|
|
478
523
|
error: message`Factory error: ${msg}`
|
|
479
524
|
});
|
|
480
525
|
}
|
|
481
|
-
return derivedParser.parse(input);
|
|
526
|
+
return Promise.resolve(derivedParser.parse(input));
|
|
482
527
|
},
|
|
483
528
|
format(value) {
|
|
484
529
|
const sourceValue = options.defaultValue();
|
|
@@ -516,8 +561,17 @@ function createAsyncDerivedParserFromSyncFactory(sourceId, options) {
|
|
|
516
561
|
[derivedValueParserMarker]: true,
|
|
517
562
|
[dependencyId]: sourceId,
|
|
518
563
|
parse(input) {
|
|
519
|
-
|
|
520
|
-
|
|
564
|
+
let derivedParser;
|
|
565
|
+
try {
|
|
566
|
+
const sourceValue = options.defaultValue();
|
|
567
|
+
derivedParser = options.factory(sourceValue);
|
|
568
|
+
} catch (e) {
|
|
569
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
570
|
+
return Promise.resolve({
|
|
571
|
+
success: false,
|
|
572
|
+
error: message`Derived parser error: ${msg}`
|
|
573
|
+
});
|
|
574
|
+
}
|
|
521
575
|
return Promise.resolve(derivedParser.parse(input));
|
|
522
576
|
},
|
|
523
577
|
[parseWithDependency](input, dependencyValue) {
|
package/dist/valueparser.cjs
CHANGED
|
@@ -20,31 +20,68 @@ function choice(choices, options = {}) {
|
|
|
20
20
|
if (isNumberChoice) {
|
|
21
21
|
const numberChoices = choices;
|
|
22
22
|
const numberOptions = options;
|
|
23
|
+
const hasNaN = numberChoices.some((v) => Number.isNaN(v));
|
|
24
|
+
const validNumberChoices = hasNaN ? numberChoices.filter((v) => !Number.isNaN(v)) : numberChoices;
|
|
25
|
+
const numberStrings = numberChoices.map((v) => Object.is(v, -0) ? "-0" : String(v));
|
|
23
26
|
return {
|
|
24
27
|
$mode: "sync",
|
|
25
28
|
metavar,
|
|
26
|
-
choices,
|
|
29
|
+
choices: hasNaN ? validNumberChoices : numberChoices,
|
|
27
30
|
parse(input) {
|
|
28
|
-
const
|
|
29
|
-
if (Number.isNaN(
|
|
30
|
-
success: false,
|
|
31
|
-
error: formatNumberChoiceError(input, numberChoices, numberOptions)
|
|
32
|
-
};
|
|
33
|
-
const index = numberChoices.indexOf(parsed);
|
|
34
|
-
if (index < 0) return {
|
|
35
|
-
success: false,
|
|
36
|
-
error: formatNumberChoiceError(input, numberChoices, numberOptions)
|
|
37
|
-
};
|
|
38
|
-
return {
|
|
31
|
+
const index = numberStrings.indexOf(input);
|
|
32
|
+
if (index >= 0 && !Number.isNaN(numberChoices[index])) return {
|
|
39
33
|
success: true,
|
|
40
34
|
value: numberChoices[index]
|
|
41
35
|
};
|
|
36
|
+
if (/^[+-]?(\d+\.?\d*|\.\d+)$/.test(input)) {
|
|
37
|
+
const parsed = Number(input);
|
|
38
|
+
if (Number.isFinite(parsed)) {
|
|
39
|
+
const canonical = Object.is(parsed, -0) ? "-0" : String(parsed);
|
|
40
|
+
const normalizedInput = normalizeDecimal(input);
|
|
41
|
+
const normalizedCanonical = normalizeDecimal(expandScientific(canonical));
|
|
42
|
+
if (normalizedInput === normalizedCanonical) {
|
|
43
|
+
const fallbackIndex = numberChoices.findIndex((v) => Object.is(v, parsed));
|
|
44
|
+
if (fallbackIndex >= 0) return {
|
|
45
|
+
success: true,
|
|
46
|
+
value: numberChoices[fallbackIndex]
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (parsed === 0 && normalizedInput.replace(/^-/, "") === "0" && !numberChoices.some((v) => Object.is(v, -0))) {
|
|
50
|
+
const zeroIndex = numberChoices.indexOf(0);
|
|
51
|
+
if (zeroIndex >= 0) return {
|
|
52
|
+
success: true,
|
|
53
|
+
value: numberChoices[zeroIndex]
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (/^[+-]?(\d+\.?\d*|\.\d+)[eE][+-]?\d+$/.test(input)) {
|
|
59
|
+
const parsed = Number(input);
|
|
60
|
+
if (Number.isFinite(parsed)) {
|
|
61
|
+
const canonical = Object.is(parsed, -0) ? "-0" : String(parsed);
|
|
62
|
+
if (/[eE]/.test(canonical)) {
|
|
63
|
+
const normalizedInput = normalizeDecimal(expandScientific(input));
|
|
64
|
+
const normalizedCanonical = normalizeDecimal(expandScientific(canonical));
|
|
65
|
+
if (normalizedInput === normalizedCanonical) {
|
|
66
|
+
const fallbackIndex = numberChoices.findIndex((v) => Object.is(v, parsed));
|
|
67
|
+
if (fallbackIndex >= 0) return {
|
|
68
|
+
success: true,
|
|
69
|
+
value: numberChoices[fallbackIndex]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
error: formatNumberChoiceError(input, validNumberChoices, numberChoices, numberOptions)
|
|
78
|
+
};
|
|
42
79
|
},
|
|
43
80
|
format(value) {
|
|
44
|
-
return String(value);
|
|
81
|
+
return Object.is(value, -0) ? "-0" : String(value);
|
|
45
82
|
},
|
|
46
83
|
suggest(prefix) {
|
|
47
|
-
return
|
|
84
|
+
return numberStrings.filter((valueStr, i) => !Number.isNaN(numberChoices[i]) && valueStr.startsWith(prefix)).map((valueStr) => ({
|
|
48
85
|
kind: "literal",
|
|
49
86
|
text: valueStr
|
|
50
87
|
}));
|
|
@@ -86,6 +123,54 @@ function choice(choices, options = {}) {
|
|
|
86
123
|
};
|
|
87
124
|
}
|
|
88
125
|
/**
|
|
126
|
+
* Expands a numeric string in scientific notation (e.g., `"1e+21"`,
|
|
127
|
+
* `"1.5e-3"`, `".1e-6"`) into plain decimal form for normalization.
|
|
128
|
+
* Used for both canonical `String(number)` output and user input.
|
|
129
|
+
* Returns the input unchanged if it does not contain scientific notation.
|
|
130
|
+
*/
|
|
131
|
+
function expandScientific(s) {
|
|
132
|
+
const match = /^([+-]?)(\d+\.?\d*|\.\d+)[eE]([+-]?\d+)$/.exec(s);
|
|
133
|
+
if (!match) return s;
|
|
134
|
+
const [, rawSign, mantissa, expStr] = match;
|
|
135
|
+
const sign = rawSign === "-" ? "-" : "";
|
|
136
|
+
const exp = parseInt(expStr, 10);
|
|
137
|
+
const dotPos = mantissa.indexOf(".");
|
|
138
|
+
const digits = mantissa.replace(".", "");
|
|
139
|
+
const intLen = dotPos >= 0 ? dotPos : digits.length;
|
|
140
|
+
const newIntLen = intLen + exp;
|
|
141
|
+
let result;
|
|
142
|
+
if (newIntLen >= digits.length) result = digits + "0".repeat(newIntLen - digits.length);
|
|
143
|
+
else if (newIntLen <= 0) result = "0." + "0".repeat(-newIntLen) + digits;
|
|
144
|
+
else result = digits.slice(0, newIntLen) + "." + digits.slice(newIntLen);
|
|
145
|
+
return sign + result;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Normalizes a plain decimal string by stripping leading zeros from the
|
|
149
|
+
* integer part and trailing zeros from the fractional part, so that two
|
|
150
|
+
* strings representing the same mathematical value compare as equal.
|
|
151
|
+
*/
|
|
152
|
+
function normalizeDecimal(s) {
|
|
153
|
+
let sign = "";
|
|
154
|
+
let str = s;
|
|
155
|
+
if (str.startsWith("-") || str.startsWith("+")) {
|
|
156
|
+
if (str[0] === "-") sign = "-";
|
|
157
|
+
str = str.slice(1);
|
|
158
|
+
}
|
|
159
|
+
const dot = str.indexOf(".");
|
|
160
|
+
let int;
|
|
161
|
+
let frac;
|
|
162
|
+
if (dot >= 0) {
|
|
163
|
+
int = str.slice(0, dot);
|
|
164
|
+
frac = str.slice(dot + 1);
|
|
165
|
+
} else {
|
|
166
|
+
int = str;
|
|
167
|
+
frac = "";
|
|
168
|
+
}
|
|
169
|
+
int = int.replace(/^0+/, "") || "0";
|
|
170
|
+
frac = frac.replace(/0+$/, "");
|
|
171
|
+
return sign + (frac ? int + "." + frac : int);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
89
174
|
* Formats error message for string choice parser.
|
|
90
175
|
*/
|
|
91
176
|
function formatStringChoiceError(input, choices, options) {
|
|
@@ -95,15 +180,16 @@ function formatStringChoiceError(input, choices, options) {
|
|
|
95
180
|
/**
|
|
96
181
|
* Formats error message for number choice parser.
|
|
97
182
|
*/
|
|
98
|
-
function formatNumberChoiceError(input,
|
|
99
|
-
if (options.errors?.invalidChoice) return typeof options.errors.invalidChoice === "function" ? options.errors.invalidChoice(input,
|
|
100
|
-
return formatDefaultChoiceError(input,
|
|
183
|
+
function formatNumberChoiceError(input, validChoices, allChoices, options) {
|
|
184
|
+
if (options.errors?.invalidChoice) return typeof options.errors.invalidChoice === "function" ? options.errors.invalidChoice(input, validChoices) : options.errors.invalidChoice;
|
|
185
|
+
return formatDefaultChoiceError(input, allChoices);
|
|
101
186
|
}
|
|
102
187
|
/**
|
|
103
188
|
* Formats default error message for choice parser.
|
|
104
189
|
*/
|
|
105
190
|
function formatDefaultChoiceError(input, choices) {
|
|
106
|
-
const choiceStrings = choices.map((c) => String(c));
|
|
191
|
+
const choiceStrings = choices.filter((c) => typeof c === "string" || !Number.isNaN(c)).map((c) => Object.is(c, -0) ? "-0" : String(c));
|
|
192
|
+
if (choiceStrings.length === 0 && choices.length > 0) return require_message.message`No valid choices are configured, but got ${input}.`;
|
|
107
193
|
return require_message.message`Expected one of ${require_message.valueSet(choiceStrings, { locale: "en-US" })}, but got ${input}.`;
|
|
108
194
|
}
|
|
109
195
|
/**
|
package/dist/valueparser.js
CHANGED
|
@@ -20,31 +20,68 @@ function choice(choices, options = {}) {
|
|
|
20
20
|
if (isNumberChoice) {
|
|
21
21
|
const numberChoices = choices;
|
|
22
22
|
const numberOptions = options;
|
|
23
|
+
const hasNaN = numberChoices.some((v) => Number.isNaN(v));
|
|
24
|
+
const validNumberChoices = hasNaN ? numberChoices.filter((v) => !Number.isNaN(v)) : numberChoices;
|
|
25
|
+
const numberStrings = numberChoices.map((v) => Object.is(v, -0) ? "-0" : String(v));
|
|
23
26
|
return {
|
|
24
27
|
$mode: "sync",
|
|
25
28
|
metavar,
|
|
26
|
-
choices,
|
|
29
|
+
choices: hasNaN ? validNumberChoices : numberChoices,
|
|
27
30
|
parse(input) {
|
|
28
|
-
const
|
|
29
|
-
if (Number.isNaN(
|
|
30
|
-
success: false,
|
|
31
|
-
error: formatNumberChoiceError(input, numberChoices, numberOptions)
|
|
32
|
-
};
|
|
33
|
-
const index = numberChoices.indexOf(parsed);
|
|
34
|
-
if (index < 0) return {
|
|
35
|
-
success: false,
|
|
36
|
-
error: formatNumberChoiceError(input, numberChoices, numberOptions)
|
|
37
|
-
};
|
|
38
|
-
return {
|
|
31
|
+
const index = numberStrings.indexOf(input);
|
|
32
|
+
if (index >= 0 && !Number.isNaN(numberChoices[index])) return {
|
|
39
33
|
success: true,
|
|
40
34
|
value: numberChoices[index]
|
|
41
35
|
};
|
|
36
|
+
if (/^[+-]?(\d+\.?\d*|\.\d+)$/.test(input)) {
|
|
37
|
+
const parsed = Number(input);
|
|
38
|
+
if (Number.isFinite(parsed)) {
|
|
39
|
+
const canonical = Object.is(parsed, -0) ? "-0" : String(parsed);
|
|
40
|
+
const normalizedInput = normalizeDecimal(input);
|
|
41
|
+
const normalizedCanonical = normalizeDecimal(expandScientific(canonical));
|
|
42
|
+
if (normalizedInput === normalizedCanonical) {
|
|
43
|
+
const fallbackIndex = numberChoices.findIndex((v) => Object.is(v, parsed));
|
|
44
|
+
if (fallbackIndex >= 0) return {
|
|
45
|
+
success: true,
|
|
46
|
+
value: numberChoices[fallbackIndex]
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (parsed === 0 && normalizedInput.replace(/^-/, "") === "0" && !numberChoices.some((v) => Object.is(v, -0))) {
|
|
50
|
+
const zeroIndex = numberChoices.indexOf(0);
|
|
51
|
+
if (zeroIndex >= 0) return {
|
|
52
|
+
success: true,
|
|
53
|
+
value: numberChoices[zeroIndex]
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (/^[+-]?(\d+\.?\d*|\.\d+)[eE][+-]?\d+$/.test(input)) {
|
|
59
|
+
const parsed = Number(input);
|
|
60
|
+
if (Number.isFinite(parsed)) {
|
|
61
|
+
const canonical = Object.is(parsed, -0) ? "-0" : String(parsed);
|
|
62
|
+
if (/[eE]/.test(canonical)) {
|
|
63
|
+
const normalizedInput = normalizeDecimal(expandScientific(input));
|
|
64
|
+
const normalizedCanonical = normalizeDecimal(expandScientific(canonical));
|
|
65
|
+
if (normalizedInput === normalizedCanonical) {
|
|
66
|
+
const fallbackIndex = numberChoices.findIndex((v) => Object.is(v, parsed));
|
|
67
|
+
if (fallbackIndex >= 0) return {
|
|
68
|
+
success: true,
|
|
69
|
+
value: numberChoices[fallbackIndex]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
error: formatNumberChoiceError(input, validNumberChoices, numberChoices, numberOptions)
|
|
78
|
+
};
|
|
42
79
|
},
|
|
43
80
|
format(value) {
|
|
44
|
-
return String(value);
|
|
81
|
+
return Object.is(value, -0) ? "-0" : String(value);
|
|
45
82
|
},
|
|
46
83
|
suggest(prefix) {
|
|
47
|
-
return
|
|
84
|
+
return numberStrings.filter((valueStr, i) => !Number.isNaN(numberChoices[i]) && valueStr.startsWith(prefix)).map((valueStr) => ({
|
|
48
85
|
kind: "literal",
|
|
49
86
|
text: valueStr
|
|
50
87
|
}));
|
|
@@ -86,6 +123,54 @@ function choice(choices, options = {}) {
|
|
|
86
123
|
};
|
|
87
124
|
}
|
|
88
125
|
/**
|
|
126
|
+
* Expands a numeric string in scientific notation (e.g., `"1e+21"`,
|
|
127
|
+
* `"1.5e-3"`, `".1e-6"`) into plain decimal form for normalization.
|
|
128
|
+
* Used for both canonical `String(number)` output and user input.
|
|
129
|
+
* Returns the input unchanged if it does not contain scientific notation.
|
|
130
|
+
*/
|
|
131
|
+
function expandScientific(s) {
|
|
132
|
+
const match = /^([+-]?)(\d+\.?\d*|\.\d+)[eE]([+-]?\d+)$/.exec(s);
|
|
133
|
+
if (!match) return s;
|
|
134
|
+
const [, rawSign, mantissa, expStr] = match;
|
|
135
|
+
const sign = rawSign === "-" ? "-" : "";
|
|
136
|
+
const exp = parseInt(expStr, 10);
|
|
137
|
+
const dotPos = mantissa.indexOf(".");
|
|
138
|
+
const digits = mantissa.replace(".", "");
|
|
139
|
+
const intLen = dotPos >= 0 ? dotPos : digits.length;
|
|
140
|
+
const newIntLen = intLen + exp;
|
|
141
|
+
let result;
|
|
142
|
+
if (newIntLen >= digits.length) result = digits + "0".repeat(newIntLen - digits.length);
|
|
143
|
+
else if (newIntLen <= 0) result = "0." + "0".repeat(-newIntLen) + digits;
|
|
144
|
+
else result = digits.slice(0, newIntLen) + "." + digits.slice(newIntLen);
|
|
145
|
+
return sign + result;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Normalizes a plain decimal string by stripping leading zeros from the
|
|
149
|
+
* integer part and trailing zeros from the fractional part, so that two
|
|
150
|
+
* strings representing the same mathematical value compare as equal.
|
|
151
|
+
*/
|
|
152
|
+
function normalizeDecimal(s) {
|
|
153
|
+
let sign = "";
|
|
154
|
+
let str = s;
|
|
155
|
+
if (str.startsWith("-") || str.startsWith("+")) {
|
|
156
|
+
if (str[0] === "-") sign = "-";
|
|
157
|
+
str = str.slice(1);
|
|
158
|
+
}
|
|
159
|
+
const dot = str.indexOf(".");
|
|
160
|
+
let int;
|
|
161
|
+
let frac;
|
|
162
|
+
if (dot >= 0) {
|
|
163
|
+
int = str.slice(0, dot);
|
|
164
|
+
frac = str.slice(dot + 1);
|
|
165
|
+
} else {
|
|
166
|
+
int = str;
|
|
167
|
+
frac = "";
|
|
168
|
+
}
|
|
169
|
+
int = int.replace(/^0+/, "") || "0";
|
|
170
|
+
frac = frac.replace(/0+$/, "");
|
|
171
|
+
return sign + (frac ? int + "." + frac : int);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
89
174
|
* Formats error message for string choice parser.
|
|
90
175
|
*/
|
|
91
176
|
function formatStringChoiceError(input, choices, options) {
|
|
@@ -95,15 +180,16 @@ function formatStringChoiceError(input, choices, options) {
|
|
|
95
180
|
/**
|
|
96
181
|
* Formats error message for number choice parser.
|
|
97
182
|
*/
|
|
98
|
-
function formatNumberChoiceError(input,
|
|
99
|
-
if (options.errors?.invalidChoice) return typeof options.errors.invalidChoice === "function" ? options.errors.invalidChoice(input,
|
|
100
|
-
return formatDefaultChoiceError(input,
|
|
183
|
+
function formatNumberChoiceError(input, validChoices, allChoices, options) {
|
|
184
|
+
if (options.errors?.invalidChoice) return typeof options.errors.invalidChoice === "function" ? options.errors.invalidChoice(input, validChoices) : options.errors.invalidChoice;
|
|
185
|
+
return formatDefaultChoiceError(input, allChoices);
|
|
101
186
|
}
|
|
102
187
|
/**
|
|
103
188
|
* Formats default error message for choice parser.
|
|
104
189
|
*/
|
|
105
190
|
function formatDefaultChoiceError(input, choices) {
|
|
106
|
-
const choiceStrings = choices.map((c) => String(c));
|
|
191
|
+
const choiceStrings = choices.filter((c) => typeof c === "string" || !Number.isNaN(c)).map((c) => Object.is(c, -0) ? "-0" : String(c));
|
|
192
|
+
if (choiceStrings.length === 0 && choices.length > 0) return message`No valid choices are configured, but got ${input}.`;
|
|
107
193
|
return message`Expected one of ${valueSet(choiceStrings, { locale: "en-US" })}, but got ${input}.`;
|
|
108
194
|
}
|
|
109
195
|
/**
|