@optique/core 1.0.0-dev.1616 → 1.0.0-dev.1658

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/parser.js CHANGED
@@ -1,16 +1,29 @@
1
1
  import { injectAnnotations, isInjectedAnnotationWrapper, unwrapInjectedAnnotationWrapper } from "./annotations.js";
2
2
  import { cloneMessage, message } from "./message.js";
3
- import { isDeferredParseState } from "./dependency.js";
4
- import { collectSourcesFromState, createDependencyRuntimeContext, resolveStateWithRuntime, resolveStateWithRuntimeAsync } from "./dependency-runtime.js";
5
- import { dispatchByMode } from "./mode-dispatch.js";
6
3
  import { cloneUsage, normalizeUsage } from "./usage.js";
7
4
  import { cloneDocEntry, isDocEntryHidden } from "./doc.js";
8
- import { DuplicateOptionError, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
5
+ import { dispatchByMode } from "./mode-dispatch.js";
6
+ import { createInputTrace } from "./input-trace.js";
9
7
  import { WithDefaultError, map, multiple, nonEmpty, optional, withDefault } from "./modifiers.js";
10
8
  import { argument, command, constant, fail, flag, option, passThrough } from "./primitives.js";
9
+ import { collectExplicitSourceValues, collectExplicitSourceValuesAsync, createDependencyRuntimeContext } from "./dependency-runtime.js";
10
+ import { DuplicateOptionError, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
11
11
 
12
12
  //#region src/parser.ts
13
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
+ /**
14
27
  * Creates a {@link ParserContext} from a {@link ParseFrame} and an
15
28
  * {@link ExecutionContext}. The returned object provides both structured
16
29
  * access (`frame`, `exec`) and flat access (`buffer`, `state`, etc.)
@@ -25,6 +38,7 @@ import { argument, command, constant, fail, flag, option, passThrough } from "./
25
38
  function createParserContext(frame, exec) {
26
39
  return {
27
40
  exec,
41
+ trace: exec.trace,
28
42
  buffer: frame.buffer,
29
43
  state: frame.state,
30
44
  optionsTerminated: frame.optionsTerminated,
@@ -55,6 +69,8 @@ function injectAnnotationsIntoState(state, options) {
55
69
  * successful or not. If successful, it contains the parsed value of
56
70
  * type `T`. If not, it contains an error message describing the
57
71
  * failure.
72
+ * @throws {TypeError} When a synchronous dependency source extractor returns a
73
+ * thenable during completion-time dependency seeding.
58
74
  * @since 0.9.0 Renamed from the original `parse` function which now delegates
59
75
  * to this for sync parsers.
60
76
  * @since 0.10.0 Added optional `options` parameter for annotations support.
@@ -65,7 +81,8 @@ function parseSync(parser, args, options) {
65
81
  const exec = {
66
82
  usage: parser.usage,
67
83
  phase: "parse",
68
- path: []
84
+ path: [],
85
+ trace: createInputTrace()
69
86
  };
70
87
  let context = createParserContext({
71
88
  buffer: args,
@@ -86,14 +103,14 @@ function parseSync(parser, args, options) {
86
103
  };
87
104
  } while (context.buffer.length > 0);
88
105
  const runtime = createDependencyRuntimeContext();
89
- const resolvedState = isDeferredParseState(context.state) ? resolveStateWithRuntime(context.state, runtime) : context.state;
90
106
  const completeExec = {
91
107
  ...exec,
92
108
  phase: "complete",
93
109
  dependencyRuntime: runtime,
94
- dependencyRegistry: runtime.registry
110
+ dependencyRegistry: runtime.registry,
111
+ trace: context.exec?.trace ?? context.trace ?? exec.trace
95
112
  };
96
- const endResult = parser.complete(resolvedState, completeExec);
113
+ const endResult = parser.complete(context.state, completeExec);
97
114
  return endResult.success ? {
98
115
  success: true,
99
116
  value: shouldUnwrapAnnotatedValue ? unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value,
@@ -136,7 +153,8 @@ async function parseAsync(parser, args, options) {
136
153
  const exec = {
137
154
  usage: parser.usage,
138
155
  phase: "parse",
139
- path: []
156
+ path: [],
157
+ trace: createInputTrace()
140
158
  };
141
159
  let context = createParserContext({
142
160
  buffer: args,
@@ -157,14 +175,14 @@ async function parseAsync(parser, args, options) {
157
175
  };
158
176
  } while (context.buffer.length > 0);
159
177
  const runtime = createDependencyRuntimeContext();
160
- const resolvedState = isDeferredParseState(context.state) ? await resolveStateWithRuntimeAsync(context.state, runtime) : context.state;
161
178
  const completeExec = {
162
179
  ...exec,
163
180
  phase: "complete",
164
181
  dependencyRuntime: runtime,
165
- dependencyRegistry: runtime.registry
182
+ dependencyRegistry: runtime.registry,
183
+ trace: context.exec?.trace ?? context.trace ?? exec.trace
166
184
  };
167
- const endResult = await parser.complete(resolvedState, completeExec);
185
+ const endResult = await parser.complete(context.state, completeExec);
168
186
  return endResult.success ? {
169
187
  success: true,
170
188
  value: shouldUnwrapAnnotatedValue ? unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value,
@@ -195,6 +213,8 @@ async function parseAsync(parser, args, options) {
195
213
  * @param options Optional {@link ParseOptions} for customizing parsing behavior.
196
214
  * @returns A {@link Result} object (for sync) or Promise thereof (for async)
197
215
  * indicating whether the parsing was successful or not.
216
+ * @throws {TypeError} When a synchronous dependency source extractor returns a
217
+ * thenable during completion-time dependency seeding.
198
218
  * @since 0.10.0 Added optional `options` parameter for annotations support.
199
219
  */
200
220
  function parse(parser, args, options) {
@@ -232,6 +252,8 @@ function parse(parser, args, options) {
232
252
  * const suggestions2 = suggestSync(parser, ["-v", "--format="]);
233
253
  * // Returns: [{ text: "--format=json" }, { text: "--format=yaml" }]
234
254
  * ```
255
+ * @throws {TypeError} When a synchronous dependency source extractor returns a
256
+ * thenable during suggestion seeding.
235
257
  * @since 0.6.0
236
258
  * @since 0.9.0 Renamed from the original `suggest` function.
237
259
  * @since 0.10.0 Added optional `options` parameter for annotations support.
@@ -247,16 +269,17 @@ function suggestSync(parser, args, options) {
247
269
  }, {
248
270
  usage: parser.usage,
249
271
  phase: "suggest",
250
- path: []
272
+ path: [],
273
+ trace: createInputTrace()
251
274
  });
252
275
  while (context.buffer.length > 0) {
253
276
  const result = parser.parse(context);
254
- if (!result.success) return Array.from(parser.suggest(withSuggestRuntime(context), prefix));
277
+ if (!result.success) return Array.from(parser.suggest(withSuggestRuntime(parser, context), prefix));
255
278
  const previousBuffer = context.buffer;
256
279
  context = result.next;
257
280
  if (isBufferUnchanged(previousBuffer, context.buffer)) return [];
258
281
  }
259
- return Array.from(parser.suggest(withSuggestRuntime(context), prefix));
282
+ return Array.from(parser.suggest(withSuggestRuntime(parser, context), prefix));
260
283
  }
261
284
  /**
262
285
  * Creates a dependency runtime from the current parser state and returns
@@ -265,19 +288,54 @@ function suggestSync(parser, args, options) {
265
288
  * a context with a dependency registry.
266
289
  * @internal
267
290
  */
268
- function withSuggestRuntime(context) {
291
+ function withSuggestRuntime(parser, context) {
292
+ const runtime = createDependencyRuntimeContext();
293
+ const nodes = getParserSuggestRuntimeNodes(parser, context.state, context.exec?.path ?? []);
294
+ if (nodes.length > 0) collectExplicitSourceValues(nodes, runtime);
295
+ return {
296
+ ...context,
297
+ dependencyRegistry: runtime.registry,
298
+ exec: context.exec ? {
299
+ ...context.exec,
300
+ dependencyRuntime: runtime,
301
+ dependencyRegistry: runtime.registry
302
+ } : void 0
303
+ };
304
+ }
305
+ async function withSuggestRuntimeAsync(parser, context) {
269
306
  const runtime = createDependencyRuntimeContext();
270
- collectSourcesFromState(context.state, runtime);
307
+ const nodes = getParserSuggestRuntimeNodes(parser, context.state, context.exec?.path ?? []);
308
+ if (nodes.length > 0) await collectExplicitSourceValuesAsync(nodes, runtime);
271
309
  return {
272
310
  ...context,
273
311
  dependencyRegistry: runtime.registry,
274
312
  exec: context.exec ? {
275
313
  ...context.exec,
314
+ dependencyRuntime: runtime,
276
315
  dependencyRegistry: runtime.registry
277
316
  } : void 0
278
317
  };
279
318
  }
280
319
  /**
320
+ * Returns suggest-time runtime nodes for a parser, falling back to the
321
+ * parser's own source metadata when it does not expose a custom hook.
322
+ *
323
+ * @param parser The parser whose suggest-time runtime nodes should be resolved.
324
+ * @param state The current parser state.
325
+ * @param path The path to this parser within the parse tree.
326
+ * @returns The runtime nodes used to seed suggest-time dependency resolution.
327
+ * @internal
328
+ */
329
+ function getParserSuggestRuntimeNodes(parser, state, path) {
330
+ if (typeof parser.getSuggestRuntimeNodes === "function") return parser.getSuggestRuntimeNodes(state, path);
331
+ if (parser.dependencyMetadata?.source == null) return [];
332
+ return [{
333
+ path,
334
+ parser,
335
+ state
336
+ }];
337
+ }
338
+ /**
281
339
  * Generates command-line suggestions based on current parsing state.
282
340
  * This function processes the input arguments up to the last argument,
283
341
  * then calls the parser's suggest method with the remaining prefix.
@@ -308,12 +366,13 @@ async function suggestAsync(parser, args, options) {
308
366
  }, {
309
367
  usage: parser.usage,
310
368
  phase: "suggest",
311
- path: []
369
+ path: [],
370
+ trace: createInputTrace()
312
371
  });
313
372
  while (context.buffer.length > 0) {
314
373
  const result = await parser.parse(context);
315
374
  if (!result.success) {
316
- const ctx$1 = withSuggestRuntime(context);
375
+ const ctx$1 = await withSuggestRuntimeAsync(parser, context);
317
376
  const suggestions$1 = [];
318
377
  for await (const suggestion of parser.suggest(ctx$1, prefix)) suggestions$1.push(suggestion);
319
378
  return suggestions$1;
@@ -322,7 +381,7 @@ async function suggestAsync(parser, args, options) {
322
381
  context = result.next;
323
382
  if (isBufferUnchanged(previousBuffer, context.buffer)) return [];
324
383
  }
325
- const ctx = withSuggestRuntime(context);
384
+ const ctx = await withSuggestRuntimeAsync(parser, context);
326
385
  const suggestions = [];
327
386
  for await (const suggestion of parser.suggest(ctx, prefix)) suggestions.push(suggestion);
328
387
  return suggestions;
@@ -347,6 +406,8 @@ async function suggestAsync(parser, args, options) {
347
406
  * @param options Optional {@link ParseOptions} for customizing parsing behavior.
348
407
  * @returns An array of {@link Suggestion} objects (for sync) or Promise thereof
349
408
  * (for async) containing completion candidates.
409
+ * @throws {TypeError} When a synchronous dependency source extractor returns a
410
+ * thenable during suggestion seeding.
350
411
  * @since 0.6.0
351
412
  * @since 0.10.0 Added optional `options` parameter for annotations support.
352
413
  */
@@ -561,4 +622,4 @@ function buildDocPage(parser, context, args) {
561
622
  }
562
623
 
563
624
  //#endregion
564
- export { DuplicateOptionError, WithDefaultError, argument, command, concat, conditional, constant, createParserContext, fail, flag, getDocPage, getDocPageAsync, getDocPageSync, group, longestMatch, map, merge, multiple, nonEmpty, object, option, optional, or, parse, parseAsync, parseSync, passThrough, suggest, suggestAsync, suggestSync, tuple, withDefault };
625
+ export { DuplicateOptionError, WithDefaultError, argument, command, concat, conditional, constant, createParserContext, fail, flag, getDocPage, getDocPageAsync, getDocPageSync, getParserSuggestRuntimeNodes, group, longestMatch, map, merge, multiple, nonEmpty, object, option, optional, or, parse, parseAsync, parseSync, passThrough, suggest, suggestAsync, suggestSync, tuple, unmatchedNonCliDependencySourceStateMarker, withDefault };