@optique/core 1.0.5 → 1.0.7

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.
@@ -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 { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
12
12
  import { completeOrExtractPhase2Seed, extractPhase2Seed, extractPhase2SeedKey, phase2SeedFromValueResult } from "./phase2-seed.js";
@@ -77,8 +77,8 @@ function unionLeadingNames(parsers) {
77
77
  * Computes `leadingNames` for shared-buffer compositions (`tuple()`,
78
78
  * `object()`, `merge()`, `concat()`).
79
79
  *
80
- * Children are processed in descending priority order (matching the
81
- * round-robin parse loop). Once a child with `acceptingAnyToken` is
80
+ * Sources are processed in descending priority order (matching the
81
+ * round-robin parse loop). Once a source with `acceptingAnyToken` is
82
82
  * encountered, no lower-priority children can match at position 0, so
83
83
  * their names are excluded.
84
84
  */
@@ -1448,6 +1448,33 @@ function longestMatch(...args) {
1448
1448
  return multiResult;
1449
1449
  }
1450
1450
  /**
1451
+ * Creates the initial parse error shared by object-like combinators.
1452
+ * @param context The current parser context.
1453
+ * @param noMatchContext The kinds of input accepted by the combinator.
1454
+ * @param errors Optional custom error formatters.
1455
+ * @returns A zero-consumption parse error.
1456
+ */
1457
+ function createObjectLikeInitialError(context, noMatchContext, errors) {
1458
+ if (context.buffer.length < 1) {
1459
+ const customEndOfInput = errors?.endOfInput;
1460
+ return {
1461
+ consumed: 0,
1462
+ error: customEndOfInput ? typeof customEndOfInput === "function" ? customEndOfInput(noMatchContext) : customEndOfInput : generateNoMatchError(noMatchContext)
1463
+ };
1464
+ }
1465
+ const token = context.buffer[0];
1466
+ const customMessage = errors?.unexpectedInput;
1467
+ if (customMessage) return {
1468
+ consumed: 0,
1469
+ error: typeof customMessage === "function" ? customMessage(token) : customMessage
1470
+ };
1471
+ const baseError = message`Unexpected option or argument: ${token}.`;
1472
+ return {
1473
+ consumed: 0,
1474
+ error: createErrorWithSuggestions(baseError, token, context.usage, "both", errors?.suggestions)
1475
+ };
1476
+ }
1477
+ /**
1451
1478
  * Internal sync helper for object suggest functionality.
1452
1479
  * @internal
1453
1480
  */
@@ -1839,19 +1866,53 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1839
1866
  if (!options.allowDuplicates) checkDuplicateOptionNames(parserPairs.map(([field, parser]) => [field, parser.usage]));
1840
1867
  const noMatchContext = analyzeNoMatchContext(parserKeys.map((k) => parsers[k]));
1841
1868
  const combinedMode = parserKeys.some((k) => parsers[k].mode === "async") ? "async" : "sync";
1842
- const getInitialError = (context) => ({
1843
- consumed: 0,
1844
- error: context.buffer.length > 0 ? (() => {
1845
- const token = context.buffer[0];
1846
- const customMessage = options.errors?.unexpectedInput;
1847
- if (customMessage) return typeof customMessage === "function" ? customMessage(token) : customMessage;
1848
- const baseError = message`Unexpected option or argument: ${token}.`;
1849
- return createErrorWithSuggestions(baseError, token, context.usage, "both", options.errors?.suggestions);
1850
- })() : (() => {
1851
- const customEndOfInput = options.errors?.endOfInput;
1852
- return customEndOfInput ? typeof customEndOfInput === "function" ? customEndOfInput(noMatchContext) : customEndOfInput : generateNoMatchError(noMatchContext);
1853
- })()
1854
- });
1869
+ const getInitialError = (context) => createObjectLikeInitialError(context, noMatchContext, options.errors);
1870
+ const adaptFieldLaneResult = (context, field, parser, fieldState, result) => {
1871
+ if (!result.success) return result;
1872
+ if (result.consumed.length === 0 && result.next.state === fieldState) return {
1873
+ success: true,
1874
+ next: context,
1875
+ consumed: []
1876
+ };
1877
+ const mergedExec = mergeChildExec(context.exec, result.next.exec);
1878
+ const nextState = result.next.state === fieldState ? context.state : {
1879
+ ...context.state,
1880
+ [field]: getWrappedChildState(context.state, result.next.state, parser)
1881
+ };
1882
+ return {
1883
+ success: true,
1884
+ next: {
1885
+ ...context,
1886
+ buffer: result.next.buffer,
1887
+ optionsTerminated: result.next.optionsTerminated,
1888
+ state: nextState,
1889
+ ...mergedExec != null ? {
1890
+ trace: mergedExec.trace,
1891
+ exec: mergedExec,
1892
+ dependencyRegistry: mergedExec.dependencyRegistry
1893
+ } : {}
1894
+ },
1895
+ consumed: result.consumed
1896
+ };
1897
+ };
1898
+ const objectZeroConsumptionGroup = {};
1899
+ const objectParseLanes = parserPairs.map(([field, parser]) => ({
1900
+ priority: parser.priority,
1901
+ zeroConsumptionGroup: objectZeroConsumptionGroup,
1902
+ settlesZeroConsumption: false,
1903
+ leadingNames: parser.leadingNames,
1904
+ acceptingAnyToken: parser.acceptingAnyToken,
1905
+ parse(context) {
1906
+ const fieldState = createFieldStateGetter(context.state, getObjectParseChildState)(field, parser);
1907
+ return dispatchByMode(combinedMode, () => {
1908
+ const result = parser.parse(withChildContext$1(context, field, fieldState, parser));
1909
+ return adaptFieldLaneResult(context, field, parser, fieldState, result);
1910
+ }, async () => {
1911
+ const result = await parser.parse(withChildContext$1(context, field, fieldState, parser));
1912
+ return adaptFieldLaneResult(context, field, parser, fieldState, result);
1913
+ });
1914
+ }
1915
+ }));
1855
1916
  const parseSync = (context) => {
1856
1917
  let error = getInitialError(context);
1857
1918
  let currentContext = context;
@@ -2345,6 +2406,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
2345
2406
  configurable: true,
2346
2407
  enumerable: false
2347
2408
  });
2409
+ defineParseLanes(objectParser, objectParseLanes);
2348
2410
  defineInheritedAnnotationParser(objectParser);
2349
2411
  return objectParser;
2350
2412
  }
@@ -3000,6 +3062,7 @@ function merge(...args) {
3000
3062
  const syncSorted = syncWithIndex.toSorted(([a], [b]) => b.priority - a.priority);
3001
3063
  const syncParsers = syncSorted.map(([p]) => p);
3002
3064
  if (!options.allowDuplicates) checkDuplicateOptionNames(sorted.map(([parser, originalIndex]) => [String(originalIndex), parser.usage]));
3065
+ const noMatchContext = analyzeNoMatchContext(rawParsers);
3003
3066
  const mergedFieldParsers = collectChildFieldParsers(parsers);
3004
3067
  const duplicateOutputFieldNames = collectDuplicateFieldNames(mergedFieldParsers);
3005
3068
  const parserStateKey = (index) => `__parser_${index}`;
@@ -3011,17 +3074,17 @@ function merge(...args) {
3011
3074
  if (parser.initialState === void 0) initialState[parserStateKey(i)] = void 0;
3012
3075
  else if (parser.initialState && typeof parser.initialState === "object") for (const field in parser.initialState) initialState[field] = parser.initialState[field];
3013
3076
  }
3014
- const extractParserState = (parser, context, index) => {
3077
+ const extractParserState = (parser, state, index) => {
3015
3078
  if (parser.initialState === void 0) {
3016
3079
  const key = parserStateKey(index);
3017
- if (context.state && typeof context.state === "object" && key in context.state) return context.state[key];
3080
+ if (state && typeof state === "object" && key in state) return state[key];
3018
3081
  return void 0;
3019
3082
  } else if (parser.initialState && typeof parser.initialState === "object") {
3020
3083
  const localStateKey = localObjectStateKey(index);
3021
- if (shouldPreserveLocalChildState(parser) && context.state && typeof context.state === "object" && localStateKey in context.state) return context.state[localStateKey];
3022
- if (context.state && typeof context.state === "object") {
3084
+ if (shouldPreserveLocalChildState(parser) && state && typeof state === "object" && localStateKey in state) return state[localStateKey];
3085
+ if (state && typeof state === "object") {
3023
3086
  const extractedState = {};
3024
- for (const field in parser.initialState) extractedState[field] = field in context.state ? context.state[field] : parser.initialState[field];
3087
+ for (const field in parser.initialState) extractedState[field] = field in state ? state[field] : parser.initialState[field];
3025
3088
  return extractedState;
3026
3089
  }
3027
3090
  return parser.initialState;
@@ -3051,12 +3114,145 @@ function merge(...args) {
3051
3114
  [localObjectStateKey(index)]: result.next.state
3052
3115
  };
3053
3116
  };
3054
- const parseSync = (context) => {
3117
+ const adaptMergeLaneResult = (parser, context, parserState, parsedState, result, index) => {
3118
+ if (!result.success) return result;
3119
+ const mergedExec = mergeChildExec(context.exec, result.next.exec);
3120
+ const newState = result.next.state === parsedState ? context.state : mergeResultState(parser, context, parserState, result, index);
3121
+ return {
3122
+ success: true,
3123
+ next: {
3124
+ ...context,
3125
+ buffer: result.next.buffer,
3126
+ optionsTerminated: result.next.optionsTerminated,
3127
+ state: newState,
3128
+ ...mergedExec != null ? {
3129
+ trace: mergedExec.trace,
3130
+ exec: mergedExec,
3131
+ dependencyRegistry: mergedExec.dependencyRegistry
3132
+ } : {}
3133
+ },
3134
+ consumed: result.consumed
3135
+ };
3136
+ };
3137
+ const childrenInDeclarationOrder = sorted.map(([parser, originalIndex], sortedIndex) => ({
3138
+ parser,
3139
+ originalIndex,
3140
+ sortedIndex
3141
+ })).toSorted((a, b) => a.originalIndex - b.originalIndex);
3142
+ const mergeParseLanes = childrenInDeclarationOrder.flatMap(({ parser, sortedIndex }) => {
3143
+ const occurrenceConsumptionGroups = /* @__PURE__ */ new WeakMap();
3144
+ const occurrenceZeroConsumptionGroups = /* @__PURE__ */ new WeakMap();
3145
+ const scopeConsumptionGroup = (group$1) => {
3146
+ const existing = occurrenceConsumptionGroups.get(group$1);
3147
+ if (existing != null) return existing;
3148
+ const scoped = {};
3149
+ occurrenceConsumptionGroups.set(group$1, scoped);
3150
+ return scoped;
3151
+ };
3152
+ const childLanes = getOwnParseLanes(parser);
3153
+ const lanes = childLanes ?? [{
3154
+ priority: parser.priority,
3155
+ leadingNames: parser.leadingNames,
3156
+ acceptingAnyToken: parser.acceptingAnyToken,
3157
+ parse(context) {
3158
+ return parser.parse(context);
3159
+ }
3160
+ }];
3161
+ return lanes.map((lane) => ({
3162
+ priority: lane.priority,
3163
+ zeroConsumptionGroup: (() => {
3164
+ const group$1 = lane.zeroConsumptionGroup ?? lane;
3165
+ const existing = occurrenceZeroConsumptionGroups.get(group$1);
3166
+ if (existing != null) return existing;
3167
+ const scoped = {};
3168
+ occurrenceZeroConsumptionGroups.set(group$1, scoped);
3169
+ return scoped;
3170
+ })(),
3171
+ settlesZeroConsumption: lane.settlesZeroConsumption,
3172
+ leadingNames: lane.leadingNames,
3173
+ acceptingAnyToken: lane.acceptingAnyToken,
3174
+ requiredConsumptionGroups: lane.requiredConsumptionGroups?.map((group$1) => ({
3175
+ id: scopeConsumptionGroup(group$1.id),
3176
+ ...group$1.isActive == null ? {} : { isActive(state) {
3177
+ if (state == null || typeof state !== "object") return false;
3178
+ const parserState = extractParserState(parser, state, sortedIndex);
3179
+ return group$1.isActive?.(parserState) ?? true;
3180
+ } }
3181
+ })),
3182
+ parse(context) {
3183
+ const parserState = extractParserState(parser, context.state, sortedIndex);
3184
+ const childContext = withChildContext$1(context, sortedIndex, parserState, parser);
3185
+ return dispatchByMode(combinedMode, () => {
3186
+ const result = lane.parse(childContext);
3187
+ return adaptMergeLaneResult(parser, context, parserState, childContext.state, result, sortedIndex);
3188
+ }, async () => {
3189
+ const result = await lane.parse(childContext);
3190
+ return adaptMergeLaneResult(parser, context, parserState, childContext.state, result, sortedIndex);
3191
+ });
3192
+ }
3193
+ }));
3194
+ }).toSorted((a, b) => b.priority - a.priority);
3195
+ const settleZeroConsumptionLanes = (context, laneResults) => {
3196
+ const groups = /* @__PURE__ */ new Map();
3197
+ for (const lane of mergeParseLanes) {
3198
+ const group$1 = lane.zeroConsumptionGroup ?? lane;
3199
+ const groupedLanes = groups.get(group$1);
3200
+ if (groupedLanes == null) groups.set(group$1, [lane]);
3201
+ else groupedLanes.push(lane);
3202
+ }
3203
+ const settledGroups = [];
3204
+ for (const groupedLanes of groups.values()) {
3205
+ const groupSucceeded = groupedLanes.every((lane) => {
3206
+ const result = laneResults.get(lane);
3207
+ return lane.settlesZeroConsumption !== false && result?.success === true && result.consumed.length === 0 && (lane.requiredConsumptionGroups?.length ?? 0) === 0;
3208
+ });
3209
+ if (!groupSucceeded) return null;
3210
+ settledGroups.push(groupedLanes);
3211
+ }
3212
+ let settledContext = context;
3213
+ for (const groupedLanes of settledGroups) for (const lane of groupedLanes) {
3214
+ const result = laneResults.get(lane);
3215
+ if (result?.success !== true) continue;
3216
+ const originalState = context.state;
3217
+ const resultState = result.next.state;
3218
+ const nextState = { ...settledContext.state };
3219
+ const stateKeys = new Set([...Reflect.ownKeys(originalState), ...Reflect.ownKeys(resultState)]);
3220
+ for (const key of stateKeys) {
3221
+ const originalHasKey = Object.hasOwn(originalState, key);
3222
+ const resultHasKey = Object.hasOwn(resultState, key);
3223
+ if (originalHasKey === resultHasKey && originalState[key] === resultState[key]) continue;
3224
+ if (resultHasKey) nextState[key] = resultState[key];
3225
+ else delete nextState[key];
3226
+ }
3227
+ const mergedExec = mergeChildExec(settledContext.exec, result.next.exec);
3228
+ settledContext = {
3229
+ ...settledContext,
3230
+ buffer: result.next.buffer,
3231
+ optionsTerminated: result.next.optionsTerminated,
3232
+ state: nextState,
3233
+ ...mergedExec != null ? {
3234
+ trace: mergedExec.trace,
3235
+ exec: mergedExec,
3236
+ dependencyRegistry: mergedExec.dependencyRegistry
3237
+ } : {}
3238
+ };
3239
+ }
3240
+ return {
3241
+ success: true,
3242
+ next: settledContext,
3243
+ consumed: []
3244
+ };
3245
+ };
3246
+ const canTryPositionalLaneAfterFailure = (lane, context) => {
3247
+ const token = context.buffer[0];
3248
+ return token != null && (context.optionsTerminated || !token.startsWith("-")) && (lane.acceptingAnyToken || lane.leadingNames.has(token));
3249
+ };
3250
+ const parseChildrenSync = (context) => {
3055
3251
  let currentContext = context;
3056
3252
  let zeroConsumedSuccess = null;
3057
3253
  for (let i = 0; i < syncParsers.length; i++) {
3058
3254
  const parser = syncParsers[i];
3059
- const parserState = extractParserState(parser, currentContext, i);
3255
+ const parserState = extractParserState(parser, currentContext.state, i);
3060
3256
  const result = parser.parse(withChildContext$1(currentContext, i, parserState, parser));
3061
3257
  if (result.success) {
3062
3258
  const mergedExec = mergeChildExec(currentContext.exec, result.next.exec);
@@ -3092,16 +3288,15 @@ function merge(...args) {
3092
3288
  };
3093
3289
  return {
3094
3290
  success: false,
3095
- consumed: 0,
3096
- error: message`No matching option or argument found.`
3291
+ ...createObjectLikeInitialError(context, noMatchContext)
3097
3292
  };
3098
3293
  };
3099
- const parseAsync = async (context) => {
3294
+ const parseChildrenAsync = async (context) => {
3100
3295
  let currentContext = context;
3101
3296
  let zeroConsumedSuccess = null;
3102
3297
  for (let i = 0; i < parsers.length; i++) {
3103
3298
  const parser = parsers[i];
3104
- const parserState = extractParserState(parser, currentContext, i);
3299
+ const parserState = extractParserState(parser, currentContext.state, i);
3105
3300
  const resultOrPromise = parser.parse(withChildContext$1(currentContext, i, parserState, parser));
3106
3301
  const result = await resultOrPromise;
3107
3302
  if (result.success) {
@@ -3138,8 +3333,133 @@ function merge(...args) {
3138
3333
  };
3139
3334
  return {
3140
3335
  success: false,
3141
- consumed: 0,
3142
- error: message`No matching option or argument found.`
3336
+ ...createObjectLikeInitialError(context, noMatchContext)
3337
+ };
3338
+ };
3339
+ const parseSync = (context) => {
3340
+ let currentContext = context;
3341
+ let error = createObjectLikeInitialError(context, noMatchContext);
3342
+ const allConsumed = [];
3343
+ const consumedLanes = /* @__PURE__ */ new Set();
3344
+ const consumedGroups = /* @__PURE__ */ new Set();
3345
+ const laneResults = /* @__PURE__ */ new Map();
3346
+ let attemptedLane = false;
3347
+ let madeProgress = true;
3348
+ while (madeProgress && currentContext.buffer.length > 0) {
3349
+ madeProgress = false;
3350
+ let consumingError = null;
3351
+ for (const lane of mergeParseLanes) {
3352
+ if (consumingError != null && lane.priority < consumingError.priority) {
3353
+ if (!canTryPositionalLaneAfterFailure(lane, currentContext)) continue;
3354
+ }
3355
+ attemptedLane = true;
3356
+ const result = lane.parse(currentContext);
3357
+ laneResults.set(lane, result);
3358
+ if (result.success && result.consumed.length > 0) {
3359
+ currentContext = result.next;
3360
+ allConsumed.push(...result.consumed);
3361
+ consumedLanes.add(lane);
3362
+ for (const group$1 of lane.requiredConsumptionGroups ?? []) if (group$1.isActive?.(result.next.state) !== false) consumedGroups.add(group$1.id);
3363
+ madeProgress = true;
3364
+ break;
3365
+ }
3366
+ if (!result.success) {
3367
+ if (result.consumed > 0 && consumingError == null) consumingError = {
3368
+ priority: lane.priority,
3369
+ result
3370
+ };
3371
+ if (error.consumed < result.consumed) error = result;
3372
+ }
3373
+ }
3374
+ if (!madeProgress && consumingError != null && allConsumed.length === 0) return consumingError.result;
3375
+ }
3376
+ if (allConsumed.length === 0) {
3377
+ if (attemptedLane) {
3378
+ const settled = settleZeroConsumptionLanes(context, laneResults);
3379
+ return settled ?? {
3380
+ ...error,
3381
+ success: false
3382
+ };
3383
+ }
3384
+ const fallback = parseChildrenSync(context);
3385
+ if (!fallback.success && fallback.consumed < error.consumed) return {
3386
+ ...error,
3387
+ success: false
3388
+ };
3389
+ return fallback;
3390
+ }
3391
+ for (const lane of mergeParseLanes) {
3392
+ if (consumedLanes.has(lane) || lane.leadingNames.size > 0 || lane.acceptingAnyToken || lane.requiredConsumptionGroups?.some((group$1) => !consumedGroups.has(group$1.id)) === true) continue;
3393
+ const result = lane.parse(currentContext);
3394
+ if (result.success && result.consumed.length === 0 && result.next.state !== currentContext.state) currentContext = result.next;
3395
+ }
3396
+ return {
3397
+ success: true,
3398
+ next: currentContext,
3399
+ consumed: allConsumed
3400
+ };
3401
+ };
3402
+ const parseAsync = async (context) => {
3403
+ let currentContext = context;
3404
+ let error = createObjectLikeInitialError(context, noMatchContext);
3405
+ const allConsumed = [];
3406
+ const consumedLanes = /* @__PURE__ */ new Set();
3407
+ const consumedGroups = /* @__PURE__ */ new Set();
3408
+ const laneResults = /* @__PURE__ */ new Map();
3409
+ let attemptedLane = false;
3410
+ let madeProgress = true;
3411
+ while (madeProgress && currentContext.buffer.length > 0) {
3412
+ madeProgress = false;
3413
+ let consumingError = null;
3414
+ for (const lane of mergeParseLanes) {
3415
+ if (consumingError != null && lane.priority < consumingError.priority) {
3416
+ if (!canTryPositionalLaneAfterFailure(lane, currentContext)) continue;
3417
+ }
3418
+ attemptedLane = true;
3419
+ const result = await lane.parse(currentContext);
3420
+ laneResults.set(lane, result);
3421
+ if (result.success && result.consumed.length > 0) {
3422
+ currentContext = result.next;
3423
+ allConsumed.push(...result.consumed);
3424
+ consumedLanes.add(lane);
3425
+ for (const group$1 of lane.requiredConsumptionGroups ?? []) if (group$1.isActive?.(result.next.state) !== false) consumedGroups.add(group$1.id);
3426
+ madeProgress = true;
3427
+ break;
3428
+ }
3429
+ if (!result.success) {
3430
+ if (result.consumed > 0 && consumingError == null) consumingError = {
3431
+ priority: lane.priority,
3432
+ result
3433
+ };
3434
+ if (error.consumed < result.consumed) error = result;
3435
+ }
3436
+ }
3437
+ if (!madeProgress && consumingError != null && allConsumed.length === 0) return consumingError.result;
3438
+ }
3439
+ if (allConsumed.length === 0) {
3440
+ if (attemptedLane) {
3441
+ const settled = settleZeroConsumptionLanes(context, laneResults);
3442
+ return settled ?? {
3443
+ ...error,
3444
+ success: false
3445
+ };
3446
+ }
3447
+ const fallback = await parseChildrenAsync(context);
3448
+ if (!fallback.success && fallback.consumed < error.consumed) return {
3449
+ ...error,
3450
+ success: false
3451
+ };
3452
+ return fallback;
3453
+ }
3454
+ for (const lane of mergeParseLanes) {
3455
+ if (consumedLanes.has(lane) || lane.leadingNames.size > 0 || lane.acceptingAnyToken || lane.requiredConsumptionGroups?.some((group$1) => !consumedGroups.has(group$1.id)) === true) continue;
3456
+ const result = await lane.parse(currentContext);
3457
+ if (result.success && result.consumed.length === 0 && result.next.state !== currentContext.state) currentContext = result.next;
3458
+ }
3459
+ return {
3460
+ success: true,
3461
+ next: currentContext,
3462
+ consumed: allConsumed
3143
3463
  };
3144
3464
  };
3145
3465
  const mergeParser = {
@@ -3147,10 +3467,10 @@ function merge(...args) {
3147
3467
  $valueType: [],
3148
3468
  $stateType: [],
3149
3469
  [fieldParsersKey]: mergedFieldParsers,
3150
- priority: Math.max(...parsers.map((p) => p.priority)),
3470
+ priority: Math.max(...mergeParseLanes.map((lane) => lane.priority)),
3151
3471
  usage: applyHiddenToUsage(parsers.flatMap((p) => p.usage), options.hidden),
3152
- leadingNames: sharedBufferLeadingNames(parsers),
3153
- acceptingAnyToken: parsers.some((p) => p.acceptingAnyToken),
3472
+ leadingNames: sharedBufferLeadingNames(mergeParseLanes),
3473
+ acceptingAnyToken: mergeParseLanes.some((lane) => lane.acceptingAnyToken),
3154
3474
  initialState,
3155
3475
  parse(context) {
3156
3476
  if (isAsync) return parseAsync(context);
@@ -3619,6 +3939,7 @@ function merge(...args) {
3619
3939
  };
3620
3940
  }
3621
3941
  };
3942
+ defineParseLanes(mergeParser, mergeParseLanes);
3622
3943
  defineInheritedAnnotationParser(mergeParser);
3623
3944
  return mergeParser;
3624
3945
  }
@@ -4296,6 +4617,7 @@ function group(label, parser, options = {}) {
4296
4617
  };
4297
4618
  }
4298
4619
  };
4620
+ defineParseLanes(groupParser, getOwnParseLanes(parser));
4299
4621
  Object.defineProperty(groupParser, extractPhase2SeedKey, {
4300
4622
  value(state, exec) {
4301
4623
  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.
@@ -385,6 +391,32 @@ function composeWrappedSourceMetadata(dependencyMetadata, wrapSource) {
385
391
  };
386
392
  }
387
393
  /**
394
+ * Defines internal parse-lane metadata without exposing it through object
395
+ * spreads used by custom parser wrappers.
396
+ *
397
+ * @internal
398
+ */
399
+ function defineParseLanes(parser, lanes) {
400
+ if (lanes == null) return;
401
+ Object.defineProperty(parser, parseLanesKey, {
402
+ value: lanes,
403
+ configurable: true,
404
+ enumerable: false
405
+ });
406
+ }
407
+ /**
408
+ * Gets parse-lane metadata defined directly on a parser.
409
+ *
410
+ * Inherited metadata belongs to the parser's prototype and must not bypass an
411
+ * overriding `parse()` implementation on a custom wrapper.
412
+ *
413
+ * @internal
414
+ */
415
+ function getOwnParseLanes(parser) {
416
+ if (!Object.hasOwn(parser, parseLanesKey)) return void 0;
417
+ return parser[parseLanesKey];
418
+ }
419
+ /**
388
420
  * Marks a parser as inheriting parent-state annotations through wrapper-state
389
421
  * reconstruction.
390
422
  *
@@ -712,11 +744,13 @@ exports.annotationWrapperRequiresSourceBindingKey = annotationWrapperRequiresSou
712
744
  exports.composeWrappedSourceMetadata = composeWrappedSourceMetadata;
713
745
  exports.createParserContext = createParserContext;
714
746
  exports.defineInheritedAnnotationParser = defineInheritedAnnotationParser;
747
+ exports.defineParseLanes = defineParseLanes;
715
748
  exports.defineSourceBindingOnlyAnnotationCompletionParser = defineSourceBindingOnlyAnnotationCompletionParser;
716
749
  exports.getDelegatingSuggestRuntimeNodes = getDelegatingSuggestRuntimeNodes;
717
750
  exports.getDocPage = getDocPage;
718
751
  exports.getDocPageAsync = getDocPageAsync;
719
752
  exports.getDocPageSync = getDocPageSync;
753
+ exports.getOwnParseLanes = getOwnParseLanes;
720
754
  exports.getParserSuggestRuntimeNodes = getParserSuggestRuntimeNodes;
721
755
  exports.inheritParentAnnotationsKey = inheritParentAnnotationsKey;
722
756
  exports.parse = parse;
@@ -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"`.
@@ -148,6 +193,13 @@ interface Parser<M extends Mode = "sync", TValue = unknown, TState = unknown> {
148
193
  * state when parsing starts.
149
194
  */
150
195
  readonly initialState: TState;
196
+ /**
197
+ * Independently competing parse operations exposed by transparent
198
+ * combinators. Shared-buffer parents use these to arbitrate below an
199
+ * aggregate parser boundary without bypassing the owner's state adapters.
200
+ * @internal
201
+ */
202
+ readonly [parseLanesKey]?: readonly ParseLane<TState>[];
151
203
  /**
152
204
  * Internal marker for wrappers whose `{ hasCliValue: false }` states should
153
205
  * be treated as unmatched dependency-source states during completion-time
@@ -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"`.
@@ -148,6 +193,13 @@ interface Parser<M extends Mode = "sync", TValue = unknown, TState = unknown> {
148
193
  * state when parsing starts.
149
194
  */
150
195
  readonly initialState: TState;
196
+ /**
197
+ * Independently competing parse operations exposed by transparent
198
+ * combinators. Shared-buffer parents use these to arbitrate below an
199
+ * aggregate parser boundary without bypassing the owner's state adapters.
200
+ * @internal
201
+ */
202
+ readonly [parseLanesKey]?: readonly ParseLane<TState>[];
151
203
  /**
152
204
  * Internal marker for wrappers whose `{ hasCliValue: false }` states should
153
205
  * be treated as unmatched dependency-source states during completion-time