@optique/core 1.0.0-dev.1608 → 1.0.0-dev.1611

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.
@@ -879,6 +879,31 @@ async function completeDependencySourceDefaultsAsync(context, parserPairs, regis
879
879
  * mappings from its child `object()` (or nested `merge()`) parsers.
880
880
  * @internal
881
881
  */
882
+ /**
883
+ * Removes entries for duplicate field names from a pre-completed results
884
+ * map. When a child construct's field-parser pairs contain the same field
885
+ * name more than once (e.g., inner merges with overlapping output keys),
886
+ * the flat map collapses branch-specific results. This function strips
887
+ * only the ambiguous entries, preserving cached results for unique fields.
888
+ *
889
+ * Returns `undefined` if the filtered map is empty (no cacheable fields).
890
+ *
891
+ * @see https://github.com/dahlia/optique/issues/762
892
+ * @internal
893
+ */
894
+ function filterDuplicateKeys(preCompleted, pairs) {
895
+ const counts = /* @__PURE__ */ new Map();
896
+ for (const [field] of pairs) counts.set(field, (counts.get(field) ?? 0) + 1);
897
+ let hasDuplicates = false;
898
+ for (const count of counts.values()) if (count > 1) {
899
+ hasDuplicates = true;
900
+ break;
901
+ }
902
+ if (!hasDuplicates) return preCompleted;
903
+ const filtered = /* @__PURE__ */ new Map();
904
+ for (const [field, value] of preCompleted) if ((counts.get(field) ?? 0) <= 1) filtered.set(field, value);
905
+ return filtered.size > 0 ? filtered : void 0;
906
+ }
882
907
  function collectChildFieldParsers(parsers) {
883
908
  const pairs = [];
884
909
  for (const parser of parsers) if (fieldParsersKey in parser) pairs.push(...parser[fieldParsersKey]);
@@ -901,7 +926,14 @@ function collectChildFieldParsers(parsers) {
901
926
  */
902
927
  function preCompleteAndRegisterDependencies(state, fieldParserPairs, registry, exec) {
903
928
  const preCompleted = /* @__PURE__ */ new Map();
929
+ const parentResults = exec?.preCompletedByParser;
904
930
  for (const [field, fieldParser] of fieldParserPairs) {
931
+ const cached = parentResults?.get(field);
932
+ if (cached !== void 0) {
933
+ preCompleted.set(field, cached);
934
+ registerCompletedDependency(cached, registry);
935
+ continue;
936
+ }
905
937
  const fieldState = state[field];
906
938
  if (Array.isArray(fieldState) && fieldState.length === 1 && require_dependency.isPendingDependencySourceState(fieldState[0])) {
907
939
  const completed = fieldParser.complete(fieldState, exec);
@@ -932,7 +964,14 @@ function preCompleteAndRegisterDependencies(state, fieldParserPairs, registry, e
932
964
  */
933
965
  async function preCompleteAndRegisterDependenciesAsync(state, fieldParserPairs, registry, exec) {
934
966
  const preCompleted = /* @__PURE__ */ new Map();
967
+ const parentResults = exec?.preCompletedByParser;
935
968
  for (const [field, fieldParser] of fieldParserPairs) {
969
+ const cached = parentResults?.get(field);
970
+ if (cached !== void 0) {
971
+ preCompleted.set(field, cached);
972
+ registerCompletedDependency(cached, registry);
973
+ continue;
974
+ }
936
975
  const fieldState = state[field];
937
976
  if (Array.isArray(fieldState) && fieldState.length === 1 && require_dependency.isPendingDependencySourceState(fieldState[0])) {
938
977
  const completed = await fieldParser.complete(fieldState, exec);
@@ -1304,7 +1343,12 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1304
1343
  ...exec,
1305
1344
  dependencyRuntime: runtime
1306
1345
  };
1307
- const preCompleted = preCompleteAndRegisterDependencies(state, parserPairs, runtime.registry, childExec);
1346
+ const typedParserPairs = parserPairs;
1347
+ const preCompleted = preCompleteAndRegisterDependencies(state, typedParserPairs, runtime.registry, childExec);
1348
+ const phase3Exec = {
1349
+ ...childExec,
1350
+ preCompletedByParser: void 0
1351
+ };
1308
1352
  const getFieldState = createFieldStateGetter(state);
1309
1353
  const annotatedState = {};
1310
1354
  for (const field of parserKeys) {
@@ -1324,7 +1368,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1324
1368
  if (preCompletedResult !== void 0) valueResult = unwrapCompleteResult(preCompletedResult);
1325
1369
  else {
1326
1370
  const fieldState = resolvedFieldStates[fieldKey];
1327
- valueResult = unwrapCompleteResult(fieldParser.complete(fieldState, childExec));
1371
+ valueResult = unwrapCompleteResult(fieldParser.complete(fieldState, phase3Exec));
1328
1372
  }
1329
1373
  if (valueResult.success) {
1330
1374
  result[fieldKey] = valueResult.value;
@@ -1351,7 +1395,12 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1351
1395
  ...exec,
1352
1396
  dependencyRuntime: runtime
1353
1397
  };
1354
- const preCompleted = await preCompleteAndRegisterDependenciesAsync(state, parserPairs, runtime.registry, childExec);
1398
+ const asyncParserPairs = parserPairs;
1399
+ const preCompleted = await preCompleteAndRegisterDependenciesAsync(state, asyncParserPairs, runtime.registry, childExec);
1400
+ const phase3Exec = {
1401
+ ...childExec,
1402
+ preCompletedByParser: void 0
1403
+ };
1355
1404
  const getFieldState = createFieldStateGetter(state);
1356
1405
  const annotatedState = {};
1357
1406
  for (const field of parserKeys) {
@@ -1371,7 +1420,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1371
1420
  if (preCompletedResult !== void 0) valueResult = unwrapCompleteResult(preCompletedResult);
1372
1421
  else {
1373
1422
  const fieldState = resolvedFieldStates[fieldKey];
1374
- valueResult = unwrapCompleteResult(await fieldParser.complete(fieldState, childExec));
1423
+ valueResult = unwrapCompleteResult(await fieldParser.complete(fieldState, phase3Exec));
1375
1424
  }
1376
1425
  if (valueResult.success) {
1377
1426
  result[fieldKey] = valueResult.value;
@@ -1644,6 +1693,10 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1644
1693
  const tuplePairs = syncParsers.map((p, i) => [String(i), p]);
1645
1694
  const tupleState = Object.fromEntries(stateArray.map((s, i) => [String(i), s]));
1646
1695
  const preCompleted = preCompleteAndRegisterDependencies(tupleState, tuplePairs, runtime.registry, childExec);
1696
+ const phase3Exec = {
1697
+ ...childExec,
1698
+ preCompletedByParser: void 0
1699
+ };
1647
1700
  const resolvedArray = require_dependency_runtime.resolveStateWithRuntime(stateArray, runtime);
1648
1701
  const result = [];
1649
1702
  const deferredKeys = /* @__PURE__ */ new Map();
@@ -1655,7 +1708,7 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1655
1708
  if (preCompletedResult !== void 0) valueResult = unwrapCompleteResult(preCompletedResult);
1656
1709
  else {
1657
1710
  const elementState = prepareStateForCompletion(resolvedArray[i], elementParser);
1658
- valueResult = unwrapCompleteResult(elementParser.complete(elementState, childExec));
1711
+ valueResult = unwrapCompleteResult(elementParser.complete(elementState, phase3Exec));
1659
1712
  }
1660
1713
  if (valueResult.success) {
1661
1714
  result[i] = valueResult.value;
@@ -1686,6 +1739,10 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1686
1739
  const tuplePairs = parsers.map((p, i) => [String(i), p]);
1687
1740
  const tupleState = Object.fromEntries(stateArray.map((s, i) => [String(i), s]));
1688
1741
  const preCompleted = await preCompleteAndRegisterDependenciesAsync(tupleState, tuplePairs, runtime.registry, childExec);
1742
+ const phase3Exec = {
1743
+ ...childExec,
1744
+ preCompletedByParser: void 0
1745
+ };
1689
1746
  const resolvedArray = await require_dependency_runtime.resolveStateWithRuntimeAsync(stateArray, runtime);
1690
1747
  const result = [];
1691
1748
  const deferredKeys = /* @__PURE__ */ new Map();
@@ -1697,7 +1754,7 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1697
1754
  if (preCompletedResult !== void 0) valueResult = unwrapCompleteResult(preCompletedResult);
1698
1755
  else {
1699
1756
  const elementState = prepareStateForCompletion(resolvedArray[i], elementParser);
1700
- valueResult = unwrapCompleteResult(await elementParser.complete(elementState, childExec));
1757
+ valueResult = unwrapCompleteResult(await elementParser.complete(elementState, phase3Exec));
1701
1758
  }
1702
1759
  if (valueResult.success) {
1703
1760
  result[i] = valueResult.value;
@@ -1963,8 +2020,14 @@ function merge(...args) {
1963
2020
  ...exec,
1964
2021
  dependencyRuntime: runtime
1965
2022
  };
1966
- const childFieldPairs = collectChildFieldParsers(syncParsers);
1967
- preCompleteAndRegisterDependencies(state, childFieldPairs, runtime.registry, childExec);
2023
+ const perChildCache = syncParsers.map((parser) => {
2024
+ if (fieldParsersKey in parser) {
2025
+ const pairs = parser[fieldParsersKey];
2026
+ const preCompleted = preCompleteAndRegisterDependencies(state, pairs, runtime.registry, childExec);
2027
+ return filterDuplicateKeys(preCompleted, pairs);
2028
+ }
2029
+ return void 0;
2030
+ });
1968
2031
  const resolvedState = require_dependency_runtime.resolveStateWithRuntime(state, runtime);
1969
2032
  const object$1 = {};
1970
2033
  const deferredKeys = /* @__PURE__ */ new Map();
@@ -1972,7 +2035,11 @@ function merge(...args) {
1972
2035
  for (let i = 0; i < syncParsers.length; i++) {
1973
2036
  const parser = syncParsers[i];
1974
2037
  const parserState = extractCompleteState(parser, resolvedState, i);
1975
- const result = unwrapCompleteResult(parser.complete(parserState, childExec));
2038
+ const cache = perChildCache[i];
2039
+ const result = unwrapCompleteResult(parser.complete(parserState, {
2040
+ ...childExec,
2041
+ preCompletedByParser: cache
2042
+ }));
1976
2043
  if (!result.success) return result;
1977
2044
  const resultValue = result.value;
1978
2045
  for (const field in resultValue) {
@@ -1998,8 +2065,12 @@ function merge(...args) {
1998
2065
  ...exec,
1999
2066
  dependencyRuntime: runtime
2000
2067
  };
2001
- const childFieldPairs = collectChildFieldParsers(parsers);
2002
- await preCompleteAndRegisterDependenciesAsync(state, childFieldPairs, runtime.registry, childExec);
2068
+ const perChildCache = [];
2069
+ for (const parser of parsers) if (fieldParsersKey in parser) {
2070
+ const pairs = parser[fieldParsersKey];
2071
+ const preCompleted = await preCompleteAndRegisterDependenciesAsync(state, pairs, runtime.registry, childExec);
2072
+ perChildCache.push(filterDuplicateKeys(preCompleted, pairs));
2073
+ } else perChildCache.push(void 0);
2003
2074
  const resolvedState = await require_dependency_runtime.resolveStateWithRuntimeAsync(state, runtime);
2004
2075
  const object$1 = {};
2005
2076
  const deferredKeys = /* @__PURE__ */ new Map();
@@ -2007,7 +2078,11 @@ function merge(...args) {
2007
2078
  for (let i = 0; i < parsers.length; i++) {
2008
2079
  const parser = parsers[i];
2009
2080
  const parserState = extractCompleteState(parser, resolvedState, i);
2010
- const result = unwrapCompleteResult(await parser.complete(parserState, childExec));
2081
+ const asyncCache = perChildCache[i];
2082
+ const result = unwrapCompleteResult(await parser.complete(parserState, {
2083
+ ...childExec,
2084
+ preCompletedByParser: asyncCache
2085
+ }));
2011
2086
  if (!result.success) return result;
2012
2087
  const resultValue = result.value;
2013
2088
  for (const field in resultValue) {
@@ -2400,7 +2475,8 @@ function concat(...parsers) {
2400
2475
  const runtime = exec?.dependencyRuntime ?? require_dependency_runtime.createDependencyRuntimeContext(exec?.dependencyRegistry);
2401
2476
  const childExec = {
2402
2477
  ...exec,
2403
- dependencyRuntime: runtime
2478
+ dependencyRuntime: runtime,
2479
+ preCompletedByParser: void 0
2404
2480
  };
2405
2481
  const resolvedArray = require_dependency_runtime.resolveStateWithRuntime(stateArray, runtime);
2406
2482
  const results = [];
@@ -2442,7 +2518,8 @@ function concat(...parsers) {
2442
2518
  const runtime = exec?.dependencyRuntime ?? require_dependency_runtime.createDependencyRuntimeContext(exec?.dependencyRegistry);
2443
2519
  const childExec = {
2444
2520
  ...exec,
2445
- dependencyRuntime: runtime
2521
+ dependencyRuntime: runtime,
2522
+ preCompletedByParser: void 0
2446
2523
  };
2447
2524
  const resolvedArray = await require_dependency_runtime.resolveStateWithRuntimeAsync(stateArray, runtime);
2448
2525
  const results = [];
@@ -879,6 +879,31 @@ async function completeDependencySourceDefaultsAsync(context, parserPairs, regis
879
879
  * mappings from its child `object()` (or nested `merge()`) parsers.
880
880
  * @internal
881
881
  */
882
+ /**
883
+ * Removes entries for duplicate field names from a pre-completed results
884
+ * map. When a child construct's field-parser pairs contain the same field
885
+ * name more than once (e.g., inner merges with overlapping output keys),
886
+ * the flat map collapses branch-specific results. This function strips
887
+ * only the ambiguous entries, preserving cached results for unique fields.
888
+ *
889
+ * Returns `undefined` if the filtered map is empty (no cacheable fields).
890
+ *
891
+ * @see https://github.com/dahlia/optique/issues/762
892
+ * @internal
893
+ */
894
+ function filterDuplicateKeys(preCompleted, pairs) {
895
+ const counts = /* @__PURE__ */ new Map();
896
+ for (const [field] of pairs) counts.set(field, (counts.get(field) ?? 0) + 1);
897
+ let hasDuplicates = false;
898
+ for (const count of counts.values()) if (count > 1) {
899
+ hasDuplicates = true;
900
+ break;
901
+ }
902
+ if (!hasDuplicates) return preCompleted;
903
+ const filtered = /* @__PURE__ */ new Map();
904
+ for (const [field, value] of preCompleted) if ((counts.get(field) ?? 0) <= 1) filtered.set(field, value);
905
+ return filtered.size > 0 ? filtered : void 0;
906
+ }
882
907
  function collectChildFieldParsers(parsers) {
883
908
  const pairs = [];
884
909
  for (const parser of parsers) if (fieldParsersKey in parser) pairs.push(...parser[fieldParsersKey]);
@@ -901,7 +926,14 @@ function collectChildFieldParsers(parsers) {
901
926
  */
902
927
  function preCompleteAndRegisterDependencies(state, fieldParserPairs, registry, exec) {
903
928
  const preCompleted = /* @__PURE__ */ new Map();
929
+ const parentResults = exec?.preCompletedByParser;
904
930
  for (const [field, fieldParser] of fieldParserPairs) {
931
+ const cached = parentResults?.get(field);
932
+ if (cached !== void 0) {
933
+ preCompleted.set(field, cached);
934
+ registerCompletedDependency(cached, registry);
935
+ continue;
936
+ }
905
937
  const fieldState = state[field];
906
938
  if (Array.isArray(fieldState) && fieldState.length === 1 && isPendingDependencySourceState(fieldState[0])) {
907
939
  const completed = fieldParser.complete(fieldState, exec);
@@ -932,7 +964,14 @@ function preCompleteAndRegisterDependencies(state, fieldParserPairs, registry, e
932
964
  */
933
965
  async function preCompleteAndRegisterDependenciesAsync(state, fieldParserPairs, registry, exec) {
934
966
  const preCompleted = /* @__PURE__ */ new Map();
967
+ const parentResults = exec?.preCompletedByParser;
935
968
  for (const [field, fieldParser] of fieldParserPairs) {
969
+ const cached = parentResults?.get(field);
970
+ if (cached !== void 0) {
971
+ preCompleted.set(field, cached);
972
+ registerCompletedDependency(cached, registry);
973
+ continue;
974
+ }
936
975
  const fieldState = state[field];
937
976
  if (Array.isArray(fieldState) && fieldState.length === 1 && isPendingDependencySourceState(fieldState[0])) {
938
977
  const completed = await fieldParser.complete(fieldState, exec);
@@ -1304,7 +1343,12 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1304
1343
  ...exec,
1305
1344
  dependencyRuntime: runtime
1306
1345
  };
1307
- const preCompleted = preCompleteAndRegisterDependencies(state, parserPairs, runtime.registry, childExec);
1346
+ const typedParserPairs = parserPairs;
1347
+ const preCompleted = preCompleteAndRegisterDependencies(state, typedParserPairs, runtime.registry, childExec);
1348
+ const phase3Exec = {
1349
+ ...childExec,
1350
+ preCompletedByParser: void 0
1351
+ };
1308
1352
  const getFieldState = createFieldStateGetter(state);
1309
1353
  const annotatedState = {};
1310
1354
  for (const field of parserKeys) {
@@ -1324,7 +1368,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1324
1368
  if (preCompletedResult !== void 0) valueResult = unwrapCompleteResult(preCompletedResult);
1325
1369
  else {
1326
1370
  const fieldState = resolvedFieldStates[fieldKey];
1327
- valueResult = unwrapCompleteResult(fieldParser.complete(fieldState, childExec));
1371
+ valueResult = unwrapCompleteResult(fieldParser.complete(fieldState, phase3Exec));
1328
1372
  }
1329
1373
  if (valueResult.success) {
1330
1374
  result[fieldKey] = valueResult.value;
@@ -1351,7 +1395,12 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1351
1395
  ...exec,
1352
1396
  dependencyRuntime: runtime
1353
1397
  };
1354
- const preCompleted = await preCompleteAndRegisterDependenciesAsync(state, parserPairs, runtime.registry, childExec);
1398
+ const asyncParserPairs = parserPairs;
1399
+ const preCompleted = await preCompleteAndRegisterDependenciesAsync(state, asyncParserPairs, runtime.registry, childExec);
1400
+ const phase3Exec = {
1401
+ ...childExec,
1402
+ preCompletedByParser: void 0
1403
+ };
1355
1404
  const getFieldState = createFieldStateGetter(state);
1356
1405
  const annotatedState = {};
1357
1406
  for (const field of parserKeys) {
@@ -1371,7 +1420,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1371
1420
  if (preCompletedResult !== void 0) valueResult = unwrapCompleteResult(preCompletedResult);
1372
1421
  else {
1373
1422
  const fieldState = resolvedFieldStates[fieldKey];
1374
- valueResult = unwrapCompleteResult(await fieldParser.complete(fieldState, childExec));
1423
+ valueResult = unwrapCompleteResult(await fieldParser.complete(fieldState, phase3Exec));
1375
1424
  }
1376
1425
  if (valueResult.success) {
1377
1426
  result[fieldKey] = valueResult.value;
@@ -1644,6 +1693,10 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1644
1693
  const tuplePairs = syncParsers.map((p, i) => [String(i), p]);
1645
1694
  const tupleState = Object.fromEntries(stateArray.map((s, i) => [String(i), s]));
1646
1695
  const preCompleted = preCompleteAndRegisterDependencies(tupleState, tuplePairs, runtime.registry, childExec);
1696
+ const phase3Exec = {
1697
+ ...childExec,
1698
+ preCompletedByParser: void 0
1699
+ };
1647
1700
  const resolvedArray = resolveStateWithRuntime(stateArray, runtime);
1648
1701
  const result = [];
1649
1702
  const deferredKeys = /* @__PURE__ */ new Map();
@@ -1655,7 +1708,7 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1655
1708
  if (preCompletedResult !== void 0) valueResult = unwrapCompleteResult(preCompletedResult);
1656
1709
  else {
1657
1710
  const elementState = prepareStateForCompletion(resolvedArray[i], elementParser);
1658
- valueResult = unwrapCompleteResult(elementParser.complete(elementState, childExec));
1711
+ valueResult = unwrapCompleteResult(elementParser.complete(elementState, phase3Exec));
1659
1712
  }
1660
1713
  if (valueResult.success) {
1661
1714
  result[i] = valueResult.value;
@@ -1686,6 +1739,10 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1686
1739
  const tuplePairs = parsers.map((p, i) => [String(i), p]);
1687
1740
  const tupleState = Object.fromEntries(stateArray.map((s, i) => [String(i), s]));
1688
1741
  const preCompleted = await preCompleteAndRegisterDependenciesAsync(tupleState, tuplePairs, runtime.registry, childExec);
1742
+ const phase3Exec = {
1743
+ ...childExec,
1744
+ preCompletedByParser: void 0
1745
+ };
1689
1746
  const resolvedArray = await resolveStateWithRuntimeAsync(stateArray, runtime);
1690
1747
  const result = [];
1691
1748
  const deferredKeys = /* @__PURE__ */ new Map();
@@ -1697,7 +1754,7 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
1697
1754
  if (preCompletedResult !== void 0) valueResult = unwrapCompleteResult(preCompletedResult);
1698
1755
  else {
1699
1756
  const elementState = prepareStateForCompletion(resolvedArray[i], elementParser);
1700
- valueResult = unwrapCompleteResult(await elementParser.complete(elementState, childExec));
1757
+ valueResult = unwrapCompleteResult(await elementParser.complete(elementState, phase3Exec));
1701
1758
  }
1702
1759
  if (valueResult.success) {
1703
1760
  result[i] = valueResult.value;
@@ -1963,8 +2020,14 @@ function merge(...args) {
1963
2020
  ...exec,
1964
2021
  dependencyRuntime: runtime
1965
2022
  };
1966
- const childFieldPairs = collectChildFieldParsers(syncParsers);
1967
- preCompleteAndRegisterDependencies(state, childFieldPairs, runtime.registry, childExec);
2023
+ const perChildCache = syncParsers.map((parser) => {
2024
+ if (fieldParsersKey in parser) {
2025
+ const pairs = parser[fieldParsersKey];
2026
+ const preCompleted = preCompleteAndRegisterDependencies(state, pairs, runtime.registry, childExec);
2027
+ return filterDuplicateKeys(preCompleted, pairs);
2028
+ }
2029
+ return void 0;
2030
+ });
1968
2031
  const resolvedState = resolveStateWithRuntime(state, runtime);
1969
2032
  const object$1 = {};
1970
2033
  const deferredKeys = /* @__PURE__ */ new Map();
@@ -1972,7 +2035,11 @@ function merge(...args) {
1972
2035
  for (let i = 0; i < syncParsers.length; i++) {
1973
2036
  const parser = syncParsers[i];
1974
2037
  const parserState = extractCompleteState(parser, resolvedState, i);
1975
- const result = unwrapCompleteResult(parser.complete(parserState, childExec));
2038
+ const cache = perChildCache[i];
2039
+ const result = unwrapCompleteResult(parser.complete(parserState, {
2040
+ ...childExec,
2041
+ preCompletedByParser: cache
2042
+ }));
1976
2043
  if (!result.success) return result;
1977
2044
  const resultValue = result.value;
1978
2045
  for (const field in resultValue) {
@@ -1998,8 +2065,12 @@ function merge(...args) {
1998
2065
  ...exec,
1999
2066
  dependencyRuntime: runtime
2000
2067
  };
2001
- const childFieldPairs = collectChildFieldParsers(parsers);
2002
- await preCompleteAndRegisterDependenciesAsync(state, childFieldPairs, runtime.registry, childExec);
2068
+ const perChildCache = [];
2069
+ for (const parser of parsers) if (fieldParsersKey in parser) {
2070
+ const pairs = parser[fieldParsersKey];
2071
+ const preCompleted = await preCompleteAndRegisterDependenciesAsync(state, pairs, runtime.registry, childExec);
2072
+ perChildCache.push(filterDuplicateKeys(preCompleted, pairs));
2073
+ } else perChildCache.push(void 0);
2003
2074
  const resolvedState = await resolveStateWithRuntimeAsync(state, runtime);
2004
2075
  const object$1 = {};
2005
2076
  const deferredKeys = /* @__PURE__ */ new Map();
@@ -2007,7 +2078,11 @@ function merge(...args) {
2007
2078
  for (let i = 0; i < parsers.length; i++) {
2008
2079
  const parser = parsers[i];
2009
2080
  const parserState = extractCompleteState(parser, resolvedState, i);
2010
- const result = unwrapCompleteResult(await parser.complete(parserState, childExec));
2081
+ const asyncCache = perChildCache[i];
2082
+ const result = unwrapCompleteResult(await parser.complete(parserState, {
2083
+ ...childExec,
2084
+ preCompletedByParser: asyncCache
2085
+ }));
2011
2086
  if (!result.success) return result;
2012
2087
  const resultValue = result.value;
2013
2088
  for (const field in resultValue) {
@@ -2400,7 +2475,8 @@ function concat(...parsers) {
2400
2475
  const runtime = exec?.dependencyRuntime ?? createDependencyRuntimeContext(exec?.dependencyRegistry);
2401
2476
  const childExec = {
2402
2477
  ...exec,
2403
- dependencyRuntime: runtime
2478
+ dependencyRuntime: runtime,
2479
+ preCompletedByParser: void 0
2404
2480
  };
2405
2481
  const resolvedArray = resolveStateWithRuntime(stateArray, runtime);
2406
2482
  const results = [];
@@ -2442,7 +2518,8 @@ function concat(...parsers) {
2442
2518
  const runtime = exec?.dependencyRuntime ?? createDependencyRuntimeContext(exec?.dependencyRegistry);
2443
2519
  const childExec = {
2444
2520
  ...exec,
2445
- dependencyRuntime: runtime
2521
+ dependencyRuntime: runtime,
2522
+ preCompletedByParser: void 0
2446
2523
  };
2447
2524
  const resolvedArray = await resolveStateWithRuntimeAsync(stateArray, runtime);
2448
2525
  const results = [];
package/dist/parser.d.cts CHANGED
@@ -325,6 +325,25 @@ interface ExecutionContext {
325
325
  * @since 1.0.0
326
326
  */
327
327
  readonly dependencyRuntime?: DependencyRuntimeContext;
328
+ /**
329
+ * Immutable map of pre-completed results from the parent construct's
330
+ * Phase 1, keyed by field name. Each construct passes its own
331
+ * `preCompleteAndRegisterDependencies` results directly to children
332
+ * in Phase 3. Children read it in their own Phase 1 to avoid
333
+ * re-evaluating non-idempotent default thunks, but never write to
334
+ * it — this prevents sibling completions from leaking into each
335
+ * other.
336
+ *
337
+ * Field-name keying naturally handles parser reuse across different
338
+ * fields (e.g., `merge(object({a: shared}), object({b: shared}))`)
339
+ * because each field maps to its own result regardless of whether
340
+ * the underlying parser instance is the same.
341
+ *
342
+ * @see https://github.com/dahlia/optique/issues/762
343
+ * @internal
344
+ * @since 1.0.0
345
+ */
346
+ readonly preCompletedByParser?: ReadonlyMap<string | symbol, unknown>;
328
347
  }
329
348
  /**
330
349
  * The context of the parser, which includes the input buffer and the state.
package/dist/parser.d.ts CHANGED
@@ -325,6 +325,25 @@ interface ExecutionContext {
325
325
  * @since 1.0.0
326
326
  */
327
327
  readonly dependencyRuntime?: DependencyRuntimeContext;
328
+ /**
329
+ * Immutable map of pre-completed results from the parent construct's
330
+ * Phase 1, keyed by field name. Each construct passes its own
331
+ * `preCompleteAndRegisterDependencies` results directly to children
332
+ * in Phase 3. Children read it in their own Phase 1 to avoid
333
+ * re-evaluating non-idempotent default thunks, but never write to
334
+ * it — this prevents sibling completions from leaking into each
335
+ * other.
336
+ *
337
+ * Field-name keying naturally handles parser reuse across different
338
+ * fields (e.g., `merge(object({a: shared}), object({b: shared}))`)
339
+ * because each field maps to its own result regardless of whether
340
+ * the underlying parser instance is the same.
341
+ *
342
+ * @see https://github.com/dahlia/optique/issues/762
343
+ * @internal
344
+ * @since 1.0.0
345
+ */
346
+ readonly preCompletedByParser?: ReadonlyMap<string | symbol, unknown>;
328
347
  }
329
348
  /**
330
349
  * The context of the parser, which includes the input buffer and the state.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.0.0-dev.1608+66e67b9f",
3
+ "version": "1.0.0-dev.1611+0344ff75",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",