@optique/core 0.10.0-dev.323 → 0.10.0-dev.327

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/modifiers.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { formatMessage, message, text } from "./message.js";
2
2
  import { createDependencySourceState, dependencyId, isDependencySourceState, isPendingDependencySourceState, isWrappedDependencySource, transformsDependencyValue, transformsDependencyValueMarker, wrappedDependencySourceMarker } from "./dependency.js";
3
+ import { dispatchByMode, dispatchIterableByMode } from "./mode-dispatch.js";
3
4
 
4
5
  //#region src/modifiers.ts
5
6
  /**
@@ -74,7 +75,6 @@ function processOptionalStyleResult(result, innerState, context) {
74
75
  */
75
76
  function optional(parser) {
76
77
  const syncParser = parser;
77
- const isAsync = parser.$mode === "async";
78
78
  function* suggestSync(context, prefix) {
79
79
  const innerState = typeof context.state === "undefined" ? syncParser.initialState : context.state[0];
80
80
  yield* syncParser.suggest({
@@ -107,36 +107,27 @@ function optional(parser) {
107
107
  initialState: void 0,
108
108
  ...wrappedDependencyMarker,
109
109
  parse(context) {
110
- if (isAsync) return parseOptionalStyleAsync(context, parser);
111
- return parseOptionalStyleSync(context, syncParser);
110
+ return dispatchByMode(parser.$mode, () => parseOptionalStyleSync(context, syncParser), () => parseOptionalStyleAsync(context, parser));
112
111
  },
113
112
  complete(state) {
114
113
  if (typeof state === "undefined") {
115
- if (innerHasWrappedDependency && wrappedPendingState) {
116
- if (!isAsync) return syncParser.complete([wrappedPendingState]);
117
- return parser.complete([wrappedPendingState]);
118
- }
114
+ if (innerHasWrappedDependency && wrappedPendingState) return dispatchByMode(parser.$mode, () => syncParser.complete([wrappedPendingState]), () => parser.complete([wrappedPendingState]));
119
115
  return {
120
116
  success: true,
121
117
  value: void 0
122
118
  };
123
119
  }
124
120
  if (Array.isArray(state) && state.length === 1 && isPendingDependencySourceState(state[0])) {
125
- if (innerHasWrappedDependency) {
126
- if (!isAsync) return syncParser.complete(state);
127
- return parser.complete(state);
128
- }
121
+ if (innerHasWrappedDependency) return dispatchByMode(parser.$mode, () => syncParser.complete(state), () => parser.complete(state));
129
122
  return {
130
123
  success: true,
131
124
  value: void 0
132
125
  };
133
126
  }
134
- if (!isAsync) return syncParser.complete(state[0]);
135
- return parser.complete(state[0]);
127
+ return dispatchByMode(parser.$mode, () => syncParser.complete(state[0]), () => parser.complete(state[0]));
136
128
  },
137
129
  suggest(context, prefix) {
138
- if (isAsync) return suggestAsync(context, prefix);
139
- return suggestSync(context, prefix);
130
+ return dispatchIterableByMode(parser.$mode, () => suggestSync(context, prefix), () => suggestAsync(context, prefix));
140
131
  },
141
132
  getDocFragments(state, defaultValue) {
142
133
  const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state === void 0 ? { kind: "unavailable" } : {
@@ -183,7 +174,6 @@ var WithDefaultError = class extends Error {
183
174
  };
184
175
  function withDefault(parser, defaultValue, options) {
185
176
  const syncParser = parser;
186
- const isAsync = parser.$mode === "async";
187
177
  function* suggestSync(context, prefix) {
188
178
  const innerState = typeof context.state === "undefined" ? syncParser.initialState : context.state[0];
189
179
  yield* syncParser.suggest({
@@ -213,13 +203,12 @@ function withDefault(parser, defaultValue, options) {
213
203
  initialState: void 0,
214
204
  ...wrappedDependencyMarker,
215
205
  parse(context) {
216
- if (isAsync) return parseOptionalStyleAsync(context, parser);
217
- return parseOptionalStyleSync(context, syncParser);
206
+ return dispatchByMode(parser.$mode, () => parseOptionalStyleSync(context, syncParser), () => parseOptionalStyleAsync(context, parser));
218
207
  },
219
208
  complete(state) {
220
209
  if (typeof state === "undefined") {
221
210
  if (transformsDependencyValue(parser)) {
222
- const innerResult = !isAsync ? syncParser.complete(void 0) : parser.complete(void 0);
211
+ const innerResult = dispatchByMode(parser.$mode, () => syncParser.complete(void 0), () => parser.complete(void 0));
223
212
  const handleInnerResult = (res) => {
224
213
  if (isDependencySourceState(res)) try {
225
214
  const value = typeof defaultValue === "function" ? defaultValue() : defaultValue;
@@ -277,7 +266,7 @@ function withDefault(parser, defaultValue, options) {
277
266
  }
278
267
  if (isPendingDependencySourceState(state[0])) {
279
268
  if (transformsDependencyValue(parser)) {
280
- const innerResult = !isAsync ? syncParser.complete(state) : parser.complete(state);
269
+ const innerResult = dispatchByMode(parser.$mode, () => syncParser.complete(state), () => parser.complete(state));
281
270
  const handleInnerResult = (res) => {
282
271
  if (isDependencySourceState(res)) try {
283
272
  const value = typeof defaultValue === "function" ? defaultValue() : defaultValue;
@@ -320,12 +309,10 @@ function withDefault(parser, defaultValue, options) {
320
309
  };
321
310
  }
322
311
  }
323
- if (!isAsync) return syncParser.complete(state[0]);
324
- return parser.complete(state[0]);
312
+ return dispatchByMode(parser.$mode, () => syncParser.complete(state[0]), () => parser.complete(state[0]));
325
313
  },
326
314
  suggest(context, prefix) {
327
- if (isAsync) return suggestAsync(context, prefix);
328
- return suggestSync(context, prefix);
315
+ return dispatchIterableByMode(parser.$mode, () => suggestSync(context, prefix), () => suggestAsync(context, prefix));
329
316
  },
330
317
  getDocFragments(state, upperDefaultValue) {
331
318
  const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state === void 0 ? { kind: "unavailable" } : {
@@ -432,7 +419,6 @@ function map(parser, transform) {
432
419
  */
433
420
  function multiple(parser, options = {}) {
434
421
  const syncParser = parser;
435
- const isAsync = parser.$mode === "async";
436
422
  const { min = 0, max = Infinity } = options;
437
423
  const parseSync = (context) => {
438
424
  let added = context.state.length < 1;
@@ -494,11 +480,10 @@ function multiple(parser, options = {}) {
494
480
  }],
495
481
  initialState: [],
496
482
  parse(context) {
497
- if (isAsync) return parseAsync(context);
498
- return parseSync(context);
483
+ return dispatchByMode(parser.$mode, () => parseSync(context), () => parseAsync(context));
499
484
  },
500
485
  complete(state) {
501
- if (!isAsync) {
486
+ return dispatchByMode(parser.$mode, () => {
502
487
  const result = [];
503
488
  for (const s of state) {
504
489
  const valueResult = syncParser.complete(s);
@@ -509,8 +494,7 @@ function multiple(parser, options = {}) {
509
494
  };
510
495
  }
511
496
  return validateMultipleResult(result);
512
- }
513
- return (async () => {
497
+ }, async () => {
514
498
  const results = await Promise.all(state.map((s) => parser.complete(s)));
515
499
  const values = [];
516
500
  for (const valueResult of results) if (valueResult.success) values.push(valueResult.value);
@@ -519,7 +503,7 @@ function multiple(parser, options = {}) {
519
503
  error: valueResult.error
520
504
  };
521
505
  return validateMultipleResult(values);
522
- })();
506
+ });
523
507
  },
524
508
  suggest(context, prefix) {
525
509
  const innerState = context.state.length > 0 ? context.state.at(-1) : parser.initialState;
@@ -535,19 +519,18 @@ function multiple(parser, options = {}) {
535
519
  if (suggestion.kind === "literal") return !selectedValues.has(suggestion.text);
536
520
  return true;
537
521
  };
538
- if (isAsync) return async function* () {
522
+ return dispatchIterableByMode(parser.$mode, function* () {
523
+ for (const s of syncParser.suggest({
524
+ ...context,
525
+ state: innerState
526
+ }, prefix)) if (shouldInclude(s)) yield s;
527
+ }, async function* () {
539
528
  const suggestions = parser.suggest({
540
529
  ...context,
541
530
  state: innerState
542
531
  }, prefix);
543
532
  for await (const s of suggestions) if (shouldInclude(s)) yield s;
544
- }();
545
- return function* () {
546
- for (const s of syncParser.suggest({
547
- ...context,
548
- state: innerState
549
- }, prefix)) if (shouldInclude(s)) yield s;
550
- }();
533
+ });
551
534
  },
552
535
  getDocFragments(state, defaultValue) {
553
536
  const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state.length > 0 ? {
@@ -617,7 +600,6 @@ function multiple(parser, options = {}) {
617
600
  */
618
601
  function nonEmpty(parser) {
619
602
  const syncParser = parser;
620
- const isAsync = parser.$mode === "async";
621
603
  const processNonEmptyResult = (result) => {
622
604
  if (!result.success) return result;
623
605
  if (result.consumed.length === 0) return {
@@ -643,8 +625,7 @@ function nonEmpty(parser) {
643
625
  usage: parser.usage,
644
626
  initialState: parser.initialState,
645
627
  parse(context) {
646
- if (isAsync) return parseAsync(context);
647
- return parseSync(context);
628
+ return dispatchByMode(parser.$mode, () => parseSync(context), () => parseAsync(context));
648
629
  },
649
630
  complete(state) {
650
631
  return parser.complete(state);
package/dist/parser.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Message } from "./message.cjs";
2
2
  import { Usage } from "./usage.cjs";
3
3
  import { DocFragments, DocPage } from "./doc.cjs";
4
+ import { DependencyRegistryLike } from "./registry-types.cjs";
4
5
  import { ValueParserResult } from "./valueparser.cjs";
5
6
  import { ConditionalErrorOptions, ConditionalOptions, DuplicateOptionError, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.cjs";
6
7
  import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOptions, map, multiple, nonEmpty, optional, withDefault } from "./modifiers.cjs";
@@ -191,11 +192,9 @@ interface ParserContext<TState> {
191
192
  * A registry containing resolved dependency values from DependencySource parsers.
192
193
  * This is used during shell completion to provide suggestions based on
193
194
  * the actual dependency values that have been parsed, rather than defaults.
194
- * The type is `unknown` to avoid circular dependency issues; the actual type
195
- * is `DependencyRegistry` from `./dependency.ts`.
196
195
  * @since 0.10.0
197
196
  */
198
- readonly dependencyRegistry?: unknown;
197
+ readonly dependencyRegistry?: DependencyRegistryLike;
199
198
  }
200
199
  /**
201
200
  * Represents a suggestion for command-line completion or guidance.
package/dist/parser.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Message } from "./message.js";
2
2
  import { Usage } from "./usage.js";
3
3
  import { DocFragments, DocPage } from "./doc.js";
4
+ import { DependencyRegistryLike } from "./registry-types.js";
4
5
  import { ValueParserResult } from "./valueparser.js";
5
6
  import { ConditionalErrorOptions, ConditionalOptions, DuplicateOptionError, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
6
7
  import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOptions, map, multiple, nonEmpty, optional, withDefault } from "./modifiers.js";
@@ -191,11 +192,9 @@ interface ParserContext<TState> {
191
192
  * A registry containing resolved dependency values from DependencySource parsers.
192
193
  * This is used during shell completion to provide suggestions based on
193
194
  * the actual dependency values that have been parsed, rather than defaults.
194
- * The type is `unknown` to avoid circular dependency issues; the actual type
195
- * is `DependencyRegistry` from `./dependency.ts`.
196
195
  * @since 0.10.0
197
196
  */
198
- readonly dependencyRegistry?: unknown;
197
+ readonly dependencyRegistry?: DependencyRegistryLike;
199
198
  }
200
199
  /**
201
200
  * Represents a suggestion for command-line completion or guidance.
@@ -63,15 +63,15 @@ function* getSuggestionsWithDependency(valueParser, prefix, dependencyRegistry)
63
63
  const derived = valueParser;
64
64
  const suggestWithDep = derived[require_dependency.suggestWithDependency];
65
65
  if (suggestWithDep && dependencyRegistry) {
66
- const depIds = require_dependency.dependencyIds in derived ? derived[require_dependency.dependencyIds] : [derived[require_dependency.dependencyId]];
67
- const defaults = require_dependency.defaultValues in derived ? derived[require_dependency.defaultValues]?.() : void 0;
68
- const registry = dependencyRegistry;
66
+ const depIds = require_dependency.getDependencyIds(derived);
67
+ const defaultsFn = require_dependency.getDefaultValuesFunction(derived);
68
+ const defaults = defaultsFn?.();
69
69
  const dependencyValues = [];
70
70
  let hasAnyValue = false;
71
71
  for (let i = 0; i < depIds.length; i++) {
72
72
  const depId = depIds[i];
73
- if (registry.has(depId)) {
74
- dependencyValues.push(registry.get(depId));
73
+ if (dependencyRegistry.has(depId)) {
74
+ dependencyValues.push(dependencyRegistry.get(depId));
75
75
  hasAnyValue = true;
76
76
  } else if (defaults && i < defaults.length) dependencyValues.push(defaults[i]);
77
77
  else {
@@ -144,15 +144,15 @@ async function* getSuggestionsWithDependencyAsync(valueParser, prefix, dependenc
144
144
  const derived = valueParser;
145
145
  const suggestWithDep = derived[require_dependency.suggestWithDependency];
146
146
  if (suggestWithDep && dependencyRegistry) {
147
- const depIds = require_dependency.dependencyIds in derived ? derived[require_dependency.dependencyIds] : [derived[require_dependency.dependencyId]];
148
- const defaults = require_dependency.defaultValues in derived ? derived[require_dependency.defaultValues]?.() : void 0;
149
- const registry = dependencyRegistry;
147
+ const depIds = require_dependency.getDependencyIds(derived);
148
+ const defaultsFn = require_dependency.getDefaultValuesFunction(derived);
149
+ const defaults = defaultsFn?.();
150
150
  const dependencyValues = [];
151
151
  let hasAnyValue = false;
152
152
  for (let i = 0; i < depIds.length; i++) {
153
153
  const depId = depIds[i];
154
- if (registry.has(depId)) {
155
- dependencyValues.push(registry.get(depId));
154
+ if (dependencyRegistry.has(depId)) {
155
+ dependencyValues.push(dependencyRegistry.get(depId));
156
156
  hasAnyValue = true;
157
157
  } else if (defaults && i < defaults.length) dependencyValues.push(defaults[i]);
158
158
  else {
@@ -1,5 +1,5 @@
1
1
  import { message, metavar, optionName, optionNames } from "./message.js";
2
- import { createDeferredParseState, createDependencySourceState, createPendingDependencySourceState, defaultValues, dependencyId, dependencyIds, isDeferredParseState, isDependencySource, isDependencySourceState, isDerivedValueParser, isPendingDependencySourceState, suggestWithDependency } from "./dependency.js";
2
+ import { createDeferredParseState, createDependencySourceState, createPendingDependencySourceState, dependencyId, getDefaultValuesFunction, getDependencyIds, isDeferredParseState, isDependencySource, isDependencySourceState, isDerivedValueParser, isPendingDependencySourceState, suggestWithDependency } from "./dependency.js";
3
3
  import { extractCommandNames, extractOptionNames } from "./usage.js";
4
4
  import { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, findSimilar } from "./suggestion.js";
5
5
  import { isValueParser } from "./valueparser.js";
@@ -63,15 +63,15 @@ function* getSuggestionsWithDependency(valueParser, prefix, dependencyRegistry)
63
63
  const derived = valueParser;
64
64
  const suggestWithDep = derived[suggestWithDependency];
65
65
  if (suggestWithDep && dependencyRegistry) {
66
- const depIds = dependencyIds in derived ? derived[dependencyIds] : [derived[dependencyId]];
67
- const defaults = defaultValues in derived ? derived[defaultValues]?.() : void 0;
68
- const registry = dependencyRegistry;
66
+ const depIds = getDependencyIds(derived);
67
+ const defaultsFn = getDefaultValuesFunction(derived);
68
+ const defaults = defaultsFn?.();
69
69
  const dependencyValues = [];
70
70
  let hasAnyValue = false;
71
71
  for (let i = 0; i < depIds.length; i++) {
72
72
  const depId = depIds[i];
73
- if (registry.has(depId)) {
74
- dependencyValues.push(registry.get(depId));
73
+ if (dependencyRegistry.has(depId)) {
74
+ dependencyValues.push(dependencyRegistry.get(depId));
75
75
  hasAnyValue = true;
76
76
  } else if (defaults && i < defaults.length) dependencyValues.push(defaults[i]);
77
77
  else {
@@ -144,15 +144,15 @@ async function* getSuggestionsWithDependencyAsync(valueParser, prefix, dependenc
144
144
  const derived = valueParser;
145
145
  const suggestWithDep = derived[suggestWithDependency];
146
146
  if (suggestWithDep && dependencyRegistry) {
147
- const depIds = dependencyIds in derived ? derived[dependencyIds] : [derived[dependencyId]];
148
- const defaults = defaultValues in derived ? derived[defaultValues]?.() : void 0;
149
- const registry = dependencyRegistry;
147
+ const depIds = getDependencyIds(derived);
148
+ const defaultsFn = getDefaultValuesFunction(derived);
149
+ const defaults = defaultsFn?.();
150
150
  const dependencyValues = [];
151
151
  let hasAnyValue = false;
152
152
  for (let i = 0; i < depIds.length; i++) {
153
153
  const depId = depIds[i];
154
- if (registry.has(depId)) {
155
- dependencyValues.push(registry.get(depId));
154
+ if (dependencyRegistry.has(depId)) {
155
+ dependencyValues.push(dependencyRegistry.get(depId));
156
156
  hasAnyValue = true;
157
157
  } else if (defaults && i < defaults.length) dependencyValues.push(defaults[i]);
158
158
  else {
@@ -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.11.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.11.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 };