@optique/core 1.0.0-dev.1470 → 1.0.0-dev.1492

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.
package/dist/doc.cjs CHANGED
@@ -248,7 +248,7 @@ function formatDocPage(programName, page, options = {}) {
248
248
  const splitEntryMin = termIndent + 2 + Math.max(2, 2 * minDescWidth - 1);
249
249
  const fixedEntryMin = termIndent + 2 + termWidth + minDescWidth;
250
250
  const entryMin = needsDescColumn ? Math.min(splitEntryMin, fixedEntryMin) : hasEntries ? termIndent + 1 : 1;
251
- const usageMin = page.usage != null ? 8 + programName.length : 1;
251
+ const usageMin = page.usage != null ? 7 + Math.max(programName.length, Math.min(maxVisibleAtomicWidth(page.usage), programName.length + 7)) : 1;
252
252
  let sectionMin = 1;
253
253
  if (page.examples != null) sectionMin = Math.max(sectionMin, 9);
254
254
  if (page.author != null) sectionMin = Math.max(sectionMin, 7);
@@ -451,6 +451,54 @@ function formatDocPage(programName, page, options = {}) {
451
451
  function indentLines(text$1, indent) {
452
452
  return text$1.split("\n").join("\n" + " ".repeat(indent));
453
453
  }
454
+ /**
455
+ * Returns the width of the widest non-breakable segment among visible
456
+ * (non-usage-hidden) terms in a usage tree. Hidden terms are excluded
457
+ * because they are filtered out before rendering, so they do not
458
+ * contribute to the rendered width.
459
+ */
460
+ function maxVisibleAtomicWidth(usage) {
461
+ let max = 0;
462
+ for (const term of usage) switch (term.type) {
463
+ case "argument":
464
+ if (!require_usage.isUsageHidden(term.hidden)) max = Math.max(max, term.metavar.length);
465
+ break;
466
+ case "option":
467
+ if (!require_usage.isUsageHidden(term.hidden) && term.names.length > 0) {
468
+ for (const name of term.names) max = Math.max(max, name.length);
469
+ if (term.metavar != null) max = Math.max(max, term.metavar.length);
470
+ }
471
+ break;
472
+ case "command":
473
+ if (!require_usage.isUsageHidden(term.hidden)) max = Math.max(max, term.name.length);
474
+ break;
475
+ case "passthrough":
476
+ if (!require_usage.isUsageHidden(term.hidden)) max = Math.max(max, 5);
477
+ break;
478
+ case "optional":
479
+ max = Math.max(max, maxVisibleAtomicWidth(term.terms));
480
+ break;
481
+ case "multiple": {
482
+ const innerMax = maxVisibleAtomicWidth(term.terms);
483
+ if (innerMax > 0) max = Math.max(max, 3, innerMax);
484
+ break;
485
+ }
486
+ case "exclusive":
487
+ for (const branch of term.terms) {
488
+ const first = branch[0];
489
+ if (first?.type === "command" && require_usage.isUsageHidden(first.hidden)) continue;
490
+ max = Math.max(max, maxVisibleAtomicWidth(branch));
491
+ }
492
+ break;
493
+ case "literal":
494
+ if (term.value !== "") max = Math.max(max, term.value.length);
495
+ break;
496
+ case "ellipsis":
497
+ max = Math.max(max, 3);
498
+ break;
499
+ }
500
+ return max;
501
+ }
454
502
  const ansiEscapeCodeRegex = /\x1B\[[0-9;]*[a-zA-Z]/g;
455
503
  function ansiAwareRightPad(text$1, length, char = " ") {
456
504
  const strippedText = text$1.replace(ansiEscapeCodeRegex, "");
package/dist/doc.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { cloneMessage, formatMessage, text } from "./message.js";
2
2
  import { validateProgramName } from "./validate.js";
3
- import { cloneUsageTerm, formatUsage, formatUsageTerm, isDocHidden } from "./usage.js";
3
+ import { cloneUsageTerm, formatUsage, formatUsageTerm, isDocHidden, isUsageHidden } from "./usage.js";
4
4
 
5
5
  //#region src/doc.ts
6
6
  /**
@@ -248,7 +248,7 @@ function formatDocPage(programName, page, options = {}) {
248
248
  const splitEntryMin = termIndent + 2 + Math.max(2, 2 * minDescWidth - 1);
249
249
  const fixedEntryMin = termIndent + 2 + termWidth + minDescWidth;
250
250
  const entryMin = needsDescColumn ? Math.min(splitEntryMin, fixedEntryMin) : hasEntries ? termIndent + 1 : 1;
251
- const usageMin = page.usage != null ? 8 + programName.length : 1;
251
+ const usageMin = page.usage != null ? 7 + Math.max(programName.length, Math.min(maxVisibleAtomicWidth(page.usage), programName.length + 7)) : 1;
252
252
  let sectionMin = 1;
253
253
  if (page.examples != null) sectionMin = Math.max(sectionMin, 9);
254
254
  if (page.author != null) sectionMin = Math.max(sectionMin, 7);
@@ -451,6 +451,54 @@ function formatDocPage(programName, page, options = {}) {
451
451
  function indentLines(text$1, indent) {
452
452
  return text$1.split("\n").join("\n" + " ".repeat(indent));
453
453
  }
454
+ /**
455
+ * Returns the width of the widest non-breakable segment among visible
456
+ * (non-usage-hidden) terms in a usage tree. Hidden terms are excluded
457
+ * because they are filtered out before rendering, so they do not
458
+ * contribute to the rendered width.
459
+ */
460
+ function maxVisibleAtomicWidth(usage) {
461
+ let max = 0;
462
+ for (const term of usage) switch (term.type) {
463
+ case "argument":
464
+ if (!isUsageHidden(term.hidden)) max = Math.max(max, term.metavar.length);
465
+ break;
466
+ case "option":
467
+ if (!isUsageHidden(term.hidden) && term.names.length > 0) {
468
+ for (const name of term.names) max = Math.max(max, name.length);
469
+ if (term.metavar != null) max = Math.max(max, term.metavar.length);
470
+ }
471
+ break;
472
+ case "command":
473
+ if (!isUsageHidden(term.hidden)) max = Math.max(max, term.name.length);
474
+ break;
475
+ case "passthrough":
476
+ if (!isUsageHidden(term.hidden)) max = Math.max(max, 5);
477
+ break;
478
+ case "optional":
479
+ max = Math.max(max, maxVisibleAtomicWidth(term.terms));
480
+ break;
481
+ case "multiple": {
482
+ const innerMax = maxVisibleAtomicWidth(term.terms);
483
+ if (innerMax > 0) max = Math.max(max, 3, innerMax);
484
+ break;
485
+ }
486
+ case "exclusive":
487
+ for (const branch of term.terms) {
488
+ const first = branch[0];
489
+ if (first?.type === "command" && isUsageHidden(first.hidden)) continue;
490
+ max = Math.max(max, maxVisibleAtomicWidth(branch));
491
+ }
492
+ break;
493
+ case "literal":
494
+ if (term.value !== "") max = Math.max(max, term.value.length);
495
+ break;
496
+ case "ellipsis":
497
+ max = Math.max(max, 3);
498
+ break;
499
+ }
500
+ return max;
501
+ }
454
502
  const ansiEscapeCodeRegex = /\x1B\[[0-9;]*[a-zA-Z]/g;
455
503
  function ansiAwareRightPad(text$1, length, char = " ") {
456
504
  const strippedText = text$1.replace(ansiEscapeCodeRegex, "");
package/dist/usage.cjs CHANGED
@@ -164,13 +164,24 @@ function formatUsage(programName, usage, options = {}) {
164
164
  if (usage.length > 1) command = [...usage.slice(0, -1), ...command];
165
165
  lines.push(formatUsage(programName, command, options));
166
166
  }
167
- return lines.join("\n");
167
+ if (lines.length > 0) return lines.join("\n");
168
168
  }
169
169
  }
170
- let output = options.colors ? `\x1b[1m${programName}\x1b[0m ` : `${programName} `;
171
- let lineWidth = programName.length + 1;
170
+ let output = options.colors ? `\x1b[1m${programName}\x1b[0m` : programName;
171
+ let lineWidth = programName.length;
172
+ let first = true;
172
173
  for (const { text, width } of formatUsageTerms(usage, options)) {
173
- if (options.maxWidth != null && lineWidth + width > options.maxWidth) {
174
+ if (first) {
175
+ first = false;
176
+ if (options.maxWidth != null && lineWidth + 1 + width > options.maxWidth) {
177
+ output += "\n";
178
+ lineWidth = 0;
179
+ } else {
180
+ output += " ";
181
+ lineWidth += 1;
182
+ }
183
+ } else if (options.maxWidth != null && lineWidth + width > options.maxWidth) {
184
+ if (output.endsWith(" ")) output = output.slice(0, -1);
174
185
  output += "\n";
175
186
  lineWidth = 0;
176
187
  if (text === " ") continue;
@@ -408,6 +419,7 @@ function formatUsageTerm(term, options = {}) {
408
419
  let output = "";
409
420
  for (const { text, width } of formatUsageTermInternal(visibleTerms[0], options)) {
410
421
  if (options.maxWidth != null && lineWidth + width > options.maxWidth) {
422
+ if (output.endsWith(" ")) output = output.slice(0, -1);
411
423
  output += "\n";
412
424
  lineWidth = 0;
413
425
  if (text === " ") continue;
package/dist/usage.js CHANGED
@@ -164,13 +164,24 @@ function formatUsage(programName, usage, options = {}) {
164
164
  if (usage.length > 1) command = [...usage.slice(0, -1), ...command];
165
165
  lines.push(formatUsage(programName, command, options));
166
166
  }
167
- return lines.join("\n");
167
+ if (lines.length > 0) return lines.join("\n");
168
168
  }
169
169
  }
170
- let output = options.colors ? `\x1b[1m${programName}\x1b[0m ` : `${programName} `;
171
- let lineWidth = programName.length + 1;
170
+ let output = options.colors ? `\x1b[1m${programName}\x1b[0m` : programName;
171
+ let lineWidth = programName.length;
172
+ let first = true;
172
173
  for (const { text, width } of formatUsageTerms(usage, options)) {
173
- if (options.maxWidth != null && lineWidth + width > options.maxWidth) {
174
+ if (first) {
175
+ first = false;
176
+ if (options.maxWidth != null && lineWidth + 1 + width > options.maxWidth) {
177
+ output += "\n";
178
+ lineWidth = 0;
179
+ } else {
180
+ output += " ";
181
+ lineWidth += 1;
182
+ }
183
+ } else if (options.maxWidth != null && lineWidth + width > options.maxWidth) {
184
+ if (output.endsWith(" ")) output = output.slice(0, -1);
174
185
  output += "\n";
175
186
  lineWidth = 0;
176
187
  if (text === " ") continue;
@@ -408,6 +419,7 @@ function formatUsageTerm(term, options = {}) {
408
419
  let output = "";
409
420
  for (const { text, width } of formatUsageTermInternal(visibleTerms[0], options)) {
410
421
  if (options.maxWidth != null && lineWidth + width > options.maxWidth) {
422
+ if (output.endsWith(" ")) output = output.slice(0, -1);
411
423
  output += "\n";
412
424
  lineWidth = 0;
413
425
  if (text === " ") continue;
@@ -1726,73 +1726,182 @@ function socketAddress(options) {
1726
1726
  metavar,
1727
1727
  parse(input) {
1728
1728
  const trimmed = input.trim();
1729
- const separatorIndex = trimmed.lastIndexOf(separator);
1730
- let hostPart;
1731
- let portPart;
1732
- if (separatorIndex === -1) {
1733
- hostPart = trimmed;
1734
- portPart = void 0;
1735
- } else {
1736
- hostPart = trimmed.substring(0, separatorIndex);
1737
- portPart = trimmed.substring(separatorIndex + separator.length);
1738
- }
1739
- const hostResult = parseHost(hostPart);
1740
- if (!hostResult.success) {
1741
- const errorMsg = options?.errors?.invalidFormat;
1742
- if (errorMsg) {
1743
- const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg;
1729
+ const canOmitPort = defaultPort !== void 0 && !requirePort;
1730
+ let firstHostError;
1731
+ let validHostNumericPortInvalid = false;
1732
+ let trailingSepHost;
1733
+ let trailingSepHostError;
1734
+ let anySeparatorFound = false;
1735
+ let searchFrom = trimmed.length;
1736
+ while (searchFrom > 0) {
1737
+ const separatorIndex = trimmed.lastIndexOf(separator, searchFrom - 1);
1738
+ if (separatorIndex === -1) break;
1739
+ anySeparatorFound = true;
1740
+ const hostPart = trimmed.substring(0, separatorIndex);
1741
+ const portPart = trimmed.substring(separatorIndex + separator.length);
1742
+ if (portPart === "") {
1743
+ if (trailingSepHost === void 0 && trailingSepHostError === void 0) {
1744
+ const hostResult = parseHost(hostPart);
1745
+ if (hostResult.success) trailingSepHost = hostResult.value;
1746
+ else trailingSepHostError = {
1747
+ hostPart,
1748
+ error: hostResult.error
1749
+ };
1750
+ }
1751
+ } else {
1752
+ const portResult = portParser.parse(portPart);
1753
+ if (portResult.success) {
1754
+ const hostResult = parseHost(hostPart);
1755
+ if (hostResult.success) return {
1756
+ success: true,
1757
+ value: {
1758
+ host: hostResult.value,
1759
+ port: portResult.value
1760
+ }
1761
+ };
1762
+ if (firstHostError === void 0 || (looksLikeIpv4(hostPart) || looksLikeAltIpv4Literal(hostPart)) && !looksLikeIpv4(firstHostError.hostPart) && !looksLikeAltIpv4Literal(firstHostError.hostPart)) firstHostError = {
1763
+ hostPart,
1764
+ error: hostResult.error
1765
+ };
1766
+ } else if (!validHostNumericPortInvalid && hostPart !== "" && /^[0-9]+$/.test(portPart)) if (looksLikeIpv4(hostPart) || looksLikeAltIpv4Literal(hostPart)) {
1767
+ const hostResult = parseHost(hostPart);
1768
+ if (!hostResult.success) {
1769
+ if (firstHostError === void 0 || !looksLikeIpv4(firstHostError.hostPart) && !looksLikeAltIpv4Literal(firstHostError.hostPart)) firstHostError = {
1770
+ hostPart,
1771
+ error: hostResult.error
1772
+ };
1773
+ } else validHostNumericPortInvalid = true;
1774
+ } else validHostNumericPortInvalid = true;
1775
+ else if ((firstHostError === void 0 || !looksLikeIpv4(firstHostError.hostPart) && !looksLikeAltIpv4Literal(firstHostError.hostPart)) && (looksLikeIpv4(hostPart) || looksLikeAltIpv4Literal(hostPart))) {
1776
+ const hostResult = parseHost(hostPart);
1777
+ if (!hostResult.success) firstHostError = {
1778
+ hostPart,
1779
+ error: hostResult.error
1780
+ };
1781
+ }
1782
+ }
1783
+ searchFrom = separatorIndex;
1784
+ }
1785
+ if (validHostNumericPortInvalid) {
1786
+ const errorMsg$1 = options?.errors?.invalidFormat;
1787
+ if (errorMsg$1) {
1788
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1;
1744
1789
  return {
1745
1790
  success: false,
1746
1791
  error: msg
1747
1792
  };
1748
1793
  }
1749
- if (looksLikeIpv4(hostPart) || looksLikeAltIpv4Literal(hostPart)) return {
1794
+ return {
1750
1795
  success: false,
1751
- error: hostResult.error
1796
+ error: require_message.message`Expected a socket address in format host${separator}port, but got ${input}.`
1797
+ };
1798
+ }
1799
+ if (firstHostError !== void 0) {
1800
+ if (looksLikeIpv4(firstHostError.hostPart) || looksLikeAltIpv4Literal(firstHostError.hostPart)) {
1801
+ const errorMsg$1 = options?.errors?.invalidFormat;
1802
+ if (errorMsg$1) {
1803
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1;
1804
+ return {
1805
+ success: false,
1806
+ error: msg
1807
+ };
1808
+ }
1809
+ return {
1810
+ success: false,
1811
+ error: firstHostError.error
1812
+ };
1813
+ }
1814
+ }
1815
+ let hostOnlyResult;
1816
+ if (!requirePort || trailingSepHost !== void 0 || trailingSepHostError !== void 0) {
1817
+ hostOnlyResult = parseHost(trimmed);
1818
+ if (canOmitPort && hostOnlyResult.success) return {
1819
+ success: true,
1820
+ value: {
1821
+ host: hostOnlyResult.value,
1822
+ port: defaultPort
1823
+ }
1824
+ };
1825
+ }
1826
+ if (trailingSepHost !== void 0 && hostOnlyResult !== void 0 && !hostOnlyResult.success) {
1827
+ if (canOmitPort) return {
1828
+ success: true,
1829
+ value: {
1830
+ host: trailingSepHost,
1831
+ port: defaultPort
1832
+ }
1752
1833
  };
1834
+ const errorMsg$1 = options?.errors?.missingPort;
1835
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1 ?? require_message.message`Port number is required but was not specified.`;
1753
1836
  return {
1754
1837
  success: false,
1755
- error: require_message.message`Expected a socket address in format host${separator}port, but got ${input}.`
1838
+ error: msg
1756
1839
  };
1757
1840
  }
1758
- const validatedHost = hostResult.value;
1759
- let validatedPort;
1760
- if (portPart === void 0 || portPart === "") {
1761
- if (requirePort) {
1762
- const errorMsg = options?.errors?.missingPort;
1763
- const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? require_message.message`Port number is required but was not specified.`;
1841
+ if (trailingSepHostError !== void 0 && hostOnlyResult !== void 0 && !hostOnlyResult.success) {
1842
+ const errorMsg$1 = options?.errors?.invalidFormat;
1843
+ if (errorMsg$1) {
1844
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1;
1764
1845
  return {
1765
1846
  success: false,
1766
1847
  error: msg
1767
1848
  };
1768
1849
  }
1769
- if (defaultPort !== void 0) validatedPort = defaultPort;
1770
- else {
1771
- const errorMsg = options?.errors?.missingPort;
1772
- const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? require_message.message`Port number is required but was not specified.`;
1850
+ if (looksLikeIpv4(trailingSepHostError.hostPart) || looksLikeAltIpv4Literal(trailingSepHostError.hostPart)) return {
1851
+ success: false,
1852
+ error: trailingSepHostError.error
1853
+ };
1854
+ return {
1855
+ success: false,
1856
+ error: require_message.message`Expected a socket address in format host${separator}port, but got ${input}.`
1857
+ };
1858
+ }
1859
+ if (!canOmitPort && !requirePort && hostOnlyResult !== void 0 && hostOnlyResult.success) {
1860
+ const errorMsg$1 = options?.errors?.missingPort;
1861
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1 ?? require_message.message`Port number is required but was not specified.`;
1862
+ return {
1863
+ success: false,
1864
+ error: msg
1865
+ };
1866
+ }
1867
+ if (!canOmitPort && !anySeparatorFound) {
1868
+ hostOnlyResult = hostOnlyResult ?? parseHost(trimmed);
1869
+ if (hostOnlyResult.success) {
1870
+ const errorMsg$1 = options?.errors?.missingPort;
1871
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1 ?? require_message.message`Port number is required but was not specified.`;
1773
1872
  return {
1774
1873
  success: false,
1775
1874
  error: msg
1776
1875
  };
1777
1876
  }
1778
- } else {
1779
- const portResult = portParser.parse(portPart);
1780
- if (!portResult.success) {
1781
- const errorMsg = options?.errors?.invalidFormat;
1782
- const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? require_message.message`Expected a socket address in format host${separator}port, but got ${input}.`;
1877
+ }
1878
+ if (firstHostError !== void 0) {
1879
+ const errorMsg$1 = options?.errors?.invalidFormat;
1880
+ if (errorMsg$1) {
1881
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1;
1783
1882
  return {
1784
1883
  success: false,
1785
1884
  error: msg
1786
1885
  };
1787
1886
  }
1788
- validatedPort = portResult.value;
1887
+ }
1888
+ const errorMsg = options?.errors?.invalidFormat;
1889
+ if (errorMsg) {
1890
+ const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg;
1891
+ return {
1892
+ success: false,
1893
+ error: msg
1894
+ };
1895
+ }
1896
+ if (hostOnlyResult !== void 0 && !hostOnlyResult.success) {
1897
+ if (looksLikeIpv4(trimmed) || looksLikeAltIpv4Literal(trimmed)) return {
1898
+ success: false,
1899
+ error: hostOnlyResult.error
1900
+ };
1789
1901
  }
1790
1902
  return {
1791
- success: true,
1792
- value: {
1793
- host: validatedHost,
1794
- port: validatedPort
1795
- }
1903
+ success: false,
1904
+ error: require_message.message`Expected a socket address in format host${separator}port, but got ${input}.`
1796
1905
  };
1797
1906
  },
1798
1907
  format(value) {
@@ -1726,73 +1726,182 @@ function socketAddress(options) {
1726
1726
  metavar,
1727
1727
  parse(input) {
1728
1728
  const trimmed = input.trim();
1729
- const separatorIndex = trimmed.lastIndexOf(separator);
1730
- let hostPart;
1731
- let portPart;
1732
- if (separatorIndex === -1) {
1733
- hostPart = trimmed;
1734
- portPart = void 0;
1735
- } else {
1736
- hostPart = trimmed.substring(0, separatorIndex);
1737
- portPart = trimmed.substring(separatorIndex + separator.length);
1738
- }
1739
- const hostResult = parseHost(hostPart);
1740
- if (!hostResult.success) {
1741
- const errorMsg = options?.errors?.invalidFormat;
1742
- if (errorMsg) {
1743
- const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg;
1729
+ const canOmitPort = defaultPort !== void 0 && !requirePort;
1730
+ let firstHostError;
1731
+ let validHostNumericPortInvalid = false;
1732
+ let trailingSepHost;
1733
+ let trailingSepHostError;
1734
+ let anySeparatorFound = false;
1735
+ let searchFrom = trimmed.length;
1736
+ while (searchFrom > 0) {
1737
+ const separatorIndex = trimmed.lastIndexOf(separator, searchFrom - 1);
1738
+ if (separatorIndex === -1) break;
1739
+ anySeparatorFound = true;
1740
+ const hostPart = trimmed.substring(0, separatorIndex);
1741
+ const portPart = trimmed.substring(separatorIndex + separator.length);
1742
+ if (portPart === "") {
1743
+ if (trailingSepHost === void 0 && trailingSepHostError === void 0) {
1744
+ const hostResult = parseHost(hostPart);
1745
+ if (hostResult.success) trailingSepHost = hostResult.value;
1746
+ else trailingSepHostError = {
1747
+ hostPart,
1748
+ error: hostResult.error
1749
+ };
1750
+ }
1751
+ } else {
1752
+ const portResult = portParser.parse(portPart);
1753
+ if (portResult.success) {
1754
+ const hostResult = parseHost(hostPart);
1755
+ if (hostResult.success) return {
1756
+ success: true,
1757
+ value: {
1758
+ host: hostResult.value,
1759
+ port: portResult.value
1760
+ }
1761
+ };
1762
+ if (firstHostError === void 0 || (looksLikeIpv4(hostPart) || looksLikeAltIpv4Literal(hostPart)) && !looksLikeIpv4(firstHostError.hostPart) && !looksLikeAltIpv4Literal(firstHostError.hostPart)) firstHostError = {
1763
+ hostPart,
1764
+ error: hostResult.error
1765
+ };
1766
+ } else if (!validHostNumericPortInvalid && hostPart !== "" && /^[0-9]+$/.test(portPart)) if (looksLikeIpv4(hostPart) || looksLikeAltIpv4Literal(hostPart)) {
1767
+ const hostResult = parseHost(hostPart);
1768
+ if (!hostResult.success) {
1769
+ if (firstHostError === void 0 || !looksLikeIpv4(firstHostError.hostPart) && !looksLikeAltIpv4Literal(firstHostError.hostPart)) firstHostError = {
1770
+ hostPart,
1771
+ error: hostResult.error
1772
+ };
1773
+ } else validHostNumericPortInvalid = true;
1774
+ } else validHostNumericPortInvalid = true;
1775
+ else if ((firstHostError === void 0 || !looksLikeIpv4(firstHostError.hostPart) && !looksLikeAltIpv4Literal(firstHostError.hostPart)) && (looksLikeIpv4(hostPart) || looksLikeAltIpv4Literal(hostPart))) {
1776
+ const hostResult = parseHost(hostPart);
1777
+ if (!hostResult.success) firstHostError = {
1778
+ hostPart,
1779
+ error: hostResult.error
1780
+ };
1781
+ }
1782
+ }
1783
+ searchFrom = separatorIndex;
1784
+ }
1785
+ if (validHostNumericPortInvalid) {
1786
+ const errorMsg$1 = options?.errors?.invalidFormat;
1787
+ if (errorMsg$1) {
1788
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1;
1744
1789
  return {
1745
1790
  success: false,
1746
1791
  error: msg
1747
1792
  };
1748
1793
  }
1749
- if (looksLikeIpv4(hostPart) || looksLikeAltIpv4Literal(hostPart)) return {
1794
+ return {
1750
1795
  success: false,
1751
- error: hostResult.error
1796
+ error: message`Expected a socket address in format host${separator}port, but got ${input}.`
1797
+ };
1798
+ }
1799
+ if (firstHostError !== void 0) {
1800
+ if (looksLikeIpv4(firstHostError.hostPart) || looksLikeAltIpv4Literal(firstHostError.hostPart)) {
1801
+ const errorMsg$1 = options?.errors?.invalidFormat;
1802
+ if (errorMsg$1) {
1803
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1;
1804
+ return {
1805
+ success: false,
1806
+ error: msg
1807
+ };
1808
+ }
1809
+ return {
1810
+ success: false,
1811
+ error: firstHostError.error
1812
+ };
1813
+ }
1814
+ }
1815
+ let hostOnlyResult;
1816
+ if (!requirePort || trailingSepHost !== void 0 || trailingSepHostError !== void 0) {
1817
+ hostOnlyResult = parseHost(trimmed);
1818
+ if (canOmitPort && hostOnlyResult.success) return {
1819
+ success: true,
1820
+ value: {
1821
+ host: hostOnlyResult.value,
1822
+ port: defaultPort
1823
+ }
1824
+ };
1825
+ }
1826
+ if (trailingSepHost !== void 0 && hostOnlyResult !== void 0 && !hostOnlyResult.success) {
1827
+ if (canOmitPort) return {
1828
+ success: true,
1829
+ value: {
1830
+ host: trailingSepHost,
1831
+ port: defaultPort
1832
+ }
1752
1833
  };
1834
+ const errorMsg$1 = options?.errors?.missingPort;
1835
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1 ?? message`Port number is required but was not specified.`;
1753
1836
  return {
1754
1837
  success: false,
1755
- error: message`Expected a socket address in format host${separator}port, but got ${input}.`
1838
+ error: msg
1756
1839
  };
1757
1840
  }
1758
- const validatedHost = hostResult.value;
1759
- let validatedPort;
1760
- if (portPart === void 0 || portPart === "") {
1761
- if (requirePort) {
1762
- const errorMsg = options?.errors?.missingPort;
1763
- const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? message`Port number is required but was not specified.`;
1841
+ if (trailingSepHostError !== void 0 && hostOnlyResult !== void 0 && !hostOnlyResult.success) {
1842
+ const errorMsg$1 = options?.errors?.invalidFormat;
1843
+ if (errorMsg$1) {
1844
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1;
1764
1845
  return {
1765
1846
  success: false,
1766
1847
  error: msg
1767
1848
  };
1768
1849
  }
1769
- if (defaultPort !== void 0) validatedPort = defaultPort;
1770
- else {
1771
- const errorMsg = options?.errors?.missingPort;
1772
- const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? message`Port number is required but was not specified.`;
1850
+ if (looksLikeIpv4(trailingSepHostError.hostPart) || looksLikeAltIpv4Literal(trailingSepHostError.hostPart)) return {
1851
+ success: false,
1852
+ error: trailingSepHostError.error
1853
+ };
1854
+ return {
1855
+ success: false,
1856
+ error: message`Expected a socket address in format host${separator}port, but got ${input}.`
1857
+ };
1858
+ }
1859
+ if (!canOmitPort && !requirePort && hostOnlyResult !== void 0 && hostOnlyResult.success) {
1860
+ const errorMsg$1 = options?.errors?.missingPort;
1861
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1 ?? message`Port number is required but was not specified.`;
1862
+ return {
1863
+ success: false,
1864
+ error: msg
1865
+ };
1866
+ }
1867
+ if (!canOmitPort && !anySeparatorFound) {
1868
+ hostOnlyResult = hostOnlyResult ?? parseHost(trimmed);
1869
+ if (hostOnlyResult.success) {
1870
+ const errorMsg$1 = options?.errors?.missingPort;
1871
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1 ?? message`Port number is required but was not specified.`;
1773
1872
  return {
1774
1873
  success: false,
1775
1874
  error: msg
1776
1875
  };
1777
1876
  }
1778
- } else {
1779
- const portResult = portParser.parse(portPart);
1780
- if (!portResult.success) {
1781
- const errorMsg = options?.errors?.invalidFormat;
1782
- const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? message`Expected a socket address in format host${separator}port, but got ${input}.`;
1877
+ }
1878
+ if (firstHostError !== void 0) {
1879
+ const errorMsg$1 = options?.errors?.invalidFormat;
1880
+ if (errorMsg$1) {
1881
+ const msg = typeof errorMsg$1 === "function" ? errorMsg$1(input) : errorMsg$1;
1783
1882
  return {
1784
1883
  success: false,
1785
1884
  error: msg
1786
1885
  };
1787
1886
  }
1788
- validatedPort = portResult.value;
1887
+ }
1888
+ const errorMsg = options?.errors?.invalidFormat;
1889
+ if (errorMsg) {
1890
+ const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg;
1891
+ return {
1892
+ success: false,
1893
+ error: msg
1894
+ };
1895
+ }
1896
+ if (hostOnlyResult !== void 0 && !hostOnlyResult.success) {
1897
+ if (looksLikeIpv4(trimmed) || looksLikeAltIpv4Literal(trimmed)) return {
1898
+ success: false,
1899
+ error: hostOnlyResult.error
1900
+ };
1789
1901
  }
1790
1902
  return {
1791
- success: true,
1792
- value: {
1793
- host: validatedHost,
1794
- port: validatedPort
1795
- }
1903
+ success: false,
1904
+ error: message`Expected a socket address in format host${separator}port, but got ${input}.`
1796
1905
  };
1797
1906
  },
1798
1907
  format(value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.0.0-dev.1470+e7499369",
3
+ "version": "1.0.0-dev.1492+3035da3f",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",