@optique/core 1.2.3 → 1.2.4

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.
@@ -79,8 +79,8 @@ function unionLeadingNames(parsers) {
79
79
  * Computes `leadingNames` for shared-buffer compositions (`tuple()`,
80
80
  * `object()`, `merge()`, `concat()`).
81
81
  *
82
- * Children are processed in descending priority order (matching the
83
- * round-robin parse loop). Once a child with `acceptingAnyToken` is
82
+ * Sources are processed in descending priority order (matching the
83
+ * round-robin parse loop). Once a source with `acceptingAnyToken` is
84
84
  * encountered, no lower-priority children can match at position 0, so
85
85
  * their names are excluded.
86
86
  */
@@ -1949,6 +1949,52 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1949
1949
  const noMatchContext = analyzeNoMatchContext(parserKeys.map((k) => parsers[k]));
1950
1950
  const combinedMode = parserKeys.some((k) => parsers[k].mode === "async") ? "async" : "sync";
1951
1951
  const getInitialError = (context) => createObjectLikeInitialError(context, noMatchContext, options.errors);
1952
+ const adaptFieldLaneResult = (context, field, parser, fieldState, result) => {
1953
+ if (!result.success) return result;
1954
+ if (result.consumed.length === 0 && result.next.state === fieldState) return {
1955
+ success: true,
1956
+ next: context,
1957
+ consumed: []
1958
+ };
1959
+ const mergedExec = require_execution_context.mergeChildExec(context.exec, result.next.exec);
1960
+ const nextState = result.next.state === fieldState ? context.state : {
1961
+ ...context.state,
1962
+ [field]: require_annotation_state.getWrappedChildState(context.state, result.next.state, parser)
1963
+ };
1964
+ return {
1965
+ success: true,
1966
+ next: {
1967
+ ...context,
1968
+ buffer: result.next.buffer,
1969
+ optionsTerminated: result.next.optionsTerminated,
1970
+ state: nextState,
1971
+ ...mergedExec != null ? {
1972
+ trace: mergedExec.trace,
1973
+ exec: mergedExec,
1974
+ dependencyRegistry: mergedExec.dependencyRegistry
1975
+ } : {}
1976
+ },
1977
+ consumed: result.consumed
1978
+ };
1979
+ };
1980
+ const objectZeroConsumptionGroup = {};
1981
+ const objectParseLanes = parserPairs.map(([field, parser]) => ({
1982
+ priority: parser.priority,
1983
+ zeroConsumptionGroup: objectZeroConsumptionGroup,
1984
+ settlesZeroConsumption: false,
1985
+ leadingNames: parser.leadingNames,
1986
+ acceptingAnyToken: parser.acceptingAnyToken,
1987
+ parse(context) {
1988
+ const fieldState = createFieldStateGetter(context.state, getObjectParseChildState)(field, parser);
1989
+ return require_mode_dispatch.dispatchByMode(combinedMode, () => {
1990
+ const result = parser.parse(withChildContext$1(context, field, fieldState, parser));
1991
+ return adaptFieldLaneResult(context, field, parser, fieldState, result);
1992
+ }, async () => {
1993
+ const result = await parser.parse(withChildContext$1(context, field, fieldState, parser));
1994
+ return adaptFieldLaneResult(context, field, parser, fieldState, result);
1995
+ });
1996
+ }
1997
+ }));
1952
1998
  const parseSync = (context) => {
1953
1999
  let error = getInitialError(context);
1954
2000
  let currentContext = context;
@@ -2467,6 +2513,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
2467
2513
  configurable: true,
2468
2514
  enumerable: false
2469
2515
  });
2516
+ require_internal_parser.defineParseLanes(objectParser, objectParseLanes);
2470
2517
  require_internal_parser.defineInheritedAnnotationParser(objectParser);
2471
2518
  return require_modifiers.fluent(objectParser);
2472
2519
  }
@@ -3871,7 +3918,140 @@ function merge(...args) {
3871
3918
  [localObjectStateKey(index)]: result.next.state
3872
3919
  };
3873
3920
  };
3874
- const parseSync = (context) => {
3921
+ const adaptMergeLaneResult = (parser, context, parserState, parsedState, result, index) => {
3922
+ if (!result.success) return result;
3923
+ const mergedExec = require_execution_context.mergeChildExec(context.exec, result.next.exec);
3924
+ const newState = result.next.state === parsedState ? context.state : mergeResultState(parser, context, parserState, result, index);
3925
+ return {
3926
+ success: true,
3927
+ next: {
3928
+ ...context,
3929
+ buffer: result.next.buffer,
3930
+ optionsTerminated: result.next.optionsTerminated,
3931
+ state: newState,
3932
+ ...mergedExec != null ? {
3933
+ trace: mergedExec.trace,
3934
+ exec: mergedExec,
3935
+ dependencyRegistry: mergedExec.dependencyRegistry
3936
+ } : {}
3937
+ },
3938
+ consumed: result.consumed
3939
+ };
3940
+ };
3941
+ const childrenInDeclarationOrder = sorted.map(([parser, originalIndex], sortedIndex) => ({
3942
+ parser,
3943
+ originalIndex,
3944
+ sortedIndex
3945
+ })).toSorted((a, b) => a.originalIndex - b.originalIndex);
3946
+ const mergeParseLanes = childrenInDeclarationOrder.flatMap(({ parser, sortedIndex }) => {
3947
+ const occurrenceConsumptionGroups = /* @__PURE__ */ new WeakMap();
3948
+ const occurrenceZeroConsumptionGroups = /* @__PURE__ */ new WeakMap();
3949
+ const scopeConsumptionGroup = (group$1) => {
3950
+ const existing = occurrenceConsumptionGroups.get(group$1);
3951
+ if (existing != null) return existing;
3952
+ const scoped = {};
3953
+ occurrenceConsumptionGroups.set(group$1, scoped);
3954
+ return scoped;
3955
+ };
3956
+ const childLanes = require_internal_parser.getOwnParseLanes(parser);
3957
+ const lanes = childLanes ?? [{
3958
+ priority: parser.priority,
3959
+ leadingNames: parser.leadingNames,
3960
+ acceptingAnyToken: parser.acceptingAnyToken,
3961
+ parse(context) {
3962
+ return parser.parse(context);
3963
+ }
3964
+ }];
3965
+ return lanes.map((lane) => ({
3966
+ priority: lane.priority,
3967
+ zeroConsumptionGroup: (() => {
3968
+ const group$1 = lane.zeroConsumptionGroup ?? lane;
3969
+ const existing = occurrenceZeroConsumptionGroups.get(group$1);
3970
+ if (existing != null) return existing;
3971
+ const scoped = {};
3972
+ occurrenceZeroConsumptionGroups.set(group$1, scoped);
3973
+ return scoped;
3974
+ })(),
3975
+ settlesZeroConsumption: lane.settlesZeroConsumption,
3976
+ leadingNames: lane.leadingNames,
3977
+ acceptingAnyToken: lane.acceptingAnyToken,
3978
+ requiredConsumptionGroups: lane.requiredConsumptionGroups?.map((group$1) => ({
3979
+ id: scopeConsumptionGroup(group$1.id),
3980
+ ...group$1.isActive == null ? {} : { isActive(state) {
3981
+ if (state == null || typeof state !== "object") return false;
3982
+ const parserState = extractParserStateFromState(parser, state, sortedIndex);
3983
+ return group$1.isActive?.(parserState) ?? true;
3984
+ } }
3985
+ })),
3986
+ parse(context) {
3987
+ const parserState = extractParserState(parser, context, sortedIndex);
3988
+ const childContext = withChildContext$1(context, sortedIndex, parserState, parser);
3989
+ return require_mode_dispatch.dispatchByMode(combinedMode, () => {
3990
+ const result = lane.parse(childContext);
3991
+ return adaptMergeLaneResult(parser, context, parserState, childContext.state, result, sortedIndex);
3992
+ }, async () => {
3993
+ const result = await lane.parse(childContext);
3994
+ return adaptMergeLaneResult(parser, context, parserState, childContext.state, result, sortedIndex);
3995
+ });
3996
+ }
3997
+ }));
3998
+ }).toSorted((a, b) => b.priority - a.priority);
3999
+ const settleZeroConsumptionLanes = (context, laneResults) => {
4000
+ const groups = /* @__PURE__ */ new Map();
4001
+ for (const lane of mergeParseLanes) {
4002
+ const group$1 = lane.zeroConsumptionGroup ?? lane;
4003
+ const groupedLanes = groups.get(group$1);
4004
+ if (groupedLanes == null) groups.set(group$1, [lane]);
4005
+ else groupedLanes.push(lane);
4006
+ }
4007
+ const settledGroups = [];
4008
+ for (const groupedLanes of groups.values()) {
4009
+ const groupSucceeded = groupedLanes.every((lane) => {
4010
+ const result = laneResults.get(lane);
4011
+ return lane.settlesZeroConsumption !== false && result?.success === true && result.consumed.length === 0 && (lane.requiredConsumptionGroups?.length ?? 0) === 0;
4012
+ });
4013
+ if (!groupSucceeded) return null;
4014
+ settledGroups.push(groupedLanes);
4015
+ }
4016
+ let settledContext = context;
4017
+ for (const groupedLanes of settledGroups) for (const lane of groupedLanes) {
4018
+ const result = laneResults.get(lane);
4019
+ if (result?.success !== true) continue;
4020
+ const originalState = context.state;
4021
+ const resultState = result.next.state;
4022
+ const nextState = { ...settledContext.state };
4023
+ const stateKeys = new Set([...Reflect.ownKeys(originalState), ...Reflect.ownKeys(resultState)]);
4024
+ for (const key of stateKeys) {
4025
+ const originalHasKey = Object.hasOwn(originalState, key);
4026
+ const resultHasKey = Object.hasOwn(resultState, key);
4027
+ if (originalHasKey === resultHasKey && originalState[key] === resultState[key]) continue;
4028
+ if (resultHasKey) nextState[key] = resultState[key];
4029
+ else delete nextState[key];
4030
+ }
4031
+ const mergedExec = require_execution_context.mergeChildExec(settledContext.exec, result.next.exec);
4032
+ settledContext = {
4033
+ ...settledContext,
4034
+ buffer: result.next.buffer,
4035
+ optionsTerminated: result.next.optionsTerminated,
4036
+ state: nextState,
4037
+ ...mergedExec != null ? {
4038
+ trace: mergedExec.trace,
4039
+ exec: mergedExec,
4040
+ dependencyRegistry: mergedExec.dependencyRegistry
4041
+ } : {}
4042
+ };
4043
+ }
4044
+ return {
4045
+ success: true,
4046
+ next: settledContext,
4047
+ consumed: []
4048
+ };
4049
+ };
4050
+ const canTryPositionalLaneAfterFailure = (lane, context) => {
4051
+ const token = context.buffer[0];
4052
+ return token != null && (context.optionsTerminated || !token.startsWith("-")) && (lane.acceptingAnyToken || lane.leadingNames.has(token));
4053
+ };
4054
+ const parseChildrenSync = (context) => {
3875
4055
  let currentContext = context;
3876
4056
  let zeroConsumedSuccess = null;
3877
4057
  for (let i = 0; i < syncParsers.length; i++) {
@@ -3915,7 +4095,7 @@ function merge(...args) {
3915
4095
  ...createObjectLikeInitialError(context, noMatchContext)
3916
4096
  };
3917
4097
  };
3918
- const parseAsync = async (context) => {
4098
+ const parseChildrenAsync = async (context) => {
3919
4099
  let currentContext = context;
3920
4100
  let zeroConsumedSuccess = null;
3921
4101
  for (let i = 0; i < parsers.length; i++) {
@@ -3960,15 +4140,141 @@ function merge(...args) {
3960
4140
  ...createObjectLikeInitialError(context, noMatchContext)
3961
4141
  };
3962
4142
  };
4143
+ const parseSync = (context) => {
4144
+ let currentContext = context;
4145
+ let error = createObjectLikeInitialError(context, noMatchContext);
4146
+ const allConsumed = [];
4147
+ const consumedLanes = /* @__PURE__ */ new Set();
4148
+ const consumedGroups = /* @__PURE__ */ new Set();
4149
+ const laneResults = /* @__PURE__ */ new Map();
4150
+ let attemptedLane = false;
4151
+ let madeProgress = true;
4152
+ while (madeProgress && currentContext.buffer.length > 0) {
4153
+ madeProgress = false;
4154
+ let consumingError = null;
4155
+ for (const lane of mergeParseLanes) {
4156
+ if (consumingError != null && lane.priority < consumingError.priority) {
4157
+ if (!canTryPositionalLaneAfterFailure(lane, currentContext)) continue;
4158
+ }
4159
+ attemptedLane = true;
4160
+ const result = lane.parse(currentContext);
4161
+ laneResults.set(lane, result);
4162
+ if (result.success && result.consumed.length > 0) {
4163
+ currentContext = result.next;
4164
+ allConsumed.push(...result.consumed);
4165
+ consumedLanes.add(lane);
4166
+ for (const group$1 of lane.requiredConsumptionGroups ?? []) if (group$1.isActive?.(result.next.state) !== false) consumedGroups.add(group$1.id);
4167
+ madeProgress = true;
4168
+ break;
4169
+ }
4170
+ if (!result.success) {
4171
+ if (result.consumed > 0 && consumingError == null) consumingError = {
4172
+ priority: lane.priority,
4173
+ result
4174
+ };
4175
+ if (error.consumed < result.consumed) error = result;
4176
+ }
4177
+ }
4178
+ if (!madeProgress && consumingError != null && allConsumed.length === 0) return consumingError.result;
4179
+ }
4180
+ if (allConsumed.length === 0) {
4181
+ if (attemptedLane) {
4182
+ const settled = settleZeroConsumptionLanes(context, laneResults);
4183
+ return settled ?? {
4184
+ ...error,
4185
+ success: false
4186
+ };
4187
+ }
4188
+ const fallback = parseChildrenSync(context);
4189
+ if (!fallback.success && fallback.consumed < error.consumed) return {
4190
+ ...error,
4191
+ success: false
4192
+ };
4193
+ return fallback;
4194
+ }
4195
+ for (const lane of mergeParseLanes) {
4196
+ if (consumedLanes.has(lane) || lane.leadingNames.size > 0 || lane.acceptingAnyToken || lane.requiredConsumptionGroups?.some((group$1) => !consumedGroups.has(group$1.id)) === true) continue;
4197
+ const result = lane.parse(currentContext);
4198
+ if (result.success && result.consumed.length === 0 && result.next.state !== currentContext.state) currentContext = result.next;
4199
+ }
4200
+ return {
4201
+ success: true,
4202
+ next: currentContext,
4203
+ consumed: allConsumed
4204
+ };
4205
+ };
4206
+ const parseAsync = async (context) => {
4207
+ let currentContext = context;
4208
+ let error = createObjectLikeInitialError(context, noMatchContext);
4209
+ const allConsumed = [];
4210
+ const consumedLanes = /* @__PURE__ */ new Set();
4211
+ const consumedGroups = /* @__PURE__ */ new Set();
4212
+ const laneResults = /* @__PURE__ */ new Map();
4213
+ let attemptedLane = false;
4214
+ let madeProgress = true;
4215
+ while (madeProgress && currentContext.buffer.length > 0) {
4216
+ madeProgress = false;
4217
+ let consumingError = null;
4218
+ for (const lane of mergeParseLanes) {
4219
+ if (consumingError != null && lane.priority < consumingError.priority) {
4220
+ if (!canTryPositionalLaneAfterFailure(lane, currentContext)) continue;
4221
+ }
4222
+ attemptedLane = true;
4223
+ const result = await lane.parse(currentContext);
4224
+ laneResults.set(lane, result);
4225
+ if (result.success && result.consumed.length > 0) {
4226
+ currentContext = result.next;
4227
+ allConsumed.push(...result.consumed);
4228
+ consumedLanes.add(lane);
4229
+ for (const group$1 of lane.requiredConsumptionGroups ?? []) if (group$1.isActive?.(result.next.state) !== false) consumedGroups.add(group$1.id);
4230
+ madeProgress = true;
4231
+ break;
4232
+ }
4233
+ if (!result.success) {
4234
+ if (result.consumed > 0 && consumingError == null) consumingError = {
4235
+ priority: lane.priority,
4236
+ result
4237
+ };
4238
+ if (error.consumed < result.consumed) error = result;
4239
+ }
4240
+ }
4241
+ if (!madeProgress && consumingError != null && allConsumed.length === 0) return consumingError.result;
4242
+ }
4243
+ if (allConsumed.length === 0) {
4244
+ if (attemptedLane) {
4245
+ const settled = settleZeroConsumptionLanes(context, laneResults);
4246
+ return settled ?? {
4247
+ ...error,
4248
+ success: false
4249
+ };
4250
+ }
4251
+ const fallback = await parseChildrenAsync(context);
4252
+ if (!fallback.success && fallback.consumed < error.consumed) return {
4253
+ ...error,
4254
+ success: false
4255
+ };
4256
+ return fallback;
4257
+ }
4258
+ for (const lane of mergeParseLanes) {
4259
+ if (consumedLanes.has(lane) || lane.leadingNames.size > 0 || lane.acceptingAnyToken || lane.requiredConsumptionGroups?.some((group$1) => !consumedGroups.has(group$1.id)) === true) continue;
4260
+ const result = await lane.parse(currentContext);
4261
+ if (result.success && result.consumed.length === 0 && result.next.state !== currentContext.state) currentContext = result.next;
4262
+ }
4263
+ return {
4264
+ success: true,
4265
+ next: currentContext,
4266
+ consumed: allConsumed
4267
+ };
4268
+ };
3963
4269
  const mergeParser = {
3964
4270
  mode: combinedMode,
3965
4271
  $valueType: [],
3966
4272
  $stateType: [],
3967
4273
  [fieldParsersKey]: mergedFieldParsers,
3968
- priority: Math.max(...parsers.map((p) => p.priority)),
4274
+ priority: Math.max(...mergeParseLanes.map((lane) => lane.priority)),
3969
4275
  usage: applyHiddenToUsage(parsers.flatMap((p) => p.usage), options.hidden),
3970
- leadingNames: sharedBufferLeadingNames(parsers),
3971
- acceptingAnyToken: parsers.some((p) => p.acceptingAnyToken),
4276
+ leadingNames: sharedBufferLeadingNames(mergeParseLanes),
4277
+ acceptingAnyToken: mergeParseLanes.some((lane) => lane.acceptingAnyToken),
3972
4278
  initialState,
3973
4279
  canSkip(state, exec) {
3974
4280
  return parsers.every((parser, index) => {
@@ -4443,6 +4749,7 @@ function merge(...args) {
4443
4749
  };
4444
4750
  }
4445
4751
  };
4752
+ require_internal_parser.defineParseLanes(mergeParser, mergeParseLanes);
4446
4753
  require_internal_parser.defineInheritedAnnotationParser(mergeParser);
4447
4754
  return require_modifiers.fluent(mergeParser);
4448
4755
  }
@@ -5125,6 +5432,7 @@ function group(label, parser, options = {}) {
5125
5432
  };
5126
5433
  }
5127
5434
  };
5435
+ require_internal_parser.defineParseLanes(groupParser, require_internal_parser.getOwnParseLanes(parser));
5128
5436
  Object.defineProperty(groupParser, require_phase2_seed.extractPhase2SeedKey, {
5129
5437
  value(state, exec) {
5130
5438
  return require_phase2_seed.extractPhase2Seed(parser, state, exec);
@@ -6,7 +6,7 @@ import { deduplicateDocFragments } from "./doc.js";
6
6
  import { dispatchByMode, dispatchIterableByMode } from "./internal/mode-dispatch.js";
7
7
  import { createDependencySourceState, dependencyId, isDependencySourceState, isPendingDependencySourceState, isWrappedDependencySource, wrappedDependencySourceMarker } from "./internal/dependency.js";
8
8
  import { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, createDependencyRuntimeContext, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync } from "./dependency-runtime.js";
9
- import { defineInheritedAnnotationParser, getParserSuggestRuntimeNodes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
9
+ import { defineInheritedAnnotationParser, defineParseLanes, getOwnParseLanes, getParserSuggestRuntimeNodes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
10
10
  import { annotationViewTargets, getWrappedChildParseState, getWrappedChildState, reconcileObjectChildState, unwrapAnnotationView } from "./annotation-state.js";
11
11
  import { allowDuplicateLeadingCommandNamesKey } from "./internal/command-alias.js";
12
12
  import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
@@ -79,8 +79,8 @@ function unionLeadingNames(parsers) {
79
79
  * Computes `leadingNames` for shared-buffer compositions (`tuple()`,
80
80
  * `object()`, `merge()`, `concat()`).
81
81
  *
82
- * Children are processed in descending priority order (matching the
83
- * round-robin parse loop). Once a child with `acceptingAnyToken` is
82
+ * Sources are processed in descending priority order (matching the
83
+ * round-robin parse loop). Once a source with `acceptingAnyToken` is
84
84
  * encountered, no lower-priority children can match at position 0, so
85
85
  * their names are excluded.
86
86
  */
@@ -1949,6 +1949,52 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1949
1949
  const noMatchContext = analyzeNoMatchContext(parserKeys.map((k) => parsers[k]));
1950
1950
  const combinedMode = parserKeys.some((k) => parsers[k].mode === "async") ? "async" : "sync";
1951
1951
  const getInitialError = (context) => createObjectLikeInitialError(context, noMatchContext, options.errors);
1952
+ const adaptFieldLaneResult = (context, field, parser, fieldState, result) => {
1953
+ if (!result.success) return result;
1954
+ if (result.consumed.length === 0 && result.next.state === fieldState) return {
1955
+ success: true,
1956
+ next: context,
1957
+ consumed: []
1958
+ };
1959
+ const mergedExec = mergeChildExec(context.exec, result.next.exec);
1960
+ const nextState = result.next.state === fieldState ? context.state : {
1961
+ ...context.state,
1962
+ [field]: getWrappedChildState(context.state, result.next.state, parser)
1963
+ };
1964
+ return {
1965
+ success: true,
1966
+ next: {
1967
+ ...context,
1968
+ buffer: result.next.buffer,
1969
+ optionsTerminated: result.next.optionsTerminated,
1970
+ state: nextState,
1971
+ ...mergedExec != null ? {
1972
+ trace: mergedExec.trace,
1973
+ exec: mergedExec,
1974
+ dependencyRegistry: mergedExec.dependencyRegistry
1975
+ } : {}
1976
+ },
1977
+ consumed: result.consumed
1978
+ };
1979
+ };
1980
+ const objectZeroConsumptionGroup = {};
1981
+ const objectParseLanes = parserPairs.map(([field, parser]) => ({
1982
+ priority: parser.priority,
1983
+ zeroConsumptionGroup: objectZeroConsumptionGroup,
1984
+ settlesZeroConsumption: false,
1985
+ leadingNames: parser.leadingNames,
1986
+ acceptingAnyToken: parser.acceptingAnyToken,
1987
+ parse(context) {
1988
+ const fieldState = createFieldStateGetter(context.state, getObjectParseChildState)(field, parser);
1989
+ return dispatchByMode(combinedMode, () => {
1990
+ const result = parser.parse(withChildContext$1(context, field, fieldState, parser));
1991
+ return adaptFieldLaneResult(context, field, parser, fieldState, result);
1992
+ }, async () => {
1993
+ const result = await parser.parse(withChildContext$1(context, field, fieldState, parser));
1994
+ return adaptFieldLaneResult(context, field, parser, fieldState, result);
1995
+ });
1996
+ }
1997
+ }));
1952
1998
  const parseSync = (context) => {
1953
1999
  let error = getInitialError(context);
1954
2000
  let currentContext = context;
@@ -2467,6 +2513,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
2467
2513
  configurable: true,
2468
2514
  enumerable: false
2469
2515
  });
2516
+ defineParseLanes(objectParser, objectParseLanes);
2470
2517
  defineInheritedAnnotationParser(objectParser);
2471
2518
  return fluent(objectParser);
2472
2519
  }
@@ -3871,7 +3918,140 @@ function merge(...args) {
3871
3918
  [localObjectStateKey(index)]: result.next.state
3872
3919
  };
3873
3920
  };
3874
- const parseSync = (context) => {
3921
+ const adaptMergeLaneResult = (parser, context, parserState, parsedState, result, index) => {
3922
+ if (!result.success) return result;
3923
+ const mergedExec = mergeChildExec(context.exec, result.next.exec);
3924
+ const newState = result.next.state === parsedState ? context.state : mergeResultState(parser, context, parserState, result, index);
3925
+ return {
3926
+ success: true,
3927
+ next: {
3928
+ ...context,
3929
+ buffer: result.next.buffer,
3930
+ optionsTerminated: result.next.optionsTerminated,
3931
+ state: newState,
3932
+ ...mergedExec != null ? {
3933
+ trace: mergedExec.trace,
3934
+ exec: mergedExec,
3935
+ dependencyRegistry: mergedExec.dependencyRegistry
3936
+ } : {}
3937
+ },
3938
+ consumed: result.consumed
3939
+ };
3940
+ };
3941
+ const childrenInDeclarationOrder = sorted.map(([parser, originalIndex], sortedIndex) => ({
3942
+ parser,
3943
+ originalIndex,
3944
+ sortedIndex
3945
+ })).toSorted((a, b) => a.originalIndex - b.originalIndex);
3946
+ const mergeParseLanes = childrenInDeclarationOrder.flatMap(({ parser, sortedIndex }) => {
3947
+ const occurrenceConsumptionGroups = /* @__PURE__ */ new WeakMap();
3948
+ const occurrenceZeroConsumptionGroups = /* @__PURE__ */ new WeakMap();
3949
+ const scopeConsumptionGroup = (group$1) => {
3950
+ const existing = occurrenceConsumptionGroups.get(group$1);
3951
+ if (existing != null) return existing;
3952
+ const scoped = {};
3953
+ occurrenceConsumptionGroups.set(group$1, scoped);
3954
+ return scoped;
3955
+ };
3956
+ const childLanes = getOwnParseLanes(parser);
3957
+ const lanes = childLanes ?? [{
3958
+ priority: parser.priority,
3959
+ leadingNames: parser.leadingNames,
3960
+ acceptingAnyToken: parser.acceptingAnyToken,
3961
+ parse(context) {
3962
+ return parser.parse(context);
3963
+ }
3964
+ }];
3965
+ return lanes.map((lane) => ({
3966
+ priority: lane.priority,
3967
+ zeroConsumptionGroup: (() => {
3968
+ const group$1 = lane.zeroConsumptionGroup ?? lane;
3969
+ const existing = occurrenceZeroConsumptionGroups.get(group$1);
3970
+ if (existing != null) return existing;
3971
+ const scoped = {};
3972
+ occurrenceZeroConsumptionGroups.set(group$1, scoped);
3973
+ return scoped;
3974
+ })(),
3975
+ settlesZeroConsumption: lane.settlesZeroConsumption,
3976
+ leadingNames: lane.leadingNames,
3977
+ acceptingAnyToken: lane.acceptingAnyToken,
3978
+ requiredConsumptionGroups: lane.requiredConsumptionGroups?.map((group$1) => ({
3979
+ id: scopeConsumptionGroup(group$1.id),
3980
+ ...group$1.isActive == null ? {} : { isActive(state) {
3981
+ if (state == null || typeof state !== "object") return false;
3982
+ const parserState = extractParserStateFromState(parser, state, sortedIndex);
3983
+ return group$1.isActive?.(parserState) ?? true;
3984
+ } }
3985
+ })),
3986
+ parse(context) {
3987
+ const parserState = extractParserState(parser, context, sortedIndex);
3988
+ const childContext = withChildContext$1(context, sortedIndex, parserState, parser);
3989
+ return dispatchByMode(combinedMode, () => {
3990
+ const result = lane.parse(childContext);
3991
+ return adaptMergeLaneResult(parser, context, parserState, childContext.state, result, sortedIndex);
3992
+ }, async () => {
3993
+ const result = await lane.parse(childContext);
3994
+ return adaptMergeLaneResult(parser, context, parserState, childContext.state, result, sortedIndex);
3995
+ });
3996
+ }
3997
+ }));
3998
+ }).toSorted((a, b) => b.priority - a.priority);
3999
+ const settleZeroConsumptionLanes = (context, laneResults) => {
4000
+ const groups = /* @__PURE__ */ new Map();
4001
+ for (const lane of mergeParseLanes) {
4002
+ const group$1 = lane.zeroConsumptionGroup ?? lane;
4003
+ const groupedLanes = groups.get(group$1);
4004
+ if (groupedLanes == null) groups.set(group$1, [lane]);
4005
+ else groupedLanes.push(lane);
4006
+ }
4007
+ const settledGroups = [];
4008
+ for (const groupedLanes of groups.values()) {
4009
+ const groupSucceeded = groupedLanes.every((lane) => {
4010
+ const result = laneResults.get(lane);
4011
+ return lane.settlesZeroConsumption !== false && result?.success === true && result.consumed.length === 0 && (lane.requiredConsumptionGroups?.length ?? 0) === 0;
4012
+ });
4013
+ if (!groupSucceeded) return null;
4014
+ settledGroups.push(groupedLanes);
4015
+ }
4016
+ let settledContext = context;
4017
+ for (const groupedLanes of settledGroups) for (const lane of groupedLanes) {
4018
+ const result = laneResults.get(lane);
4019
+ if (result?.success !== true) continue;
4020
+ const originalState = context.state;
4021
+ const resultState = result.next.state;
4022
+ const nextState = { ...settledContext.state };
4023
+ const stateKeys = new Set([...Reflect.ownKeys(originalState), ...Reflect.ownKeys(resultState)]);
4024
+ for (const key of stateKeys) {
4025
+ const originalHasKey = Object.hasOwn(originalState, key);
4026
+ const resultHasKey = Object.hasOwn(resultState, key);
4027
+ if (originalHasKey === resultHasKey && originalState[key] === resultState[key]) continue;
4028
+ if (resultHasKey) nextState[key] = resultState[key];
4029
+ else delete nextState[key];
4030
+ }
4031
+ const mergedExec = mergeChildExec(settledContext.exec, result.next.exec);
4032
+ settledContext = {
4033
+ ...settledContext,
4034
+ buffer: result.next.buffer,
4035
+ optionsTerminated: result.next.optionsTerminated,
4036
+ state: nextState,
4037
+ ...mergedExec != null ? {
4038
+ trace: mergedExec.trace,
4039
+ exec: mergedExec,
4040
+ dependencyRegistry: mergedExec.dependencyRegistry
4041
+ } : {}
4042
+ };
4043
+ }
4044
+ return {
4045
+ success: true,
4046
+ next: settledContext,
4047
+ consumed: []
4048
+ };
4049
+ };
4050
+ const canTryPositionalLaneAfterFailure = (lane, context) => {
4051
+ const token = context.buffer[0];
4052
+ return token != null && (context.optionsTerminated || !token.startsWith("-")) && (lane.acceptingAnyToken || lane.leadingNames.has(token));
4053
+ };
4054
+ const parseChildrenSync = (context) => {
3875
4055
  let currentContext = context;
3876
4056
  let zeroConsumedSuccess = null;
3877
4057
  for (let i = 0; i < syncParsers.length; i++) {
@@ -3915,7 +4095,7 @@ function merge(...args) {
3915
4095
  ...createObjectLikeInitialError(context, noMatchContext)
3916
4096
  };
3917
4097
  };
3918
- const parseAsync = async (context) => {
4098
+ const parseChildrenAsync = async (context) => {
3919
4099
  let currentContext = context;
3920
4100
  let zeroConsumedSuccess = null;
3921
4101
  for (let i = 0; i < parsers.length; i++) {
@@ -3960,15 +4140,141 @@ function merge(...args) {
3960
4140
  ...createObjectLikeInitialError(context, noMatchContext)
3961
4141
  };
3962
4142
  };
4143
+ const parseSync = (context) => {
4144
+ let currentContext = context;
4145
+ let error = createObjectLikeInitialError(context, noMatchContext);
4146
+ const allConsumed = [];
4147
+ const consumedLanes = /* @__PURE__ */ new Set();
4148
+ const consumedGroups = /* @__PURE__ */ new Set();
4149
+ const laneResults = /* @__PURE__ */ new Map();
4150
+ let attemptedLane = false;
4151
+ let madeProgress = true;
4152
+ while (madeProgress && currentContext.buffer.length > 0) {
4153
+ madeProgress = false;
4154
+ let consumingError = null;
4155
+ for (const lane of mergeParseLanes) {
4156
+ if (consumingError != null && lane.priority < consumingError.priority) {
4157
+ if (!canTryPositionalLaneAfterFailure(lane, currentContext)) continue;
4158
+ }
4159
+ attemptedLane = true;
4160
+ const result = lane.parse(currentContext);
4161
+ laneResults.set(lane, result);
4162
+ if (result.success && result.consumed.length > 0) {
4163
+ currentContext = result.next;
4164
+ allConsumed.push(...result.consumed);
4165
+ consumedLanes.add(lane);
4166
+ for (const group$1 of lane.requiredConsumptionGroups ?? []) if (group$1.isActive?.(result.next.state) !== false) consumedGroups.add(group$1.id);
4167
+ madeProgress = true;
4168
+ break;
4169
+ }
4170
+ if (!result.success) {
4171
+ if (result.consumed > 0 && consumingError == null) consumingError = {
4172
+ priority: lane.priority,
4173
+ result
4174
+ };
4175
+ if (error.consumed < result.consumed) error = result;
4176
+ }
4177
+ }
4178
+ if (!madeProgress && consumingError != null && allConsumed.length === 0) return consumingError.result;
4179
+ }
4180
+ if (allConsumed.length === 0) {
4181
+ if (attemptedLane) {
4182
+ const settled = settleZeroConsumptionLanes(context, laneResults);
4183
+ return settled ?? {
4184
+ ...error,
4185
+ success: false
4186
+ };
4187
+ }
4188
+ const fallback = parseChildrenSync(context);
4189
+ if (!fallback.success && fallback.consumed < error.consumed) return {
4190
+ ...error,
4191
+ success: false
4192
+ };
4193
+ return fallback;
4194
+ }
4195
+ for (const lane of mergeParseLanes) {
4196
+ if (consumedLanes.has(lane) || lane.leadingNames.size > 0 || lane.acceptingAnyToken || lane.requiredConsumptionGroups?.some((group$1) => !consumedGroups.has(group$1.id)) === true) continue;
4197
+ const result = lane.parse(currentContext);
4198
+ if (result.success && result.consumed.length === 0 && result.next.state !== currentContext.state) currentContext = result.next;
4199
+ }
4200
+ return {
4201
+ success: true,
4202
+ next: currentContext,
4203
+ consumed: allConsumed
4204
+ };
4205
+ };
4206
+ const parseAsync = async (context) => {
4207
+ let currentContext = context;
4208
+ let error = createObjectLikeInitialError(context, noMatchContext);
4209
+ const allConsumed = [];
4210
+ const consumedLanes = /* @__PURE__ */ new Set();
4211
+ const consumedGroups = /* @__PURE__ */ new Set();
4212
+ const laneResults = /* @__PURE__ */ new Map();
4213
+ let attemptedLane = false;
4214
+ let madeProgress = true;
4215
+ while (madeProgress && currentContext.buffer.length > 0) {
4216
+ madeProgress = false;
4217
+ let consumingError = null;
4218
+ for (const lane of mergeParseLanes) {
4219
+ if (consumingError != null && lane.priority < consumingError.priority) {
4220
+ if (!canTryPositionalLaneAfterFailure(lane, currentContext)) continue;
4221
+ }
4222
+ attemptedLane = true;
4223
+ const result = await lane.parse(currentContext);
4224
+ laneResults.set(lane, result);
4225
+ if (result.success && result.consumed.length > 0) {
4226
+ currentContext = result.next;
4227
+ allConsumed.push(...result.consumed);
4228
+ consumedLanes.add(lane);
4229
+ for (const group$1 of lane.requiredConsumptionGroups ?? []) if (group$1.isActive?.(result.next.state) !== false) consumedGroups.add(group$1.id);
4230
+ madeProgress = true;
4231
+ break;
4232
+ }
4233
+ if (!result.success) {
4234
+ if (result.consumed > 0 && consumingError == null) consumingError = {
4235
+ priority: lane.priority,
4236
+ result
4237
+ };
4238
+ if (error.consumed < result.consumed) error = result;
4239
+ }
4240
+ }
4241
+ if (!madeProgress && consumingError != null && allConsumed.length === 0) return consumingError.result;
4242
+ }
4243
+ if (allConsumed.length === 0) {
4244
+ if (attemptedLane) {
4245
+ const settled = settleZeroConsumptionLanes(context, laneResults);
4246
+ return settled ?? {
4247
+ ...error,
4248
+ success: false
4249
+ };
4250
+ }
4251
+ const fallback = await parseChildrenAsync(context);
4252
+ if (!fallback.success && fallback.consumed < error.consumed) return {
4253
+ ...error,
4254
+ success: false
4255
+ };
4256
+ return fallback;
4257
+ }
4258
+ for (const lane of mergeParseLanes) {
4259
+ if (consumedLanes.has(lane) || lane.leadingNames.size > 0 || lane.acceptingAnyToken || lane.requiredConsumptionGroups?.some((group$1) => !consumedGroups.has(group$1.id)) === true) continue;
4260
+ const result = await lane.parse(currentContext);
4261
+ if (result.success && result.consumed.length === 0 && result.next.state !== currentContext.state) currentContext = result.next;
4262
+ }
4263
+ return {
4264
+ success: true,
4265
+ next: currentContext,
4266
+ consumed: allConsumed
4267
+ };
4268
+ };
3963
4269
  const mergeParser = {
3964
4270
  mode: combinedMode,
3965
4271
  $valueType: [],
3966
4272
  $stateType: [],
3967
4273
  [fieldParsersKey]: mergedFieldParsers,
3968
- priority: Math.max(...parsers.map((p) => p.priority)),
4274
+ priority: Math.max(...mergeParseLanes.map((lane) => lane.priority)),
3969
4275
  usage: applyHiddenToUsage(parsers.flatMap((p) => p.usage), options.hidden),
3970
- leadingNames: sharedBufferLeadingNames(parsers),
3971
- acceptingAnyToken: parsers.some((p) => p.acceptingAnyToken),
4276
+ leadingNames: sharedBufferLeadingNames(mergeParseLanes),
4277
+ acceptingAnyToken: mergeParseLanes.some((lane) => lane.acceptingAnyToken),
3972
4278
  initialState,
3973
4279
  canSkip(state, exec) {
3974
4280
  return parsers.every((parser, index) => {
@@ -4443,6 +4749,7 @@ function merge(...args) {
4443
4749
  };
4444
4750
  }
4445
4751
  };
4752
+ defineParseLanes(mergeParser, mergeParseLanes);
4446
4753
  defineInheritedAnnotationParser(mergeParser);
4447
4754
  return fluent(mergeParser);
4448
4755
  }
@@ -5125,6 +5432,7 @@ function group(label, parser, options = {}) {
5125
5432
  };
5126
5433
  }
5127
5434
  };
5435
+ defineParseLanes(groupParser, getOwnParseLanes(parser));
5128
5436
  Object.defineProperty(groupParser, extractPhase2SeedKey, {
5129
5437
  value(state, exec) {
5130
5438
  return extractPhase2Seed(parser, state, exec);
@@ -8,6 +8,12 @@ const require_input_trace = require('../input-trace.cjs');
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
  *
@@ -854,15 +886,18 @@ exports.annotationWrapperRequiresSourceBindingKey = annotationWrapperRequiresSou
854
886
  exports.composeWrappedSourceMetadata = composeWrappedSourceMetadata;
855
887
  exports.createParserContext = createParserContext;
856
888
  exports.defineInheritedAnnotationParser = defineInheritedAnnotationParser;
889
+ exports.defineParseLanes = defineParseLanes;
857
890
  exports.defineSourceBindingOnlyAnnotationCompletionParser = defineSourceBindingOnlyAnnotationCompletionParser;
858
891
  exports.getDelegatingSuggestRuntimeNodes = getDelegatingSuggestRuntimeNodes;
859
892
  exports.getDocPage = getDocPage;
860
893
  exports.getDocPageAsync = getDocPageAsync;
861
894
  exports.getDocPageSync = getDocPageSync;
895
+ exports.getOwnParseLanes = getOwnParseLanes;
862
896
  exports.getParserSuggestRuntimeNodes = getParserSuggestRuntimeNodes;
863
897
  exports.inheritParentAnnotationsKey = inheritParentAnnotationsKey;
864
898
  exports.parse = parse;
865
899
  exports.parseAsync = parseAsync;
900
+ exports.parseLanesKey = parseLanesKey;
866
901
  exports.parseSync = parseSync;
867
902
  exports.suggest = suggest;
868
903
  exports.suggestAsync = suggestAsync;
@@ -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"`.
@@ -851,6 +896,22 @@ declare function getDelegatingSuggestRuntimeNodes<TInnerState>(innerParser: Pars
851
896
  * @internal
852
897
  */
853
898
  declare function composeWrappedSourceMetadata(dependencyMetadata: ParserDependencyMetadata | undefined, wrapSource: (source: NonNullable<ParserDependencyMetadata["source"]>) => NonNullable<ParserDependencyMetadata["source"]>): ParserDependencyMetadata | undefined;
899
+ /**
900
+ * Defines internal parse-lane metadata without exposing it through object
901
+ * spreads used by custom parser wrappers.
902
+ *
903
+ * @internal
904
+ */
905
+ declare function defineParseLanes<TState>(parser: object, lanes: readonly ParseLane<TState>[] | undefined): void;
906
+ /**
907
+ * Gets parse-lane metadata defined directly on a parser.
908
+ *
909
+ * Inherited metadata belongs to the parser's prototype and must not bypass an
910
+ * overriding `parse()` implementation on a custom wrapper.
911
+ *
912
+ * @internal
913
+ */
914
+ declare function getOwnParseLanes<TState>(parser: object): readonly ParseLane<TState>[] | undefined;
854
915
  /**
855
916
  * Marks a parser as inheriting parent-state annotations through wrapper-state
856
917
  * reconstruction.
@@ -999,4 +1060,4 @@ declare function getDocPage(parser: Parser<"sync", unknown, unknown>, argsOrOpti
999
1060
  declare function getDocPage(parser: Parser<"async", unknown, unknown>, argsOrOptions?: readonly string[] | ParseOptions, options?: ParseOptions): Promise<DocPage | undefined>;
1000
1061
  declare function getDocPage<M extends Mode>(parser: Parser<M, unknown, unknown>, argsOrOptions?: readonly string[] | ParseOptions, options?: ParseOptions): ModeValue<M, DocPage | undefined>;
1001
1062
  //#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 };
1063
+ 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 };
@@ -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"`.
@@ -851,6 +896,22 @@ declare function getDelegatingSuggestRuntimeNodes<TInnerState>(innerParser: Pars
851
896
  * @internal
852
897
  */
853
898
  declare function composeWrappedSourceMetadata(dependencyMetadata: ParserDependencyMetadata | undefined, wrapSource: (source: NonNullable<ParserDependencyMetadata["source"]>) => NonNullable<ParserDependencyMetadata["source"]>): ParserDependencyMetadata | undefined;
899
+ /**
900
+ * Defines internal parse-lane metadata without exposing it through object
901
+ * spreads used by custom parser wrappers.
902
+ *
903
+ * @internal
904
+ */
905
+ declare function defineParseLanes<TState>(parser: object, lanes: readonly ParseLane<TState>[] | undefined): void;
906
+ /**
907
+ * Gets parse-lane metadata defined directly on a parser.
908
+ *
909
+ * Inherited metadata belongs to the parser's prototype and must not bypass an
910
+ * overriding `parse()` implementation on a custom wrapper.
911
+ *
912
+ * @internal
913
+ */
914
+ declare function getOwnParseLanes<TState>(parser: object): readonly ParseLane<TState>[] | undefined;
854
915
  /**
855
916
  * Marks a parser as inheriting parent-state annotations through wrapper-state
856
917
  * reconstruction.
@@ -999,4 +1060,4 @@ declare function getDocPage(parser: Parser<"sync", unknown, unknown>, argsOrOpti
999
1060
  declare function getDocPage(parser: Parser<"async", unknown, unknown>, argsOrOptions?: readonly string[] | ParseOptions, options?: ParseOptions): Promise<DocPage | undefined>;
1000
1061
  declare function getDocPage<M extends Mode>(parser: Parser<M, unknown, unknown>, argsOrOptions?: readonly string[] | ParseOptions, options?: ParseOptions): ModeValue<M, DocPage | undefined>;
1001
1062
  //#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 };
1063
+ 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
  *
@@ -850,4 +882,4 @@ function findNextMatchedCommandArgIndex(args, matchedCommandArgIndices, start) {
850
882
  }
851
883
 
852
884
  //#endregion
853
- export { annotationWrapperRequiresSourceBindingKey, composeWrappedSourceMetadata, createParserContext, defineInheritedAnnotationParser, defineSourceBindingOnlyAnnotationCompletionParser, getDelegatingSuggestRuntimeNodes, getDocPage, getDocPageAsync, getDocPageSync, getParserSuggestRuntimeNodes, inheritParentAnnotationsKey, parse, parseAsync, parseSync, suggest, suggestAsync, suggestSync, unmatchedNonCliDependencySourceStateMarker };
885
+ 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 fluent(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 fluent(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", {
@@ -1680,6 +1721,7 @@ function multiple(parser, options = {}) {
1680
1721
  function nonEmpty(parser) {
1681
1722
  const syncParser = parser;
1682
1723
  const initialState = parser.initialState;
1724
+ const consumptionGroup = {};
1683
1725
  const processNonEmptyResult = (result) => {
1684
1726
  if (!result.success) return result;
1685
1727
  if (result.consumed.length === 0) return {
@@ -1732,6 +1774,17 @@ function nonEmpty(parser) {
1732
1774
  return syncParser.getDocFragments(state, defaultValue);
1733
1775
  }
1734
1776
  };
1777
+ require_internal_parser.defineParseLanes(nonEmptyParser, require_internal_parser.getOwnParseLanes(parser)?.map((lane) => ({
1778
+ priority: lane.priority,
1779
+ zeroConsumptionGroup: lane.zeroConsumptionGroup,
1780
+ settlesZeroConsumption: lane.settlesZeroConsumption,
1781
+ leadingNames: lane.leadingNames,
1782
+ acceptingAnyToken: lane.acceptingAnyToken,
1783
+ requiredConsumptionGroups: [...lane.requiredConsumptionGroups ?? [], { id: consumptionGroup }],
1784
+ parse(context) {
1785
+ return lane.parse(context);
1786
+ }
1787
+ })));
1735
1788
  if ("placeholder" in parser) Object.defineProperty(nonEmptyParser, "placeholder", {
1736
1789
  get() {
1737
1790
  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 fluent(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 fluent(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", {
@@ -1680,6 +1721,7 @@ function multiple(parser, options = {}) {
1680
1721
  function nonEmpty(parser) {
1681
1722
  const syncParser = parser;
1682
1723
  const initialState = parser.initialState;
1724
+ const consumptionGroup = {};
1683
1725
  const processNonEmptyResult = (result) => {
1684
1726
  if (!result.success) return result;
1685
1727
  if (result.consumed.length === 0) return {
@@ -1732,6 +1774,17 @@ function nonEmpty(parser) {
1732
1774
  return syncParser.getDocFragments(state, defaultValue);
1733
1775
  }
1734
1776
  };
1777
+ defineParseLanes(nonEmptyParser, getOwnParseLanes(parser)?.map((lane) => ({
1778
+ priority: lane.priority,
1779
+ zeroConsumptionGroup: lane.zeroConsumptionGroup,
1780
+ settlesZeroConsumption: lane.settlesZeroConsumption,
1781
+ leadingNames: lane.leadingNames,
1782
+ acceptingAnyToken: lane.acceptingAnyToken,
1783
+ requiredConsumptionGroups: [...lane.requiredConsumptionGroups ?? [], { id: consumptionGroup }],
1784
+ parse(context) {
1785
+ return lane.parse(context);
1786
+ }
1787
+ })));
1735
1788
  if ("placeholder" in parser) Object.defineProperty(nonEmptyParser, "placeholder", {
1736
1789
  get() {
1737
1790
  return parser.placeholder;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -225,7 +225,7 @@
225
225
  "fast-check": "^4.7.0",
226
226
  "tsdown": "^0.13.0",
227
227
  "typescript": "^5.8.3",
228
- "@optique/env": "1.2.3"
228
+ "@optique/env": "1.2.4"
229
229
  },
230
230
  "scripts": {
231
231
  "build": "tsdown",