@optique/core 0.10.0-dev.324 → 0.10.0-dev.328

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/doc.cjs CHANGED
@@ -46,7 +46,8 @@ function formatDocPage(programName, page, options = {}) {
46
46
  output += "\n";
47
47
  }
48
48
  if (page.usage != null) {
49
- output += "Usage: ";
49
+ const usageLabel = options.colors ? "\x1B[1;2mUsage:\x1B[0m " : "Usage: ";
50
+ output += usageLabel;
50
51
  output += indentLines(require_usage.formatUsage(programName, page.usage, {
51
52
  colors: options.colors,
52
53
  maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - 7,
@@ -67,7 +68,10 @@ function formatDocPage(programName, page, options = {}) {
67
68
  for (const section of sections) {
68
69
  if (section.entries.length < 1) continue;
69
70
  output += "\n";
70
- if (section.title != null) output += `${section.title}:\n`;
71
+ if (section.title != null) {
72
+ const sectionLabel = options.colors ? `\x1b[1;2m${section.title}:\x1b[0m\n` : `${section.title}:\n`;
73
+ output += sectionLabel;
74
+ }
71
75
  for (const entry of section.entries) {
72
76
  const term = require_usage.formatUsageTerm(entry.term, {
73
77
  colors: options.colors,
@@ -92,6 +96,42 @@ function formatDocPage(programName, page, options = {}) {
92
96
  output += `${" ".repeat(termIndent)}${ansiAwareRightPad(term, termWidth)} ${description === "" ? "" : indentLines(description, termIndent + termWidth + 2)}\n`;
93
97
  }
94
98
  }
99
+ if (page.examples != null) {
100
+ output += "\n";
101
+ const examplesLabel = options.colors ? "\x1B[1;2mExamples:\x1B[0m\n" : "Examples:\n";
102
+ output += examplesLabel;
103
+ const examplesContent = require_message.formatMessage(page.examples, {
104
+ colors: options.colors,
105
+ maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - 2,
106
+ quotes: !options.colors
107
+ });
108
+ output += " " + indentLines(examplesContent, 2);
109
+ output += "\n";
110
+ }
111
+ if (page.author != null) {
112
+ output += "\n";
113
+ const authorLabel = options.colors ? "\x1B[1;2mAuthor:\x1B[0m\n" : "Author:\n";
114
+ output += authorLabel;
115
+ const authorContent = require_message.formatMessage(page.author, {
116
+ colors: options.colors,
117
+ maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - 2,
118
+ quotes: !options.colors
119
+ });
120
+ output += " " + indentLines(authorContent, 2);
121
+ output += "\n";
122
+ }
123
+ if (page.bugs != null) {
124
+ output += "\n";
125
+ const bugsLabel = options.colors ? "\x1B[1;2mBugs:\x1B[0m\n" : "Bugs:\n";
126
+ output += bugsLabel;
127
+ const bugsContent = require_message.formatMessage(page.bugs, {
128
+ colors: options.colors,
129
+ maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - 2,
130
+ quotes: !options.colors
131
+ });
132
+ output += " " + indentLines(bugsContent, 2);
133
+ output += "\n";
134
+ }
95
135
  if (page.footer != null) {
96
136
  output += "\n";
97
137
  output += require_message.formatMessage(page.footer, {
package/dist/doc.d.cts CHANGED
@@ -43,6 +43,21 @@ interface DocPage {
43
43
  readonly usage?: Usage;
44
44
  readonly description?: Message;
45
45
  readonly sections: readonly DocSection[];
46
+ /**
47
+ * Usage examples for the program.
48
+ * @since 0.10.0
49
+ */
50
+ readonly examples?: Message;
51
+ /**
52
+ * Author information.
53
+ * @since 0.10.0
54
+ */
55
+ readonly author?: Message;
56
+ /**
57
+ * Information about where to report bugs.
58
+ * @since 0.10.0
59
+ */
60
+ readonly bugs?: Message;
46
61
  readonly footer?: Message;
47
62
  }
48
63
  /**
package/dist/doc.d.ts CHANGED
@@ -43,6 +43,21 @@ interface DocPage {
43
43
  readonly usage?: Usage;
44
44
  readonly description?: Message;
45
45
  readonly sections: readonly DocSection[];
46
+ /**
47
+ * Usage examples for the program.
48
+ * @since 0.10.0
49
+ */
50
+ readonly examples?: Message;
51
+ /**
52
+ * Author information.
53
+ * @since 0.10.0
54
+ */
55
+ readonly author?: Message;
56
+ /**
57
+ * Information about where to report bugs.
58
+ * @since 0.10.0
59
+ */
60
+ readonly bugs?: Message;
46
61
  readonly footer?: Message;
47
62
  }
48
63
  /**
package/dist/doc.js CHANGED
@@ -46,7 +46,8 @@ function formatDocPage(programName, page, options = {}) {
46
46
  output += "\n";
47
47
  }
48
48
  if (page.usage != null) {
49
- output += "Usage: ";
49
+ const usageLabel = options.colors ? "\x1B[1;2mUsage:\x1B[0m " : "Usage: ";
50
+ output += usageLabel;
50
51
  output += indentLines(formatUsage(programName, page.usage, {
51
52
  colors: options.colors,
52
53
  maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - 7,
@@ -67,7 +68,10 @@ function formatDocPage(programName, page, options = {}) {
67
68
  for (const section of sections) {
68
69
  if (section.entries.length < 1) continue;
69
70
  output += "\n";
70
- if (section.title != null) output += `${section.title}:\n`;
71
+ if (section.title != null) {
72
+ const sectionLabel = options.colors ? `\x1b[1;2m${section.title}:\x1b[0m\n` : `${section.title}:\n`;
73
+ output += sectionLabel;
74
+ }
71
75
  for (const entry of section.entries) {
72
76
  const term = formatUsageTerm(entry.term, {
73
77
  colors: options.colors,
@@ -92,6 +96,42 @@ function formatDocPage(programName, page, options = {}) {
92
96
  output += `${" ".repeat(termIndent)}${ansiAwareRightPad(term, termWidth)} ${description === "" ? "" : indentLines(description, termIndent + termWidth + 2)}\n`;
93
97
  }
94
98
  }
99
+ if (page.examples != null) {
100
+ output += "\n";
101
+ const examplesLabel = options.colors ? "\x1B[1;2mExamples:\x1B[0m\n" : "Examples:\n";
102
+ output += examplesLabel;
103
+ const examplesContent = formatMessage(page.examples, {
104
+ colors: options.colors,
105
+ maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - 2,
106
+ quotes: !options.colors
107
+ });
108
+ output += " " + indentLines(examplesContent, 2);
109
+ output += "\n";
110
+ }
111
+ if (page.author != null) {
112
+ output += "\n";
113
+ const authorLabel = options.colors ? "\x1B[1;2mAuthor:\x1B[0m\n" : "Author:\n";
114
+ output += authorLabel;
115
+ const authorContent = formatMessage(page.author, {
116
+ colors: options.colors,
117
+ maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - 2,
118
+ quotes: !options.colors
119
+ });
120
+ output += " " + indentLines(authorContent, 2);
121
+ output += "\n";
122
+ }
123
+ if (page.bugs != null) {
124
+ output += "\n";
125
+ const bugsLabel = options.colors ? "\x1B[1;2mBugs:\x1B[0m\n" : "Bugs:\n";
126
+ output += bugsLabel;
127
+ const bugsContent = formatMessage(page.bugs, {
128
+ colors: options.colors,
129
+ maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - 2,
130
+ quotes: !options.colors
131
+ });
132
+ output += " " + indentLines(bugsContent, 2);
133
+ output += "\n";
134
+ }
95
135
  if (page.footer != null) {
96
136
  output += "\n";
97
137
  output += formatMessage(page.footer, {
package/dist/facade.cjs CHANGED
@@ -403,10 +403,36 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
403
403
  for (const chunk of shell.encodeSuggestions(suggestions)) stdout(chunk);
404
404
  return callOnCompletion(0);
405
405
  }
406
- function runParser(parser, programName, args, options = {}) {
406
+ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsParam) {
407
+ const isProgram = typeof programNameOrArgs !== "string";
408
+ let parser;
409
+ let programName;
410
+ let args;
411
+ let options;
412
+ if (isProgram) {
413
+ const program = parserOrProgram;
414
+ parser = program.parser;
415
+ programName = program.metadata.name;
416
+ args = programNameOrArgs;
417
+ options = argsOrOptions ?? {};
418
+ options = {
419
+ ...options,
420
+ brief: options.brief ?? program.metadata.brief,
421
+ description: options.description ?? program.metadata.description,
422
+ examples: options.examples ?? program.metadata.examples,
423
+ author: options.author ?? program.metadata.author,
424
+ bugs: options.bugs ?? program.metadata.bugs,
425
+ footer: options.footer ?? program.metadata.footer
426
+ };
427
+ } else {
428
+ parser = parserOrProgram;
429
+ programName = programNameOrArgs;
430
+ args = argsOrOptions;
431
+ options = optionsParam ?? {};
432
+ }
407
433
  const { colors, maxWidth, showDefault, aboveError = "usage", onError = () => {
408
434
  throw new RunParserError("Failed to parse command line arguments.");
409
- }, stderr = console.error, stdout = console.log, brief, description, footer } = options;
435
+ }, stderr = console.error, stdout = console.log, brief, description, examples, author, bugs, footer } = options;
410
436
  const helpMode = options.help?.mode ?? "option";
411
437
  const onHelp = options.help?.onShow ?? (() => ({}));
412
438
  const versionMode = options.version?.mode ?? "option";
@@ -507,6 +533,9 @@ function runParser(parser, programName, args, options = {}) {
507
533
  ...doc,
508
534
  brief: !isMetaCommandHelp ? brief ?? doc.brief : doc.brief,
509
535
  description: !isMetaCommandHelp ? description ?? doc.description : doc.description,
536
+ examples: !isMetaCommandHelp ? examples ?? doc.examples : doc.examples,
537
+ author: !isMetaCommandHelp ? author ?? doc.author : doc.author,
538
+ bugs: !isMetaCommandHelp ? bugs ?? doc.bugs : doc.bugs,
510
539
  footer: !isMetaCommandHelp ? footer ?? doc.footer : doc.footer
511
540
  };
512
541
  stdout(require_doc.formatDocPage(programName, augmentedDoc, {
@@ -534,6 +563,9 @@ function runParser(parser, programName, args, options = {}) {
534
563
  ...doc,
535
564
  brief: brief ?? doc.brief,
536
565
  description: description ?? doc.description,
566
+ examples: examples ?? doc.examples,
567
+ author: author ?? doc.author,
568
+ bugs: bugs ?? doc.bugs,
537
569
  footer: footer ?? doc.footer
538
570
  };
539
571
  stderr(require_doc.formatDocPage(programName, augmentedDoc, {
package/dist/facade.d.cts CHANGED
@@ -2,6 +2,7 @@ import { Message } from "./message.cjs";
2
2
  import { ShowDefaultOptions } from "./doc.cjs";
3
3
  import { InferMode, InferValue, Mode, ModeValue, Parser } from "./parser.cjs";
4
4
  import { ShellCompletion } from "./completion.cjs";
5
+ import { Program } from "./program.cjs";
5
6
 
6
7
  //#region src/facade.d.ts
7
8
 
@@ -177,6 +178,24 @@ interface RunOptions<THelp, TError> {
177
178
  * @since 0.4.0
178
179
  */
179
180
  readonly description?: Message;
181
+ /**
182
+ * Usage examples for the program.
183
+ *
184
+ * @since 0.10.0
185
+ */
186
+ readonly examples?: Message;
187
+ /**
188
+ * Author information.
189
+ *
190
+ * @since 0.10.0
191
+ */
192
+ readonly author?: Message;
193
+ /**
194
+ * Information about where to report bugs.
195
+ *
196
+ * @since 0.10.0
197
+ */
198
+ readonly bugs?: Message;
180
199
  /**
181
200
  * Footer text shown at the bottom of help text.
182
201
  *
@@ -213,7 +232,10 @@ interface RunOptions<THelp, TError> {
213
232
  * callbacks.
214
233
  * @throws {RunParserError} When parsing fails and no `onError` callback is
215
234
  * provided.
235
+ * @since 0.10.0 Added support for {@link Program} objects.
216
236
  */
237
+ declare function runParser<T, THelp = void, TError = never>(program: Program<"sync", T>, args: readonly string[], options?: RunOptions<THelp, TError>): T;
238
+ declare function runParser<T, THelp = void, TError = never>(program: Program<"async", T>, args: readonly string[], options?: RunOptions<THelp, TError>): Promise<T>;
217
239
  declare function runParser<TParser extends Parser<"sync", unknown, unknown>, THelp = void, TError = never>(parser: TParser, programName: string, args: readonly string[], options?: RunOptions<THelp, TError>): InferValue<TParser>;
218
240
  declare function runParser<TParser extends Parser<"async", unknown, unknown>, THelp = void, TError = never>(parser: TParser, programName: string, args: readonly string[], options?: RunOptions<THelp, TError>): Promise<InferValue<TParser>>;
219
241
  declare function runParser<TParser extends Parser<Mode, unknown, unknown>, THelp = void, TError = never>(parser: TParser, programName: string, args: readonly string[], options?: RunOptions<THelp, TError>): ModeValue<InferMode<TParser>, InferValue<TParser>>;
package/dist/facade.d.ts CHANGED
@@ -2,6 +2,7 @@ import { Message } from "./message.js";
2
2
  import { ShowDefaultOptions } from "./doc.js";
3
3
  import { InferMode, InferValue, Mode, ModeValue, Parser } from "./parser.js";
4
4
  import { ShellCompletion } from "./completion.js";
5
+ import { Program } from "./program.js";
5
6
 
6
7
  //#region src/facade.d.ts
7
8
 
@@ -177,6 +178,24 @@ interface RunOptions<THelp, TError> {
177
178
  * @since 0.4.0
178
179
  */
179
180
  readonly description?: Message;
181
+ /**
182
+ * Usage examples for the program.
183
+ *
184
+ * @since 0.10.0
185
+ */
186
+ readonly examples?: Message;
187
+ /**
188
+ * Author information.
189
+ *
190
+ * @since 0.10.0
191
+ */
192
+ readonly author?: Message;
193
+ /**
194
+ * Information about where to report bugs.
195
+ *
196
+ * @since 0.10.0
197
+ */
198
+ readonly bugs?: Message;
180
199
  /**
181
200
  * Footer text shown at the bottom of help text.
182
201
  *
@@ -213,7 +232,10 @@ interface RunOptions<THelp, TError> {
213
232
  * callbacks.
214
233
  * @throws {RunParserError} When parsing fails and no `onError` callback is
215
234
  * provided.
235
+ * @since 0.10.0 Added support for {@link Program} objects.
216
236
  */
237
+ declare function runParser<T, THelp = void, TError = never>(program: Program<"sync", T>, args: readonly string[], options?: RunOptions<THelp, TError>): T;
238
+ declare function runParser<T, THelp = void, TError = never>(program: Program<"async", T>, args: readonly string[], options?: RunOptions<THelp, TError>): Promise<T>;
217
239
  declare function runParser<TParser extends Parser<"sync", unknown, unknown>, THelp = void, TError = never>(parser: TParser, programName: string, args: readonly string[], options?: RunOptions<THelp, TError>): InferValue<TParser>;
218
240
  declare function runParser<TParser extends Parser<"async", unknown, unknown>, THelp = void, TError = never>(parser: TParser, programName: string, args: readonly string[], options?: RunOptions<THelp, TError>): Promise<InferValue<TParser>>;
219
241
  declare function runParser<TParser extends Parser<Mode, unknown, unknown>, THelp = void, TError = never>(parser: TParser, programName: string, args: readonly string[], options?: RunOptions<THelp, TError>): ModeValue<InferMode<TParser>, InferValue<TParser>>;
package/dist/facade.js CHANGED
@@ -403,10 +403,36 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
403
403
  for (const chunk of shell.encodeSuggestions(suggestions)) stdout(chunk);
404
404
  return callOnCompletion(0);
405
405
  }
406
- function runParser(parser, programName, args, options = {}) {
406
+ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsParam) {
407
+ const isProgram = typeof programNameOrArgs !== "string";
408
+ let parser;
409
+ let programName;
410
+ let args;
411
+ let options;
412
+ if (isProgram) {
413
+ const program = parserOrProgram;
414
+ parser = program.parser;
415
+ programName = program.metadata.name;
416
+ args = programNameOrArgs;
417
+ options = argsOrOptions ?? {};
418
+ options = {
419
+ ...options,
420
+ brief: options.brief ?? program.metadata.brief,
421
+ description: options.description ?? program.metadata.description,
422
+ examples: options.examples ?? program.metadata.examples,
423
+ author: options.author ?? program.metadata.author,
424
+ bugs: options.bugs ?? program.metadata.bugs,
425
+ footer: options.footer ?? program.metadata.footer
426
+ };
427
+ } else {
428
+ parser = parserOrProgram;
429
+ programName = programNameOrArgs;
430
+ args = argsOrOptions;
431
+ options = optionsParam ?? {};
432
+ }
407
433
  const { colors, maxWidth, showDefault, aboveError = "usage", onError = () => {
408
434
  throw new RunParserError("Failed to parse command line arguments.");
409
- }, stderr = console.error, stdout = console.log, brief, description, footer } = options;
435
+ }, stderr = console.error, stdout = console.log, brief, description, examples, author, bugs, footer } = options;
410
436
  const helpMode = options.help?.mode ?? "option";
411
437
  const onHelp = options.help?.onShow ?? (() => ({}));
412
438
  const versionMode = options.version?.mode ?? "option";
@@ -507,6 +533,9 @@ function runParser(parser, programName, args, options = {}) {
507
533
  ...doc,
508
534
  brief: !isMetaCommandHelp ? brief ?? doc.brief : doc.brief,
509
535
  description: !isMetaCommandHelp ? description ?? doc.description : doc.description,
536
+ examples: !isMetaCommandHelp ? examples ?? doc.examples : doc.examples,
537
+ author: !isMetaCommandHelp ? author ?? doc.author : doc.author,
538
+ bugs: !isMetaCommandHelp ? bugs ?? doc.bugs : doc.bugs,
510
539
  footer: !isMetaCommandHelp ? footer ?? doc.footer : doc.footer
511
540
  };
512
541
  stdout(formatDocPage(programName, augmentedDoc, {
@@ -534,6 +563,9 @@ function runParser(parser, programName, args, options = {}) {
534
563
  ...doc,
535
564
  brief: brief ?? doc.brief,
536
565
  description: description ?? doc.description,
566
+ examples: examples ?? doc.examples,
567
+ author: author ?? doc.author,
568
+ bugs: bugs ?? doc.bugs,
537
569
  footer: footer ?? doc.footer
538
570
  };
539
571
  stderr(formatDocPage(programName, augmentedDoc, {
@@ -0,0 +1,44 @@
1
+
2
+ //#region src/program.ts
3
+ /**
4
+ * Defines a CLI program with a parser and metadata.
5
+ *
6
+ * This is a helper function that returns its argument unchanged, but provides
7
+ * automatic type inference for the program. This eliminates the need to
8
+ * manually specify type parameters when defining a program.
9
+ *
10
+ * @template M - The mode of the parser ("sync" or "async").
11
+ * @template T - The type of value produced by the parser.
12
+ * @param program - The program definition with parser and metadata.
13
+ * @returns The same program object with inferred types.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { defineProgram } from "@optique/core/program";
18
+ * import { object } from "@optique/core/constructs";
19
+ * import { option } from "@optique/core/primitives";
20
+ * import { string } from "@optique/core/valueparser";
21
+ * import { message } from "@optique/core/message";
22
+ *
23
+ * const prog = defineProgram({
24
+ * parser: object({
25
+ * name: option("-n", "--name", string()),
26
+ * }),
27
+ * metadata: {
28
+ * name: "myapp",
29
+ * version: "1.0.0",
30
+ * brief: message`A simple CLI tool`,
31
+ * },
32
+ * });
33
+ * // TypeScript automatically infers:
34
+ * // Program<"sync", { readonly name: string }>
35
+ * ```
36
+ *
37
+ * @since 0.11.0
38
+ */
39
+ function defineProgram(program) {
40
+ return program;
41
+ }
42
+
43
+ //#endregion
44
+ exports.defineProgram = defineProgram;
@@ -0,0 +1,124 @@
1
+ import { Message } from "./message.cjs";
2
+ import { Mode, Parser } from "./parser.cjs";
3
+
4
+ //#region src/program.d.ts
5
+
6
+ /**
7
+ * Metadata for a CLI program.
8
+ *
9
+ * @since 0.10.0
10
+ */
11
+ interface ProgramMetadata {
12
+ /**
13
+ * The name of the program.
14
+ */
15
+ readonly name: string;
16
+ /**
17
+ * The version of the program.
18
+ */
19
+ readonly version?: string;
20
+ /**
21
+ * A brief one-line description of the program.
22
+ */
23
+ readonly brief?: Message;
24
+ /**
25
+ * A detailed description of the program.
26
+ */
27
+ readonly description?: Message;
28
+ /**
29
+ * Author information.
30
+ */
31
+ readonly author?: Message;
32
+ /**
33
+ * Information about where to report bugs.
34
+ */
35
+ readonly bugs?: Message;
36
+ /**
37
+ * Usage examples for the program.
38
+ */
39
+ readonly examples?: Message;
40
+ /**
41
+ * Footer text shown at the end of help output.
42
+ */
43
+ readonly footer?: Message;
44
+ }
45
+ /**
46
+ * A CLI program consisting of a parser and its metadata.
47
+ *
48
+ * This interface bundles a parser with its metadata (name, version,
49
+ * description, etc.), providing a single source of truth for CLI application
50
+ * information that can be shared across different functionalities.
51
+ *
52
+ * @template M - The mode of the parser ("sync" or "async").
53
+ * @template T - The type of value produced by the parser.
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * import type { Program } from "@optique/core/program";
58
+ * import { command, option, string } from "@optique/core";
59
+ * import { message } from "@optique/core/message";
60
+ *
61
+ * const prog: Program<"sync", { name: string }> = {
62
+ * parser: command("greet", () => ({
63
+ * name: option("name", string()).default("World"),
64
+ * })),
65
+ * metadata: {
66
+ * name: "greet",
67
+ * version: "1.0.0",
68
+ * brief: message`A simple greeting CLI tool`,
69
+ * author: message`Jane Doe <jane@example.com>`,
70
+ * },
71
+ * };
72
+ * ```
73
+ *
74
+ * @since 0.11.0
75
+ */
76
+ interface Program<M extends Mode, T> {
77
+ /**
78
+ * The parser for the program.
79
+ */
80
+ readonly parser: Parser<M, T, unknown>;
81
+ /**
82
+ * Metadata about the program.
83
+ */
84
+ readonly metadata: ProgramMetadata;
85
+ }
86
+ /**
87
+ * Defines a CLI program with a parser and metadata.
88
+ *
89
+ * This is a helper function that returns its argument unchanged, but provides
90
+ * automatic type inference for the program. This eliminates the need to
91
+ * manually specify type parameters when defining a program.
92
+ *
93
+ * @template M - The mode of the parser ("sync" or "async").
94
+ * @template T - The type of value produced by the parser.
95
+ * @param program - The program definition with parser and metadata.
96
+ * @returns The same program object with inferred types.
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * import { defineProgram } from "@optique/core/program";
101
+ * import { object } from "@optique/core/constructs";
102
+ * import { option } from "@optique/core/primitives";
103
+ * import { string } from "@optique/core/valueparser";
104
+ * import { message } from "@optique/core/message";
105
+ *
106
+ * const prog = defineProgram({
107
+ * parser: object({
108
+ * name: option("-n", "--name", string()),
109
+ * }),
110
+ * metadata: {
111
+ * name: "myapp",
112
+ * version: "1.0.0",
113
+ * brief: message`A simple CLI tool`,
114
+ * },
115
+ * });
116
+ * // TypeScript automatically infers:
117
+ * // Program<"sync", { readonly name: string }>
118
+ * ```
119
+ *
120
+ * @since 0.11.0
121
+ */
122
+ declare function defineProgram<M extends Mode, T>(program: Program<M, T>): Program<M, T>;
123
+ //#endregion
124
+ export { Program, ProgramMetadata, defineProgram };
@@ -0,0 +1,124 @@
1
+ import { Message } from "./message.js";
2
+ import { Mode, Parser } from "./parser.js";
3
+
4
+ //#region src/program.d.ts
5
+
6
+ /**
7
+ * Metadata for a CLI program.
8
+ *
9
+ * @since 0.10.0
10
+ */
11
+ interface ProgramMetadata {
12
+ /**
13
+ * The name of the program.
14
+ */
15
+ readonly name: string;
16
+ /**
17
+ * The version of the program.
18
+ */
19
+ readonly version?: string;
20
+ /**
21
+ * A brief one-line description of the program.
22
+ */
23
+ readonly brief?: Message;
24
+ /**
25
+ * A detailed description of the program.
26
+ */
27
+ readonly description?: Message;
28
+ /**
29
+ * Author information.
30
+ */
31
+ readonly author?: Message;
32
+ /**
33
+ * Information about where to report bugs.
34
+ */
35
+ readonly bugs?: Message;
36
+ /**
37
+ * Usage examples for the program.
38
+ */
39
+ readonly examples?: Message;
40
+ /**
41
+ * Footer text shown at the end of help output.
42
+ */
43
+ readonly footer?: Message;
44
+ }
45
+ /**
46
+ * A CLI program consisting of a parser and its metadata.
47
+ *
48
+ * This interface bundles a parser with its metadata (name, version,
49
+ * description, etc.), providing a single source of truth for CLI application
50
+ * information that can be shared across different functionalities.
51
+ *
52
+ * @template M - The mode of the parser ("sync" or "async").
53
+ * @template T - The type of value produced by the parser.
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * import type { Program } from "@optique/core/program";
58
+ * import { command, option, string } from "@optique/core";
59
+ * import { message } from "@optique/core/message";
60
+ *
61
+ * const prog: Program<"sync", { name: string }> = {
62
+ * parser: command("greet", () => ({
63
+ * name: option("name", string()).default("World"),
64
+ * })),
65
+ * metadata: {
66
+ * name: "greet",
67
+ * version: "1.0.0",
68
+ * brief: message`A simple greeting CLI tool`,
69
+ * author: message`Jane Doe <jane@example.com>`,
70
+ * },
71
+ * };
72
+ * ```
73
+ *
74
+ * @since 0.11.0
75
+ */
76
+ interface Program<M extends Mode, T> {
77
+ /**
78
+ * The parser for the program.
79
+ */
80
+ readonly parser: Parser<M, T, unknown>;
81
+ /**
82
+ * Metadata about the program.
83
+ */
84
+ readonly metadata: ProgramMetadata;
85
+ }
86
+ /**
87
+ * Defines a CLI program with a parser and metadata.
88
+ *
89
+ * This is a helper function that returns its argument unchanged, but provides
90
+ * automatic type inference for the program. This eliminates the need to
91
+ * manually specify type parameters when defining a program.
92
+ *
93
+ * @template M - The mode of the parser ("sync" or "async").
94
+ * @template T - The type of value produced by the parser.
95
+ * @param program - The program definition with parser and metadata.
96
+ * @returns The same program object with inferred types.
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * import { defineProgram } from "@optique/core/program";
101
+ * import { object } from "@optique/core/constructs";
102
+ * import { option } from "@optique/core/primitives";
103
+ * import { string } from "@optique/core/valueparser";
104
+ * import { message } from "@optique/core/message";
105
+ *
106
+ * const prog = defineProgram({
107
+ * parser: object({
108
+ * name: option("-n", "--name", string()),
109
+ * }),
110
+ * metadata: {
111
+ * name: "myapp",
112
+ * version: "1.0.0",
113
+ * brief: message`A simple CLI tool`,
114
+ * },
115
+ * });
116
+ * // TypeScript automatically infers:
117
+ * // Program<"sync", { readonly name: string }>
118
+ * ```
119
+ *
120
+ * @since 0.11.0
121
+ */
122
+ declare function defineProgram<M extends Mode, T>(program: Program<M, T>): Program<M, T>;
123
+ //#endregion
124
+ export { Program, ProgramMetadata, defineProgram };
@@ -0,0 +1,43 @@
1
+ //#region src/program.ts
2
+ /**
3
+ * Defines a CLI program with a parser and metadata.
4
+ *
5
+ * This is a helper function that returns its argument unchanged, but provides
6
+ * automatic type inference for the program. This eliminates the need to
7
+ * manually specify type parameters when defining a program.
8
+ *
9
+ * @template M - The mode of the parser ("sync" or "async").
10
+ * @template T - The type of value produced by the parser.
11
+ * @param program - The program definition with parser and metadata.
12
+ * @returns The same program object with inferred types.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { defineProgram } from "@optique/core/program";
17
+ * import { object } from "@optique/core/constructs";
18
+ * import { option } from "@optique/core/primitives";
19
+ * import { string } from "@optique/core/valueparser";
20
+ * import { message } from "@optique/core/message";
21
+ *
22
+ * const prog = defineProgram({
23
+ * parser: object({
24
+ * name: option("-n", "--name", string()),
25
+ * }),
26
+ * metadata: {
27
+ * name: "myapp",
28
+ * version: "1.0.0",
29
+ * brief: message`A simple CLI tool`,
30
+ * },
31
+ * });
32
+ * // TypeScript automatically infers:
33
+ * // Program<"sync", { readonly name: string }>
34
+ * ```
35
+ *
36
+ * @since 0.11.0
37
+ */
38
+ function defineProgram(program) {
39
+ return program;
40
+ }
41
+
42
+ //#endregion
43
+ export { defineProgram };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "0.10.0-dev.324+5bba27e5",
3
+ "version": "0.10.0-dev.328+4e5f40d5",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -131,6 +131,14 @@
131
131
  "import": "./dist/primitives.js",
132
132
  "require": "./dist/primitives.cjs"
133
133
  },
134
+ "./program": {
135
+ "types": {
136
+ "import": "./dist/program.d.ts",
137
+ "require": "./dist/program.d.cts"
138
+ },
139
+ "import": "./dist/program.js",
140
+ "require": "./dist/program.cjs"
141
+ },
134
142
  "./usage": {
135
143
  "types": {
136
144
  "import": "./dist/usage.d.ts",