@optique/core 1.0.0-dev.908 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/dist/annotation-state.cjs +425 -0
  2. package/dist/annotation-state.d.cts +24 -0
  3. package/dist/annotation-state.d.ts +24 -0
  4. package/dist/annotation-state.js +414 -0
  5. package/dist/annotations.cjs +2 -248
  6. package/dist/annotations.d.cts +2 -137
  7. package/dist/annotations.d.ts +2 -137
  8. package/dist/annotations.js +2 -238
  9. package/dist/completion.cjs +611 -100
  10. package/dist/completion.d.cts +1 -1
  11. package/dist/completion.d.ts +1 -1
  12. package/dist/completion.js +611 -100
  13. package/dist/constructs.cjs +3338 -827
  14. package/dist/constructs.d.cts +48 -7
  15. package/dist/constructs.d.ts +48 -7
  16. package/dist/constructs.js +3338 -827
  17. package/dist/context.cjs +0 -23
  18. package/dist/context.d.cts +119 -53
  19. package/dist/context.d.ts +119 -53
  20. package/dist/context.js +0 -22
  21. package/dist/dependency-metadata.cjs +139 -0
  22. package/dist/dependency-metadata.d.cts +112 -0
  23. package/dist/dependency-metadata.d.ts +112 -0
  24. package/dist/dependency-metadata.js +138 -0
  25. package/dist/dependency-runtime.cjs +698 -0
  26. package/dist/dependency-runtime.d.cts +149 -0
  27. package/dist/dependency-runtime.d.ts +149 -0
  28. package/dist/dependency-runtime.js +687 -0
  29. package/dist/dependency.cjs +7 -928
  30. package/dist/dependency.d.cts +2 -794
  31. package/dist/dependency.d.ts +2 -794
  32. package/dist/dependency.js +2 -899
  33. package/dist/displaywidth.cjs +44 -0
  34. package/dist/displaywidth.js +43 -0
  35. package/dist/doc.cjs +285 -23
  36. package/dist/doc.d.cts +57 -2
  37. package/dist/doc.d.ts +57 -2
  38. package/dist/doc.js +283 -25
  39. package/dist/execution-context.cjs +56 -0
  40. package/dist/execution-context.js +53 -0
  41. package/dist/extension.cjs +87 -0
  42. package/dist/extension.d.cts +97 -0
  43. package/dist/extension.d.ts +97 -0
  44. package/dist/extension.js +76 -0
  45. package/dist/facade.cjs +718 -523
  46. package/dist/facade.d.cts +87 -18
  47. package/dist/facade.d.ts +87 -18
  48. package/dist/facade.js +718 -523
  49. package/dist/index.cjs +14 -29
  50. package/dist/index.d.cts +10 -10
  51. package/dist/index.d.ts +10 -10
  52. package/dist/index.js +7 -7
  53. package/dist/input-trace.cjs +56 -0
  54. package/dist/input-trace.d.cts +77 -0
  55. package/dist/input-trace.d.ts +77 -0
  56. package/dist/input-trace.js +55 -0
  57. package/dist/internal/annotations.cjs +316 -0
  58. package/dist/internal/annotations.d.cts +140 -0
  59. package/dist/internal/annotations.d.ts +140 -0
  60. package/dist/internal/annotations.js +306 -0
  61. package/dist/internal/dependency.cjs +984 -0
  62. package/dist/internal/dependency.d.cts +539 -0
  63. package/dist/internal/dependency.d.ts +539 -0
  64. package/dist/internal/dependency.js +964 -0
  65. package/dist/{mode-dispatch.cjs → internal/mode-dispatch.cjs} +1 -3
  66. package/dist/{mode-dispatch.d.cts → internal/mode-dispatch.d.cts} +3 -7
  67. package/dist/{mode-dispatch.d.ts → internal/mode-dispatch.d.ts} +3 -7
  68. package/dist/{mode-dispatch.js → internal/mode-dispatch.js} +1 -3
  69. package/dist/internal/parser.cjs +728 -0
  70. package/dist/internal/parser.d.cts +947 -0
  71. package/dist/internal/parser.d.ts +947 -0
  72. package/dist/internal/parser.js +711 -0
  73. package/dist/message.cjs +84 -26
  74. package/dist/message.d.cts +49 -9
  75. package/dist/message.d.ts +49 -9
  76. package/dist/message.js +84 -27
  77. package/dist/modifiers.cjs +1023 -240
  78. package/dist/modifiers.d.cts +42 -1
  79. package/dist/modifiers.d.ts +42 -1
  80. package/dist/modifiers.js +1023 -240
  81. package/dist/parser.cjs +11 -463
  82. package/dist/parser.d.cts +3 -537
  83. package/dist/parser.d.ts +3 -537
  84. package/dist/parser.js +2 -433
  85. package/dist/phase2-seed.cjs +59 -0
  86. package/dist/phase2-seed.js +56 -0
  87. package/dist/primitives.cjs +557 -208
  88. package/dist/primitives.d.cts +10 -14
  89. package/dist/primitives.d.ts +10 -14
  90. package/dist/primitives.js +557 -208
  91. package/dist/program.cjs +5 -1
  92. package/dist/program.d.cts +5 -3
  93. package/dist/program.d.ts +5 -3
  94. package/dist/program.js +6 -1
  95. package/dist/suggestion.cjs +22 -8
  96. package/dist/suggestion.js +22 -8
  97. package/dist/usage-internals.cjs +3 -2
  98. package/dist/usage-internals.js +4 -2
  99. package/dist/usage.cjs +195 -40
  100. package/dist/usage.d.cts +92 -11
  101. package/dist/usage.d.ts +92 -11
  102. package/dist/usage.js +194 -41
  103. package/dist/validate.cjs +170 -0
  104. package/dist/validate.js +164 -0
  105. package/dist/valueparser.cjs +1278 -191
  106. package/dist/valueparser.d.cts +330 -20
  107. package/dist/valueparser.d.ts +330 -20
  108. package/dist/valueparser.js +1277 -192
  109. package/package.json +9 -9
@@ -0,0 +1,170 @@
1
+
2
+ //#region src/validate.ts
3
+ /**
4
+ * Matches Unicode control characters: C0 (U+0000–U+001F), DEL (U+007F),
5
+ * C1 (U+0080–U+009F), and line separators (U+2028, U+2029).
6
+ */
7
+ const CONTROL_CHAR_RE = /[\x00-\x1f\x7f-\x9f\u2028\u2029]/;
8
+ const CONTROL_CHAR_RE_GLOBAL = new RegExp(CONTROL_CHAR_RE.source, "g");
9
+ /**
10
+ * Escapes control characters in a string for readable error messages.
11
+ *
12
+ * @param value The string to escape.
13
+ * @returns The escaped string with control characters replaced by escape
14
+ * sequences.
15
+ */
16
+ function escapeControlChars(value) {
17
+ return value.replace(CONTROL_CHAR_RE_GLOBAL, (ch) => {
18
+ const code = ch.charCodeAt(0);
19
+ switch (code) {
20
+ case 9: return "\\t";
21
+ case 10: return "\\n";
22
+ case 13: return "\\r";
23
+ default: return code > 255 ? `\\u${code.toString(16).padStart(4, "0")}` : `\\x${code.toString(16).padStart(2, "0")}`;
24
+ }
25
+ });
26
+ }
27
+ /**
28
+ * Validates option names at runtime.
29
+ *
30
+ * @param names The option names to validate.
31
+ * @param label A human-readable label for error messages (e.g.,
32
+ * `"Option"`, `"Flag"`, `"Help option"`).
33
+ * @throws {TypeError} If the names array is empty, or any name is empty,
34
+ * lacks a valid prefix, or contains whitespace or control characters.
35
+ */
36
+ function validateOptionNames(names, label) {
37
+ if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
38
+ for (const name of names) {
39
+ if (name === "") throw new TypeError(`${label} name must not be empty.`);
40
+ if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
41
+ if (CONTROL_CHAR_RE.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
42
+ if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
43
+ if (!/^(--|[-/+])/.test(name)) throw new TypeError(`${label} name must start with "--", "-", "/", or "+": "${name}".`);
44
+ if (name === "--") throw new TypeError(`${label} name must not be the options terminator "--".`);
45
+ }
46
+ }
47
+ /**
48
+ * Validates command names at runtime.
49
+ *
50
+ * @param names The command names to validate.
51
+ * @param label A human-readable label for error messages (e.g.,
52
+ * `"Help command"`).
53
+ * @throws {TypeError} If the names array is empty, or any name is empty,
54
+ * whitespace-only, or contains whitespace or control characters.
55
+ */
56
+ function validateCommandNames(names, label) {
57
+ if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
58
+ for (const name of names) {
59
+ if (name === "") throw new TypeError(`${label} name must not be empty.`);
60
+ if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
61
+ if (CONTROL_CHAR_RE.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
62
+ if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
63
+ }
64
+ }
65
+ /**
66
+ * Validates that there are no name collisions among active meta features
67
+ * (help, version, completion).
68
+ *
69
+ * User parser names are accepted even when they overlap with meta names.
70
+ * Runtime parsing resolves those cases parser-first so ordinary parser data
71
+ * can shadow built-in meta behavior.
72
+ *
73
+ * Meta-vs-meta collisions are always checked in a unified namespace,
74
+ * because a meta command named `"--help"` and a meta option named
75
+ * `"--help"` both compete for the same token.
76
+ *
77
+ * @param metaEntries Active meta feature entries annotated with their kind.
78
+ * @throws {TypeError} If any meta/meta collision or duplicate is detected.
79
+ * @since 1.0.0
80
+ */
81
+ function validateMetaNameCollisions(metaEntries) {
82
+ for (const [, label, names] of metaEntries) {
83
+ const seen = /* @__PURE__ */ new Set();
84
+ for (const name of names) {
85
+ if (seen.has(name)) throw new TypeError(`${capitalize(label)} has a duplicate name: "${name}"`);
86
+ seen.add(name);
87
+ }
88
+ }
89
+ const nameToLabel = /* @__PURE__ */ new Map();
90
+ for (const [, label, names] of metaEntries) for (const name of names) {
91
+ const existingLabel = nameToLabel.get(name);
92
+ if (existingLabel != null) throw new TypeError(`Name "${name}" is used by both ${existingLabel} and ${label}.`);
93
+ nameToLabel.set(name, label);
94
+ }
95
+ for (let i = 0; i < metaEntries.length; i++) {
96
+ const [, label, names, prefixMatch] = metaEntries[i];
97
+ if (!prefixMatch) continue;
98
+ for (const name of names) {
99
+ const prefix = name + "=";
100
+ for (let j = 0; j < metaEntries.length; j++) {
101
+ const [, otherLabel, otherNames] = metaEntries[j];
102
+ for (const otherName of otherNames) {
103
+ if (i === j && otherName === name) continue;
104
+ if (!otherName.startsWith(prefix)) continue;
105
+ throw new TypeError("The prefix form of name \"" + name + "\" in " + label + " shadows \"" + otherName + "\" in " + otherLabel + ".");
106
+ }
107
+ }
108
+ }
109
+ }
110
+ }
111
+ function capitalize(s) {
112
+ return s.charAt(0).toUpperCase() + s.slice(1);
113
+ }
114
+ /**
115
+ * Validates a program name at runtime.
116
+ *
117
+ * Program names may contain spaces (e.g., file paths), but must not be empty,
118
+ * whitespace-only, or contain control characters.
119
+ *
120
+ * @param programName The program name to validate.
121
+ * @throws {TypeError} If the value is not a string, is empty,
122
+ * whitespace-only, or contains control characters.
123
+ */
124
+ function validateProgramName(programName) {
125
+ if (typeof programName !== "string") throw new TypeError("Program name must be a string.");
126
+ if (programName === "") throw new TypeError("Program name must not be empty.");
127
+ if (/^\s+$/.test(programName)) throw new TypeError(`Program name must not be whitespace-only: "${escapeControlChars(programName)}".`);
128
+ if (CONTROL_CHAR_RE.test(programName)) throw new TypeError(`Program name must not contain control characters: "${escapeControlChars(programName)}".`);
129
+ }
130
+ /**
131
+ * Validates a label at runtime.
132
+ *
133
+ * Labels are used as section titles in documentation output. They may contain
134
+ * spaces (e.g., "Connection options"), but must not be empty, whitespace-only,
135
+ * or contain control characters.
136
+ *
137
+ * @param label The label to validate.
138
+ * @throws {TypeError} If the label is not a string, is empty,
139
+ * whitespace-only, or contains control characters.
140
+ * @since 1.0.0
141
+ */
142
+ function validateLabel(label) {
143
+ if (typeof label !== "string") throw new TypeError("Label must be a string.");
144
+ if (label === "") throw new TypeError("Label must not be empty.");
145
+ if (/^\s+$/.test(label)) throw new TypeError(`Label must not be whitespace-only: "${escapeControlChars(label)}".`);
146
+ if (CONTROL_CHAR_RE.test(label)) throw new TypeError(`Label must not contain control characters: "${escapeControlChars(label)}".`);
147
+ }
148
+ /**
149
+ * Validates that all source contexts have unique
150
+ * {@link import("./context.ts").SourceContext.id | id} values.
151
+ *
152
+ * @param contexts The source contexts to validate.
153
+ * @throws {TypeError} If two or more contexts share the same id.
154
+ * @since 1.0.0
155
+ */
156
+ function validateContextIds(contexts) {
157
+ const seen = /* @__PURE__ */ new Set();
158
+ for (const context of contexts) {
159
+ if (seen.has(context.id)) throw new TypeError(`Duplicate SourceContext id: ${String(context.id)}`);
160
+ seen.add(context.id);
161
+ }
162
+ }
163
+
164
+ //#endregion
165
+ exports.validateCommandNames = validateCommandNames;
166
+ exports.validateContextIds = validateContextIds;
167
+ exports.validateLabel = validateLabel;
168
+ exports.validateMetaNameCollisions = validateMetaNameCollisions;
169
+ exports.validateOptionNames = validateOptionNames;
170
+ exports.validateProgramName = validateProgramName;
@@ -0,0 +1,164 @@
1
+ //#region src/validate.ts
2
+ /**
3
+ * Matches Unicode control characters: C0 (U+0000–U+001F), DEL (U+007F),
4
+ * C1 (U+0080–U+009F), and line separators (U+2028, U+2029).
5
+ */
6
+ const CONTROL_CHAR_RE = /[\x00-\x1f\x7f-\x9f\u2028\u2029]/;
7
+ const CONTROL_CHAR_RE_GLOBAL = new RegExp(CONTROL_CHAR_RE.source, "g");
8
+ /**
9
+ * Escapes control characters in a string for readable error messages.
10
+ *
11
+ * @param value The string to escape.
12
+ * @returns The escaped string with control characters replaced by escape
13
+ * sequences.
14
+ */
15
+ function escapeControlChars(value) {
16
+ return value.replace(CONTROL_CHAR_RE_GLOBAL, (ch) => {
17
+ const code = ch.charCodeAt(0);
18
+ switch (code) {
19
+ case 9: return "\\t";
20
+ case 10: return "\\n";
21
+ case 13: return "\\r";
22
+ default: return code > 255 ? `\\u${code.toString(16).padStart(4, "0")}` : `\\x${code.toString(16).padStart(2, "0")}`;
23
+ }
24
+ });
25
+ }
26
+ /**
27
+ * Validates option names at runtime.
28
+ *
29
+ * @param names The option names to validate.
30
+ * @param label A human-readable label for error messages (e.g.,
31
+ * `"Option"`, `"Flag"`, `"Help option"`).
32
+ * @throws {TypeError} If the names array is empty, or any name is empty,
33
+ * lacks a valid prefix, or contains whitespace or control characters.
34
+ */
35
+ function validateOptionNames(names, label) {
36
+ if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
37
+ for (const name of names) {
38
+ if (name === "") throw new TypeError(`${label} name must not be empty.`);
39
+ if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
40
+ if (CONTROL_CHAR_RE.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
41
+ if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
42
+ if (!/^(--|[-/+])/.test(name)) throw new TypeError(`${label} name must start with "--", "-", "/", or "+": "${name}".`);
43
+ if (name === "--") throw new TypeError(`${label} name must not be the options terminator "--".`);
44
+ }
45
+ }
46
+ /**
47
+ * Validates command names at runtime.
48
+ *
49
+ * @param names The command names to validate.
50
+ * @param label A human-readable label for error messages (e.g.,
51
+ * `"Help command"`).
52
+ * @throws {TypeError} If the names array is empty, or any name is empty,
53
+ * whitespace-only, or contains whitespace or control characters.
54
+ */
55
+ function validateCommandNames(names, label) {
56
+ if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
57
+ for (const name of names) {
58
+ if (name === "") throw new TypeError(`${label} name must not be empty.`);
59
+ if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
60
+ if (CONTROL_CHAR_RE.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
61
+ if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
62
+ }
63
+ }
64
+ /**
65
+ * Validates that there are no name collisions among active meta features
66
+ * (help, version, completion).
67
+ *
68
+ * User parser names are accepted even when they overlap with meta names.
69
+ * Runtime parsing resolves those cases parser-first so ordinary parser data
70
+ * can shadow built-in meta behavior.
71
+ *
72
+ * Meta-vs-meta collisions are always checked in a unified namespace,
73
+ * because a meta command named `"--help"` and a meta option named
74
+ * `"--help"` both compete for the same token.
75
+ *
76
+ * @param metaEntries Active meta feature entries annotated with their kind.
77
+ * @throws {TypeError} If any meta/meta collision or duplicate is detected.
78
+ * @since 1.0.0
79
+ */
80
+ function validateMetaNameCollisions(metaEntries) {
81
+ for (const [, label, names] of metaEntries) {
82
+ const seen = /* @__PURE__ */ new Set();
83
+ for (const name of names) {
84
+ if (seen.has(name)) throw new TypeError(`${capitalize(label)} has a duplicate name: "${name}"`);
85
+ seen.add(name);
86
+ }
87
+ }
88
+ const nameToLabel = /* @__PURE__ */ new Map();
89
+ for (const [, label, names] of metaEntries) for (const name of names) {
90
+ const existingLabel = nameToLabel.get(name);
91
+ if (existingLabel != null) throw new TypeError(`Name "${name}" is used by both ${existingLabel} and ${label}.`);
92
+ nameToLabel.set(name, label);
93
+ }
94
+ for (let i = 0; i < metaEntries.length; i++) {
95
+ const [, label, names, prefixMatch] = metaEntries[i];
96
+ if (!prefixMatch) continue;
97
+ for (const name of names) {
98
+ const prefix = name + "=";
99
+ for (let j = 0; j < metaEntries.length; j++) {
100
+ const [, otherLabel, otherNames] = metaEntries[j];
101
+ for (const otherName of otherNames) {
102
+ if (i === j && otherName === name) continue;
103
+ if (!otherName.startsWith(prefix)) continue;
104
+ throw new TypeError("The prefix form of name \"" + name + "\" in " + label + " shadows \"" + otherName + "\" in " + otherLabel + ".");
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ function capitalize(s) {
111
+ return s.charAt(0).toUpperCase() + s.slice(1);
112
+ }
113
+ /**
114
+ * Validates a program name at runtime.
115
+ *
116
+ * Program names may contain spaces (e.g., file paths), but must not be empty,
117
+ * whitespace-only, or contain control characters.
118
+ *
119
+ * @param programName The program name to validate.
120
+ * @throws {TypeError} If the value is not a string, is empty,
121
+ * whitespace-only, or contains control characters.
122
+ */
123
+ function validateProgramName(programName) {
124
+ if (typeof programName !== "string") throw new TypeError("Program name must be a string.");
125
+ if (programName === "") throw new TypeError("Program name must not be empty.");
126
+ if (/^\s+$/.test(programName)) throw new TypeError(`Program name must not be whitespace-only: "${escapeControlChars(programName)}".`);
127
+ if (CONTROL_CHAR_RE.test(programName)) throw new TypeError(`Program name must not contain control characters: "${escapeControlChars(programName)}".`);
128
+ }
129
+ /**
130
+ * Validates a label at runtime.
131
+ *
132
+ * Labels are used as section titles in documentation output. They may contain
133
+ * spaces (e.g., "Connection options"), but must not be empty, whitespace-only,
134
+ * or contain control characters.
135
+ *
136
+ * @param label The label to validate.
137
+ * @throws {TypeError} If the label is not a string, is empty,
138
+ * whitespace-only, or contains control characters.
139
+ * @since 1.0.0
140
+ */
141
+ function validateLabel(label) {
142
+ if (typeof label !== "string") throw new TypeError("Label must be a string.");
143
+ if (label === "") throw new TypeError("Label must not be empty.");
144
+ if (/^\s+$/.test(label)) throw new TypeError(`Label must not be whitespace-only: "${escapeControlChars(label)}".`);
145
+ if (CONTROL_CHAR_RE.test(label)) throw new TypeError(`Label must not contain control characters: "${escapeControlChars(label)}".`);
146
+ }
147
+ /**
148
+ * Validates that all source contexts have unique
149
+ * {@link import("./context.ts").SourceContext.id | id} values.
150
+ *
151
+ * @param contexts The source contexts to validate.
152
+ * @throws {TypeError} If two or more contexts share the same id.
153
+ * @since 1.0.0
154
+ */
155
+ function validateContextIds(contexts) {
156
+ const seen = /* @__PURE__ */ new Set();
157
+ for (const context of contexts) {
158
+ if (seen.has(context.id)) throw new TypeError(`Duplicate SourceContext id: ${String(context.id)}`);
159
+ seen.add(context.id);
160
+ }
161
+ }
162
+
163
+ //#endregion
164
+ export { validateCommandNames, validateContextIds, validateLabel, validateMetaNameCollisions, validateOptionNames, validateProgramName };