@optique/core 1.0.0-dev.1390 → 1.0.0-dev.1410

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/facade.cjs CHANGED
@@ -7,6 +7,7 @@ const require_doc = require('./doc.cjs');
7
7
  const require_constructs = require('./constructs.cjs');
8
8
  const require_context = require('./context.cjs');
9
9
  const require_modifiers = require('./modifiers.cjs');
10
+ const require_validate = require('./validate.cjs');
10
11
  const require_valueparser = require('./valueparser.cjs');
11
12
  const require_primitives = require('./primitives.cjs');
12
13
  const require_parser = require('./parser.cjs');
@@ -741,61 +742,6 @@ function validateVersionValue(value$1) {
741
742
  if (/[\x00-\x1f\x7f]/.test(value$1)) throw new TypeError("Version value must not contain control characters.");
742
743
  return value$1;
743
744
  }
744
- /**
745
- * Escapes control characters in a string for display in error messages.
746
- *
747
- * @param value The string to escape.
748
- * @returns The escaped string with control characters replaced by escape
749
- * sequences.
750
- */
751
- function escapeControlChars(value$1) {
752
- return value$1.replace(/[\x00-\x1f\x7f]/g, (ch) => {
753
- const code = ch.charCodeAt(0);
754
- switch (code) {
755
- case 9: return "\\t";
756
- case 10: return "\\n";
757
- case 13: return "\\r";
758
- default: return `\\x${code.toString(16).padStart(2, "0")}`;
759
- }
760
- });
761
- }
762
- /**
763
- * Validates meta option names at runtime.
764
- *
765
- * @param names The option names to validate.
766
- * @param label A human-readable label for error messages (e.g.,
767
- * `"Help option"`).
768
- * @throws {TypeError} If the names array is empty, or any name is empty,
769
- * lacks a valid prefix, or contains whitespace or control characters.
770
- */
771
- function validateOptionNames(names, label) {
772
- if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
773
- for (const name of names) {
774
- if (name === "") throw new TypeError(`${label} name must not be empty.`);
775
- if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
776
- if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
777
- if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
778
- if (!/^(--|[-/+])/.test(name)) throw new TypeError(`${label} name must start with "--", "-", "/", or "+": "${name}".`);
779
- }
780
- }
781
- /**
782
- * Validates meta command names at runtime.
783
- *
784
- * @param names The command names to validate.
785
- * @param label A human-readable label for error messages (e.g.,
786
- * `"Help command"`).
787
- * @throws {TypeError} If the names array is empty, or any name is empty,
788
- * whitespace-only, or contains whitespace or control characters.
789
- */
790
- function validateCommandNames(names, label) {
791
- if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
792
- for (const name of names) {
793
- if (name === "") throw new TypeError(`${label} name must not be empty.`);
794
- if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
795
- if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
796
- if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
797
- }
798
- }
799
745
  function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsParam) {
800
746
  const isProgram = typeof programNameOrArgs !== "string";
801
747
  let parser;
@@ -839,12 +785,12 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
839
785
  const onCompletion = options.completion?.onShow ?? (() => ({}));
840
786
  const onCompletionResult = (code) => onCompletion(code);
841
787
  const onErrorResult = (code) => onError(code);
842
- if (helpOptionConfig?.names) validateOptionNames(helpOptionConfig.names, "Help option");
843
- if (helpCommandConfig?.names) validateCommandNames(helpCommandConfig.names, "Help command");
844
- if (versionOptionConfig?.names) validateOptionNames(versionOptionConfig.names, "Version option");
845
- if (versionCommandConfig?.names) validateCommandNames(versionCommandConfig.names, "Version command");
846
- if (completionOptionConfig?.names) validateOptionNames(completionOptionConfig.names, "Completion option");
847
- if (completionCommandConfig?.names) validateCommandNames(completionCommandConfig.names, "Completion command");
788
+ if (helpOptionConfig?.names) require_validate.validateOptionNames(helpOptionConfig.names, "Help option");
789
+ if (helpCommandConfig?.names) require_validate.validateCommandNames(helpCommandConfig.names, "Help command");
790
+ if (versionOptionConfig?.names) require_validate.validateOptionNames(versionOptionConfig.names, "Version option");
791
+ if (versionCommandConfig?.names) require_validate.validateCommandNames(versionCommandConfig.names, "Version command");
792
+ if (completionOptionConfig?.names) require_validate.validateOptionNames(completionOptionConfig.names, "Completion option");
793
+ if (completionCommandConfig?.names) require_validate.validateCommandNames(completionCommandConfig.names, "Completion command");
848
794
  const helpOptionNames = helpOptionConfig?.names ?? ["--help"];
849
795
  const helpCommandNames = helpCommandConfig?.names ?? ["help"];
850
796
  const versionOptionNames = versionOptionConfig?.names ?? ["--version"];
package/dist/facade.js CHANGED
@@ -7,6 +7,7 @@ import { formatDocPage } from "./doc.js";
7
7
  import { group, longestMatch, object } from "./constructs.js";
8
8
  import { isPlaceholderValue } from "./context.js";
9
9
  import { multiple, optional, withDefault } from "./modifiers.js";
10
+ import { validateCommandNames, validateOptionNames } from "./validate.js";
10
11
  import { string } from "./valueparser.js";
11
12
  import { argument, command, constant, flag, option } from "./primitives.js";
12
13
  import { getDocPage, parseAsync, parseSync, suggest, suggestAsync } from "./parser.js";
@@ -741,61 +742,6 @@ function validateVersionValue(value$1) {
741
742
  if (/[\x00-\x1f\x7f]/.test(value$1)) throw new TypeError("Version value must not contain control characters.");
742
743
  return value$1;
743
744
  }
744
- /**
745
- * Escapes control characters in a string for display in error messages.
746
- *
747
- * @param value The string to escape.
748
- * @returns The escaped string with control characters replaced by escape
749
- * sequences.
750
- */
751
- function escapeControlChars(value$1) {
752
- return value$1.replace(/[\x00-\x1f\x7f]/g, (ch) => {
753
- const code = ch.charCodeAt(0);
754
- switch (code) {
755
- case 9: return "\\t";
756
- case 10: return "\\n";
757
- case 13: return "\\r";
758
- default: return `\\x${code.toString(16).padStart(2, "0")}`;
759
- }
760
- });
761
- }
762
- /**
763
- * Validates meta option names at runtime.
764
- *
765
- * @param names The option names to validate.
766
- * @param label A human-readable label for error messages (e.g.,
767
- * `"Help option"`).
768
- * @throws {TypeError} If the names array is empty, or any name is empty,
769
- * lacks a valid prefix, or contains whitespace or control characters.
770
- */
771
- function validateOptionNames(names, label) {
772
- if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
773
- for (const name of names) {
774
- if (name === "") throw new TypeError(`${label} name must not be empty.`);
775
- if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
776
- if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
777
- if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
778
- if (!/^(--|[-/+])/.test(name)) throw new TypeError(`${label} name must start with "--", "-", "/", or "+": "${name}".`);
779
- }
780
- }
781
- /**
782
- * Validates meta command names at runtime.
783
- *
784
- * @param names The command names to validate.
785
- * @param label A human-readable label for error messages (e.g.,
786
- * `"Help command"`).
787
- * @throws {TypeError} If the names array is empty, or any name is empty,
788
- * whitespace-only, or contains whitespace or control characters.
789
- */
790
- function validateCommandNames(names, label) {
791
- if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
792
- for (const name of names) {
793
- if (name === "") throw new TypeError(`${label} name must not be empty.`);
794
- if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
795
- if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
796
- if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
797
- }
798
- }
799
745
  function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsParam) {
800
746
  const isProgram = typeof programNameOrArgs !== "string";
801
747
  let parser;
@@ -5,6 +5,7 @@ const require_mode_dispatch = require('./mode-dispatch.cjs');
5
5
  const require_usage = require('./usage.cjs');
6
6
  const require_suggestion = require('./suggestion.cjs');
7
7
  const require_usage_internals = require('./usage-internals.cjs');
8
+ const require_validate = require('./validate.cjs');
8
9
  const require_valueparser = require('./valueparser.cjs');
9
10
 
10
11
  //#region src/primitives.ts
@@ -309,6 +310,7 @@ function option(...args) {
309
310
  optionNames$1 = args;
310
311
  valueParser = void 0;
311
312
  }
313
+ require_validate.validateOptionNames(optionNames$1, "Option");
312
314
  const mode = valueParser?.$mode ?? "sync";
313
315
  const isAsync = mode === "async";
314
316
  const result = {
@@ -585,6 +587,7 @@ function flag(...args) {
585
587
  options = lastArg;
586
588
  optionNames$1 = args.slice(0, -1);
587
589
  } else optionNames$1 = args;
590
+ require_validate.validateOptionNames(optionNames$1, "Flag");
588
591
  return {
589
592
  $valueType: [],
590
593
  $stateType: [],
@@ -119,7 +119,7 @@ interface OptionErrorOptions {
119
119
  * @returns A {@link Parser} that can parse the specified options and their
120
120
  * values.
121
121
  */
122
- declare function option<M extends Mode, T>(...args: readonly [...readonly OptionName[], ValueParser<M, T>]): Parser<M, T, ValueParserResult<T> | undefined>;
122
+ declare function option<M extends Mode, T>(...args: readonly [OptionName, ...readonly OptionName[], ValueParser<M, T>]): Parser<M, T, ValueParserResult<T> | undefined>;
123
123
  /**
124
124
  * Creates a parser for various styles of command-line options that take an
125
125
  * argument value, such as `--option=value`, `-option=value`, `-o value`,
@@ -133,7 +133,7 @@ declare function option<M extends Mode, T>(...args: readonly [...readonly Option
133
133
  * @returns A {@link Parser} that can parse the specified options and their
134
134
  * values.
135
135
  */
136
- declare function option<M extends Mode, T>(...args: readonly [...readonly OptionName[], ValueParser<M, T>, OptionOptions]): Parser<M, T, ValueParserResult<T> | undefined>;
136
+ declare function option<M extends Mode, T>(...args: readonly [OptionName, ...readonly OptionName[], ValueParser<M, T>, OptionOptions]): Parser<M, T, ValueParserResult<T> | undefined>;
137
137
  /**
138
138
  * Creates a parser for various styles of command-line options that do not
139
139
  * take an argument value, such as `--option`, `-o`, or `/option`.
@@ -141,7 +141,7 @@ declare function option<M extends Mode, T>(...args: readonly [...readonly Option
141
141
  * @return A {@link Parser} that can parse the specified options as Boolean
142
142
  * flags, producing `true` if the option is present.
143
143
  */
144
- declare function option(...optionNames: readonly OptionName[]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
144
+ declare function option(...optionNames: readonly [OptionName, ...readonly OptionName[]]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
145
145
  /**
146
146
  * Creates a parser for various styles of command-line options that take an
147
147
  * argument value, such as `--option=value`, `-option=value`, `-o value`,
@@ -152,7 +152,7 @@ declare function option(...optionNames: readonly OptionName[]): Parser<"sync", b
152
152
  * @returns A {@link Parser} that can parse the specified options and their
153
153
  * values.
154
154
  */
155
- declare function option(...args: readonly [...readonly OptionName[], OptionOptions]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
155
+ declare function option(...args: readonly [OptionName, ...readonly OptionName[], OptionOptions]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
156
156
  /**
157
157
  * Options for the {@link flag} parser.
158
158
  */
@@ -242,7 +242,7 @@ interface FlagErrorOptions {
242
242
  * });
243
243
  * ```
244
244
  */
245
- declare function flag(...args: readonly [...readonly OptionName[], FlagOptions] | readonly OptionName[]): Parser<"sync", true, ValueParserResult<true> | undefined>;
245
+ declare function flag(...args: readonly [OptionName, ...readonly OptionName[], FlagOptions] | readonly [OptionName, ...readonly OptionName[]]): Parser<"sync", true, ValueParserResult<true> | undefined>;
246
246
  /**
247
247
  * Options for the {@link argument} parser.
248
248
  */
@@ -119,7 +119,7 @@ interface OptionErrorOptions {
119
119
  * @returns A {@link Parser} that can parse the specified options and their
120
120
  * values.
121
121
  */
122
- declare function option<M extends Mode, T>(...args: readonly [...readonly OptionName[], ValueParser<M, T>]): Parser<M, T, ValueParserResult<T> | undefined>;
122
+ declare function option<M extends Mode, T>(...args: readonly [OptionName, ...readonly OptionName[], ValueParser<M, T>]): Parser<M, T, ValueParserResult<T> | undefined>;
123
123
  /**
124
124
  * Creates a parser for various styles of command-line options that take an
125
125
  * argument value, such as `--option=value`, `-option=value`, `-o value`,
@@ -133,7 +133,7 @@ declare function option<M extends Mode, T>(...args: readonly [...readonly Option
133
133
  * @returns A {@link Parser} that can parse the specified options and their
134
134
  * values.
135
135
  */
136
- declare function option<M extends Mode, T>(...args: readonly [...readonly OptionName[], ValueParser<M, T>, OptionOptions]): Parser<M, T, ValueParserResult<T> | undefined>;
136
+ declare function option<M extends Mode, T>(...args: readonly [OptionName, ...readonly OptionName[], ValueParser<M, T>, OptionOptions]): Parser<M, T, ValueParserResult<T> | undefined>;
137
137
  /**
138
138
  * Creates a parser for various styles of command-line options that do not
139
139
  * take an argument value, such as `--option`, `-o`, or `/option`.
@@ -141,7 +141,7 @@ declare function option<M extends Mode, T>(...args: readonly [...readonly Option
141
141
  * @return A {@link Parser} that can parse the specified options as Boolean
142
142
  * flags, producing `true` if the option is present.
143
143
  */
144
- declare function option(...optionNames: readonly OptionName[]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
144
+ declare function option(...optionNames: readonly [OptionName, ...readonly OptionName[]]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
145
145
  /**
146
146
  * Creates a parser for various styles of command-line options that take an
147
147
  * argument value, such as `--option=value`, `-option=value`, `-o value`,
@@ -152,7 +152,7 @@ declare function option(...optionNames: readonly OptionName[]): Parser<"sync", b
152
152
  * @returns A {@link Parser} that can parse the specified options and their
153
153
  * values.
154
154
  */
155
- declare function option(...args: readonly [...readonly OptionName[], OptionOptions]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
155
+ declare function option(...args: readonly [OptionName, ...readonly OptionName[], OptionOptions]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
156
156
  /**
157
157
  * Options for the {@link flag} parser.
158
158
  */
@@ -242,7 +242,7 @@ interface FlagErrorOptions {
242
242
  * });
243
243
  * ```
244
244
  */
245
- declare function flag(...args: readonly [...readonly OptionName[], FlagOptions] | readonly OptionName[]): Parser<"sync", true, ValueParserResult<true> | undefined>;
245
+ declare function flag(...args: readonly [OptionName, ...readonly OptionName[], FlagOptions] | readonly [OptionName, ...readonly OptionName[]]): Parser<"sync", true, ValueParserResult<true> | undefined>;
246
246
  /**
247
247
  * Options for the {@link argument} parser.
248
248
  */
@@ -5,6 +5,7 @@ import { dispatchIterableByMode } from "./mode-dispatch.js";
5
5
  import { extractOptionNames, isDocHidden, isSuggestionHidden } from "./usage.js";
6
6
  import { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, findSimilar } from "./suggestion.js";
7
7
  import { extractLeadingCommandNames } from "./usage-internals.js";
8
+ import { validateOptionNames } from "./validate.js";
8
9
  import { isValueParser } from "./valueparser.js";
9
10
 
10
11
  //#region src/primitives.ts
@@ -309,6 +310,7 @@ function option(...args) {
309
310
  optionNames$1 = args;
310
311
  valueParser = void 0;
311
312
  }
313
+ validateOptionNames(optionNames$1, "Option");
312
314
  const mode = valueParser?.$mode ?? "sync";
313
315
  const isAsync = mode === "async";
314
316
  const result = {
@@ -585,6 +587,7 @@ function flag(...args) {
585
587
  options = lastArg;
586
588
  optionNames$1 = args.slice(0, -1);
587
589
  } else optionNames$1 = args;
590
+ validateOptionNames(optionNames$1, "Flag");
588
591
  return {
589
592
  $valueType: [],
590
593
  $stateType: [],
package/dist/usage.d.cts CHANGED
@@ -10,8 +10,14 @@ import { NonEmptyString } from "./nonempty.cjs";
10
10
  * - POSIX-style short options (`-o`) or Java-style options (`-option`)
11
11
  * - MS-DOS-style options (`/o`, `/option`)
12
12
  * - Plus-prefixed options (`+o`)
13
+ *
14
+ * Each prefix must be followed by at least one character, so bare prefixes
15
+ * like `"-"`, `"/"`, or `"+"` are rejected at compile time. Due to
16
+ * TypeScript template literal limitations, `"--"` still matches the
17
+ * `-${NonEmptyString}` branch and is only rejected at runtime by the
18
+ * `option()` and `flag()` validators.
13
19
  */
14
- type OptionName = `--${string}` | `-${string}` | `/${string}` | `+${string}`;
20
+ type OptionName = `--${NonEmptyString}` | `-${NonEmptyString}` | `/${NonEmptyString}` | `+${NonEmptyString}`;
15
21
  /**
16
22
  * Visibility control for parser terms.
17
23
  *
package/dist/usage.d.ts CHANGED
@@ -10,8 +10,14 @@ import { NonEmptyString } from "./nonempty.js";
10
10
  * - POSIX-style short options (`-o`) or Java-style options (`-option`)
11
11
  * - MS-DOS-style options (`/o`, `/option`)
12
12
  * - Plus-prefixed options (`+o`)
13
+ *
14
+ * Each prefix must be followed by at least one character, so bare prefixes
15
+ * like `"-"`, `"/"`, or `"+"` are rejected at compile time. Due to
16
+ * TypeScript template literal limitations, `"--"` still matches the
17
+ * `-${NonEmptyString}` branch and is only rejected at runtime by the
18
+ * `option()` and `flag()` validators.
13
19
  */
14
- type OptionName = `--${string}` | `-${string}` | `/${string}` | `+${string}`;
20
+ type OptionName = `--${NonEmptyString}` | `-${NonEmptyString}` | `/${NonEmptyString}` | `+${NonEmptyString}`;
15
21
  /**
16
22
  * Visibility control for parser terms.
17
23
  *
@@ -0,0 +1,62 @@
1
+
2
+ //#region src/validate.ts
3
+ /**
4
+ * Escapes control characters in a string for readable error messages.
5
+ *
6
+ * @param value The string to escape.
7
+ * @returns The escaped string with control characters replaced by escape
8
+ * sequences.
9
+ */
10
+ function escapeControlChars(value) {
11
+ return value.replace(/[\x00-\x1f\x7f]/g, (ch) => {
12
+ const code = ch.charCodeAt(0);
13
+ switch (code) {
14
+ case 9: return "\\t";
15
+ case 10: return "\\n";
16
+ case 13: return "\\r";
17
+ default: return `\\x${code.toString(16).padStart(2, "0")}`;
18
+ }
19
+ });
20
+ }
21
+ /**
22
+ * Validates option names at runtime.
23
+ *
24
+ * @param names The option names to validate.
25
+ * @param label A human-readable label for error messages (e.g.,
26
+ * `"Option"`, `"Flag"`, `"Help option"`).
27
+ * @throws {TypeError} If the names array is empty, or any name is empty,
28
+ * lacks a valid prefix, or contains whitespace or control characters.
29
+ */
30
+ function validateOptionNames(names, label) {
31
+ if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
32
+ for (const name of names) {
33
+ if (name === "") throw new TypeError(`${label} name must not be empty.`);
34
+ if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
35
+ if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
36
+ if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
37
+ if (!/^(--|[-/+])/.test(name)) throw new TypeError(`${label} name must start with "--", "-", "/", or "+": "${name}".`);
38
+ if (name === "--") throw new TypeError(`${label} name must not be the options terminator "--".`);
39
+ }
40
+ }
41
+ /**
42
+ * Validates command names at runtime.
43
+ *
44
+ * @param names The command names to validate.
45
+ * @param label A human-readable label for error messages (e.g.,
46
+ * `"Help command"`).
47
+ * @throws {TypeError} If the names array is empty, or any name is empty,
48
+ * whitespace-only, or contains whitespace or control characters.
49
+ */
50
+ function validateCommandNames(names, label) {
51
+ if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
52
+ for (const name of names) {
53
+ if (name === "") throw new TypeError(`${label} name must not be empty.`);
54
+ if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
55
+ if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
56
+ if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
57
+ }
58
+ }
59
+
60
+ //#endregion
61
+ exports.validateCommandNames = validateCommandNames;
62
+ exports.validateOptionNames = validateOptionNames;
@@ -0,0 +1,60 @@
1
+ //#region src/validate.ts
2
+ /**
3
+ * Escapes control characters in a string for readable error messages.
4
+ *
5
+ * @param value The string to escape.
6
+ * @returns The escaped string with control characters replaced by escape
7
+ * sequences.
8
+ */
9
+ function escapeControlChars(value) {
10
+ return value.replace(/[\x00-\x1f\x7f]/g, (ch) => {
11
+ const code = ch.charCodeAt(0);
12
+ switch (code) {
13
+ case 9: return "\\t";
14
+ case 10: return "\\n";
15
+ case 13: return "\\r";
16
+ default: return `\\x${code.toString(16).padStart(2, "0")}`;
17
+ }
18
+ });
19
+ }
20
+ /**
21
+ * Validates option names at runtime.
22
+ *
23
+ * @param names The option names to validate.
24
+ * @param label A human-readable label for error messages (e.g.,
25
+ * `"Option"`, `"Flag"`, `"Help option"`).
26
+ * @throws {TypeError} If the names array is empty, or any name is empty,
27
+ * lacks a valid prefix, or contains whitespace or control characters.
28
+ */
29
+ function validateOptionNames(names, label) {
30
+ if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
31
+ for (const name of names) {
32
+ if (name === "") throw new TypeError(`${label} name must not be empty.`);
33
+ if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
34
+ if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
35
+ if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
36
+ if (!/^(--|[-/+])/.test(name)) throw new TypeError(`${label} name must start with "--", "-", "/", or "+": "${name}".`);
37
+ if (name === "--") throw new TypeError(`${label} name must not be the options terminator "--".`);
38
+ }
39
+ }
40
+ /**
41
+ * Validates command names at runtime.
42
+ *
43
+ * @param names The command names to validate.
44
+ * @param label A human-readable label for error messages (e.g.,
45
+ * `"Help command"`).
46
+ * @throws {TypeError} If the names array is empty, or any name is empty,
47
+ * whitespace-only, or contains whitespace or control characters.
48
+ */
49
+ function validateCommandNames(names, label) {
50
+ if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
51
+ for (const name of names) {
52
+ if (name === "") throw new TypeError(`${label} name must not be empty.`);
53
+ if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
54
+ if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
55
+ if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
56
+ }
57
+ }
58
+
59
+ //#endregion
60
+ export { validateCommandNames, validateOptionNames };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.0.0-dev.1390+84b0f473",
3
+ "version": "1.0.0-dev.1410+b8ee2165",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",