@optique/core 1.3.0-dev.2486 → 1.3.0-dev.2491

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.
@@ -2,7 +2,7 @@ import { hasMeaningfulAnnotations, inheritAnnotations, injectAnnotations, isInje
2
2
  import { cloneMessage, message } from "../message.js";
3
3
  import { cloneUsage, normalizeUsage } from "../usage.js";
4
4
  import { cloneDocEntry, isDocEntryHidden } from "../doc.js";
5
- import { dispatchByMode } from "./mode-dispatch.js";
5
+ import { dispatchByMode, dispatchParserByMode } from "./mode-dispatch.js";
6
6
  import { collectExplicitSourceValues, collectExplicitSourceValuesAsync, createDependencyRuntimeContext } from "../dependency-runtime.js";
7
7
  import { createInputTrace } from "../input-trace.js";
8
8
 
@@ -91,6 +91,160 @@ function createParserContext(frame, exec) {
91
91
  function injectAnnotationsIntoState(state, options) {
92
92
  return injectAnnotations(state, options?.annotations);
93
93
  }
94
+ function getFailureProgress(args, buffer, consumedInStep) {
95
+ return {
96
+ remainingArgs: buffer.slice(consumedInStep),
97
+ consumedCount: args.length - buffer.length + consumedInStep
98
+ };
99
+ }
100
+ function createParseExec(parser) {
101
+ return {
102
+ usage: parser.usage,
103
+ phase: "parse",
104
+ path: [],
105
+ commandPath: [],
106
+ trace: createInputTrace()
107
+ };
108
+ }
109
+ function getCommandPath(exec) {
110
+ return [...exec?.commandPath ?? []];
111
+ }
112
+ function createCompleteExec(exec, context, session) {
113
+ const runtime = createDependencyRuntimeContext();
114
+ return {
115
+ ...exec,
116
+ phase: "complete",
117
+ dependencyRuntime: runtime,
118
+ dependencyRegistry: runtime.registry,
119
+ commandPath: context.exec?.commandPath ?? exec.commandPath,
120
+ trace: context.exec?.trace ?? context.trace ?? exec.trace,
121
+ effectfulCompletionSession: session ?? createEffectfulCompletionSession()
122
+ };
123
+ }
124
+ function attemptParseSync(parser, args, options = {}) {
125
+ const initialState = injectAnnotationsIntoState(parser.initialState, options.parseOptions);
126
+ const shouldUnwrapAnnotatedValue = hasMeaningfulAnnotations(options.parseOptions?.annotations) || isInjectedAnnotationWrapper(parser.initialState);
127
+ const exec = createParseExec(parser);
128
+ let context = createParserContext({
129
+ buffer: [...args],
130
+ state: initialState,
131
+ optionsTerminated: false
132
+ }, exec);
133
+ do {
134
+ const result = parser.parse(context);
135
+ if (!result.success) {
136
+ const progress = getFailureProgress(args, context.buffer, result.consumed);
137
+ return {
138
+ kind: "failure",
139
+ error: result.error,
140
+ remainingArgs: progress.remainingArgs,
141
+ consumedCount: progress.consumedCount,
142
+ optionsTerminated: context.optionsTerminated,
143
+ commandPath: getCommandPath(context.exec)
144
+ };
145
+ }
146
+ const previousBuffer = context.buffer;
147
+ context = result.next;
148
+ if (isBufferUnchanged(previousBuffer, context.buffer)) {
149
+ const progress = getFailureProgress(args, previousBuffer, result.consumed.length);
150
+ return {
151
+ kind: "failure",
152
+ error: message`Unexpected option or argument: ${context.buffer[0]}.`,
153
+ remainingArgs: progress.remainingArgs,
154
+ consumedCount: progress.consumedCount,
155
+ optionsTerminated: context.optionsTerminated,
156
+ commandPath: getCommandPath(context.exec)
157
+ };
158
+ }
159
+ } while (context.buffer.length > 0);
160
+ if (options.mode === "parse-only") return {
161
+ kind: "success",
162
+ result: {
163
+ success: true,
164
+ value: void 0
165
+ }
166
+ };
167
+ const endResult = parser.complete(context.state, createCompleteExec(exec, context, options.session));
168
+ if (!endResult.success) return {
169
+ kind: "failure",
170
+ error: endResult.error,
171
+ remainingArgs: [],
172
+ consumedCount: args.length,
173
+ optionsTerminated: context.optionsTerminated,
174
+ commandPath: getCommandPath(context.exec)
175
+ };
176
+ return {
177
+ kind: "success",
178
+ result: {
179
+ success: true,
180
+ value: shouldUnwrapAnnotatedValue ? unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value,
181
+ ...endResult.deferred ? { deferred: true } : {},
182
+ ...endResult.deferredKeys ? { deferredKeys: endResult.deferredKeys } : {}
183
+ }
184
+ };
185
+ }
186
+ async function attemptParseAsync(parser, args, options = {}) {
187
+ const initialState = injectAnnotationsIntoState(parser.initialState, options.parseOptions);
188
+ const shouldUnwrapAnnotatedValue = hasMeaningfulAnnotations(options.parseOptions?.annotations) || isInjectedAnnotationWrapper(parser.initialState);
189
+ const exec = createParseExec(parser);
190
+ let context = createParserContext({
191
+ buffer: [...args],
192
+ state: initialState,
193
+ optionsTerminated: false
194
+ }, exec);
195
+ do {
196
+ const result = await parser.parse(context);
197
+ if (!result.success) {
198
+ const progress = getFailureProgress(args, context.buffer, result.consumed);
199
+ return {
200
+ kind: "failure",
201
+ error: result.error,
202
+ remainingArgs: progress.remainingArgs,
203
+ consumedCount: progress.consumedCount,
204
+ optionsTerminated: context.optionsTerminated,
205
+ commandPath: getCommandPath(context.exec)
206
+ };
207
+ }
208
+ const previousBuffer = context.buffer;
209
+ context = result.next;
210
+ if (isBufferUnchanged(previousBuffer, context.buffer)) {
211
+ const progress = getFailureProgress(args, previousBuffer, result.consumed.length);
212
+ return {
213
+ kind: "failure",
214
+ error: message`Unexpected option or argument: ${context.buffer[0]}.`,
215
+ remainingArgs: progress.remainingArgs,
216
+ consumedCount: progress.consumedCount,
217
+ optionsTerminated: context.optionsTerminated,
218
+ commandPath: getCommandPath(context.exec)
219
+ };
220
+ }
221
+ } while (context.buffer.length > 0);
222
+ if (options.mode === "parse-only") return {
223
+ kind: "success",
224
+ result: {
225
+ success: true,
226
+ value: void 0
227
+ }
228
+ };
229
+ const endResult = await parser.complete(context.state, createCompleteExec(exec, context, options.session));
230
+ if (!endResult.success) return {
231
+ kind: "failure",
232
+ error: endResult.error,
233
+ remainingArgs: [],
234
+ consumedCount: args.length,
235
+ optionsTerminated: context.optionsTerminated,
236
+ commandPath: getCommandPath(context.exec)
237
+ };
238
+ return {
239
+ kind: "success",
240
+ result: {
241
+ success: true,
242
+ value: shouldUnwrapAnnotatedValue ? unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value,
243
+ ...endResult.deferred ? { deferred: true } : {},
244
+ ...endResult.deferredKeys ? { deferredKeys: endResult.deferredKeys } : {}
245
+ }
246
+ };
247
+ }
94
248
  /**
95
249
  * Parses an array of command-line arguments using the provided combined parser.
96
250
  * This function processes the input arguments, applying the parser to each
@@ -116,51 +270,10 @@ function injectAnnotationsIntoState(state, options) {
116
270
  * @since 0.10.0 Added optional `options` parameter for annotations support.
117
271
  */
118
272
  function parseSync(parser, args, options) {
119
- const initialState = injectAnnotationsIntoState(parser.initialState, options);
120
- const shouldUnwrapAnnotatedValue = hasMeaningfulAnnotations(options?.annotations) || isInjectedAnnotationWrapper(parser.initialState);
121
- const exec = {
122
- usage: parser.usage,
123
- phase: "parse",
124
- path: [],
125
- trace: createInputTrace()
126
- };
127
- let context = createParserContext({
128
- buffer: args,
129
- state: initialState,
130
- optionsTerminated: false
131
- }, exec);
132
- do {
133
- const result = parser.parse(context);
134
- if (!result.success) return {
135
- success: false,
136
- error: result.error
137
- };
138
- const previousBuffer = context.buffer;
139
- context = result.next;
140
- if (isBufferUnchanged(previousBuffer, context.buffer)) return {
141
- success: false,
142
- error: message`Unexpected option or argument: ${context.buffer[0]}.`
143
- };
144
- } while (context.buffer.length > 0);
145
- const runtime = createDependencyRuntimeContext();
146
- const completeExec = {
147
- ...exec,
148
- phase: "complete",
149
- dependencyRuntime: runtime,
150
- dependencyRegistry: runtime.registry,
151
- commandPath: context.exec?.commandPath ?? exec.commandPath,
152
- trace: context.exec?.trace ?? context.trace ?? exec.trace,
153
- effectfulCompletionSession: createEffectfulCompletionSession()
154
- };
155
- const endResult = parser.complete(context.state, completeExec);
156
- return endResult.success ? {
157
- success: true,
158
- value: shouldUnwrapAnnotatedValue ? unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value,
159
- ...endResult.deferred ? { deferred: true } : {},
160
- ...endResult.deferredKeys ? { deferredKeys: endResult.deferredKeys } : {}
161
- } : {
273
+ const attempted = attemptParseSync(parser, args, { parseOptions: options });
274
+ return attempted.kind === "success" ? attempted.result : {
162
275
  success: false,
163
- error: endResult.error
276
+ error: attempted.error
164
277
  };
165
278
  }
166
279
  /**
@@ -190,52 +303,47 @@ function isBufferUnchanged(previous, current) {
190
303
  * @since 0.10.0 Added optional `options` parameter for annotations support.
191
304
  */
192
305
  async function parseAsync(parser, args, options) {
193
- const initialState = injectAnnotationsIntoState(parser.initialState, options);
194
- const shouldUnwrapAnnotatedValue = hasMeaningfulAnnotations(options?.annotations) || isInjectedAnnotationWrapper(parser.initialState);
195
- const exec = {
196
- usage: parser.usage,
197
- phase: "parse",
198
- path: [],
199
- trace: createInputTrace()
306
+ const attempted = await attemptParseAsync(parser, args, { parseOptions: options });
307
+ return attempted.kind === "success" ? attempted.result : {
308
+ success: false,
309
+ error: attempted.error
200
310
  };
201
- let context = createParserContext({
202
- buffer: args,
203
- state: initialState,
204
- optionsTerminated: false
205
- }, exec);
206
- do {
207
- const result = await parser.parse(context);
208
- if (!result.success) return {
311
+ }
312
+ /**
313
+ * Parses a complete argument list while preserving failure progress.
314
+ *
315
+ * The return mode follows the parser: synchronous parsers return immediately,
316
+ * while asynchronous parsers return a promise. Use this lower-level API when
317
+ * a caller needs the unconsumed argument suffix or matched command path.
318
+ *
319
+ * @template M The execution mode of the parser.
320
+ * @template T The type of the value produced by the parser.
321
+ * @param parser The parser to run.
322
+ * @param args The complete argument list to parse.
323
+ * @param options Optional parsing annotations.
324
+ * @returns A detailed success or failure result in the parser's mode.
325
+ * @throws {TypeError} When a synchronous dependency source extractor returns a
326
+ * thenable during completion-time dependency seeding.
327
+ * @since 1.3.0
328
+ */
329
+ function parseDetailed(parser, args, options) {
330
+ return dispatchParserByMode(parser, (parser$1) => {
331
+ const attempted = attemptParseSync(parser$1, args, { parseOptions: options });
332
+ return attempted.kind === "success" ? attempted.result : {
209
333
  success: false,
210
- error: result.error
334
+ error: attempted.error,
335
+ remainingArgs: attempted.remainingArgs,
336
+ commandPath: attempted.commandPath
211
337
  };
212
- const previousBuffer = context.buffer;
213
- context = result.next;
214
- if (isBufferUnchanged(previousBuffer, context.buffer)) return {
338
+ }, async (parser$1) => {
339
+ const attempted = await attemptParseAsync(parser$1, args, { parseOptions: options });
340
+ return attempted.kind === "success" ? attempted.result : {
215
341
  success: false,
216
- error: message`Unexpected option or argument: ${context.buffer[0]}.`
342
+ error: attempted.error,
343
+ remainingArgs: attempted.remainingArgs,
344
+ commandPath: attempted.commandPath
217
345
  };
218
- } while (context.buffer.length > 0);
219
- const runtime = createDependencyRuntimeContext();
220
- const completeExec = {
221
- ...exec,
222
- phase: "complete",
223
- dependencyRuntime: runtime,
224
- dependencyRegistry: runtime.registry,
225
- commandPath: context.exec?.commandPath ?? exec.commandPath,
226
- trace: context.exec?.trace ?? context.trace ?? exec.trace,
227
- effectfulCompletionSession: createEffectfulCompletionSession()
228
- };
229
- const endResult = await parser.complete(context.state, completeExec);
230
- return endResult.success ? {
231
- success: true,
232
- value: shouldUnwrapAnnotatedValue ? unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value,
233
- ...endResult.deferred ? { deferred: true } : {},
234
- ...endResult.deferredKeys ? { deferredKeys: endResult.deferredKeys } : {}
235
- } : {
236
- success: false,
237
- error: endResult.error
238
- };
346
+ });
239
347
  }
240
348
  /**
241
349
  * Parses an array of command-line arguments using the provided combined parser.
@@ -903,4 +1011,4 @@ function findNextMatchedCommandArgIndex(args, matchedCommandArgIndices, start) {
903
1011
  }
904
1012
 
905
1013
  //#endregion
906
- export { annotationWrapperRequiresSourceBindingKey, composeWrappedSourceMetadata, createEffectfulCompletionSession, createParserContext, defineInheritedAnnotationParser, defineParseLanes, defineSourceBindingOnlyAnnotationCompletionParser, getDelegatingSuggestRuntimeNodes, getDocPage, getDocPageAsync, getDocPageSync, getOwnParseLanes, getParserSuggestRuntimeNodes, inheritParentAnnotationsKey, parse, parseAsync, parseLanesKey, parseSync, suggest, suggestAsync, suggestSync, unmatchedNonCliDependencySourceStateMarker };
1014
+ export { annotationWrapperRequiresSourceBindingKey, attemptParseAsync, attemptParseSync, composeWrappedSourceMetadata, createEffectfulCompletionSession, createParserContext, defineInheritedAnnotationParser, defineParseLanes, defineSourceBindingOnlyAnnotationCompletionParser, getDelegatingSuggestRuntimeNodes, getDocPage, getDocPageAsync, getDocPageSync, getOwnParseLanes, getParserSuggestRuntimeNodes, inheritParentAnnotationsKey, parse, parseAsync, parseDetailed, parseLanesKey, parseSync, suggest, suggestAsync, suggestSync, unmatchedNonCliDependencySourceStateMarker };
package/dist/parser.cjs CHANGED
@@ -6,6 +6,7 @@ exports.getDocPageAsync = require_internal_parser.getDocPageAsync;
6
6
  exports.getDocPageSync = require_internal_parser.getDocPageSync;
7
7
  exports.parse = require_internal_parser.parse;
8
8
  exports.parseAsync = require_internal_parser.parseAsync;
9
+ exports.parseDetailed = require_internal_parser.parseDetailed;
9
10
  exports.parseSync = require_internal_parser.parseSync;
10
11
  exports.suggest = require_internal_parser.suggest;
11
12
  exports.suggestAsync = require_internal_parser.suggestAsync;
package/dist/parser.d.cts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { ParseOptions } from "./internal/annotations.cjs";
2
- import { CombineModes, DocState, EffectfulCompletionSession, ExecutionContext, ExecutionPhase, InferMode, InferValue, Mode, ModeIterable, ModeValue, ParseFrame, Parser, ParserContext, ParserResult, Result, Suggestion, createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseSync, suggest, suggestAsync, suggestSync } from "./internal/parser.cjs";
3
- export { type CombineModes, type DocState, type EffectfulCompletionSession, type ExecutionContext, type ExecutionPhase, type InferMode, type InferValue, type Mode, type ModeIterable, type ModeValue, type ParseFrame, type ParseOptions, type Parser, type ParserContext, type ParserResult, type Result, type Suggestion, createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseSync, suggest, suggestAsync, suggestSync };
2
+ import { CombineModes, DetailedParseResult, DocState, EffectfulCompletionSession, ExecutionContext, ExecutionPhase, InferMode, InferValue, Mode, ModeIterable, ModeValue, ParseFrame, Parser, ParserContext, ParserResult, Result, Suggestion, createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseDetailed, parseSync, suggest, suggestAsync, suggestSync } from "./internal/parser.cjs";
3
+ export { type CombineModes, type DetailedParseResult, type DocState, type EffectfulCompletionSession, type ExecutionContext, type ExecutionPhase, type InferMode, type InferValue, type Mode, type ModeIterable, type ModeValue, type ParseFrame, type ParseOptions, type Parser, type ParserContext, type ParserResult, type Result, type Suggestion, createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseDetailed, parseSync, suggest, suggestAsync, suggestSync };
package/dist/parser.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { ParseOptions } from "./internal/annotations.js";
2
- import { CombineModes, DocState, EffectfulCompletionSession, ExecutionContext, ExecutionPhase, InferMode, InferValue, Mode, ModeIterable, ModeValue, ParseFrame, Parser, ParserContext, ParserResult, Result, Suggestion, createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseSync, suggest, suggestAsync, suggestSync } from "./internal/parser.js";
3
- export { type CombineModes, type DocState, type EffectfulCompletionSession, type ExecutionContext, type ExecutionPhase, type InferMode, type InferValue, type Mode, type ModeIterable, type ModeValue, type ParseFrame, type ParseOptions, type Parser, type ParserContext, type ParserResult, type Result, type Suggestion, createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseSync, suggest, suggestAsync, suggestSync };
2
+ import { CombineModes, DetailedParseResult, DocState, EffectfulCompletionSession, ExecutionContext, ExecutionPhase, InferMode, InferValue, Mode, ModeIterable, ModeValue, ParseFrame, Parser, ParserContext, ParserResult, Result, Suggestion, createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseDetailed, parseSync, suggest, suggestAsync, suggestSync } from "./internal/parser.js";
3
+ export { type CombineModes, type DetailedParseResult, type DocState, type EffectfulCompletionSession, type ExecutionContext, type ExecutionPhase, type InferMode, type InferValue, type Mode, type ModeIterable, type ModeValue, type ParseFrame, type ParseOptions, type Parser, type ParserContext, type ParserResult, type Result, type Suggestion, createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseDetailed, parseSync, suggest, suggestAsync, suggestSync };
package/dist/parser.js CHANGED
@@ -1,3 +1,3 @@
1
- import { createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseSync, suggest, suggestAsync, suggestSync } from "./internal/parser.js";
1
+ import { createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseDetailed, parseSync, suggest, suggestAsync, suggestSync } from "./internal/parser.js";
2
2
 
3
- export { createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseSync, suggest, suggestAsync, suggestSync };
3
+ export { createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseDetailed, parseSync, suggest, suggestAsync, suggestSync };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.3.0-dev.2486",
3
+ "version": "1.3.0-dev.2491",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -221,7 +221,7 @@
221
221
  },
222
222
  "sideEffects": false,
223
223
  "devDependencies": {
224
- "@optique/env": "1.3.0-dev.2486+2e98818b",
224
+ "@optique/env": "1.3.0-dev.2491+0b23bdcf",
225
225
  "@types/node": "^24.0.0",
226
226
  "fast-check": "^4.7.0",
227
227
  "tsdown": "^0.13.0",
@@ -22,11 +22,11 @@ they cover the parts agents most often get wrong.
22
22
  Core rules
23
23
  ----------
24
24
 
25
- - Use `run()` from *@optique/run* for real CLI applications. It reads
26
- `process.argv`/`Deno.args`, handles help, version output, errors, exit
27
- codes, colors, terminal width, and shell completion. Use `parse()` from
28
- `@optique/core/parser` or `runParser()` from `@optique/core/facade` when
29
- embedding Optique in tests, libraries, tools, or custom runtimes.
25
+ - Use `run()` from *@optique/run* for applications; use `parse()` or
26
+ `runParser()` for embedded and custom runtimes.
27
+ - In parser-focused tests, use `parseArgs()` or `parseArgsSync()` from
28
+ *@optique/testing/parser* for inferred values and detailed failures without
29
+ running help, output, exits, or command handlers.
30
30
  - Compose parsers with `object()`, `tuple()`, `seq()`, `or()`, `merge()`, and
31
31
  modifiers. Do not write imperative `if`/`else` argument scanners around
32
32
  Optique parsers.