@optique/core 1.2.2 → 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
  */
@@ -1529,6 +1529,33 @@ function createLongestMatch(...args) {
1529
1529
  return require_modifiers.fluent(multiResult);
1530
1530
  }
1531
1531
  /**
1532
+ * Creates the initial parse error shared by object-like combinators.
1533
+ * @param context The current parser context.
1534
+ * @param noMatchContext The kinds of input accepted by the combinator.
1535
+ * @param errors Optional custom error formatters.
1536
+ * @returns A zero-consumption parse error.
1537
+ */
1538
+ function createObjectLikeInitialError(context, noMatchContext, errors) {
1539
+ if (context.buffer.length < 1) {
1540
+ const customEndOfInput = errors?.endOfInput;
1541
+ return {
1542
+ consumed: 0,
1543
+ error: customEndOfInput ? typeof customEndOfInput === "function" ? customEndOfInput(noMatchContext) : customEndOfInput : generateNoMatchError(noMatchContext)
1544
+ };
1545
+ }
1546
+ const token = context.buffer[0];
1547
+ const customMessage = errors?.unexpectedInput;
1548
+ if (customMessage) return {
1549
+ consumed: 0,
1550
+ error: typeof customMessage === "function" ? customMessage(token) : customMessage
1551
+ };
1552
+ const baseError = require_message.message`Unexpected option or argument: ${token}.`;
1553
+ return {
1554
+ consumed: 0,
1555
+ error: require_suggestion.createErrorWithSuggestions(baseError, token, context.usage, "both", errors?.suggestions)
1556
+ };
1557
+ }
1558
+ /**
1532
1559
  * Internal sync helper for object suggest functionality.
1533
1560
  * @internal
1534
1561
  */
@@ -1921,19 +1948,53 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1921
1948
  checkDuplicateReachableLeadingCommandNames(parserPairs.map(([field, parser]) => [field, parser]));
1922
1949
  const noMatchContext = analyzeNoMatchContext(parserKeys.map((k) => parsers[k]));
1923
1950
  const combinedMode = parserKeys.some((k) => parsers[k].mode === "async") ? "async" : "sync";
1924
- const getInitialError = (context) => ({
1925
- consumed: 0,
1926
- error: context.buffer.length > 0 ? (() => {
1927
- const token = context.buffer[0];
1928
- const customMessage = options.errors?.unexpectedInput;
1929
- if (customMessage) return typeof customMessage === "function" ? customMessage(token) : customMessage;
1930
- const baseError = require_message.message`Unexpected option or argument: ${token}.`;
1931
- return require_suggestion.createErrorWithSuggestions(baseError, token, context.usage, "both", options.errors?.suggestions);
1932
- })() : (() => {
1933
- const customEndOfInput = options.errors?.endOfInput;
1934
- return customEndOfInput ? typeof customEndOfInput === "function" ? customEndOfInput(noMatchContext) : customEndOfInput : generateNoMatchError(noMatchContext);
1935
- })()
1936
- });
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
+ }));
1937
1998
  const parseSync = (context) => {
1938
1999
  let error = getInitialError(context);
1939
2000
  let currentContext = context;
@@ -2452,6 +2513,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
2452
2513
  configurable: true,
2453
2514
  enumerable: false
2454
2515
  });
2516
+ require_internal_parser.defineParseLanes(objectParser, objectParseLanes);
2455
2517
  require_internal_parser.defineInheritedAnnotationParser(objectParser);
2456
2518
  return require_modifiers.fluent(objectParser);
2457
2519
  }
@@ -3803,6 +3865,7 @@ function merge(...args) {
3803
3865
  const syncParsers = syncSorted.map(([p]) => p);
3804
3866
  if (!options.allowDuplicates) checkDuplicateOptionNames(sorted.map(([parser, originalIndex]) => [String(originalIndex), parser.usage]));
3805
3867
  checkDuplicateReachableLeadingCommandNames(sorted.map(([parser, originalIndex]) => [String(originalIndex), parser]));
3868
+ const noMatchContext = analyzeNoMatchContext(rawParsers);
3806
3869
  const mergedFieldParsers = collectChildFieldParsers(parsers);
3807
3870
  const duplicateOutputFieldNames = collectDuplicateFieldNames(mergedFieldParsers);
3808
3871
  const parserStateKey = (index) => `__parser_${index}`;
@@ -3855,7 +3918,140 @@ function merge(...args) {
3855
3918
  [localObjectStateKey(index)]: result.next.state
3856
3919
  };
3857
3920
  };
3858
- 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) => {
3859
4055
  let currentContext = context;
3860
4056
  let zeroConsumedSuccess = null;
3861
4057
  for (let i = 0; i < syncParsers.length; i++) {
@@ -3896,11 +4092,10 @@ function merge(...args) {
3896
4092
  };
3897
4093
  return {
3898
4094
  success: false,
3899
- consumed: 0,
3900
- error: require_message.message`No matching option or argument found.`
4095
+ ...createObjectLikeInitialError(context, noMatchContext)
3901
4096
  };
3902
4097
  };
3903
- const parseAsync = async (context) => {
4098
+ const parseChildrenAsync = async (context) => {
3904
4099
  let currentContext = context;
3905
4100
  let zeroConsumedSuccess = null;
3906
4101
  for (let i = 0; i < parsers.length; i++) {
@@ -3942,8 +4137,133 @@ function merge(...args) {
3942
4137
  };
3943
4138
  return {
3944
4139
  success: false,
3945
- consumed: 0,
3946
- error: require_message.message`No matching option or argument found.`
4140
+ ...createObjectLikeInitialError(context, noMatchContext)
4141
+ };
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
3947
4267
  };
3948
4268
  };
3949
4269
  const mergeParser = {
@@ -3951,10 +4271,10 @@ function merge(...args) {
3951
4271
  $valueType: [],
3952
4272
  $stateType: [],
3953
4273
  [fieldParsersKey]: mergedFieldParsers,
3954
- priority: Math.max(...parsers.map((p) => p.priority)),
4274
+ priority: Math.max(...mergeParseLanes.map((lane) => lane.priority)),
3955
4275
  usage: applyHiddenToUsage(parsers.flatMap((p) => p.usage), options.hidden),
3956
- leadingNames: sharedBufferLeadingNames(parsers),
3957
- acceptingAnyToken: parsers.some((p) => p.acceptingAnyToken),
4276
+ leadingNames: sharedBufferLeadingNames(mergeParseLanes),
4277
+ acceptingAnyToken: mergeParseLanes.some((lane) => lane.acceptingAnyToken),
3958
4278
  initialState,
3959
4279
  canSkip(state, exec) {
3960
4280
  return parsers.every((parser, index) => {
@@ -4429,6 +4749,7 @@ function merge(...args) {
4429
4749
  };
4430
4750
  }
4431
4751
  };
4752
+ require_internal_parser.defineParseLanes(mergeParser, mergeParseLanes);
4432
4753
  require_internal_parser.defineInheritedAnnotationParser(mergeParser);
4433
4754
  return require_modifiers.fluent(mergeParser);
4434
4755
  }
@@ -5111,6 +5432,7 @@ function group(label, parser, options = {}) {
5111
5432
  };
5112
5433
  }
5113
5434
  };
5435
+ require_internal_parser.defineParseLanes(groupParser, require_internal_parser.getOwnParseLanes(parser));
5114
5436
  Object.defineProperty(groupParser, require_phase2_seed.extractPhase2SeedKey, {
5115
5437
  value(state, exec) {
5116
5438
  return require_phase2_seed.extractPhase2Seed(parser, state, exec);