@optique/core 1.0.0-dev.1901 → 1.0.0-dev.1970

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