@optique/config 0.10.0-dev.343 → 0.10.0-dev.345

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.
@@ -1,6 +1,6 @@
1
- import { Parser } from "@optique/core/parser";
2
1
  import { StandardSchemaV1 } from "@standard-schema/spec";
3
2
  import { ParserValuePlaceholder, SourceContext } from "@optique/core/context";
3
+ import { Parser } from "@optique/core/parser";
4
4
 
5
5
  //#region src/index.d.ts
6
6
 
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_src = require('./src-CTH1AjFs.cjs');
1
+ const require_src = require('./src-8nvtohSm.cjs');
2
2
 
3
3
  exports.bindConfig = require_src.bindConfig;
4
4
  exports.clearActiveConfig = require_src.clearActiveConfig;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig } from "./index-D_lTcz4s.js";
1
+ import { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig } from "./index-CYn_0yAG.js";
2
2
  export { BindConfigOptions, ConfigContext, ConfigContextOptions, ConfigContextRequiredOptions, bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig } from "./src-C9h3sfAM.js";
1
+ import { bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig } from "./src-CLJCVbow.js";
2
2
 
3
3
  export { bindConfig, clearActiveConfig, configKey, createConfigContext, getActiveConfig, setActiveConfig };
package/dist/run.cjs CHANGED
@@ -1,9 +1,71 @@
1
- const require_src = require('./src-CTH1AjFs.cjs');
1
+ const require_src = require('./src-8nvtohSm.cjs');
2
2
  const node_fs_promises = require_src.__toESM(require("node:fs/promises"));
3
- const __optique_core_parser = require_src.__toESM(require("@optique/core/parser"));
3
+ const node_path = require_src.__toESM(require("node:path"));
4
+ const node_process = require_src.__toESM(require("node:process"));
5
+ const __optique_core_facade = require_src.__toESM(require("@optique/core/facade"));
4
6
 
5
7
  //#region src/run.ts
6
8
  /**
9
+ * Helper function to create a wrapper SourceContext for config loading.
10
+ * @internal
11
+ */
12
+ function createConfigSourceContext(context, options) {
13
+ return {
14
+ id: context.id,
15
+ async getAnnotations(parsed) {
16
+ if (!parsed) return {};
17
+ let configData;
18
+ if ("load" in options) {
19
+ const customOptions = options;
20
+ try {
21
+ const rawData = await Promise.resolve(customOptions.load(parsed));
22
+ const validation = context.schema["~standard"].validate(rawData);
23
+ let validationResult;
24
+ if (validation instanceof Promise) validationResult = await validation;
25
+ else validationResult = validation;
26
+ if (validationResult.issues) {
27
+ const firstIssue = validationResult.issues[0];
28
+ throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
29
+ }
30
+ configData = validationResult.value;
31
+ } catch (error) {
32
+ if (error instanceof Error && error.message.includes("validation")) throw error;
33
+ throw error;
34
+ }
35
+ } else {
36
+ const singleFileOptions = options;
37
+ const configPath = singleFileOptions.getConfigPath(parsed);
38
+ if (configPath) try {
39
+ const contents = await (0, node_fs_promises.readFile)(configPath);
40
+ let rawData;
41
+ if (singleFileOptions.fileParser) rawData = singleFileOptions.fileParser(contents);
42
+ else {
43
+ const text = new TextDecoder().decode(contents);
44
+ rawData = JSON.parse(text);
45
+ }
46
+ const validation = context.schema["~standard"].validate(rawData);
47
+ let validationResult;
48
+ if (validation instanceof Promise) validationResult = await validation;
49
+ else validationResult = validation;
50
+ if (validationResult.issues) {
51
+ const firstIssue = validationResult.issues[0];
52
+ throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
53
+ }
54
+ configData = validationResult.value;
55
+ } catch (error) {
56
+ if (error instanceof Error && error.message.includes("validation")) throw error;
57
+ configData = void 0;
58
+ }
59
+ }
60
+ if (configData) {
61
+ require_src.setActiveConfig(context.id, configData);
62
+ return { [require_src.configKey]: configData };
63
+ }
64
+ return {};
65
+ }
66
+ };
67
+ }
68
+ /**
7
69
  * Runs a parser with configuration file support using two-pass parsing.
8
70
  *
9
71
  * This function performs the following steps:
@@ -13,10 +75,16 @@ const __optique_core_parser = require_src.__toESM(require("@optique/core/parser"
13
75
  *
14
76
  * The priority order for values is: CLI > config file > default.
15
77
  *
78
+ * The function also supports help, version, and completion features. When these
79
+ * special commands are detected, config loading is skipped entirely, ensuring
80
+ * these features work even when config files don't exist.
81
+ *
16
82
  * @template M The parser mode (sync or async).
17
83
  * @template TValue The parser value type.
18
84
  * @template TState The parser state type.
19
85
  * @template T The config data type.
86
+ * @template THelp The return type when help is shown.
87
+ * @template TError The return type when an error occurs.
20
88
  * @param parser The parser to execute.
21
89
  * @param context The config context with schema.
22
90
  * @param options Run options - either SingleFileOptions or CustomLoadOptions.
@@ -49,6 +117,8 @@ const __optique_core_parser = require_src.__toESM(require("@optique/core/parser"
49
117
  * const result = await runWithConfig(parser, context, {
50
118
  * getConfigPath: (parsed) => parsed.config,
51
119
  * args: process.argv.slice(2),
120
+ * help: { mode: "option", onShow: () => process.exit(0) },
121
+ * version: { value: "1.0.0", onShow: () => process.exit(0) },
52
122
  * });
53
123
  * ```
54
124
  *
@@ -66,82 +136,33 @@ const __optique_core_parser = require_src.__toESM(require("@optique/core/parser"
66
136
  * return deepMerge(...configs);
67
137
  * },
68
138
  * args: process.argv.slice(2),
139
+ * help: { mode: "option", onShow: () => process.exit(0) },
69
140
  * });
70
141
  * ```
71
142
  */
72
143
  async function runWithConfig(parser, context, options) {
73
- const args = options.args ?? [];
74
- const firstPass = (0, __optique_core_parser.parse)(parser, args);
75
- let firstPassResult;
76
- if (firstPass instanceof Promise) firstPassResult = await firstPass;
77
- else firstPassResult = firstPass;
78
- if (!firstPassResult.success) {
79
- const errorParts = firstPassResult.error.map((part) => {
80
- if (part.type === "text") return part.text;
81
- if (part.type === "optionName") return part.optionName;
82
- if (part.type === "optionNames") return part.optionNames.join(", ");
83
- if (part.type === "metavar") return part.metavar;
84
- if (part.type === "value") return part.value;
85
- if (part.type === "values") return part.values.join(", ");
86
- if (part.type === "envVar") return part.envVar;
87
- if (part.type === "commandLine") return part.commandLine;
88
- if (part.type === "url") return part.url;
89
- return "";
90
- });
91
- throw new Error(`Parsing failed: ${errorParts.join("")}`);
92
- }
93
- let configData;
94
- if ("load" in options) {
95
- const customOptions = options;
96
- try {
97
- const rawData = await Promise.resolve(customOptions.load(firstPassResult.value));
98
- const validation = context.schema["~standard"].validate(rawData);
99
- let validationResult;
100
- if (validation instanceof Promise) validationResult = await validation;
101
- else validationResult = validation;
102
- if (validationResult.issues) {
103
- const firstIssue = validationResult.issues[0];
104
- throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
105
- }
106
- configData = validationResult.value;
107
- } catch (error) {
108
- if (error instanceof Error && error.message.includes("validation")) throw error;
109
- throw error;
110
- }
111
- } else {
112
- const singleFileOptions = options;
113
- const configPath = singleFileOptions.getConfigPath(firstPassResult.value);
114
- if (configPath) try {
115
- const contents = await (0, node_fs_promises.readFile)(configPath);
116
- let rawData;
117
- if (singleFileOptions.fileParser) rawData = singleFileOptions.fileParser(contents);
118
- else {
119
- const text = new TextDecoder().decode(contents);
120
- rawData = JSON.parse(text);
121
- }
122
- const validation = context.schema["~standard"].validate(rawData);
123
- let validationResult;
124
- if (validation instanceof Promise) validationResult = await validation;
125
- else validationResult = validation;
126
- if (validationResult.issues) {
127
- const firstIssue = validationResult.issues[0];
128
- throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
129
- }
130
- configData = validationResult.value;
131
- } catch (error) {
132
- if (error instanceof Error && error.message.includes("validation")) throw error;
133
- configData = void 0;
134
- }
135
- }
136
- const annotations = configData ? { [require_src.configKey]: configData } : {};
137
- if (configData) require_src.setActiveConfig(context.id, configData);
144
+ const effectiveProgramName = options.programName ?? (typeof node_process.default !== "undefined" && node_process.default.argv?.[1] ? (0, node_path.basename)(node_process.default.argv[1]) : "cli");
145
+ const wrapperContext = createConfigSourceContext(context, options);
138
146
  try {
139
- const secondPass = (0, __optique_core_parser.parse)(parser, args, { annotations });
140
- let secondPassResult;
141
- if (secondPass instanceof Promise) secondPassResult = await secondPass;
142
- else secondPassResult = secondPass;
143
- if (!secondPassResult.success) throw new Error(`Parsing failed: ${String(secondPassResult.error)}`);
144
- return secondPassResult.value;
147
+ return await (0, __optique_core_facade.runWith)(parser, effectiveProgramName, [wrapperContext], {
148
+ args: options.args ?? [],
149
+ help: options.help,
150
+ version: options.version,
151
+ completion: options.completion,
152
+ stdout: options.stdout,
153
+ stderr: options.stderr,
154
+ colors: options.colors,
155
+ maxWidth: options.maxWidth,
156
+ showDefault: options.showDefault,
157
+ aboveError: options.aboveError,
158
+ brief: options.brief,
159
+ description: options.description,
160
+ examples: options.examples,
161
+ author: options.author,
162
+ bugs: options.bugs,
163
+ footer: options.footer,
164
+ onError: options.onError
165
+ });
145
166
  } finally {
146
167
  require_src.clearActiveConfig(context.id);
147
168
  }
package/dist/run.d.cts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ConfigContext } from "./index-D25zYfjf.cjs";
2
2
  import { Parser } from "@optique/core/parser";
3
+ import { RunOptions } from "@optique/core/facade";
3
4
 
4
5
  //#region src/run.d.ts
5
6
 
@@ -7,9 +8,11 @@ import { Parser } from "@optique/core/parser";
7
8
  * Options for single-file config loading.
8
9
  *
9
10
  * @template TValue The parser value type, inferred from the parser.
11
+ * @template THelp The return type when help is shown.
12
+ * @template TError The return type when an error occurs.
10
13
  * @since 0.10.0
11
14
  */
12
- interface SingleFileOptions<TValue> {
15
+ interface SingleFileOptions<TValue, THelp = void, TError = never> {
13
16
  /**
14
17
  * Function to extract the config file path from parsed CLI arguments.
15
18
  * This function receives the result of the first parse pass and should
@@ -32,14 +35,85 @@ interface SingleFileOptions<TValue> {
32
35
  * If not provided, defaults to an empty array.
33
36
  */
34
37
  readonly args?: readonly string[];
38
+ /**
39
+ * Name of the program for help/error output.
40
+ * If not provided, inferred from process.argv[1].
41
+ */
42
+ readonly programName?: string;
43
+ /**
44
+ * Help configuration. See RunOptions for details.
45
+ */
46
+ readonly help?: RunOptions<THelp, TError>["help"];
47
+ /**
48
+ * Version configuration. See RunOptions for details.
49
+ */
50
+ readonly version?: RunOptions<THelp, TError>["version"];
51
+ /**
52
+ * Completion configuration. See RunOptions for details.
53
+ */
54
+ readonly completion?: RunOptions<THelp, TError>["completion"];
55
+ /**
56
+ * Output function for standard output. See RunOptions for details.
57
+ */
58
+ readonly stdout?: RunOptions<THelp, TError>["stdout"];
59
+ /**
60
+ * Output function for standard error. See RunOptions for details.
61
+ */
62
+ readonly stderr?: RunOptions<THelp, TError>["stderr"];
63
+ /**
64
+ * Whether to enable colored output. See RunOptions for details.
65
+ */
66
+ readonly colors?: RunOptions<THelp, TError>["colors"];
67
+ /**
68
+ * Maximum width for output formatting. See RunOptions for details.
69
+ */
70
+ readonly maxWidth?: RunOptions<THelp, TError>["maxWidth"];
71
+ /**
72
+ * Whether and how to display default values. See RunOptions for details.
73
+ */
74
+ readonly showDefault?: RunOptions<THelp, TError>["showDefault"];
75
+ /**
76
+ * What to display above error messages. See RunOptions for details.
77
+ */
78
+ readonly aboveError?: RunOptions<THelp, TError>["aboveError"];
79
+ /**
80
+ * Brief description for help text. See RunOptions for details.
81
+ */
82
+ readonly brief?: RunOptions<THelp, TError>["brief"];
83
+ /**
84
+ * Detailed description for help text. See RunOptions for details.
85
+ */
86
+ readonly description?: RunOptions<THelp, TError>["description"];
87
+ /**
88
+ * Usage examples for help text. See RunOptions for details.
89
+ */
90
+ readonly examples?: RunOptions<THelp, TError>["examples"];
91
+ /**
92
+ * Author information for help text. See RunOptions for details.
93
+ */
94
+ readonly author?: RunOptions<THelp, TError>["author"];
95
+ /**
96
+ * Bug reporting information for help text. See RunOptions for details.
97
+ */
98
+ readonly bugs?: RunOptions<THelp, TError>["bugs"];
99
+ /**
100
+ * Footer text for help. See RunOptions for details.
101
+ */
102
+ readonly footer?: RunOptions<THelp, TError>["footer"];
103
+ /**
104
+ * Error handler. See RunOptions for details.
105
+ */
106
+ readonly onError?: RunOptions<THelp, TError>["onError"];
35
107
  }
36
108
  /**
37
109
  * Options for custom config loading with multi-file merging support.
38
110
  *
39
111
  * @template TValue The parser value type, inferred from the parser.
112
+ * @template THelp The return type when help is shown.
113
+ * @template TError The return type when an error occurs.
40
114
  * @since 0.10.0
41
115
  */
42
- interface CustomLoadOptions<TValue> {
116
+ interface CustomLoadOptions<TValue, THelp = void, TError = never> {
43
117
  /**
44
118
  * Custom loader function that receives the first-pass parse result and
45
119
  * returns the config data (or a Promise of it). This allows full control
@@ -56,14 +130,85 @@ interface CustomLoadOptions<TValue> {
56
130
  * If not provided, defaults to an empty array.
57
131
  */
58
132
  readonly args?: readonly string[];
133
+ /**
134
+ * Name of the program for help/error output.
135
+ * If not provided, inferred from process.argv[1].
136
+ */
137
+ readonly programName?: string;
138
+ /**
139
+ * Help configuration. See RunOptions for details.
140
+ */
141
+ readonly help?: RunOptions<THelp, TError>["help"];
142
+ /**
143
+ * Version configuration. See RunOptions for details.
144
+ */
145
+ readonly version?: RunOptions<THelp, TError>["version"];
146
+ /**
147
+ * Completion configuration. See RunOptions for details.
148
+ */
149
+ readonly completion?: RunOptions<THelp, TError>["completion"];
150
+ /**
151
+ * Output function for standard output. See RunOptions for details.
152
+ */
153
+ readonly stdout?: RunOptions<THelp, TError>["stdout"];
154
+ /**
155
+ * Output function for standard error. See RunOptions for details.
156
+ */
157
+ readonly stderr?: RunOptions<THelp, TError>["stderr"];
158
+ /**
159
+ * Whether to enable colored output. See RunOptions for details.
160
+ */
161
+ readonly colors?: RunOptions<THelp, TError>["colors"];
162
+ /**
163
+ * Maximum width for output formatting. See RunOptions for details.
164
+ */
165
+ readonly maxWidth?: RunOptions<THelp, TError>["maxWidth"];
166
+ /**
167
+ * Whether and how to display default values. See RunOptions for details.
168
+ */
169
+ readonly showDefault?: RunOptions<THelp, TError>["showDefault"];
170
+ /**
171
+ * What to display above error messages. See RunOptions for details.
172
+ */
173
+ readonly aboveError?: RunOptions<THelp, TError>["aboveError"];
174
+ /**
175
+ * Brief description for help text. See RunOptions for details.
176
+ */
177
+ readonly brief?: RunOptions<THelp, TError>["brief"];
178
+ /**
179
+ * Detailed description for help text. See RunOptions for details.
180
+ */
181
+ readonly description?: RunOptions<THelp, TError>["description"];
182
+ /**
183
+ * Usage examples for help text. See RunOptions for details.
184
+ */
185
+ readonly examples?: RunOptions<THelp, TError>["examples"];
186
+ /**
187
+ * Author information for help text. See RunOptions for details.
188
+ */
189
+ readonly author?: RunOptions<THelp, TError>["author"];
190
+ /**
191
+ * Bug reporting information for help text. See RunOptions for details.
192
+ */
193
+ readonly bugs?: RunOptions<THelp, TError>["bugs"];
194
+ /**
195
+ * Footer text for help. See RunOptions for details.
196
+ */
197
+ readonly footer?: RunOptions<THelp, TError>["footer"];
198
+ /**
199
+ * Error handler. See RunOptions for details.
200
+ */
201
+ readonly onError?: RunOptions<THelp, TError>["onError"];
59
202
  }
60
203
  /**
61
204
  * Options for runWithConfig.
62
205
  *
63
206
  * @template TValue The parser value type, inferred from the parser.
207
+ * @template THelp The return type when help is shown.
208
+ * @template TError The return type when an error occurs.
64
209
  * @since 0.10.0
65
210
  */
66
- type RunWithConfigOptions<TValue> = SingleFileOptions<TValue> | CustomLoadOptions<TValue>;
211
+ type RunWithConfigOptions<TValue, THelp = void, TError = never> = SingleFileOptions<TValue, THelp, TError> | CustomLoadOptions<TValue, THelp, TError>;
67
212
  /**
68
213
  * Runs a parser with configuration file support using two-pass parsing.
69
214
  *
@@ -74,10 +219,16 @@ type RunWithConfigOptions<TValue> = SingleFileOptions<TValue> | CustomLoadOption
74
219
  *
75
220
  * The priority order for values is: CLI > config file > default.
76
221
  *
222
+ * The function also supports help, version, and completion features. When these
223
+ * special commands are detected, config loading is skipped entirely, ensuring
224
+ * these features work even when config files don't exist.
225
+ *
77
226
  * @template M The parser mode (sync or async).
78
227
  * @template TValue The parser value type.
79
228
  * @template TState The parser state type.
80
229
  * @template T The config data type.
230
+ * @template THelp The return type when help is shown.
231
+ * @template TError The return type when an error occurs.
81
232
  * @param parser The parser to execute.
82
233
  * @param context The config context with schema.
83
234
  * @param options Run options - either SingleFileOptions or CustomLoadOptions.
@@ -110,6 +261,8 @@ type RunWithConfigOptions<TValue> = SingleFileOptions<TValue> | CustomLoadOption
110
261
  * const result = await runWithConfig(parser, context, {
111
262
  * getConfigPath: (parsed) => parsed.config,
112
263
  * args: process.argv.slice(2),
264
+ * help: { mode: "option", onShow: () => process.exit(0) },
265
+ * version: { value: "1.0.0", onShow: () => process.exit(0) },
113
266
  * });
114
267
  * ```
115
268
  *
@@ -127,9 +280,10 @@ type RunWithConfigOptions<TValue> = SingleFileOptions<TValue> | CustomLoadOption
127
280
  * return deepMerge(...configs);
128
281
  * },
129
282
  * args: process.argv.slice(2),
283
+ * help: { mode: "option", onShow: () => process.exit(0) },
130
284
  * });
131
285
  * ```
132
286
  */
133
- declare function runWithConfig<M extends "sync" | "async", TValue, TState, T>(parser: Parser<M, TValue, TState>, context: ConfigContext<T>, options: RunWithConfigOptions<TValue>): Promise<TValue>;
287
+ declare function runWithConfig<M extends "sync" | "async", TValue, TState, T, THelp = void, TError = never>(parser: Parser<M, TValue, TState>, context: ConfigContext<T>, options: RunWithConfigOptions<TValue, THelp, TError>): Promise<TValue>;
134
288
  //#endregion
135
289
  export { CustomLoadOptions, RunWithConfigOptions, SingleFileOptions, runWithConfig };
package/dist/run.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { ConfigContext } from "./index-D_lTcz4s.js";
1
+ import { ConfigContext } from "./index-CYn_0yAG.js";
2
+ import { RunOptions } from "@optique/core/facade";
2
3
  import { Parser } from "@optique/core/parser";
3
4
 
4
5
  //#region src/run.d.ts
@@ -7,9 +8,11 @@ import { Parser } from "@optique/core/parser";
7
8
  * Options for single-file config loading.
8
9
  *
9
10
  * @template TValue The parser value type, inferred from the parser.
11
+ * @template THelp The return type when help is shown.
12
+ * @template TError The return type when an error occurs.
10
13
  * @since 0.10.0
11
14
  */
12
- interface SingleFileOptions<TValue> {
15
+ interface SingleFileOptions<TValue, THelp = void, TError = never> {
13
16
  /**
14
17
  * Function to extract the config file path from parsed CLI arguments.
15
18
  * This function receives the result of the first parse pass and should
@@ -32,14 +35,85 @@ interface SingleFileOptions<TValue> {
32
35
  * If not provided, defaults to an empty array.
33
36
  */
34
37
  readonly args?: readonly string[];
38
+ /**
39
+ * Name of the program for help/error output.
40
+ * If not provided, inferred from process.argv[1].
41
+ */
42
+ readonly programName?: string;
43
+ /**
44
+ * Help configuration. See RunOptions for details.
45
+ */
46
+ readonly help?: RunOptions<THelp, TError>["help"];
47
+ /**
48
+ * Version configuration. See RunOptions for details.
49
+ */
50
+ readonly version?: RunOptions<THelp, TError>["version"];
51
+ /**
52
+ * Completion configuration. See RunOptions for details.
53
+ */
54
+ readonly completion?: RunOptions<THelp, TError>["completion"];
55
+ /**
56
+ * Output function for standard output. See RunOptions for details.
57
+ */
58
+ readonly stdout?: RunOptions<THelp, TError>["stdout"];
59
+ /**
60
+ * Output function for standard error. See RunOptions for details.
61
+ */
62
+ readonly stderr?: RunOptions<THelp, TError>["stderr"];
63
+ /**
64
+ * Whether to enable colored output. See RunOptions for details.
65
+ */
66
+ readonly colors?: RunOptions<THelp, TError>["colors"];
67
+ /**
68
+ * Maximum width for output formatting. See RunOptions for details.
69
+ */
70
+ readonly maxWidth?: RunOptions<THelp, TError>["maxWidth"];
71
+ /**
72
+ * Whether and how to display default values. See RunOptions for details.
73
+ */
74
+ readonly showDefault?: RunOptions<THelp, TError>["showDefault"];
75
+ /**
76
+ * What to display above error messages. See RunOptions for details.
77
+ */
78
+ readonly aboveError?: RunOptions<THelp, TError>["aboveError"];
79
+ /**
80
+ * Brief description for help text. See RunOptions for details.
81
+ */
82
+ readonly brief?: RunOptions<THelp, TError>["brief"];
83
+ /**
84
+ * Detailed description for help text. See RunOptions for details.
85
+ */
86
+ readonly description?: RunOptions<THelp, TError>["description"];
87
+ /**
88
+ * Usage examples for help text. See RunOptions for details.
89
+ */
90
+ readonly examples?: RunOptions<THelp, TError>["examples"];
91
+ /**
92
+ * Author information for help text. See RunOptions for details.
93
+ */
94
+ readonly author?: RunOptions<THelp, TError>["author"];
95
+ /**
96
+ * Bug reporting information for help text. See RunOptions for details.
97
+ */
98
+ readonly bugs?: RunOptions<THelp, TError>["bugs"];
99
+ /**
100
+ * Footer text for help. See RunOptions for details.
101
+ */
102
+ readonly footer?: RunOptions<THelp, TError>["footer"];
103
+ /**
104
+ * Error handler. See RunOptions for details.
105
+ */
106
+ readonly onError?: RunOptions<THelp, TError>["onError"];
35
107
  }
36
108
  /**
37
109
  * Options for custom config loading with multi-file merging support.
38
110
  *
39
111
  * @template TValue The parser value type, inferred from the parser.
112
+ * @template THelp The return type when help is shown.
113
+ * @template TError The return type when an error occurs.
40
114
  * @since 0.10.0
41
115
  */
42
- interface CustomLoadOptions<TValue> {
116
+ interface CustomLoadOptions<TValue, THelp = void, TError = never> {
43
117
  /**
44
118
  * Custom loader function that receives the first-pass parse result and
45
119
  * returns the config data (or a Promise of it). This allows full control
@@ -56,14 +130,85 @@ interface CustomLoadOptions<TValue> {
56
130
  * If not provided, defaults to an empty array.
57
131
  */
58
132
  readonly args?: readonly string[];
133
+ /**
134
+ * Name of the program for help/error output.
135
+ * If not provided, inferred from process.argv[1].
136
+ */
137
+ readonly programName?: string;
138
+ /**
139
+ * Help configuration. See RunOptions for details.
140
+ */
141
+ readonly help?: RunOptions<THelp, TError>["help"];
142
+ /**
143
+ * Version configuration. See RunOptions for details.
144
+ */
145
+ readonly version?: RunOptions<THelp, TError>["version"];
146
+ /**
147
+ * Completion configuration. See RunOptions for details.
148
+ */
149
+ readonly completion?: RunOptions<THelp, TError>["completion"];
150
+ /**
151
+ * Output function for standard output. See RunOptions for details.
152
+ */
153
+ readonly stdout?: RunOptions<THelp, TError>["stdout"];
154
+ /**
155
+ * Output function for standard error. See RunOptions for details.
156
+ */
157
+ readonly stderr?: RunOptions<THelp, TError>["stderr"];
158
+ /**
159
+ * Whether to enable colored output. See RunOptions for details.
160
+ */
161
+ readonly colors?: RunOptions<THelp, TError>["colors"];
162
+ /**
163
+ * Maximum width for output formatting. See RunOptions for details.
164
+ */
165
+ readonly maxWidth?: RunOptions<THelp, TError>["maxWidth"];
166
+ /**
167
+ * Whether and how to display default values. See RunOptions for details.
168
+ */
169
+ readonly showDefault?: RunOptions<THelp, TError>["showDefault"];
170
+ /**
171
+ * What to display above error messages. See RunOptions for details.
172
+ */
173
+ readonly aboveError?: RunOptions<THelp, TError>["aboveError"];
174
+ /**
175
+ * Brief description for help text. See RunOptions for details.
176
+ */
177
+ readonly brief?: RunOptions<THelp, TError>["brief"];
178
+ /**
179
+ * Detailed description for help text. See RunOptions for details.
180
+ */
181
+ readonly description?: RunOptions<THelp, TError>["description"];
182
+ /**
183
+ * Usage examples for help text. See RunOptions for details.
184
+ */
185
+ readonly examples?: RunOptions<THelp, TError>["examples"];
186
+ /**
187
+ * Author information for help text. See RunOptions for details.
188
+ */
189
+ readonly author?: RunOptions<THelp, TError>["author"];
190
+ /**
191
+ * Bug reporting information for help text. See RunOptions for details.
192
+ */
193
+ readonly bugs?: RunOptions<THelp, TError>["bugs"];
194
+ /**
195
+ * Footer text for help. See RunOptions for details.
196
+ */
197
+ readonly footer?: RunOptions<THelp, TError>["footer"];
198
+ /**
199
+ * Error handler. See RunOptions for details.
200
+ */
201
+ readonly onError?: RunOptions<THelp, TError>["onError"];
59
202
  }
60
203
  /**
61
204
  * Options for runWithConfig.
62
205
  *
63
206
  * @template TValue The parser value type, inferred from the parser.
207
+ * @template THelp The return type when help is shown.
208
+ * @template TError The return type when an error occurs.
64
209
  * @since 0.10.0
65
210
  */
66
- type RunWithConfigOptions<TValue> = SingleFileOptions<TValue> | CustomLoadOptions<TValue>;
211
+ type RunWithConfigOptions<TValue, THelp = void, TError = never> = SingleFileOptions<TValue, THelp, TError> | CustomLoadOptions<TValue, THelp, TError>;
67
212
  /**
68
213
  * Runs a parser with configuration file support using two-pass parsing.
69
214
  *
@@ -74,10 +219,16 @@ type RunWithConfigOptions<TValue> = SingleFileOptions<TValue> | CustomLoadOption
74
219
  *
75
220
  * The priority order for values is: CLI > config file > default.
76
221
  *
222
+ * The function also supports help, version, and completion features. When these
223
+ * special commands are detected, config loading is skipped entirely, ensuring
224
+ * these features work even when config files don't exist.
225
+ *
77
226
  * @template M The parser mode (sync or async).
78
227
  * @template TValue The parser value type.
79
228
  * @template TState The parser state type.
80
229
  * @template T The config data type.
230
+ * @template THelp The return type when help is shown.
231
+ * @template TError The return type when an error occurs.
81
232
  * @param parser The parser to execute.
82
233
  * @param context The config context with schema.
83
234
  * @param options Run options - either SingleFileOptions or CustomLoadOptions.
@@ -110,6 +261,8 @@ type RunWithConfigOptions<TValue> = SingleFileOptions<TValue> | CustomLoadOption
110
261
  * const result = await runWithConfig(parser, context, {
111
262
  * getConfigPath: (parsed) => parsed.config,
112
263
  * args: process.argv.slice(2),
264
+ * help: { mode: "option", onShow: () => process.exit(0) },
265
+ * version: { value: "1.0.0", onShow: () => process.exit(0) },
113
266
  * });
114
267
  * ```
115
268
  *
@@ -127,9 +280,10 @@ type RunWithConfigOptions<TValue> = SingleFileOptions<TValue> | CustomLoadOption
127
280
  * return deepMerge(...configs);
128
281
  * },
129
282
  * args: process.argv.slice(2),
283
+ * help: { mode: "option", onShow: () => process.exit(0) },
130
284
  * });
131
285
  * ```
132
286
  */
133
- declare function runWithConfig<M extends "sync" | "async", TValue, TState, T>(parser: Parser<M, TValue, TState>, context: ConfigContext<T>, options: RunWithConfigOptions<TValue>): Promise<TValue>;
287
+ declare function runWithConfig<M extends "sync" | "async", TValue, TState, T, THelp = void, TError = never>(parser: Parser<M, TValue, TState>, context: ConfigContext<T>, options: RunWithConfigOptions<TValue, THelp, TError>): Promise<TValue>;
134
288
  //#endregion
135
289
  export { CustomLoadOptions, RunWithConfigOptions, SingleFileOptions, runWithConfig };
package/dist/run.js CHANGED
@@ -1,9 +1,71 @@
1
- import { clearActiveConfig, configKey, setActiveConfig } from "./src-C9h3sfAM.js";
1
+ import { clearActiveConfig, configKey, setActiveConfig } from "./src-CLJCVbow.js";
2
2
  import { readFile } from "node:fs/promises";
3
- import { parse } from "@optique/core/parser";
3
+ import { basename } from "node:path";
4
+ import process from "node:process";
5
+ import { runWith } from "@optique/core/facade";
4
6
 
5
7
  //#region src/run.ts
6
8
  /**
9
+ * Helper function to create a wrapper SourceContext for config loading.
10
+ * @internal
11
+ */
12
+ function createConfigSourceContext(context, options) {
13
+ return {
14
+ id: context.id,
15
+ async getAnnotations(parsed) {
16
+ if (!parsed) return {};
17
+ let configData;
18
+ if ("load" in options) {
19
+ const customOptions = options;
20
+ try {
21
+ const rawData = await Promise.resolve(customOptions.load(parsed));
22
+ const validation = context.schema["~standard"].validate(rawData);
23
+ let validationResult;
24
+ if (validation instanceof Promise) validationResult = await validation;
25
+ else validationResult = validation;
26
+ if (validationResult.issues) {
27
+ const firstIssue = validationResult.issues[0];
28
+ throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
29
+ }
30
+ configData = validationResult.value;
31
+ } catch (error) {
32
+ if (error instanceof Error && error.message.includes("validation")) throw error;
33
+ throw error;
34
+ }
35
+ } else {
36
+ const singleFileOptions = options;
37
+ const configPath = singleFileOptions.getConfigPath(parsed);
38
+ if (configPath) try {
39
+ const contents = await readFile(configPath);
40
+ let rawData;
41
+ if (singleFileOptions.fileParser) rawData = singleFileOptions.fileParser(contents);
42
+ else {
43
+ const text = new TextDecoder().decode(contents);
44
+ rawData = JSON.parse(text);
45
+ }
46
+ const validation = context.schema["~standard"].validate(rawData);
47
+ let validationResult;
48
+ if (validation instanceof Promise) validationResult = await validation;
49
+ else validationResult = validation;
50
+ if (validationResult.issues) {
51
+ const firstIssue = validationResult.issues[0];
52
+ throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
53
+ }
54
+ configData = validationResult.value;
55
+ } catch (error) {
56
+ if (error instanceof Error && error.message.includes("validation")) throw error;
57
+ configData = void 0;
58
+ }
59
+ }
60
+ if (configData) {
61
+ setActiveConfig(context.id, configData);
62
+ return { [configKey]: configData };
63
+ }
64
+ return {};
65
+ }
66
+ };
67
+ }
68
+ /**
7
69
  * Runs a parser with configuration file support using two-pass parsing.
8
70
  *
9
71
  * This function performs the following steps:
@@ -13,10 +75,16 @@ import { parse } from "@optique/core/parser";
13
75
  *
14
76
  * The priority order for values is: CLI > config file > default.
15
77
  *
78
+ * The function also supports help, version, and completion features. When these
79
+ * special commands are detected, config loading is skipped entirely, ensuring
80
+ * these features work even when config files don't exist.
81
+ *
16
82
  * @template M The parser mode (sync or async).
17
83
  * @template TValue The parser value type.
18
84
  * @template TState The parser state type.
19
85
  * @template T The config data type.
86
+ * @template THelp The return type when help is shown.
87
+ * @template TError The return type when an error occurs.
20
88
  * @param parser The parser to execute.
21
89
  * @param context The config context with schema.
22
90
  * @param options Run options - either SingleFileOptions or CustomLoadOptions.
@@ -49,6 +117,8 @@ import { parse } from "@optique/core/parser";
49
117
  * const result = await runWithConfig(parser, context, {
50
118
  * getConfigPath: (parsed) => parsed.config,
51
119
  * args: process.argv.slice(2),
120
+ * help: { mode: "option", onShow: () => process.exit(0) },
121
+ * version: { value: "1.0.0", onShow: () => process.exit(0) },
52
122
  * });
53
123
  * ```
54
124
  *
@@ -66,82 +136,33 @@ import { parse } from "@optique/core/parser";
66
136
  * return deepMerge(...configs);
67
137
  * },
68
138
  * args: process.argv.slice(2),
139
+ * help: { mode: "option", onShow: () => process.exit(0) },
69
140
  * });
70
141
  * ```
71
142
  */
72
143
  async function runWithConfig(parser, context, options) {
73
- const args = options.args ?? [];
74
- const firstPass = parse(parser, args);
75
- let firstPassResult;
76
- if (firstPass instanceof Promise) firstPassResult = await firstPass;
77
- else firstPassResult = firstPass;
78
- if (!firstPassResult.success) {
79
- const errorParts = firstPassResult.error.map((part) => {
80
- if (part.type === "text") return part.text;
81
- if (part.type === "optionName") return part.optionName;
82
- if (part.type === "optionNames") return part.optionNames.join(", ");
83
- if (part.type === "metavar") return part.metavar;
84
- if (part.type === "value") return part.value;
85
- if (part.type === "values") return part.values.join(", ");
86
- if (part.type === "envVar") return part.envVar;
87
- if (part.type === "commandLine") return part.commandLine;
88
- if (part.type === "url") return part.url;
89
- return "";
90
- });
91
- throw new Error(`Parsing failed: ${errorParts.join("")}`);
92
- }
93
- let configData;
94
- if ("load" in options) {
95
- const customOptions = options;
96
- try {
97
- const rawData = await Promise.resolve(customOptions.load(firstPassResult.value));
98
- const validation = context.schema["~standard"].validate(rawData);
99
- let validationResult;
100
- if (validation instanceof Promise) validationResult = await validation;
101
- else validationResult = validation;
102
- if (validationResult.issues) {
103
- const firstIssue = validationResult.issues[0];
104
- throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
105
- }
106
- configData = validationResult.value;
107
- } catch (error) {
108
- if (error instanceof Error && error.message.includes("validation")) throw error;
109
- throw error;
110
- }
111
- } else {
112
- const singleFileOptions = options;
113
- const configPath = singleFileOptions.getConfigPath(firstPassResult.value);
114
- if (configPath) try {
115
- const contents = await readFile(configPath);
116
- let rawData;
117
- if (singleFileOptions.fileParser) rawData = singleFileOptions.fileParser(contents);
118
- else {
119
- const text = new TextDecoder().decode(contents);
120
- rawData = JSON.parse(text);
121
- }
122
- const validation = context.schema["~standard"].validate(rawData);
123
- let validationResult;
124
- if (validation instanceof Promise) validationResult = await validation;
125
- else validationResult = validation;
126
- if (validationResult.issues) {
127
- const firstIssue = validationResult.issues[0];
128
- throw new Error(`Config validation failed: ${firstIssue?.message ?? "Unknown error"}`);
129
- }
130
- configData = validationResult.value;
131
- } catch (error) {
132
- if (error instanceof Error && error.message.includes("validation")) throw error;
133
- configData = void 0;
134
- }
135
- }
136
- const annotations = configData ? { [configKey]: configData } : {};
137
- if (configData) setActiveConfig(context.id, configData);
144
+ const effectiveProgramName = options.programName ?? (typeof process !== "undefined" && process.argv?.[1] ? basename(process.argv[1]) : "cli");
145
+ const wrapperContext = createConfigSourceContext(context, options);
138
146
  try {
139
- const secondPass = parse(parser, args, { annotations });
140
- let secondPassResult;
141
- if (secondPass instanceof Promise) secondPassResult = await secondPass;
142
- else secondPassResult = secondPass;
143
- if (!secondPassResult.success) throw new Error(`Parsing failed: ${String(secondPassResult.error)}`);
144
- return secondPassResult.value;
147
+ return await runWith(parser, effectiveProgramName, [wrapperContext], {
148
+ args: options.args ?? [],
149
+ help: options.help,
150
+ version: options.version,
151
+ completion: options.completion,
152
+ stdout: options.stdout,
153
+ stderr: options.stderr,
154
+ colors: options.colors,
155
+ maxWidth: options.maxWidth,
156
+ showDefault: options.showDefault,
157
+ aboveError: options.aboveError,
158
+ brief: options.brief,
159
+ description: options.description,
160
+ examples: options.examples,
161
+ author: options.author,
162
+ bugs: options.bugs,
163
+ footer: options.footer,
164
+ onError: options.onError
165
+ });
145
166
  } finally {
146
167
  clearActiveConfig(context.id);
147
168
  }
@@ -83,8 +83,9 @@ function clearActiveConfig(contextId) {
83
83
  * ```
84
84
  */
85
85
  function createConfigContext(options) {
86
+ const contextId = Symbol.for(`@optique/config:${Math.random()}`);
86
87
  return {
87
- id: configKey,
88
+ id: contextId,
88
89
  schema: options.schema,
89
90
  getAnnotations(parsed) {
90
91
  if (!parsed) return {};
@@ -60,8 +60,9 @@ function clearActiveConfig(contextId) {
60
60
  * ```
61
61
  */
62
62
  function createConfigContext(options) {
63
+ const contextId = Symbol.for(`@optique/config:${Math.random()}`);
63
64
  return {
64
- id: configKey,
65
+ id: contextId,
65
66
  schema: options.schema,
66
67
  getAnnotations(parsed) {
67
68
  if (!parsed) return {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/config",
3
- "version": "0.10.0-dev.343+2ef903e6",
3
+ "version": "0.10.0-dev.345+f0eac28c",
4
4
  "description": "Configuration file support for Optique with Standard Schema validation",
5
5
  "keywords": [
6
6
  "CLI",