@optique/core 1.1.3 → 1.1.5

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.
@@ -42,6 +42,51 @@ type ModeValue<M extends Mode, T> = M extends "async" ? Promise<T> : T;
42
42
  * @since 0.9.0
43
43
  */
44
44
  type ModeIterable<M extends Mode, T> = M extends "async" ? AsyncIterable<T> : Iterable<T>;
45
+ /**
46
+ * Internal symbol used by transparent combinators to expose independently
47
+ * competing parse operations to shared-buffer parents.
48
+ * @internal
49
+ */
50
+ declare const parseLanesKey: unique symbol;
51
+ /**
52
+ * An owner-consumption requirement for a parse lane.
53
+ * @internal
54
+ */
55
+ interface ParseLaneConsumptionGroup {
56
+ /** Identity shared by lanes belonging to the same owning parser. */
57
+ readonly id: object;
58
+ /** Whether the owning parser is active after a consuming lane succeeds. */
59
+ readonly isActive?: (state: unknown) => boolean;
60
+ }
61
+ /**
62
+ * A state-preserving parse operation that competes at one exact priority.
63
+ * @internal
64
+ */
65
+ interface ParseLane<TState> {
66
+ /** The exact priority at which this lane competes. */
67
+ readonly priority: number;
68
+ /**
69
+ * Identity shared by lanes that must all succeed before their
70
+ * zero-consumption state updates can be committed.
71
+ */
72
+ readonly zeroConsumptionGroup?: object;
73
+ /**
74
+ * Whether a zero-consumption success can settle the owning parser while
75
+ * input remains. Defaults to `true`.
76
+ */
77
+ readonly settlesZeroConsumption?: boolean;
78
+ /** Fixed tokens reachable through this lane. */
79
+ readonly leadingNames: ReadonlySet<string>;
80
+ /** Whether this lane accepts any positional token. */
81
+ readonly acceptingAnyToken: boolean;
82
+ /**
83
+ * Groups in which a zero-consumption update is valid only after another
84
+ * lane in every group has consumed input during the same arbitration.
85
+ */
86
+ readonly requiredConsumptionGroups?: readonly ParseLaneConsumptionGroup[];
87
+ /** Parses through the owning parser's state and execution context. */
88
+ parse(context: ParserContext<TState>): ParserResult<TState> | Promise<ParserResult<TState>>;
89
+ }
45
90
  /**
46
91
  * Combines multiple modes into a single mode.
47
92
  * If any mode is `"async"`, the result is `"async"`; otherwise `"sync"`.
@@ -162,6 +207,13 @@ interface Parser<M extends Mode = "sync", TValue = unknown, TState = unknown> {
162
207
  * state when parsing starts.
163
208
  */
164
209
  readonly initialState: TState;
210
+ /**
211
+ * Independently competing parse operations exposed by transparent
212
+ * combinators. Shared-buffer parents use these to arbitrate below an
213
+ * aggregate parser boundary without bypassing the owner's state adapters.
214
+ * @internal
215
+ */
216
+ readonly [parseLanesKey]?: readonly ParseLane<TState>[];
165
217
  /**
166
218
  * Internal marker for wrappers whose `{ hasCliValue: false }` states should
167
219
  * be treated as unmatched dependency-source states during completion-time
@@ -851,6 +903,22 @@ declare function getDelegatingSuggestRuntimeNodes<TInnerState>(innerParser: Pars
851
903
  * @internal
852
904
  */
853
905
  declare function composeWrappedSourceMetadata(dependencyMetadata: ParserDependencyMetadata | undefined, wrapSource: (source: NonNullable<ParserDependencyMetadata["source"]>) => NonNullable<ParserDependencyMetadata["source"]>): ParserDependencyMetadata | undefined;
906
+ /**
907
+ * Defines internal parse-lane metadata without exposing it through object
908
+ * spreads used by custom parser wrappers.
909
+ *
910
+ * @internal
911
+ */
912
+ declare function defineParseLanes<TState>(parser: object, lanes: readonly ParseLane<TState>[] | undefined): void;
913
+ /**
914
+ * Gets parse-lane metadata defined directly on a parser.
915
+ *
916
+ * Inherited metadata belongs to the parser's prototype and must not bypass an
917
+ * overriding `parse()` implementation on a custom wrapper.
918
+ *
919
+ * @internal
920
+ */
921
+ declare function getOwnParseLanes<TState>(parser: object): readonly ParseLane<TState>[] | undefined;
854
922
  /**
855
923
  * Marks a parser as inheriting parent-state annotations through wrapper-state
856
924
  * reconstruction.
@@ -999,4 +1067,4 @@ declare function getDocPage(parser: Parser<"sync", unknown, unknown>, argsOrOpti
999
1067
  declare function getDocPage(parser: Parser<"async", unknown, unknown>, argsOrOptions?: readonly string[] | ParseOptions, options?: ParseOptions): Promise<DocPage | undefined>;
1000
1068
  declare function getDocPage<M extends Mode>(parser: Parser<M, unknown, unknown>, argsOrOptions?: readonly string[] | ParseOptions, options?: ParseOptions): ModeValue<M, DocPage | undefined>;
1001
1069
  //#endregion
1002
- export { CombineModes, DocState, ExecutionContext, ExecutionPhase, InferMode, InferValue, Mode, ModeIterable, ModeValue, ParseFrame, type ParseOptions, Parser, ParserContext, ParserResult, Result, Suggestion, annotationWrapperRequiresSourceBindingKey, composeWrappedSourceMetadata, createParserContext, defineInheritedAnnotationParser, defineSourceBindingOnlyAnnotationCompletionParser, getDelegatingSuggestRuntimeNodes, getDocPage, getDocPageAsync, getDocPageSync, getParserSuggestRuntimeNodes, inheritParentAnnotationsKey, parse, parseAsync, parseSync, suggest, suggestAsync, suggestSync, unmatchedNonCliDependencySourceStateMarker };
1070
+ export { CombineModes, DocState, ExecutionContext, ExecutionPhase, InferMode, InferValue, Mode, ModeIterable, ModeValue, ParseFrame, ParseLane, ParseLaneConsumptionGroup, type ParseOptions, Parser, ParserContext, ParserResult, Result, Suggestion, annotationWrapperRequiresSourceBindingKey, composeWrappedSourceMetadata, createParserContext, defineInheritedAnnotationParser, defineParseLanes, defineSourceBindingOnlyAnnotationCompletionParser, getDelegatingSuggestRuntimeNodes, getDocPage, getDocPageAsync, getDocPageSync, getOwnParseLanes, getParserSuggestRuntimeNodes, inheritParentAnnotationsKey, parse, parseAsync, parseLanesKey, parseSync, suggest, suggestAsync, suggestSync, unmatchedNonCliDependencySourceStateMarker };
@@ -8,6 +8,12 @@ import { createInputTrace } from "../input-trace.js";
8
8
 
9
9
  //#region src/internal/parser.ts
10
10
  /**
11
+ * Internal symbol used by transparent combinators to expose independently
12
+ * competing parse operations to shared-buffer parents.
13
+ * @internal
14
+ */
15
+ const parseLanesKey = Symbol("parseLanes");
16
+ /**
11
17
  * Internal marker for wrappers whose `{ hasCliValue: false }` states should
12
18
  * be treated as unmatched dependency-source states during completion-time
13
19
  * Phase 1.
@@ -387,6 +393,32 @@ function composeWrappedSourceMetadata(dependencyMetadata, wrapSource) {
387
393
  };
388
394
  }
389
395
  /**
396
+ * Defines internal parse-lane metadata without exposing it through object
397
+ * spreads used by custom parser wrappers.
398
+ *
399
+ * @internal
400
+ */
401
+ function defineParseLanes(parser, lanes) {
402
+ if (lanes == null) return;
403
+ Object.defineProperty(parser, parseLanesKey, {
404
+ value: lanes,
405
+ configurable: true,
406
+ enumerable: false
407
+ });
408
+ }
409
+ /**
410
+ * Gets parse-lane metadata defined directly on a parser.
411
+ *
412
+ * Inherited metadata belongs to the parser's prototype and must not bypass an
413
+ * overriding `parse()` implementation on a custom wrapper.
414
+ *
415
+ * @internal
416
+ */
417
+ function getOwnParseLanes(parser) {
418
+ if (!Object.hasOwn(parser, parseLanesKey)) return void 0;
419
+ return parser[parseLanesKey];
420
+ }
421
+ /**
390
422
  * Marks a parser as inheriting parent-state annotations through wrapper-state
391
423
  * reconstruction.
392
424
  *
@@ -793,4 +825,4 @@ function buildDocPage(parser, context, args, matchedCommandArgIndices) {
793
825
  }
794
826
 
795
827
  //#endregion
796
- export { annotationWrapperRequiresSourceBindingKey, composeWrappedSourceMetadata, createParserContext, defineInheritedAnnotationParser, defineSourceBindingOnlyAnnotationCompletionParser, getDelegatingSuggestRuntimeNodes, getDocPage, getDocPageAsync, getDocPageSync, getParserSuggestRuntimeNodes, inheritParentAnnotationsKey, parse, parseAsync, parseSync, suggest, suggestAsync, suggestSync, unmatchedNonCliDependencySourceStateMarker };
828
+ export { annotationWrapperRequiresSourceBindingKey, composeWrappedSourceMetadata, createParserContext, defineInheritedAnnotationParser, defineParseLanes, defineSourceBindingOnlyAnnotationCompletionParser, getDelegatingSuggestRuntimeNodes, getDocPage, getDocPageAsync, getDocPageSync, getOwnParseLanes, getParserSuggestRuntimeNodes, inheritParentAnnotationsKey, parse, parseAsync, parseLanesKey, parseSync, suggest, suggestAsync, suggestSync, unmatchedNonCliDependencySourceStateMarker };
@@ -231,6 +231,44 @@ function processOptionalStyleResult(result, innerState, context) {
231
231
  };
232
232
  return result;
233
233
  }
234
+ function adaptOptionalStyleParseLanes(parser) {
235
+ const lanes = require_internal_parser.getOwnParseLanes(parser);
236
+ if (lanes == null) return void 0;
237
+ const consumptionGroup = {};
238
+ return lanes.map((lane) => ({
239
+ priority: lane.priority,
240
+ zeroConsumptionGroup: lane.zeroConsumptionGroup,
241
+ settlesZeroConsumption: lane.settlesZeroConsumption,
242
+ leadingNames: lane.leadingNames,
243
+ acceptingAnyToken: false,
244
+ requiredConsumptionGroups: [...lane.requiredConsumptionGroups?.map((group) => ({
245
+ id: group.id,
246
+ ...group.isActive == null ? {} : { isActive(state) {
247
+ const innerState = Array.isArray(state) ? state[0] : parser.initialState;
248
+ return group.isActive?.(innerState) ?? true;
249
+ } }
250
+ })) ?? [], {
251
+ id: consumptionGroup,
252
+ isActive(state) {
253
+ return Array.isArray(state);
254
+ }
255
+ }],
256
+ parse(context) {
257
+ const innerState = deriveOptionalInnerParseState(context.state, parser);
258
+ const innerContext = {
259
+ ...context,
260
+ state: innerState
261
+ };
262
+ return require_mode_dispatch.dispatchByMode(parser.mode, () => {
263
+ const result = lane.parse(innerContext);
264
+ return processOptionalStyleResult(result, innerState, context);
265
+ }, async () => {
266
+ const result = await lane.parse(innerContext);
267
+ return processOptionalStyleResult(result, innerState, context);
268
+ });
269
+ }
270
+ }));
271
+ }
234
272
  /**
235
273
  * Creates a `shouldDeferCompletion` adapter that unwraps the outer state
236
274
  * shape (`[TState] | undefined`) used by {@link optional} and
@@ -420,6 +458,7 @@ function optional(parser) {
420
458
  const composed = require_dependency_metadata.composeDependencyMetadata(parser.dependencyMetadata, "optional");
421
459
  if (composed != null) optionalParser.dependencyMetadata = composed;
422
460
  }
461
+ require_internal_parser.defineParseLanes(optionalParser, adaptOptionalStyleParseLanes(parser));
423
462
  require_internal_parser.defineInheritedAnnotationParser(optionalParser);
424
463
  require_internal_parser.defineSourceBindingOnlyAnnotationCompletionParser(optionalParser);
425
464
  return optionalParser;
@@ -654,6 +693,7 @@ function withDefault(parser, defaultValue, options) {
654
693
  } });
655
694
  if (composed != null) withDefaultParser.dependencyMetadata = composed;
656
695
  }
696
+ require_internal_parser.defineParseLanes(withDefaultParser, adaptOptionalStyleParseLanes(parser));
657
697
  require_internal_parser.defineInheritedAnnotationParser(withDefaultParser);
658
698
  require_internal_parser.defineSourceBindingOnlyAnnotationCompletionParser(withDefaultParser);
659
699
  return withDefaultParser;
@@ -793,6 +833,7 @@ function map(parser, transform) {
793
833
  return parser.getDocFragments(state, void 0);
794
834
  }
795
835
  };
836
+ require_internal_parser.defineParseLanes(mappedParser, require_internal_parser.getOwnParseLanes(parser));
796
837
  delete mappedParser.normalizeValue;
797
838
  delete mappedParser.validateValue;
798
839
  if ("placeholder" in parser) Object.defineProperty(mappedParser, "placeholder", {
@@ -1509,6 +1550,7 @@ function multiple(parser, options = {}) {
1509
1550
  function nonEmpty(parser) {
1510
1551
  const syncParser = parser;
1511
1552
  const initialState = parser.initialState;
1553
+ const consumptionGroup = {};
1512
1554
  const processNonEmptyResult = (result) => {
1513
1555
  if (!result.success) return result;
1514
1556
  if (result.consumed.length === 0) return {
@@ -1561,6 +1603,17 @@ function nonEmpty(parser) {
1561
1603
  return syncParser.getDocFragments(state, defaultValue);
1562
1604
  }
1563
1605
  };
1606
+ require_internal_parser.defineParseLanes(nonEmptyParser, require_internal_parser.getOwnParseLanes(parser)?.map((lane) => ({
1607
+ priority: lane.priority,
1608
+ zeroConsumptionGroup: lane.zeroConsumptionGroup,
1609
+ settlesZeroConsumption: lane.settlesZeroConsumption,
1610
+ leadingNames: lane.leadingNames,
1611
+ acceptingAnyToken: lane.acceptingAnyToken,
1612
+ requiredConsumptionGroups: [...lane.requiredConsumptionGroups ?? [], { id: consumptionGroup }],
1613
+ parse(context) {
1614
+ return lane.parse(context);
1615
+ }
1616
+ })));
1564
1617
  if ("placeholder" in parser) Object.defineProperty(nonEmptyParser, "placeholder", {
1565
1618
  get() {
1566
1619
  return parser.placeholder;
package/dist/modifiers.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { annotateFreshArray, annotationKey, getAnnotations, inheritAnnotations, isInjectedAnnotationWrapper, unwrapInjectedAnnotationWrapper } from "./internal/annotations.js";
2
2
  import { formatMessage, message, text } from "./message.js";
3
3
  import { dispatchByMode, dispatchIterableByMode, mapModeValue, wrapForMode } from "./internal/mode-dispatch.js";
4
- import { defineInheritedAnnotationParser, defineSourceBindingOnlyAnnotationCompletionParser, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
4
+ import { defineInheritedAnnotationParser, defineParseLanes, defineSourceBindingOnlyAnnotationCompletionParser, getOwnParseLanes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
5
5
  import { getDelegatedAnnotationState, hasDelegatedAnnotationCarrier, isAnnotationWrappedInitialState, normalizeDelegatedAnnotationState, normalizeNestedDelegatedAnnotationState } from "./annotation-state.js";
6
6
  import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
7
7
  import { completeOrExtractPhase2Seed, extractPhase2Seed, extractPhase2SeedKey, phase2SeedFromValueResult } from "./phase2-seed.js";
@@ -231,6 +231,44 @@ function processOptionalStyleResult(result, innerState, context) {
231
231
  };
232
232
  return result;
233
233
  }
234
+ function adaptOptionalStyleParseLanes(parser) {
235
+ const lanes = getOwnParseLanes(parser);
236
+ if (lanes == null) return void 0;
237
+ const consumptionGroup = {};
238
+ return lanes.map((lane) => ({
239
+ priority: lane.priority,
240
+ zeroConsumptionGroup: lane.zeroConsumptionGroup,
241
+ settlesZeroConsumption: lane.settlesZeroConsumption,
242
+ leadingNames: lane.leadingNames,
243
+ acceptingAnyToken: false,
244
+ requiredConsumptionGroups: [...lane.requiredConsumptionGroups?.map((group) => ({
245
+ id: group.id,
246
+ ...group.isActive == null ? {} : { isActive(state) {
247
+ const innerState = Array.isArray(state) ? state[0] : parser.initialState;
248
+ return group.isActive?.(innerState) ?? true;
249
+ } }
250
+ })) ?? [], {
251
+ id: consumptionGroup,
252
+ isActive(state) {
253
+ return Array.isArray(state);
254
+ }
255
+ }],
256
+ parse(context) {
257
+ const innerState = deriveOptionalInnerParseState(context.state, parser);
258
+ const innerContext = {
259
+ ...context,
260
+ state: innerState
261
+ };
262
+ return dispatchByMode(parser.mode, () => {
263
+ const result = lane.parse(innerContext);
264
+ return processOptionalStyleResult(result, innerState, context);
265
+ }, async () => {
266
+ const result = await lane.parse(innerContext);
267
+ return processOptionalStyleResult(result, innerState, context);
268
+ });
269
+ }
270
+ }));
271
+ }
234
272
  /**
235
273
  * Creates a `shouldDeferCompletion` adapter that unwraps the outer state
236
274
  * shape (`[TState] | undefined`) used by {@link optional} and
@@ -420,6 +458,7 @@ function optional(parser) {
420
458
  const composed = composeDependencyMetadata(parser.dependencyMetadata, "optional");
421
459
  if (composed != null) optionalParser.dependencyMetadata = composed;
422
460
  }
461
+ defineParseLanes(optionalParser, adaptOptionalStyleParseLanes(parser));
423
462
  defineInheritedAnnotationParser(optionalParser);
424
463
  defineSourceBindingOnlyAnnotationCompletionParser(optionalParser);
425
464
  return optionalParser;
@@ -654,6 +693,7 @@ function withDefault(parser, defaultValue, options) {
654
693
  } });
655
694
  if (composed != null) withDefaultParser.dependencyMetadata = composed;
656
695
  }
696
+ defineParseLanes(withDefaultParser, adaptOptionalStyleParseLanes(parser));
657
697
  defineInheritedAnnotationParser(withDefaultParser);
658
698
  defineSourceBindingOnlyAnnotationCompletionParser(withDefaultParser);
659
699
  return withDefaultParser;
@@ -793,6 +833,7 @@ function map(parser, transform) {
793
833
  return parser.getDocFragments(state, void 0);
794
834
  }
795
835
  };
836
+ defineParseLanes(mappedParser, getOwnParseLanes(parser));
796
837
  delete mappedParser.normalizeValue;
797
838
  delete mappedParser.validateValue;
798
839
  if ("placeholder" in parser) Object.defineProperty(mappedParser, "placeholder", {
@@ -1509,6 +1550,7 @@ function multiple(parser, options = {}) {
1509
1550
  function nonEmpty(parser) {
1510
1551
  const syncParser = parser;
1511
1552
  const initialState = parser.initialState;
1553
+ const consumptionGroup = {};
1512
1554
  const processNonEmptyResult = (result) => {
1513
1555
  if (!result.success) return result;
1514
1556
  if (result.consumed.length === 0) return {
@@ -1561,6 +1603,17 @@ function nonEmpty(parser) {
1561
1603
  return syncParser.getDocFragments(state, defaultValue);
1562
1604
  }
1563
1605
  };
1606
+ defineParseLanes(nonEmptyParser, getOwnParseLanes(parser)?.map((lane) => ({
1607
+ priority: lane.priority,
1608
+ zeroConsumptionGroup: lane.zeroConsumptionGroup,
1609
+ settlesZeroConsumption: lane.settlesZeroConsumption,
1610
+ leadingNames: lane.leadingNames,
1611
+ acceptingAnyToken: lane.acceptingAnyToken,
1612
+ requiredConsumptionGroups: [...lane.requiredConsumptionGroups ?? [], { id: consumptionGroup }],
1613
+ parse(context) {
1614
+ return lane.parse(context);
1615
+ }
1616
+ })));
1564
1617
  if ("placeholder" in parser) Object.defineProperty(nonEmptyParser, "placeholder", {
1565
1618
  get() {
1566
1619
  return parser.placeholder;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -196,11 +196,11 @@
196
196
  },
197
197
  "sideEffects": false,
198
198
  "devDependencies": {
199
+ "@optique/env": "1.1.5",
199
200
  "@types/node": "^24.0.0",
200
201
  "fast-check": "^4.7.0",
201
202
  "tsdown": "^0.13.0",
202
- "typescript": "^5.8.3",
203
- "@optique/env": "1.1.3"
203
+ "typescript": "^5.8.3"
204
204
  },
205
205
  "scripts": {
206
206
  "build": "tsdown",