@optique/core 1.4.0-dev.2668 → 1.4.0-dev.2671
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/valueparser.cjs +16 -11
- package/dist/valueparser.d.cts +12 -0
- package/dist/valueparser.d.ts +12 -0
- package/dist/valueparser.js +16 -11
- package/package.json +2 -2
- package/skills/optique/SKILL.md +3 -3
package/dist/valueparser.cjs
CHANGED
|
@@ -161,15 +161,24 @@ function choice(choices, options = {}) {
|
|
|
161
161
|
const stringChoices = Object.freeze([...new Set(choices)]);
|
|
162
162
|
const stringOptions = options;
|
|
163
163
|
checkBooleanOption(stringOptions, "caseInsensitive");
|
|
164
|
+
const customKey = stringOptions.key;
|
|
165
|
+
if (customKey !== void 0 && typeof customKey !== "function") throw new TypeError("Expected key to be a function.");
|
|
166
|
+
if (customKey !== void 0 && stringOptions.caseInsensitive !== void 0) throw new TypeError("Cannot use key with caseInsensitive.");
|
|
164
167
|
const caseInsensitive = stringOptions.caseInsensitive ?? false;
|
|
165
|
-
const
|
|
166
|
-
|
|
168
|
+
const matchingKey = customKey ?? (caseInsensitive ? (text$1) => text$1.toLowerCase() : (text$1) => text$1);
|
|
169
|
+
function getMatchingKey(text$1) {
|
|
170
|
+
const result = matchingKey(text$1);
|
|
171
|
+
if (typeof result !== "string") throw new TypeError("Expected key to return a string.");
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
const matchingKeys = stringChoices.map(getMatchingKey);
|
|
175
|
+
if (caseInsensitive || customKey !== void 0) {
|
|
167
176
|
const seen = /* @__PURE__ */ new Map();
|
|
168
177
|
for (let i = 0; i < stringChoices.length; i++) {
|
|
169
|
-
const nv =
|
|
178
|
+
const nv = matchingKeys[i];
|
|
170
179
|
const original = stringChoices[i];
|
|
171
180
|
const prev = seen.get(nv);
|
|
172
|
-
if (prev !== void 0 && prev !== original) throw new TypeError(`Ambiguous choices for case-insensitive matching: ${JSON.stringify(prev)} and ${JSON.stringify(original)} both normalize to ${JSON.stringify(nv)}.`);
|
|
181
|
+
if (prev !== void 0 && prev !== original) throw new TypeError(`Ambiguous choices for ${caseInsensitive ? "case-insensitive" : "custom key"} matching: ${JSON.stringify(prev)} and ${JSON.stringify(original)} both normalize to ${JSON.stringify(nv)}.`);
|
|
173
182
|
seen.set(nv, original);
|
|
174
183
|
}
|
|
175
184
|
}
|
|
@@ -200,8 +209,7 @@ function choice(choices, options = {}) {
|
|
|
200
209
|
placeholder: choices[0],
|
|
201
210
|
choices: stringChoices,
|
|
202
211
|
parse(input) {
|
|
203
|
-
const
|
|
204
|
-
const index = normalizedValues.indexOf(normalizedInput);
|
|
212
|
+
const index = matchingKeys.indexOf(getMatchingKey(input));
|
|
205
213
|
if (index < 0) return {
|
|
206
214
|
success: false,
|
|
207
215
|
error: formatStringChoiceError(input, stringChoices, stringInvalidChoice, snapshotSuggest)
|
|
@@ -215,11 +223,8 @@ function choice(choices, options = {}) {
|
|
|
215
223
|
return String(value);
|
|
216
224
|
},
|
|
217
225
|
suggest(prefix) {
|
|
218
|
-
const
|
|
219
|
-
return stringChoices.filter((value) => {
|
|
220
|
-
const normalizedValue = caseInsensitive ? value.toLowerCase() : value;
|
|
221
|
-
return normalizedValue.startsWith(normalizedPrefix);
|
|
222
|
-
}).map((value) => ({
|
|
226
|
+
const matchingPrefix = getMatchingKey(prefix);
|
|
227
|
+
return stringChoices.filter((_value, index) => matchingKeys[index].startsWith(matchingPrefix)).map((value) => ({
|
|
223
228
|
kind: "literal",
|
|
224
229
|
text: value
|
|
225
230
|
}));
|
package/dist/valueparser.d.cts
CHANGED
|
@@ -270,6 +270,15 @@ interface ChoiceOptionsString extends ChoiceOptionsBase {
|
|
|
270
270
|
* @default `false`
|
|
271
271
|
*/
|
|
272
272
|
readonly caseInsensitive?: boolean;
|
|
273
|
+
/**
|
|
274
|
+
* Maps both the input and declared choices to keys for matching. Parsing
|
|
275
|
+
* and completion still return the declared spelling. Cannot be combined
|
|
276
|
+
* with `caseInsensitive`; include case folding in this function instead.
|
|
277
|
+
*
|
|
278
|
+
* The function should preserve prefixes when completion is needed.
|
|
279
|
+
* @since 1.4.0
|
|
280
|
+
*/
|
|
281
|
+
readonly key?: (text: string) => string;
|
|
273
282
|
/**
|
|
274
283
|
* Controls whether the error message for an invalid value includes a
|
|
275
284
|
* "Did you mean …?" hint based on Levenshtein distance.
|
|
@@ -384,6 +393,9 @@ declare function isValueParser<M extends Mode, T>(object: unknown): object is Va
|
|
|
384
393
|
* @throws {TypeError} If `caseInsensitive` is not a boolean.
|
|
385
394
|
* @throws {TypeError} If `caseInsensitive` is `true` and multiple choices
|
|
386
395
|
* normalize to the same lowercase value.
|
|
396
|
+
* @throws {TypeError} If `key` is not a function, is combined with
|
|
397
|
+
* `caseInsensitive`, returns a non-string key, or gives distinct
|
|
398
|
+
* choices the same key.
|
|
387
399
|
*/
|
|
388
400
|
declare function choice<const T extends string>(choices: readonly T[], options?: ChoiceOptionsString): ValueParser<"sync", T>;
|
|
389
401
|
/**
|
package/dist/valueparser.d.ts
CHANGED
|
@@ -270,6 +270,15 @@ interface ChoiceOptionsString extends ChoiceOptionsBase {
|
|
|
270
270
|
* @default `false`
|
|
271
271
|
*/
|
|
272
272
|
readonly caseInsensitive?: boolean;
|
|
273
|
+
/**
|
|
274
|
+
* Maps both the input and declared choices to keys for matching. Parsing
|
|
275
|
+
* and completion still return the declared spelling. Cannot be combined
|
|
276
|
+
* with `caseInsensitive`; include case folding in this function instead.
|
|
277
|
+
*
|
|
278
|
+
* The function should preserve prefixes when completion is needed.
|
|
279
|
+
* @since 1.4.0
|
|
280
|
+
*/
|
|
281
|
+
readonly key?: (text: string) => string;
|
|
273
282
|
/**
|
|
274
283
|
* Controls whether the error message for an invalid value includes a
|
|
275
284
|
* "Did you mean …?" hint based on Levenshtein distance.
|
|
@@ -384,6 +393,9 @@ declare function isValueParser<M extends Mode, T>(object: unknown): object is Va
|
|
|
384
393
|
* @throws {TypeError} If `caseInsensitive` is not a boolean.
|
|
385
394
|
* @throws {TypeError} If `caseInsensitive` is `true` and multiple choices
|
|
386
395
|
* normalize to the same lowercase value.
|
|
396
|
+
* @throws {TypeError} If `key` is not a function, is combined with
|
|
397
|
+
* `caseInsensitive`, returns a non-string key, or gives distinct
|
|
398
|
+
* choices the same key.
|
|
387
399
|
*/
|
|
388
400
|
declare function choice<const T extends string>(choices: readonly T[], options?: ChoiceOptionsString): ValueParser<"sync", T>;
|
|
389
401
|
/**
|
package/dist/valueparser.js
CHANGED
|
@@ -161,15 +161,24 @@ function choice(choices, options = {}) {
|
|
|
161
161
|
const stringChoices = Object.freeze([...new Set(choices)]);
|
|
162
162
|
const stringOptions = options;
|
|
163
163
|
checkBooleanOption(stringOptions, "caseInsensitive");
|
|
164
|
+
const customKey = stringOptions.key;
|
|
165
|
+
if (customKey !== void 0 && typeof customKey !== "function") throw new TypeError("Expected key to be a function.");
|
|
166
|
+
if (customKey !== void 0 && stringOptions.caseInsensitive !== void 0) throw new TypeError("Cannot use key with caseInsensitive.");
|
|
164
167
|
const caseInsensitive = stringOptions.caseInsensitive ?? false;
|
|
165
|
-
const
|
|
166
|
-
|
|
168
|
+
const matchingKey = customKey ?? (caseInsensitive ? (text$1) => text$1.toLowerCase() : (text$1) => text$1);
|
|
169
|
+
function getMatchingKey(text$1) {
|
|
170
|
+
const result = matchingKey(text$1);
|
|
171
|
+
if (typeof result !== "string") throw new TypeError("Expected key to return a string.");
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
const matchingKeys = stringChoices.map(getMatchingKey);
|
|
175
|
+
if (caseInsensitive || customKey !== void 0) {
|
|
167
176
|
const seen = /* @__PURE__ */ new Map();
|
|
168
177
|
for (let i = 0; i < stringChoices.length; i++) {
|
|
169
|
-
const nv =
|
|
178
|
+
const nv = matchingKeys[i];
|
|
170
179
|
const original = stringChoices[i];
|
|
171
180
|
const prev = seen.get(nv);
|
|
172
|
-
if (prev !== void 0 && prev !== original) throw new TypeError(`Ambiguous choices for case-insensitive matching: ${JSON.stringify(prev)} and ${JSON.stringify(original)} both normalize to ${JSON.stringify(nv)}.`);
|
|
181
|
+
if (prev !== void 0 && prev !== original) throw new TypeError(`Ambiguous choices for ${caseInsensitive ? "case-insensitive" : "custom key"} matching: ${JSON.stringify(prev)} and ${JSON.stringify(original)} both normalize to ${JSON.stringify(nv)}.`);
|
|
173
182
|
seen.set(nv, original);
|
|
174
183
|
}
|
|
175
184
|
}
|
|
@@ -200,8 +209,7 @@ function choice(choices, options = {}) {
|
|
|
200
209
|
placeholder: choices[0],
|
|
201
210
|
choices: stringChoices,
|
|
202
211
|
parse(input) {
|
|
203
|
-
const
|
|
204
|
-
const index = normalizedValues.indexOf(normalizedInput);
|
|
212
|
+
const index = matchingKeys.indexOf(getMatchingKey(input));
|
|
205
213
|
if (index < 0) return {
|
|
206
214
|
success: false,
|
|
207
215
|
error: formatStringChoiceError(input, stringChoices, stringInvalidChoice, snapshotSuggest)
|
|
@@ -215,11 +223,8 @@ function choice(choices, options = {}) {
|
|
|
215
223
|
return String(value);
|
|
216
224
|
},
|
|
217
225
|
suggest(prefix) {
|
|
218
|
-
const
|
|
219
|
-
return stringChoices.filter((value) => {
|
|
220
|
-
const normalizedValue = caseInsensitive ? value.toLowerCase() : value;
|
|
221
|
-
return normalizedValue.startsWith(normalizedPrefix);
|
|
222
|
-
}).map((value) => ({
|
|
226
|
+
const matchingPrefix = getMatchingKey(prefix);
|
|
227
|
+
return stringChoices.filter((_value, index) => matchingKeys[index].startsWith(matchingPrefix)).map((value) => ({
|
|
223
228
|
kind: "literal",
|
|
224
229
|
text: value
|
|
225
230
|
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@optique/core",
|
|
3
|
-
"version": "1.4.0-dev.
|
|
3
|
+
"version": "1.4.0-dev.2671",
|
|
4
4
|
"description": "Type-safe combinatorial command-line interface parser",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"CLI",
|
|
@@ -237,7 +237,7 @@
|
|
|
237
237
|
},
|
|
238
238
|
"sideEffects": false,
|
|
239
239
|
"devDependencies": {
|
|
240
|
-
"@optique/env": "1.4.0-dev.
|
|
240
|
+
"@optique/env": "1.4.0-dev.2671+ac89a35b",
|
|
241
241
|
"@types/node": "^24.0.0",
|
|
242
242
|
"fast-check": "^4.7.0",
|
|
243
243
|
"tsdown": "^0.13.0",
|
package/skills/optique/SKILL.md
CHANGED
|
@@ -43,9 +43,9 @@ Core rules
|
|
|
43
43
|
- Use value parsers such as `integer()`, `choice()`, `biject()`, `regExp()`,
|
|
44
44
|
`url()`, `origin()`, and `uuid()` instead of validating raw strings after
|
|
45
45
|
parsing. Use `regExp({ flags })` for user-supplied sources, `biject()` for
|
|
46
|
-
one-to-one
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
one-to-one mappings, `transform()` for mapped results, and
|
|
47
|
+
`choice(values, { key })` for custom string matching that returns the
|
|
48
|
+
declared spelling. Use `normalizeInput()` for raw-string cleanup and
|
|
49
49
|
`path()` from `@optique/run/valueparser` for file-system paths. Write a
|
|
50
50
|
custom value parser only when these tools do not cover the domain.
|
|
51
51
|
- Since 1.4.0, `-p8080`/`-vp8080` accept attached values. Values consume
|