@optique/core 0.3.0-dev.34 → 0.3.0-dev.36

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/index.cjs CHANGED
@@ -11,6 +11,7 @@ exports.choice = require_valueparser.choice;
11
11
  exports.command = require_parser.command;
12
12
  exports.concat = require_parser.concat;
13
13
  exports.constant = require_parser.constant;
14
+ exports.flag = require_parser.flag;
14
15
  exports.float = require_valueparser.float;
15
16
  exports.formatDocPage = require_doc.formatDocPage;
16
17
  exports.formatMessage = require_message.formatMessage;
package/dist/index.d.cts CHANGED
@@ -2,6 +2,6 @@ import { Message, MessageFormatOptions, MessageTerm, formatMessage, message, met
2
2
  import { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.cjs";
3
3
  import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, formatDocPage } from "./doc.cjs";
4
4
  import { ChoiceOptions, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.cjs";
5
- import { ArgumentOptions, CommandOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.cjs";
5
+ import { ArgumentOptions, CommandOptions, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.cjs";
6
6
  import { RunError, RunOptions, run } from "./facade.cjs";
7
- export { ArgumentOptions, ChoiceOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, Message, MessageFormatOptions, MessageTerm, MultipleOptions, OptionName, OptionOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, argument, choice, command, concat, constant, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
7
+ export { ArgumentOptions, ChoiceOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, Message, MessageFormatOptions, MessageTerm, MultipleOptions, OptionName, OptionOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,6 @@ import { Message, MessageFormatOptions, MessageTerm, formatMessage, message, met
2
2
  import { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
3
3
  import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, formatDocPage } from "./doc.js";
4
4
  import { ChoiceOptions, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.js";
5
- import { ArgumentOptions, CommandOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.js";
5
+ import { ArgumentOptions, CommandOptions, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.js";
6
6
  import { RunError, RunOptions, run } from "./facade.js";
7
- export { ArgumentOptions, ChoiceOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, Message, MessageFormatOptions, MessageTerm, MultipleOptions, OptionName, OptionOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, argument, choice, command, concat, constant, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
7
+ export { ArgumentOptions, ChoiceOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, Message, MessageFormatOptions, MessageTerm, MultipleOptions, OptionName, OptionOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { formatMessage, message, metavar, optionName, optionNames, text, value,
2
2
  import { formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
3
3
  import { formatDocPage } from "./doc.js";
4
4
  import { choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.js";
5
- import { argument, command, concat, constant, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.js";
5
+ import { argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.js";
6
6
  import { RunError, run } from "./facade.js";
7
7
 
8
- export { RunError, argument, choice, command, concat, constant, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
8
+ export { RunError, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
package/dist/parser.cjs CHANGED
@@ -222,6 +222,163 @@ function option(...args) {
222
222
  };
223
223
  }
224
224
  /**
225
+ * Creates a parser for command-line flags that must be explicitly provided.
226
+ * Unlike {@link option}, this parser fails if the flag is not present, making
227
+ * it suitable for required boolean flags that don't have a meaningful default.
228
+ *
229
+ * The key difference from {@link option} is:
230
+ * - {@link option} without a value parser: Returns `false` when not present
231
+ * - {@link flag}: Fails parsing when not present, only produces `true`
232
+ *
233
+ * This is useful for dependent options where the presence of a flag changes
234
+ * the shape of the result type.
235
+ *
236
+ * @param args The {@link OptionName}s to parse, followed by an optional
237
+ * {@link FlagOptions} object that allows you to specify
238
+ * a description or other metadata.
239
+ * @returns A {@link Parser} that produces `true` when the flag is present
240
+ * and fails when it is not present.
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * // Basic flag usage
245
+ * const parser = flag("-f", "--force");
246
+ * // Succeeds with true: parse(parser, ["-f"])
247
+ * // Fails: parse(parser, [])
248
+ *
249
+ * // With description
250
+ * const verboseFlag = flag("-v", "--verbose", {
251
+ * description: "Enable verbose output"
252
+ * });
253
+ * ```
254
+ */
255
+ function flag(...args) {
256
+ const lastArg = args.at(-1);
257
+ let optionNames$1;
258
+ let options = {};
259
+ if (typeof lastArg === "object" && lastArg != null && !Array.isArray(lastArg)) {
260
+ options = lastArg;
261
+ optionNames$1 = args.slice(0, -1);
262
+ } else optionNames$1 = args;
263
+ return {
264
+ $valueType: [],
265
+ $stateType: [],
266
+ priority: 10,
267
+ usage: [{
268
+ type: "option",
269
+ names: optionNames$1
270
+ }],
271
+ initialState: void 0,
272
+ parse(context) {
273
+ if (context.optionsTerminated) return {
274
+ success: false,
275
+ consumed: 0,
276
+ error: require_message.message`No more options can be parsed.`
277
+ };
278
+ else if (context.buffer.length < 1) return {
279
+ success: false,
280
+ consumed: 0,
281
+ error: require_message.message`Expected an option, but got end of input.`
282
+ };
283
+ if (context.buffer[0] === "--") return {
284
+ success: true,
285
+ next: {
286
+ ...context,
287
+ buffer: context.buffer.slice(1),
288
+ state: context.state,
289
+ optionsTerminated: true
290
+ },
291
+ consumed: context.buffer.slice(0, 1)
292
+ };
293
+ if (optionNames$1.includes(context.buffer[0])) {
294
+ if (context.state?.success) return {
295
+ success: false,
296
+ consumed: 1,
297
+ error: require_message.message`${require_message.optionName(context.buffer[0])} cannot be used multiple times.`
298
+ };
299
+ return {
300
+ success: true,
301
+ next: {
302
+ ...context,
303
+ state: {
304
+ success: true,
305
+ value: true
306
+ },
307
+ buffer: context.buffer.slice(1)
308
+ },
309
+ consumed: context.buffer.slice(0, 1)
310
+ };
311
+ }
312
+ const prefixes = optionNames$1.filter((name) => name.startsWith("--") || name.startsWith("/")).map((name) => name.startsWith("/") ? `${name}:` : `${name}=`);
313
+ for (const prefix of prefixes) if (context.buffer[0].startsWith(prefix)) {
314
+ const value = context.buffer[0].slice(prefix.length);
315
+ return {
316
+ success: false,
317
+ consumed: 1,
318
+ error: require_message.message`Flag ${require_message.optionName(prefix.slice(0, -1))} does not accept a value, but got: ${value}.`
319
+ };
320
+ }
321
+ const shortOptions = optionNames$1.filter((name) => name.match(/^-[^-]$/));
322
+ for (const shortOption of shortOptions) {
323
+ if (!context.buffer[0].startsWith(shortOption)) continue;
324
+ if (context.state?.success) return {
325
+ success: false,
326
+ consumed: 1,
327
+ error: require_message.message`${require_message.optionName(shortOption)} cannot be used multiple times.`
328
+ };
329
+ return {
330
+ success: true,
331
+ next: {
332
+ ...context,
333
+ state: {
334
+ success: true,
335
+ value: true
336
+ },
337
+ buffer: [`-${context.buffer[0].slice(2)}`, ...context.buffer.slice(1)]
338
+ },
339
+ consumed: [context.buffer[0].slice(0, 2)]
340
+ };
341
+ }
342
+ return {
343
+ success: false,
344
+ consumed: 0,
345
+ error: require_message.message`No matched option for ${require_message.optionName(context.buffer[0])}.`
346
+ };
347
+ },
348
+ complete(state) {
349
+ if (state == null) return {
350
+ success: false,
351
+ error: require_message.message`Required flag ${require_message.optionNames(optionNames$1)} is missing.`
352
+ };
353
+ if (state.success) return {
354
+ success: true,
355
+ value: true
356
+ };
357
+ return {
358
+ success: false,
359
+ error: require_message.message`${require_message.optionNames(optionNames$1)}: ${state.error}`
360
+ };
361
+ },
362
+ getDocFragments(_state, _defaultValue) {
363
+ const fragments = [{
364
+ type: "entry",
365
+ term: {
366
+ type: "option",
367
+ names: optionNames$1
368
+ },
369
+ description: options.description
370
+ }];
371
+ return {
372
+ fragments,
373
+ description: options.description
374
+ };
375
+ },
376
+ [Symbol.for("Deno.customInspect")]() {
377
+ return `flag(${optionNames$1.map((o) => JSON.stringify(o)).join(", ")})`;
378
+ }
379
+ };
380
+ }
381
+ /**
225
382
  * Creates a parser that expects a single argument value.
226
383
  * This parser is typically used for positional arguments
227
384
  * that are not options or flags.
@@ -368,12 +525,13 @@ function optional(parser) {
368
525
  * a specified default value.
369
526
  * @template TValue The type of the value returned by the wrapped parser.
370
527
  * @template TState The type of the state used by the wrapped parser.
528
+ * @template TDefault The type of the default value.
371
529
  * @param parser The {@link Parser} to wrap with default behavior.
372
530
  * @param defaultValue The default value to return when the wrapped parser
373
531
  * doesn't match or consume input. Can be a value of type
374
- * {@link TValue} or a function that returns such a value.
532
+ * {@link TDefault} or a function that returns such a value.
375
533
  * @returns A {@link Parser} that produces either the result of the wrapped parser
376
- * or the default value if the wrapped parser fails to match.
534
+ * or the default value if the wrapped parser fails to match (union type {@link TValue} | {@link TDefault}).
377
535
  */
378
536
  function withDefault(parser, defaultValue) {
379
537
  return {
@@ -408,7 +566,7 @@ function withDefault(parser, defaultValue) {
408
566
  return parser.complete(state[0]);
409
567
  },
410
568
  getDocFragments(state, upperDefaultValue) {
411
- return parser.getDocFragments(typeof state === "undefined" ? parser.initialState : state[0], upperDefaultValue == null ? typeof defaultValue === "function" ? defaultValue() : defaultValue : upperDefaultValue);
569
+ return parser.getDocFragments(typeof state === "undefined" ? parser.initialState : state[0], upperDefaultValue != null ? upperDefaultValue : typeof defaultValue === "function" ? defaultValue() : defaultValue);
412
570
  }
413
571
  };
414
572
  }
@@ -1231,6 +1389,7 @@ exports.argument = argument;
1231
1389
  exports.command = command;
1232
1390
  exports.concat = concat;
1233
1391
  exports.constant = constant;
1392
+ exports.flag = flag;
1234
1393
  exports.getDocPage = getDocPage;
1235
1394
  exports.map = map;
1236
1395
  exports.merge = merge;
package/dist/parser.d.cts CHANGED
@@ -189,6 +189,47 @@ declare function option(...optionNames: readonly OptionName[]): Parser<boolean,
189
189
  * values.
190
190
  */
191
191
  declare function option(...args: readonly [...readonly OptionName[], OptionOptions]): Parser<boolean, ValueParserResult<boolean> | undefined>;
192
+ /**
193
+ * Options for the {@link flag} parser.
194
+ */
195
+ interface FlagOptions {
196
+ /**
197
+ * The description of the flag, which can be used for help messages.
198
+ */
199
+ readonly description?: Message;
200
+ }
201
+ /**
202
+ * Creates a parser for command-line flags that must be explicitly provided.
203
+ * Unlike {@link option}, this parser fails if the flag is not present, making
204
+ * it suitable for required boolean flags that don't have a meaningful default.
205
+ *
206
+ * The key difference from {@link option} is:
207
+ * - {@link option} without a value parser: Returns `false` when not present
208
+ * - {@link flag}: Fails parsing when not present, only produces `true`
209
+ *
210
+ * This is useful for dependent options where the presence of a flag changes
211
+ * the shape of the result type.
212
+ *
213
+ * @param args The {@link OptionName}s to parse, followed by an optional
214
+ * {@link FlagOptions} object that allows you to specify
215
+ * a description or other metadata.
216
+ * @returns A {@link Parser} that produces `true` when the flag is present
217
+ * and fails when it is not present.
218
+ *
219
+ * @example
220
+ * ```typescript
221
+ * // Basic flag usage
222
+ * const parser = flag("-f", "--force");
223
+ * // Succeeds with true: parse(parser, ["-f"])
224
+ * // Fails: parse(parser, [])
225
+ *
226
+ * // With description
227
+ * const verboseFlag = flag("-v", "--verbose", {
228
+ * description: "Enable verbose output"
229
+ * });
230
+ * ```
231
+ */
232
+ declare function flag(...args: readonly [...readonly OptionName[], FlagOptions] | readonly OptionName[]): Parser<true, ValueParserResult<true> | undefined>;
192
233
  /**
193
234
  * Options for the {@link argument} parser.
194
235
  */
@@ -230,14 +271,15 @@ declare function optional<TValue, TState>(parser: Parser<TValue, TState>): Parse
230
271
  * a specified default value.
231
272
  * @template TValue The type of the value returned by the wrapped parser.
232
273
  * @template TState The type of the state used by the wrapped parser.
274
+ * @template TDefault The type of the default value.
233
275
  * @param parser The {@link Parser} to wrap with default behavior.
234
276
  * @param defaultValue The default value to return when the wrapped parser
235
277
  * doesn't match or consume input. Can be a value of type
236
- * {@link TValue} or a function that returns such a value.
278
+ * {@link TDefault} or a function that returns such a value.
237
279
  * @returns A {@link Parser} that produces either the result of the wrapped parser
238
- * or the default value if the wrapped parser fails to match.
280
+ * or the default value if the wrapped parser fails to match (union type {@link TValue} | {@link TDefault}).
239
281
  */
240
- declare function withDefault<TValue, TState>(parser: Parser<TValue, TState>, defaultValue: TValue | (() => TValue)): Parser<TValue, [TState] | undefined>;
282
+ declare function withDefault<TValue, TState, TDefault = TValue>(parser: Parser<TValue, TState>, defaultValue: TDefault | (() => TDefault)): Parser<TValue | TDefault, [TState] | undefined>;
241
283
  /**
242
284
  * Creates a parser that transforms the result value of another parser using
243
285
  * a mapping function. This enables value transformation while preserving
@@ -872,4 +914,4 @@ declare function parse<T>(parser: Parser<T, unknown>, args: readonly string[]):
872
914
  */
873
915
  declare function getDocPage(parser: Parser<unknown, unknown>, args?: readonly string[]): DocPage | undefined;
874
916
  //#endregion
875
- export { ArgumentOptions, CommandOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
917
+ export { ArgumentOptions, CommandOptions, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
package/dist/parser.d.ts CHANGED
@@ -189,6 +189,47 @@ declare function option(...optionNames: readonly OptionName[]): Parser<boolean,
189
189
  * values.
190
190
  */
191
191
  declare function option(...args: readonly [...readonly OptionName[], OptionOptions]): Parser<boolean, ValueParserResult<boolean> | undefined>;
192
+ /**
193
+ * Options for the {@link flag} parser.
194
+ */
195
+ interface FlagOptions {
196
+ /**
197
+ * The description of the flag, which can be used for help messages.
198
+ */
199
+ readonly description?: Message;
200
+ }
201
+ /**
202
+ * Creates a parser for command-line flags that must be explicitly provided.
203
+ * Unlike {@link option}, this parser fails if the flag is not present, making
204
+ * it suitable for required boolean flags that don't have a meaningful default.
205
+ *
206
+ * The key difference from {@link option} is:
207
+ * - {@link option} without a value parser: Returns `false` when not present
208
+ * - {@link flag}: Fails parsing when not present, only produces `true`
209
+ *
210
+ * This is useful for dependent options where the presence of a flag changes
211
+ * the shape of the result type.
212
+ *
213
+ * @param args The {@link OptionName}s to parse, followed by an optional
214
+ * {@link FlagOptions} object that allows you to specify
215
+ * a description or other metadata.
216
+ * @returns A {@link Parser} that produces `true` when the flag is present
217
+ * and fails when it is not present.
218
+ *
219
+ * @example
220
+ * ```typescript
221
+ * // Basic flag usage
222
+ * const parser = flag("-f", "--force");
223
+ * // Succeeds with true: parse(parser, ["-f"])
224
+ * // Fails: parse(parser, [])
225
+ *
226
+ * // With description
227
+ * const verboseFlag = flag("-v", "--verbose", {
228
+ * description: "Enable verbose output"
229
+ * });
230
+ * ```
231
+ */
232
+ declare function flag(...args: readonly [...readonly OptionName[], FlagOptions] | readonly OptionName[]): Parser<true, ValueParserResult<true> | undefined>;
192
233
  /**
193
234
  * Options for the {@link argument} parser.
194
235
  */
@@ -230,14 +271,15 @@ declare function optional<TValue, TState>(parser: Parser<TValue, TState>): Parse
230
271
  * a specified default value.
231
272
  * @template TValue The type of the value returned by the wrapped parser.
232
273
  * @template TState The type of the state used by the wrapped parser.
274
+ * @template TDefault The type of the default value.
233
275
  * @param parser The {@link Parser} to wrap with default behavior.
234
276
  * @param defaultValue The default value to return when the wrapped parser
235
277
  * doesn't match or consume input. Can be a value of type
236
- * {@link TValue} or a function that returns such a value.
278
+ * {@link TDefault} or a function that returns such a value.
237
279
  * @returns A {@link Parser} that produces either the result of the wrapped parser
238
- * or the default value if the wrapped parser fails to match.
280
+ * or the default value if the wrapped parser fails to match (union type {@link TValue} | {@link TDefault}).
239
281
  */
240
- declare function withDefault<TValue, TState>(parser: Parser<TValue, TState>, defaultValue: TValue | (() => TValue)): Parser<TValue, [TState] | undefined>;
282
+ declare function withDefault<TValue, TState, TDefault = TValue>(parser: Parser<TValue, TState>, defaultValue: TDefault | (() => TDefault)): Parser<TValue | TDefault, [TState] | undefined>;
241
283
  /**
242
284
  * Creates a parser that transforms the result value of another parser using
243
285
  * a mapping function. This enables value transformation while preserving
@@ -872,4 +914,4 @@ declare function parse<T>(parser: Parser<T, unknown>, args: readonly string[]):
872
914
  */
873
915
  declare function getDocPage(parser: Parser<unknown, unknown>, args?: readonly string[]): DocPage | undefined;
874
916
  //#endregion
875
- export { ArgumentOptions, CommandOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
917
+ export { ArgumentOptions, CommandOptions, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
package/dist/parser.js CHANGED
@@ -222,6 +222,163 @@ function option(...args) {
222
222
  };
223
223
  }
224
224
  /**
225
+ * Creates a parser for command-line flags that must be explicitly provided.
226
+ * Unlike {@link option}, this parser fails if the flag is not present, making
227
+ * it suitable for required boolean flags that don't have a meaningful default.
228
+ *
229
+ * The key difference from {@link option} is:
230
+ * - {@link option} without a value parser: Returns `false` when not present
231
+ * - {@link flag}: Fails parsing when not present, only produces `true`
232
+ *
233
+ * This is useful for dependent options where the presence of a flag changes
234
+ * the shape of the result type.
235
+ *
236
+ * @param args The {@link OptionName}s to parse, followed by an optional
237
+ * {@link FlagOptions} object that allows you to specify
238
+ * a description or other metadata.
239
+ * @returns A {@link Parser} that produces `true` when the flag is present
240
+ * and fails when it is not present.
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * // Basic flag usage
245
+ * const parser = flag("-f", "--force");
246
+ * // Succeeds with true: parse(parser, ["-f"])
247
+ * // Fails: parse(parser, [])
248
+ *
249
+ * // With description
250
+ * const verboseFlag = flag("-v", "--verbose", {
251
+ * description: "Enable verbose output"
252
+ * });
253
+ * ```
254
+ */
255
+ function flag(...args) {
256
+ const lastArg = args.at(-1);
257
+ let optionNames$1;
258
+ let options = {};
259
+ if (typeof lastArg === "object" && lastArg != null && !Array.isArray(lastArg)) {
260
+ options = lastArg;
261
+ optionNames$1 = args.slice(0, -1);
262
+ } else optionNames$1 = args;
263
+ return {
264
+ $valueType: [],
265
+ $stateType: [],
266
+ priority: 10,
267
+ usage: [{
268
+ type: "option",
269
+ names: optionNames$1
270
+ }],
271
+ initialState: void 0,
272
+ parse(context) {
273
+ if (context.optionsTerminated) return {
274
+ success: false,
275
+ consumed: 0,
276
+ error: message`No more options can be parsed.`
277
+ };
278
+ else if (context.buffer.length < 1) return {
279
+ success: false,
280
+ consumed: 0,
281
+ error: message`Expected an option, but got end of input.`
282
+ };
283
+ if (context.buffer[0] === "--") return {
284
+ success: true,
285
+ next: {
286
+ ...context,
287
+ buffer: context.buffer.slice(1),
288
+ state: context.state,
289
+ optionsTerminated: true
290
+ },
291
+ consumed: context.buffer.slice(0, 1)
292
+ };
293
+ if (optionNames$1.includes(context.buffer[0])) {
294
+ if (context.state?.success) return {
295
+ success: false,
296
+ consumed: 1,
297
+ error: message`${optionName(context.buffer[0])} cannot be used multiple times.`
298
+ };
299
+ return {
300
+ success: true,
301
+ next: {
302
+ ...context,
303
+ state: {
304
+ success: true,
305
+ value: true
306
+ },
307
+ buffer: context.buffer.slice(1)
308
+ },
309
+ consumed: context.buffer.slice(0, 1)
310
+ };
311
+ }
312
+ const prefixes = optionNames$1.filter((name) => name.startsWith("--") || name.startsWith("/")).map((name) => name.startsWith("/") ? `${name}:` : `${name}=`);
313
+ for (const prefix of prefixes) if (context.buffer[0].startsWith(prefix)) {
314
+ const value = context.buffer[0].slice(prefix.length);
315
+ return {
316
+ success: false,
317
+ consumed: 1,
318
+ error: message`Flag ${optionName(prefix.slice(0, -1))} does not accept a value, but got: ${value}.`
319
+ };
320
+ }
321
+ const shortOptions = optionNames$1.filter((name) => name.match(/^-[^-]$/));
322
+ for (const shortOption of shortOptions) {
323
+ if (!context.buffer[0].startsWith(shortOption)) continue;
324
+ if (context.state?.success) return {
325
+ success: false,
326
+ consumed: 1,
327
+ error: message`${optionName(shortOption)} cannot be used multiple times.`
328
+ };
329
+ return {
330
+ success: true,
331
+ next: {
332
+ ...context,
333
+ state: {
334
+ success: true,
335
+ value: true
336
+ },
337
+ buffer: [`-${context.buffer[0].slice(2)}`, ...context.buffer.slice(1)]
338
+ },
339
+ consumed: [context.buffer[0].slice(0, 2)]
340
+ };
341
+ }
342
+ return {
343
+ success: false,
344
+ consumed: 0,
345
+ error: message`No matched option for ${optionName(context.buffer[0])}.`
346
+ };
347
+ },
348
+ complete(state) {
349
+ if (state == null) return {
350
+ success: false,
351
+ error: message`Required flag ${optionNames(optionNames$1)} is missing.`
352
+ };
353
+ if (state.success) return {
354
+ success: true,
355
+ value: true
356
+ };
357
+ return {
358
+ success: false,
359
+ error: message`${optionNames(optionNames$1)}: ${state.error}`
360
+ };
361
+ },
362
+ getDocFragments(_state, _defaultValue) {
363
+ const fragments = [{
364
+ type: "entry",
365
+ term: {
366
+ type: "option",
367
+ names: optionNames$1
368
+ },
369
+ description: options.description
370
+ }];
371
+ return {
372
+ fragments,
373
+ description: options.description
374
+ };
375
+ },
376
+ [Symbol.for("Deno.customInspect")]() {
377
+ return `flag(${optionNames$1.map((o) => JSON.stringify(o)).join(", ")})`;
378
+ }
379
+ };
380
+ }
381
+ /**
225
382
  * Creates a parser that expects a single argument value.
226
383
  * This parser is typically used for positional arguments
227
384
  * that are not options or flags.
@@ -368,12 +525,13 @@ function optional(parser) {
368
525
  * a specified default value.
369
526
  * @template TValue The type of the value returned by the wrapped parser.
370
527
  * @template TState The type of the state used by the wrapped parser.
528
+ * @template TDefault The type of the default value.
371
529
  * @param parser The {@link Parser} to wrap with default behavior.
372
530
  * @param defaultValue The default value to return when the wrapped parser
373
531
  * doesn't match or consume input. Can be a value of type
374
- * {@link TValue} or a function that returns such a value.
532
+ * {@link TDefault} or a function that returns such a value.
375
533
  * @returns A {@link Parser} that produces either the result of the wrapped parser
376
- * or the default value if the wrapped parser fails to match.
534
+ * or the default value if the wrapped parser fails to match (union type {@link TValue} | {@link TDefault}).
377
535
  */
378
536
  function withDefault(parser, defaultValue) {
379
537
  return {
@@ -408,7 +566,7 @@ function withDefault(parser, defaultValue) {
408
566
  return parser.complete(state[0]);
409
567
  },
410
568
  getDocFragments(state, upperDefaultValue) {
411
- return parser.getDocFragments(typeof state === "undefined" ? parser.initialState : state[0], upperDefaultValue == null ? typeof defaultValue === "function" ? defaultValue() : defaultValue : upperDefaultValue);
569
+ return parser.getDocFragments(typeof state === "undefined" ? parser.initialState : state[0], upperDefaultValue != null ? upperDefaultValue : typeof defaultValue === "function" ? defaultValue() : defaultValue);
412
570
  }
413
571
  };
414
572
  }
@@ -1227,4 +1385,4 @@ function getDocPage(parser, args = []) {
1227
1385
  }
1228
1386
 
1229
1387
  //#endregion
1230
- export { argument, command, concat, constant, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
1388
+ export { argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "0.3.0-dev.34+d4931ae5",
3
+ "version": "0.3.0-dev.36+680746e6",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",