@optique/core 1.0.0-dev.908 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/dist/annotation-state.cjs +425 -0
  2. package/dist/annotation-state.d.cts +24 -0
  3. package/dist/annotation-state.d.ts +24 -0
  4. package/dist/annotation-state.js +414 -0
  5. package/dist/annotations.cjs +2 -248
  6. package/dist/annotations.d.cts +2 -137
  7. package/dist/annotations.d.ts +2 -137
  8. package/dist/annotations.js +2 -238
  9. package/dist/completion.cjs +611 -100
  10. package/dist/completion.d.cts +1 -1
  11. package/dist/completion.d.ts +1 -1
  12. package/dist/completion.js +611 -100
  13. package/dist/constructs.cjs +3338 -827
  14. package/dist/constructs.d.cts +48 -7
  15. package/dist/constructs.d.ts +48 -7
  16. package/dist/constructs.js +3338 -827
  17. package/dist/context.cjs +0 -23
  18. package/dist/context.d.cts +119 -53
  19. package/dist/context.d.ts +119 -53
  20. package/dist/context.js +0 -22
  21. package/dist/dependency-metadata.cjs +139 -0
  22. package/dist/dependency-metadata.d.cts +112 -0
  23. package/dist/dependency-metadata.d.ts +112 -0
  24. package/dist/dependency-metadata.js +138 -0
  25. package/dist/dependency-runtime.cjs +698 -0
  26. package/dist/dependency-runtime.d.cts +149 -0
  27. package/dist/dependency-runtime.d.ts +149 -0
  28. package/dist/dependency-runtime.js +687 -0
  29. package/dist/dependency.cjs +7 -928
  30. package/dist/dependency.d.cts +2 -794
  31. package/dist/dependency.d.ts +2 -794
  32. package/dist/dependency.js +2 -899
  33. package/dist/displaywidth.cjs +44 -0
  34. package/dist/displaywidth.js +43 -0
  35. package/dist/doc.cjs +285 -23
  36. package/dist/doc.d.cts +57 -2
  37. package/dist/doc.d.ts +57 -2
  38. package/dist/doc.js +283 -25
  39. package/dist/execution-context.cjs +56 -0
  40. package/dist/execution-context.js +53 -0
  41. package/dist/extension.cjs +87 -0
  42. package/dist/extension.d.cts +97 -0
  43. package/dist/extension.d.ts +97 -0
  44. package/dist/extension.js +76 -0
  45. package/dist/facade.cjs +718 -523
  46. package/dist/facade.d.cts +87 -18
  47. package/dist/facade.d.ts +87 -18
  48. package/dist/facade.js +718 -523
  49. package/dist/index.cjs +14 -29
  50. package/dist/index.d.cts +10 -10
  51. package/dist/index.d.ts +10 -10
  52. package/dist/index.js +7 -7
  53. package/dist/input-trace.cjs +56 -0
  54. package/dist/input-trace.d.cts +77 -0
  55. package/dist/input-trace.d.ts +77 -0
  56. package/dist/input-trace.js +55 -0
  57. package/dist/internal/annotations.cjs +316 -0
  58. package/dist/internal/annotations.d.cts +140 -0
  59. package/dist/internal/annotations.d.ts +140 -0
  60. package/dist/internal/annotations.js +306 -0
  61. package/dist/internal/dependency.cjs +984 -0
  62. package/dist/internal/dependency.d.cts +539 -0
  63. package/dist/internal/dependency.d.ts +539 -0
  64. package/dist/internal/dependency.js +964 -0
  65. package/dist/{mode-dispatch.cjs → internal/mode-dispatch.cjs} +1 -3
  66. package/dist/{mode-dispatch.d.cts → internal/mode-dispatch.d.cts} +3 -7
  67. package/dist/{mode-dispatch.d.ts → internal/mode-dispatch.d.ts} +3 -7
  68. package/dist/{mode-dispatch.js → internal/mode-dispatch.js} +1 -3
  69. package/dist/internal/parser.cjs +728 -0
  70. package/dist/internal/parser.d.cts +947 -0
  71. package/dist/internal/parser.d.ts +947 -0
  72. package/dist/internal/parser.js +711 -0
  73. package/dist/message.cjs +84 -26
  74. package/dist/message.d.cts +49 -9
  75. package/dist/message.d.ts +49 -9
  76. package/dist/message.js +84 -27
  77. package/dist/modifiers.cjs +1023 -240
  78. package/dist/modifiers.d.cts +42 -1
  79. package/dist/modifiers.d.ts +42 -1
  80. package/dist/modifiers.js +1023 -240
  81. package/dist/parser.cjs +11 -463
  82. package/dist/parser.d.cts +3 -537
  83. package/dist/parser.d.ts +3 -537
  84. package/dist/parser.js +2 -433
  85. package/dist/phase2-seed.cjs +59 -0
  86. package/dist/phase2-seed.js +56 -0
  87. package/dist/primitives.cjs +557 -208
  88. package/dist/primitives.d.cts +10 -14
  89. package/dist/primitives.d.ts +10 -14
  90. package/dist/primitives.js +557 -208
  91. package/dist/program.cjs +5 -1
  92. package/dist/program.d.cts +5 -3
  93. package/dist/program.d.ts +5 -3
  94. package/dist/program.js +6 -1
  95. package/dist/suggestion.cjs +22 -8
  96. package/dist/suggestion.js +22 -8
  97. package/dist/usage-internals.cjs +3 -2
  98. package/dist/usage-internals.js +4 -2
  99. package/dist/usage.cjs +195 -40
  100. package/dist/usage.d.cts +92 -11
  101. package/dist/usage.d.ts +92 -11
  102. package/dist/usage.js +194 -41
  103. package/dist/validate.cjs +170 -0
  104. package/dist/validate.js +164 -0
  105. package/dist/valueparser.cjs +1278 -191
  106. package/dist/valueparser.d.cts +330 -20
  107. package/dist/valueparser.d.ts +330 -20
  108. package/dist/valueparser.js +1277 -192
  109. package/package.json +9 -9
@@ -0,0 +1,711 @@
1
+ import { hasMeaningfulAnnotations, inheritAnnotations, injectAnnotations, isInjectedAnnotationWrapper, unwrapInjectedAnnotationWrapper } from "./annotations.js";
2
+ import { cloneMessage, message } from "../message.js";
3
+ import { cloneUsage, normalizeUsage } from "../usage.js";
4
+ import { cloneDocEntry, isDocEntryHidden } from "../doc.js";
5
+ import { dispatchByMode } from "./mode-dispatch.js";
6
+ import { collectExplicitSourceValues, collectExplicitSourceValuesAsync, createDependencyRuntimeContext } from "../dependency-runtime.js";
7
+ import { createInputTrace } from "../input-trace.js";
8
+
9
+ //#region src/internal/parser.ts
10
+ /**
11
+ * Internal marker for wrappers whose `{ hasCliValue: false }` states should
12
+ * be treated as unmatched dependency-source states during completion-time
13
+ * Phase 1.
14
+ *
15
+ * Wrappers like `bindEnv()` and `bindConfig()` opt in because their missing
16
+ * CLI states still carry enough fallback context to pre-complete exactly
17
+ * once. Wrappers like `prompt()` intentionally do not opt in because
18
+ * prompted values are not yet registered as dependency sources.
19
+ *
20
+ * @internal
21
+ */
22
+ const unmatchedNonCliDependencySourceStateMarker = Symbol.for("@optique/core/parser/unmatchedNonCliDependencySourceStateMarker");
23
+ /**
24
+ * Internal marker for parsers that want parent-state annotations injected
25
+ * directly into rebuilt child states instead of relying on structural
26
+ * inheritance.
27
+ *
28
+ * Wrappers like `bindConfig()`, `bindEnv()`, and `prompt()` opt in because
29
+ * their fallback contracts depend on carrying annotations through wrapper
30
+ * state objects during parse, complete, and suggest.
31
+ *
32
+ * @internal
33
+ */
34
+ const inheritParentAnnotationsKey = Symbol.for("@optique/core/inheritParentAnnotations");
35
+ /**
36
+ * Internal marker for wrapper parsers that should only treat annotation-only
37
+ * primitive wrapper states as completable when a nested source-binding wrapper
38
+ * produced them.
39
+ *
40
+ * @internal
41
+ */
42
+ const annotationWrapperRequiresSourceBindingKey = Symbol.for("@optique/core/annotationWrapperRequiresSourceBinding");
43
+ /**
44
+ * Creates a {@link ParserContext} from a {@link ParseFrame} and an
45
+ * {@link ExecutionContext}. The returned object provides both structured
46
+ * access (`frame`, `exec`) and flat access (`buffer`, `state`, etc.)
47
+ * for backward compatibility.
48
+ *
49
+ * @template TState The type of the state used during parsing.
50
+ * @param frame Parser-local frame data.
51
+ * @param exec Shared execution context.
52
+ * @returns A {@link ParserContext} instance.
53
+ * @since 1.0.0
54
+ */
55
+ function createParserContext(frame, exec) {
56
+ return {
57
+ exec,
58
+ trace: exec.trace,
59
+ buffer: frame.buffer,
60
+ state: frame.state,
61
+ optionsTerminated: frame.optionsTerminated,
62
+ usage: exec.usage,
63
+ dependencyRegistry: exec.dependencyRegistry
64
+ };
65
+ }
66
+ function injectAnnotationsIntoState(state, options) {
67
+ return injectAnnotations(state, options?.annotations);
68
+ }
69
+ /**
70
+ * Parses an array of command-line arguments using the provided combined parser.
71
+ * This function processes the input arguments, applying the parser to each
72
+ * argument until all arguments are consumed or an error occurs.
73
+ *
74
+ * This function only accepts synchronous parsers. For asynchronous parsers,
75
+ * use {@link parseAsync}.
76
+ *
77
+ * @template T The type of the value produced by the parser.
78
+ * @param parser The combined {@link Parser} to use for parsing the input
79
+ * arguments. Must be a synchronous parser.
80
+ * @param args The array of command-line arguments to parse. Usually this is
81
+ * `process.argv.slice(2)` in Node.js or `Deno.args` in Deno.
82
+ * @param options Optional {@link ParseOptions} for customizing parsing behavior.
83
+ * @returns A {@link Result} object indicating whether the parsing was
84
+ * successful or not. If successful, it contains the parsed value of
85
+ * type `T`. If not, it contains an error message describing the
86
+ * failure.
87
+ * @throws {TypeError} When a synchronous dependency source extractor returns a
88
+ * thenable during completion-time dependency seeding.
89
+ * @since 0.9.0 Renamed from the original `parse` function which now delegates
90
+ * to this for sync parsers.
91
+ * @since 0.10.0 Added optional `options` parameter for annotations support.
92
+ */
93
+ function parseSync(parser, args, options) {
94
+ const initialState = injectAnnotationsIntoState(parser.initialState, options);
95
+ const shouldUnwrapAnnotatedValue = hasMeaningfulAnnotations(options?.annotations) || isInjectedAnnotationWrapper(parser.initialState);
96
+ const exec = {
97
+ usage: parser.usage,
98
+ phase: "parse",
99
+ path: [],
100
+ trace: createInputTrace()
101
+ };
102
+ let context = createParserContext({
103
+ buffer: args,
104
+ state: initialState,
105
+ optionsTerminated: false
106
+ }, exec);
107
+ do {
108
+ const result = parser.parse(context);
109
+ if (!result.success) return {
110
+ success: false,
111
+ error: result.error
112
+ };
113
+ const previousBuffer = context.buffer;
114
+ context = result.next;
115
+ if (isBufferUnchanged(previousBuffer, context.buffer)) return {
116
+ success: false,
117
+ error: message`Unexpected option or argument: ${context.buffer[0]}.`
118
+ };
119
+ } while (context.buffer.length > 0);
120
+ const runtime = createDependencyRuntimeContext();
121
+ const completeExec = {
122
+ ...exec,
123
+ phase: "complete",
124
+ dependencyRuntime: runtime,
125
+ dependencyRegistry: runtime.registry,
126
+ trace: context.exec?.trace ?? context.trace ?? exec.trace
127
+ };
128
+ const endResult = parser.complete(context.state, completeExec);
129
+ return endResult.success ? {
130
+ success: true,
131
+ value: shouldUnwrapAnnotatedValue ? unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value,
132
+ ...endResult.deferred ? { deferred: true } : {},
133
+ ...endResult.deferredKeys ? { deferredKeys: endResult.deferredKeys } : {}
134
+ } : {
135
+ success: false,
136
+ error: endResult.error
137
+ };
138
+ }
139
+ /**
140
+ * Returns `true` when the buffer has not changed between iterations,
141
+ * indicating a parser is stalling without consuming input.
142
+ */
143
+ function isBufferUnchanged(previous, current) {
144
+ return current.length > 0 && current.length === previous.length && current.every((item, i) => item === previous[i]);
145
+ }
146
+ /**
147
+ * Parses an array of command-line arguments using the provided combined parser.
148
+ * This function processes the input arguments, applying the parser to each
149
+ * argument until all arguments are consumed or an error occurs.
150
+ *
151
+ * This function accepts any parser (sync or async) and always returns a Promise.
152
+ * For synchronous parsing with sync parsers, use {@link parseSync} instead.
153
+ *
154
+ * @template T The type of the value produced by the parser.
155
+ * @param parser The combined {@link Parser} to use for parsing the input
156
+ * arguments.
157
+ * @param args The array of command-line arguments to parse. Usually this is
158
+ * `process.argv.slice(2)` in Node.js or `Deno.args` in Deno.
159
+ * @param options Optional {@link ParseOptions} for customizing parsing behavior.
160
+ * @returns A Promise that resolves to a {@link Result} object indicating
161
+ * whether the parsing was successful or not.
162
+ * @since 0.9.0
163
+ * @since 0.10.0 Added optional `options` parameter for annotations support.
164
+ */
165
+ async function parseAsync(parser, args, options) {
166
+ const initialState = injectAnnotationsIntoState(parser.initialState, options);
167
+ const shouldUnwrapAnnotatedValue = hasMeaningfulAnnotations(options?.annotations) || isInjectedAnnotationWrapper(parser.initialState);
168
+ const exec = {
169
+ usage: parser.usage,
170
+ phase: "parse",
171
+ path: [],
172
+ trace: createInputTrace()
173
+ };
174
+ let context = createParserContext({
175
+ buffer: args,
176
+ state: initialState,
177
+ optionsTerminated: false
178
+ }, exec);
179
+ do {
180
+ const result = await parser.parse(context);
181
+ if (!result.success) return {
182
+ success: false,
183
+ error: result.error
184
+ };
185
+ const previousBuffer = context.buffer;
186
+ context = result.next;
187
+ if (isBufferUnchanged(previousBuffer, context.buffer)) return {
188
+ success: false,
189
+ error: message`Unexpected option or argument: ${context.buffer[0]}.`
190
+ };
191
+ } while (context.buffer.length > 0);
192
+ const runtime = createDependencyRuntimeContext();
193
+ const completeExec = {
194
+ ...exec,
195
+ phase: "complete",
196
+ dependencyRuntime: runtime,
197
+ dependencyRegistry: runtime.registry,
198
+ trace: context.exec?.trace ?? context.trace ?? exec.trace
199
+ };
200
+ const endResult = await parser.complete(context.state, completeExec);
201
+ return endResult.success ? {
202
+ success: true,
203
+ value: shouldUnwrapAnnotatedValue ? unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value,
204
+ ...endResult.deferred ? { deferred: true } : {},
205
+ ...endResult.deferredKeys ? { deferredKeys: endResult.deferredKeys } : {}
206
+ } : {
207
+ success: false,
208
+ error: endResult.error
209
+ };
210
+ }
211
+ /**
212
+ * Parses an array of command-line arguments using the provided combined parser.
213
+ * This function processes the input arguments, applying the parser to each
214
+ * argument until all arguments are consumed or an error occurs.
215
+ *
216
+ * The return type depends on the parser's mode:
217
+ * - Sync parsers return `Result<T>` directly.
218
+ * - Async parsers return `Promise<Result<T>>`.
219
+ *
220
+ * For explicit control, use {@link parseSync} or {@link parseAsync}.
221
+ *
222
+ * @template M The execution mode of the parser.
223
+ * @template T The type of the value produced by the parser.
224
+ * @param parser The combined {@link Parser} to use for parsing the input
225
+ * arguments.
226
+ * @param args The array of command-line arguments to parse. Usually this is
227
+ * `process.argv.slice(2)` in Node.js or `Deno.args` in Deno.
228
+ * @param options Optional {@link ParseOptions} for customizing parsing behavior.
229
+ * @returns A {@link Result} object (for sync) or Promise thereof (for async)
230
+ * indicating whether the parsing was successful or not.
231
+ * @throws {TypeError} When a synchronous dependency source extractor returns a
232
+ * thenable during completion-time dependency seeding.
233
+ * @since 0.10.0 Added optional `options` parameter for annotations support.
234
+ */
235
+ function parse(parser, args, options) {
236
+ return dispatchByMode(parser.mode, () => parseSync(parser, args, options), () => parseAsync(parser, args, options));
237
+ }
238
+ /**
239
+ * Generates command-line suggestions based on current parsing state.
240
+ * This function processes the input arguments up to the last argument,
241
+ * then calls the parser's suggest method with the remaining prefix.
242
+ *
243
+ * This function only accepts synchronous parsers. For asynchronous parsers,
244
+ * use {@link suggestAsync}.
245
+ *
246
+ * @template T The type of the value produced by the parser.
247
+ * @param parser The {@link Parser} to use for generating suggestions.
248
+ * Must be a synchronous parser.
249
+ * @param args The array of command-line arguments including the partial
250
+ * argument to complete. The last element is treated as
251
+ * the prefix for suggestions.
252
+ * @param options Optional {@link ParseOptions} for customizing parsing behavior.
253
+ * @returns An array of {@link Suggestion} objects containing completion
254
+ * candidates.
255
+ * @example
256
+ * ```typescript
257
+ * const parser = object({
258
+ * verbose: option("-v", "--verbose"),
259
+ * format: option("-f", "--format", choice(["json", "yaml"]))
260
+ * });
261
+ *
262
+ * // Get suggestions for options starting with "--"
263
+ * const suggestions = suggestSync(parser, ["--"]);
264
+ * // Returns: [{ text: "--verbose" }, { text: "--format" }]
265
+ *
266
+ * // Get suggestions after parsing some arguments
267
+ * const suggestions2 = suggestSync(parser, ["-v", "--format="]);
268
+ * // Returns: [{ text: "--format=json" }, { text: "--format=yaml" }]
269
+ * ```
270
+ * @throws {TypeError} When a synchronous dependency source extractor returns a
271
+ * thenable during suggestion seeding.
272
+ * @since 0.6.0
273
+ * @since 0.9.0 Renamed from the original `suggest` function.
274
+ * @since 0.10.0 Added optional `options` parameter for annotations support.
275
+ */
276
+ function suggestSync(parser, args, options) {
277
+ const allButLast = args.slice(0, -1);
278
+ const prefix = args[args.length - 1];
279
+ const initialState = injectAnnotationsIntoState(parser.initialState, options);
280
+ let context = createParserContext({
281
+ buffer: allButLast,
282
+ state: initialState,
283
+ optionsTerminated: false
284
+ }, {
285
+ usage: parser.usage,
286
+ phase: "suggest",
287
+ path: [],
288
+ trace: createInputTrace()
289
+ });
290
+ while (context.buffer.length > 0) {
291
+ const result = parser.parse(context);
292
+ if (!result.success) return Array.from(parser.suggest(withSuggestRuntime(parser, context), prefix));
293
+ const previousBuffer = context.buffer;
294
+ context = result.next;
295
+ if (isBufferUnchanged(previousBuffer, context.buffer)) return [];
296
+ }
297
+ return Array.from(parser.suggest(withSuggestRuntime(parser, context), prefix));
298
+ }
299
+ /**
300
+ * Creates a dependency runtime from the current parser state and returns
301
+ * a context with the populated registry. Used by top-level suggest
302
+ * functions to mirror the construct-owned model where suggest() receives
303
+ * a context with a dependency registry.
304
+ * @internal
305
+ */
306
+ function withSuggestRuntime(parser, context) {
307
+ const runtime = createDependencyRuntimeContext();
308
+ const nodes = getParserSuggestRuntimeNodes(parser, context.state, context.exec?.path ?? []);
309
+ if (nodes.length > 0) collectExplicitSourceValues(nodes, runtime);
310
+ return {
311
+ ...context,
312
+ dependencyRegistry: runtime.registry,
313
+ exec: context.exec ? {
314
+ ...context.exec,
315
+ dependencyRuntime: runtime,
316
+ dependencyRegistry: runtime.registry
317
+ } : void 0
318
+ };
319
+ }
320
+ async function withSuggestRuntimeAsync(parser, context) {
321
+ const runtime = createDependencyRuntimeContext();
322
+ const nodes = getParserSuggestRuntimeNodes(parser, context.state, context.exec?.path ?? []);
323
+ if (nodes.length > 0) await collectExplicitSourceValuesAsync(nodes, runtime);
324
+ return {
325
+ ...context,
326
+ dependencyRegistry: runtime.registry,
327
+ exec: context.exec ? {
328
+ ...context.exec,
329
+ dependencyRuntime: runtime,
330
+ dependencyRegistry: runtime.registry
331
+ } : void 0
332
+ };
333
+ }
334
+ /**
335
+ * Returns suggest-time runtime nodes for a parser, falling back to the
336
+ * parser's own source metadata when it does not expose a custom hook.
337
+ *
338
+ * @param parser The parser whose suggest-time runtime nodes should be resolved.
339
+ * @param state The current parser state.
340
+ * @param path The path to this parser within the parse tree.
341
+ * @returns The runtime nodes used to seed suggest-time dependency resolution.
342
+ * @internal
343
+ */
344
+ function getParserSuggestRuntimeNodes(parser, state, path) {
345
+ if (typeof parser.getSuggestRuntimeNodes === "function") return parser.getSuggestRuntimeNodes(state, path);
346
+ if (parser.dependencyMetadata?.source == null) return [];
347
+ return [{
348
+ path,
349
+ parser,
350
+ state
351
+ }];
352
+ }
353
+ /**
354
+ * Returns wrapper-aware suggest-time runtime nodes for parsers that delegate
355
+ * to an inner parser while also exposing their own source metadata.
356
+ *
357
+ * The inner parser's nodes are always preserved so nested wrappers and
358
+ * constructs can continue to seed the dependency runtime recursively. When
359
+ * the outer parser itself owns source metadata, its `(path, parser, state)`
360
+ * node is appended so outer precedence rules still apply.
361
+ *
362
+ * @internal
363
+ */
364
+ function getDelegatingSuggestRuntimeNodes(innerParser, outerParser, state, path, innerState, outerPosition = "append") {
365
+ const innerNodes = getParserSuggestRuntimeNodes(innerParser, innerState, path);
366
+ if (outerParser.dependencyMetadata?.source == null) return innerNodes;
367
+ const outerNode = {
368
+ path,
369
+ parser: outerParser,
370
+ state
371
+ };
372
+ return outerPosition === "prepend" ? [outerNode, ...innerNodes] : [...innerNodes, outerNode];
373
+ }
374
+ /**
375
+ * Composes source metadata for a wrapper parser while preserving any derived
376
+ * or transform capabilities from the inner parser unchanged.
377
+ *
378
+ * @internal
379
+ */
380
+ function composeWrappedSourceMetadata(dependencyMetadata, wrapSource) {
381
+ if (dependencyMetadata?.source == null) return dependencyMetadata;
382
+ return {
383
+ ...dependencyMetadata,
384
+ source: wrapSource(dependencyMetadata.source)
385
+ };
386
+ }
387
+ /**
388
+ * Marks a parser as inheriting parent-state annotations through wrapper-state
389
+ * reconstruction.
390
+ *
391
+ * @internal
392
+ */
393
+ function defineInheritedAnnotationParser(parser) {
394
+ Object.defineProperty(parser, inheritParentAnnotationsKey, {
395
+ value: true,
396
+ configurable: true,
397
+ enumerable: false
398
+ });
399
+ }
400
+ /**
401
+ * Marks a wrapper parser as requiring a real source-binding state before
402
+ * annotation-only primitive wrappers should trigger completion.
403
+ *
404
+ * @internal
405
+ */
406
+ function defineSourceBindingOnlyAnnotationCompletionParser(parser) {
407
+ Object.defineProperty(parser, annotationWrapperRequiresSourceBindingKey, {
408
+ value: true,
409
+ configurable: true,
410
+ enumerable: false
411
+ });
412
+ }
413
+ /**
414
+ * Generates command-line suggestions based on current parsing state.
415
+ * This function processes the input arguments up to the last argument,
416
+ * then calls the parser's suggest method with the remaining prefix.
417
+ *
418
+ * This function accepts any parser (sync or async) and always returns a Promise.
419
+ * For synchronous suggestion generation with sync parsers, use
420
+ * {@link suggestSync} instead.
421
+ *
422
+ * @template T The type of the value produced by the parser.
423
+ * @param parser The {@link Parser} to use for generating suggestions.
424
+ * @param args The array of command-line arguments including the partial
425
+ * argument to complete. The last element is treated as
426
+ * the prefix for suggestions.
427
+ * @param options Optional {@link ParseOptions} for customizing parsing behavior.
428
+ * @returns A Promise that resolves to an array of {@link Suggestion} objects
429
+ * containing completion candidates.
430
+ * @since 0.9.0
431
+ * @since 0.10.0 Added optional `options` parameter for annotations support.
432
+ */
433
+ async function suggestAsync(parser, args, options) {
434
+ const allButLast = args.slice(0, -1);
435
+ const prefix = args[args.length - 1];
436
+ const initialState = injectAnnotationsIntoState(parser.initialState, options);
437
+ let context = createParserContext({
438
+ buffer: allButLast,
439
+ state: initialState,
440
+ optionsTerminated: false
441
+ }, {
442
+ usage: parser.usage,
443
+ phase: "suggest",
444
+ path: [],
445
+ trace: createInputTrace()
446
+ });
447
+ while (context.buffer.length > 0) {
448
+ const result = await parser.parse(context);
449
+ if (!result.success) {
450
+ const ctx$1 = await withSuggestRuntimeAsync(parser, context);
451
+ const suggestions$1 = [];
452
+ for await (const suggestion of parser.suggest(ctx$1, prefix)) suggestions$1.push(suggestion);
453
+ return suggestions$1;
454
+ }
455
+ const previousBuffer = context.buffer;
456
+ context = result.next;
457
+ if (isBufferUnchanged(previousBuffer, context.buffer)) return [];
458
+ }
459
+ const ctx = await withSuggestRuntimeAsync(parser, context);
460
+ const suggestions = [];
461
+ for await (const suggestion of parser.suggest(ctx, prefix)) suggestions.push(suggestion);
462
+ return suggestions;
463
+ }
464
+ /**
465
+ * Generates command-line suggestions based on current parsing state.
466
+ * This function processes the input arguments up to the last argument,
467
+ * then calls the parser's suggest method with the remaining prefix.
468
+ *
469
+ * The return type depends on the parser's mode:
470
+ * - Sync parsers return `readonly Suggestion[]` directly.
471
+ * - Async parsers return `Promise<readonly Suggestion[]>`.
472
+ *
473
+ * For explicit control, use {@link suggestSync} or {@link suggestAsync}.
474
+ *
475
+ * @template M The execution mode of the parser.
476
+ * @template T The type of the value produced by the parser.
477
+ * @param parser The {@link Parser} to use for generating suggestions.
478
+ * @param args The array of command-line arguments including the partial
479
+ * argument to complete. The last element is treated as
480
+ * the prefix for suggestions.
481
+ * @param options Optional {@link ParseOptions} for customizing parsing behavior.
482
+ * @returns An array of {@link Suggestion} objects (for sync) or Promise thereof
483
+ * (for async) containing completion candidates.
484
+ * @throws {TypeError} When a synchronous dependency source extractor returns a
485
+ * thenable during suggestion seeding.
486
+ * @since 0.6.0
487
+ * @since 0.10.0 Added optional `options` parameter for annotations support.
488
+ */
489
+ function suggest(parser, args, options) {
490
+ return dispatchByMode(parser.mode, () => suggestSync(parser, args, options), () => suggestAsync(parser, args, options));
491
+ }
492
+ /**
493
+ * Recursively searches for a command within nested exclusive usage terms.
494
+ * When the command is found, returns the expanded usage terms for that command.
495
+ *
496
+ * @param term The usage term to search in
497
+ * @param commandName The command name to find
498
+ * @returns The expanded usage terms if found, null otherwise
499
+ */
500
+ function findCommandInExclusive(term, commandName) {
501
+ if (term.type !== "exclusive") return null;
502
+ for (const termGroup of term.terms) {
503
+ const firstTerm = termGroup[0];
504
+ if (firstTerm?.type === "command" && firstTerm.name === commandName) return termGroup;
505
+ if (firstTerm?.type === "exclusive") {
506
+ const found = findCommandInExclusive(firstTerm, commandName);
507
+ if (found) return [...found, ...termGroup.slice(1)];
508
+ }
509
+ }
510
+ return null;
511
+ }
512
+ /**
513
+ * Generates a documentation page for a synchronous parser.
514
+ *
515
+ * This is the sync-specific version of {@link getDocPage}. It only accepts
516
+ * sync parsers and returns the documentation page directly (not wrapped
517
+ * in a Promise).
518
+ *
519
+ * @param parser The sync parser to generate documentation for.
520
+ * @param argsOrOptions Optional array of command-line arguments for context,
521
+ * or a {@link ParseOptions} object for annotations. When a
522
+ * `ParseOptions` is passed here, the `options` parameter is ignored.
523
+ * @param options Optional {@link ParseOptions} for customizing parsing
524
+ * behavior. Only used when `argsOrOptions` is an array or omitted.
525
+ * @returns A {@link DocPage} or `undefined`.
526
+ * @since 0.9.0
527
+ * @since 0.10.0 Added optional `options` parameter for annotations support.
528
+ * @since 1.0.0 The second parameter now also accepts a `ParseOptions` object
529
+ * directly.
530
+ */
531
+ function getDocPageSync(parser, argsOrOptions, options) {
532
+ if (Array.isArray(argsOrOptions)) return getDocPageSyncImpl(parser, argsOrOptions, options);
533
+ return getDocPageSyncImpl(parser, [], argsOrOptions ?? options);
534
+ }
535
+ /**
536
+ * Generates a documentation page for any parser, returning a Promise.
537
+ *
538
+ * This function accepts parsers of any mode (sync or async) and always
539
+ * returns a Promise. Use this when working with parsers that may contain
540
+ * async value parsers.
541
+ *
542
+ * @param parser The parser to generate documentation for.
543
+ * @param argsOrOptions Optional array of command-line arguments for context,
544
+ * or a {@link ParseOptions} object for annotations. When a
545
+ * `ParseOptions` is passed here, the `options` parameter is ignored.
546
+ * @param options Optional {@link ParseOptions} for customizing parsing
547
+ * behavior. Only used when `argsOrOptions` is an array or omitted.
548
+ * @returns A Promise of {@link DocPage} or `undefined`.
549
+ * @since 0.9.0
550
+ * @since 0.10.0 Added optional `options` parameter for annotations support.
551
+ * @since 1.0.0 The second parameter now also accepts a `ParseOptions` object
552
+ * directly.
553
+ */
554
+ function getDocPageAsync(parser, argsOrOptions, options) {
555
+ const args = Array.isArray(argsOrOptions) ? argsOrOptions : [];
556
+ const opts = Array.isArray(argsOrOptions) ? options : argsOrOptions ?? options;
557
+ if (parser.mode === "sync") return Promise.resolve(getDocPageSyncImpl(parser, args, opts));
558
+ return getDocPageAsyncImpl(parser, args, opts);
559
+ }
560
+ function getDocPage(parser, argsOrOptions, options) {
561
+ const args = Array.isArray(argsOrOptions) ? argsOrOptions : [];
562
+ const opts = Array.isArray(argsOrOptions) ? options : argsOrOptions ?? options;
563
+ if (parser.mode === "sync") return getDocPageSyncImpl(parser, args, opts);
564
+ return getDocPageAsyncImpl(parser, args, opts);
565
+ }
566
+ /**
567
+ * Internal sync implementation of getDocPage.
568
+ */
569
+ function getDocPageSyncImpl(parser, args, options) {
570
+ const initialState = injectAnnotationsIntoState(parser.initialState, options);
571
+ const exec = {
572
+ usage: parser.usage,
573
+ phase: "parse",
574
+ path: [],
575
+ trace: createInputTrace()
576
+ };
577
+ let context = createParserContext({
578
+ buffer: args,
579
+ state: initialState,
580
+ optionsTerminated: false
581
+ }, exec);
582
+ while (context.buffer.length > 0) {
583
+ const result = parser.parse(context);
584
+ if (!result.success) break;
585
+ const previousBuffer = context.buffer;
586
+ context = result.next;
587
+ if (isBufferUnchanged(previousBuffer, context.buffer)) break;
588
+ }
589
+ return buildDocPage(parser, context, args);
590
+ }
591
+ /**
592
+ * Internal async implementation of getDocPage.
593
+ */
594
+ async function getDocPageAsyncImpl(parser, args, options) {
595
+ const initialState = injectAnnotationsIntoState(parser.initialState, options);
596
+ const exec = {
597
+ usage: parser.usage,
598
+ phase: "parse",
599
+ path: [],
600
+ trace: createInputTrace()
601
+ };
602
+ let context = createParserContext({
603
+ buffer: args,
604
+ state: initialState,
605
+ optionsTerminated: false
606
+ }, exec);
607
+ while (context.buffer.length > 0) {
608
+ const result = await parser.parse(context);
609
+ if (!result.success) break;
610
+ const previousBuffer = context.buffer;
611
+ context = result.next;
612
+ if (isBufferUnchanged(previousBuffer, context.buffer)) break;
613
+ }
614
+ return buildDocPage(parser, context, args);
615
+ }
616
+ /**
617
+ * Builds a DocPage from the parser and context.
618
+ * Shared by both sync and async implementations.
619
+ */
620
+ function buildDocPage(parser, context, args) {
621
+ let effectiveArgs = args;
622
+ let { brief, description, fragments, footer } = parser.getDocFragments({
623
+ kind: "available",
624
+ state: context.state
625
+ }, void 0);
626
+ if (args.length === 0 && Reflect.get(parser, Symbol.for("@optique/core/commandParser")) === true && fragments.length === 1 && fragments[0].type === "entry" && fragments[0].term.type === "command") {
627
+ const cmdName = fragments[0].term.name;
628
+ const matchedState = inheritAnnotations(context.state, ["matched", cmdName]);
629
+ const matched = parser.getDocFragments({
630
+ kind: "available",
631
+ state: matchedState
632
+ }, void 0);
633
+ ({brief, description, fragments, footer} = matched);
634
+ effectiveArgs = [cmdName];
635
+ }
636
+ const buildingSections = [];
637
+ let untitledSection = null;
638
+ const titledSectionMap = /* @__PURE__ */ new Map();
639
+ for (const fragment of fragments) if (fragment.type === "entry") {
640
+ if (isDocEntryHidden(fragment)) continue;
641
+ if (untitledSection == null) {
642
+ untitledSection = { entries: [] };
643
+ buildingSections.push(untitledSection);
644
+ }
645
+ untitledSection.entries.push(cloneDocEntry(fragment));
646
+ } else if (fragment.type === "section") {
647
+ const visible = fragment.entries.filter((e) => !isDocEntryHidden(e));
648
+ if (visible.length === 0) continue;
649
+ if (fragment.title == null) {
650
+ if (untitledSection == null) {
651
+ untitledSection = { entries: [] };
652
+ buildingSections.push(untitledSection);
653
+ }
654
+ untitledSection.entries.push(...visible.map(cloneDocEntry));
655
+ } else {
656
+ let section = titledSectionMap.get(fragment.title);
657
+ if (section == null) {
658
+ section = {
659
+ title: fragment.title,
660
+ entries: []
661
+ };
662
+ titledSectionMap.set(fragment.title, section);
663
+ buildingSections.push(section);
664
+ }
665
+ section.entries.push(...visible.map(cloneDocEntry));
666
+ }
667
+ }
668
+ const sections = buildingSections;
669
+ const usage = [...normalizeUsage(parser.usage)];
670
+ const maybeApplyCommandUsageLine = (term, arg, isLastArg, usageIndex) => {
671
+ if (term?.type !== "command" || term.name !== arg || !isLastArg || term.usageLine == null) return;
672
+ const defaultUsageLine = cloneUsage(usage.slice(usageIndex + 1));
673
+ const customUsageLine = typeof term.usageLine === "function" ? term.usageLine(defaultUsageLine) : term.usageLine;
674
+ const normalizedCustomUsageLine = normalizeUsage(customUsageLine);
675
+ usage.splice(usageIndex + 1, usage.length - (usageIndex + 1), ...normalizedCustomUsageLine);
676
+ };
677
+ let i = 0;
678
+ for (let argIndex = 0; argIndex < effectiveArgs.length; argIndex++) {
679
+ const arg = effectiveArgs[argIndex];
680
+ if (i >= usage.length) break;
681
+ let term = usage[i];
682
+ if (term.type === "exclusive") {
683
+ const found = findCommandInExclusive(term, arg);
684
+ if (found) {
685
+ usage.splice(i, 1, ...found);
686
+ term = usage[i];
687
+ }
688
+ }
689
+ maybeApplyCommandUsageLine(term, arg, argIndex === effectiveArgs.length - 1, i);
690
+ i++;
691
+ }
692
+ if (effectiveArgs.length === 0 && usage.length > 0) {
693
+ const first = usage[0];
694
+ if (first.type === "command" && first.usageLine != null) {
695
+ const defaultUsageLine = cloneUsage(usage.slice(1));
696
+ const customUsageLine = typeof first.usageLine === "function" ? first.usageLine(defaultUsageLine) : first.usageLine;
697
+ const normalizedCustomUsageLine = normalizeUsage(customUsageLine);
698
+ usage.splice(1, usage.length - 1, ...normalizedCustomUsageLine);
699
+ }
700
+ }
701
+ return {
702
+ usage,
703
+ sections,
704
+ ...brief != null && { brief: cloneMessage(brief) },
705
+ ...description != null && { description: cloneMessage(description) },
706
+ ...footer != null && { footer: cloneMessage(footer) }
707
+ };
708
+ }
709
+
710
+ //#endregion
711
+ export { annotationWrapperRequiresSourceBindingKey, composeWrappedSourceMetadata, createParserContext, defineInheritedAnnotationParser, defineSourceBindingOnlyAnnotationCompletionParser, getDelegatingSuggestRuntimeNodes, getDocPage, getDocPageAsync, getDocPageSync, getParserSuggestRuntimeNodes, inheritParentAnnotationsKey, parse, parseAsync, parseSync, suggest, suggestAsync, suggestSync, unmatchedNonCliDependencySourceStateMarker };