@optique/core 1.0.0-dev.1555 → 1.0.0-dev.1565
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/context.d.cts +3 -1
- package/dist/context.d.ts +3 -1
- package/dist/facade.cjs +8 -0
- package/dist/facade.d.cts +6 -0
- package/dist/facade.d.ts +6 -0
- package/dist/facade.js +9 -1
- package/dist/usage.cjs +13 -7
- package/dist/usage.d.cts +5 -2
- package/dist/usage.d.ts +5 -2
- package/dist/usage.js +13 -7
- package/dist/validate.cjs +16 -0
- package/dist/validate.js +16 -1
- package/dist/valueparser.cjs +31 -51
- package/dist/valueparser.js +31 -51
- package/package.json +1 -1
package/dist/context.d.cts
CHANGED
|
@@ -96,7 +96,9 @@ interface SourceContext<TRequiredOptions = void> {
|
|
|
96
96
|
* Unique identifier for this context.
|
|
97
97
|
*
|
|
98
98
|
* This symbol is typically the same as the annotation key used by parsers
|
|
99
|
-
* that consume this context's data.
|
|
99
|
+
* that consume this context's data. Passing multiple contexts with the
|
|
100
|
+
* same id to {@link runWith}, {@link runWithSync}, or {@link runWithAsync}
|
|
101
|
+
* throws a `TypeError`.
|
|
100
102
|
*/
|
|
101
103
|
readonly id: symbol;
|
|
102
104
|
/**
|
package/dist/context.d.ts
CHANGED
|
@@ -96,7 +96,9 @@ interface SourceContext<TRequiredOptions = void> {
|
|
|
96
96
|
* Unique identifier for this context.
|
|
97
97
|
*
|
|
98
98
|
* This symbol is typically the same as the annotation key used by parsers
|
|
99
|
-
* that consume this context's data.
|
|
99
|
+
* that consume this context's data. Passing multiple contexts with the
|
|
100
|
+
* same id to {@link runWith}, {@link runWithSync}, or {@link runWithAsync}
|
|
101
|
+
* throws a `TypeError`.
|
|
100
102
|
*/
|
|
101
103
|
readonly id: symbol;
|
|
102
104
|
/**
|
package/dist/facade.cjs
CHANGED
|
@@ -1192,6 +1192,8 @@ function disposeContextsSync(contexts) {
|
|
|
1192
1192
|
* @param contexts Source contexts to use (priority: earlier overrides later).
|
|
1193
1193
|
* @param options Run options including args, help, version, etc.
|
|
1194
1194
|
* @returns Promise that resolves to the parsed result.
|
|
1195
|
+
* @throws {TypeError} If two or more contexts share the same
|
|
1196
|
+
* {@link SourceContext.id}.
|
|
1195
1197
|
* @since 0.10.0
|
|
1196
1198
|
*
|
|
1197
1199
|
* @example
|
|
@@ -1221,6 +1223,7 @@ async function runWith(parser, programName, contexts, options) {
|
|
|
1221
1223
|
return Promise.resolve(runParser(parser, programName, args, options));
|
|
1222
1224
|
}
|
|
1223
1225
|
try {
|
|
1226
|
+
require_validate.validateContextIds(contexts);
|
|
1224
1227
|
if (needsEarlyExit(args, options)) {
|
|
1225
1228
|
if (parser.$mode === "async") return runParser(parser, programName, args, options);
|
|
1226
1229
|
return Promise.resolve(runParser(parser, programName, args, options));
|
|
@@ -1281,6 +1284,8 @@ async function runWith(parser, programName, contexts, options) {
|
|
|
1281
1284
|
* @returns The parsed result.
|
|
1282
1285
|
* @throws {TypeError} If an async parser is passed at runtime. Use
|
|
1283
1286
|
* {@link runWith} or {@link runWithAsync} for async parsers.
|
|
1287
|
+
* @throws {TypeError} If two or more contexts share the same
|
|
1288
|
+
* {@link SourceContext.id}.
|
|
1284
1289
|
* @throws {Error} If any context returns a Promise or if a context's
|
|
1285
1290
|
* `[Symbol.asyncDispose]` returns a Promise.
|
|
1286
1291
|
* @since 0.10.0
|
|
@@ -1290,6 +1295,7 @@ function runWithSync(parser, programName, contexts, options) {
|
|
|
1290
1295
|
const args = options?.args ?? [];
|
|
1291
1296
|
if (contexts.length === 0) return runParser(parser, programName, args, options);
|
|
1292
1297
|
try {
|
|
1298
|
+
require_validate.validateContextIds(contexts);
|
|
1293
1299
|
if (needsEarlyExit(args, options)) return runParser(parser, programName, args, options);
|
|
1294
1300
|
const ctxOptions = options?.contextOptions;
|
|
1295
1301
|
const { annotations: phase1Annotations, annotationsList: phase1AnnotationsList, hasDynamic: needsTwoPhase } = collectPhase1AnnotationsSync(contexts, ctxOptions);
|
|
@@ -1333,6 +1339,8 @@ function runWithSync(parser, programName, contexts, options) {
|
|
|
1333
1339
|
* @param contexts Source contexts to use (priority: earlier overrides later).
|
|
1334
1340
|
* @param options Run options including args, help, version, etc.
|
|
1335
1341
|
* @returns Promise that resolves to the parsed result.
|
|
1342
|
+
* @throws {TypeError} If two or more contexts share the same
|
|
1343
|
+
* {@link SourceContext.id}.
|
|
1336
1344
|
* @since 0.10.0
|
|
1337
1345
|
*/
|
|
1338
1346
|
function runWithAsync(parser, programName, contexts, options) {
|
package/dist/facade.d.cts
CHANGED
|
@@ -428,6 +428,8 @@ type ContextOptionsParam<TContexts extends readonly SourceContext<unknown>[], TV
|
|
|
428
428
|
* @param contexts Source contexts to use (priority: earlier overrides later).
|
|
429
429
|
* @param options Run options including args, help, version, etc.
|
|
430
430
|
* @returns Promise that resolves to the parsed result.
|
|
431
|
+
* @throws {TypeError} If two or more contexts share the same
|
|
432
|
+
* {@link SourceContext.id}.
|
|
431
433
|
* @since 0.10.0
|
|
432
434
|
*
|
|
433
435
|
* @example
|
|
@@ -467,6 +469,8 @@ declare function runWith<TParser extends Parser<Mode, unknown, unknown>, TContex
|
|
|
467
469
|
* @returns The parsed result.
|
|
468
470
|
* @throws {TypeError} If an async parser is passed at runtime. Use
|
|
469
471
|
* {@link runWith} or {@link runWithAsync} for async parsers.
|
|
472
|
+
* @throws {TypeError} If two or more contexts share the same
|
|
473
|
+
* {@link SourceContext.id}.
|
|
470
474
|
* @throws {Error} If any context returns a Promise or if a context's
|
|
471
475
|
* `[Symbol.asyncDispose]` returns a Promise.
|
|
472
476
|
* @since 0.10.0
|
|
@@ -486,6 +490,8 @@ declare function runWithSync<TParser extends Parser<"sync", unknown, unknown>, T
|
|
|
486
490
|
* @param contexts Source contexts to use (priority: earlier overrides later).
|
|
487
491
|
* @param options Run options including args, help, version, etc.
|
|
488
492
|
* @returns Promise that resolves to the parsed result.
|
|
493
|
+
* @throws {TypeError} If two or more contexts share the same
|
|
494
|
+
* {@link SourceContext.id}.
|
|
489
495
|
* @since 0.10.0
|
|
490
496
|
*/
|
|
491
497
|
declare function runWithAsync<TParser extends Parser<Mode, unknown, unknown>, TContexts extends readonly SourceContext<unknown>[], THelp = void, TError = never>(parser: TParser, programName: string, contexts: TContexts, options: RunWithOptions<THelp, TError> & ContextOptionsParam<TContexts, InferValue<TParser>>): Promise<InferValue<TParser>>;
|
package/dist/facade.d.ts
CHANGED
|
@@ -428,6 +428,8 @@ type ContextOptionsParam<TContexts extends readonly SourceContext<unknown>[], TV
|
|
|
428
428
|
* @param contexts Source contexts to use (priority: earlier overrides later).
|
|
429
429
|
* @param options Run options including args, help, version, etc.
|
|
430
430
|
* @returns Promise that resolves to the parsed result.
|
|
431
|
+
* @throws {TypeError} If two or more contexts share the same
|
|
432
|
+
* {@link SourceContext.id}.
|
|
431
433
|
* @since 0.10.0
|
|
432
434
|
*
|
|
433
435
|
* @example
|
|
@@ -467,6 +469,8 @@ declare function runWith<TParser extends Parser<Mode, unknown, unknown>, TContex
|
|
|
467
469
|
* @returns The parsed result.
|
|
468
470
|
* @throws {TypeError} If an async parser is passed at runtime. Use
|
|
469
471
|
* {@link runWith} or {@link runWithAsync} for async parsers.
|
|
472
|
+
* @throws {TypeError} If two or more contexts share the same
|
|
473
|
+
* {@link SourceContext.id}.
|
|
470
474
|
* @throws {Error} If any context returns a Promise or if a context's
|
|
471
475
|
* `[Symbol.asyncDispose]` returns a Promise.
|
|
472
476
|
* @since 0.10.0
|
|
@@ -486,6 +490,8 @@ declare function runWithSync<TParser extends Parser<"sync", unknown, unknown>, T
|
|
|
486
490
|
* @param contexts Source contexts to use (priority: earlier overrides later).
|
|
487
491
|
* @param options Run options including args, help, version, etc.
|
|
488
492
|
* @returns Promise that resolves to the parsed result.
|
|
493
|
+
* @throws {TypeError} If two or more contexts share the same
|
|
494
|
+
* {@link SourceContext.id}.
|
|
489
495
|
* @since 0.10.0
|
|
490
496
|
*/
|
|
491
497
|
declare function runWithAsync<TParser extends Parser<Mode, unknown, unknown>, TContexts extends readonly SourceContext<unknown>[], THelp = void, TError = never>(parser: TParser, programName: string, contexts: TContexts, options: RunWithOptions<THelp, TError> & ContextOptionsParam<TContexts, InferValue<TParser>>): Promise<InferValue<TParser>>;
|
package/dist/facade.js
CHANGED
|
@@ -2,7 +2,7 @@ import { injectAnnotations } from "./annotations.js";
|
|
|
2
2
|
import { commandLine, formatMessage, lineBreak, message, optionName, text, value } from "./message.js";
|
|
3
3
|
import { bash, fish, nu, pwsh, zsh } from "./completion.js";
|
|
4
4
|
import { dispatchByMode } from "./mode-dispatch.js";
|
|
5
|
-
import { validateCommandNames, validateMetaNameCollisions, validateOptionNames, validateProgramName } from "./validate.js";
|
|
5
|
+
import { validateCommandNames, validateContextIds, validateMetaNameCollisions, validateOptionNames, validateProgramName } from "./validate.js";
|
|
6
6
|
import { extractCommandNames, extractLiteralValues, extractOptionNames, formatUsage } from "./usage.js";
|
|
7
7
|
import { formatDocPage } from "./doc.js";
|
|
8
8
|
import { group, longestMatch, object } from "./constructs.js";
|
|
@@ -1192,6 +1192,8 @@ function disposeContextsSync(contexts) {
|
|
|
1192
1192
|
* @param contexts Source contexts to use (priority: earlier overrides later).
|
|
1193
1193
|
* @param options Run options including args, help, version, etc.
|
|
1194
1194
|
* @returns Promise that resolves to the parsed result.
|
|
1195
|
+
* @throws {TypeError} If two or more contexts share the same
|
|
1196
|
+
* {@link SourceContext.id}.
|
|
1195
1197
|
* @since 0.10.0
|
|
1196
1198
|
*
|
|
1197
1199
|
* @example
|
|
@@ -1221,6 +1223,7 @@ async function runWith(parser, programName, contexts, options) {
|
|
|
1221
1223
|
return Promise.resolve(runParser(parser, programName, args, options));
|
|
1222
1224
|
}
|
|
1223
1225
|
try {
|
|
1226
|
+
validateContextIds(contexts);
|
|
1224
1227
|
if (needsEarlyExit(args, options)) {
|
|
1225
1228
|
if (parser.$mode === "async") return runParser(parser, programName, args, options);
|
|
1226
1229
|
return Promise.resolve(runParser(parser, programName, args, options));
|
|
@@ -1281,6 +1284,8 @@ async function runWith(parser, programName, contexts, options) {
|
|
|
1281
1284
|
* @returns The parsed result.
|
|
1282
1285
|
* @throws {TypeError} If an async parser is passed at runtime. Use
|
|
1283
1286
|
* {@link runWith} or {@link runWithAsync} for async parsers.
|
|
1287
|
+
* @throws {TypeError} If two or more contexts share the same
|
|
1288
|
+
* {@link SourceContext.id}.
|
|
1284
1289
|
* @throws {Error} If any context returns a Promise or if a context's
|
|
1285
1290
|
* `[Symbol.asyncDispose]` returns a Promise.
|
|
1286
1291
|
* @since 0.10.0
|
|
@@ -1290,6 +1295,7 @@ function runWithSync(parser, programName, contexts, options) {
|
|
|
1290
1295
|
const args = options?.args ?? [];
|
|
1291
1296
|
if (contexts.length === 0) return runParser(parser, programName, args, options);
|
|
1292
1297
|
try {
|
|
1298
|
+
validateContextIds(contexts);
|
|
1293
1299
|
if (needsEarlyExit(args, options)) return runParser(parser, programName, args, options);
|
|
1294
1300
|
const ctxOptions = options?.contextOptions;
|
|
1295
1301
|
const { annotations: phase1Annotations, annotationsList: phase1AnnotationsList, hasDynamic: needsTwoPhase } = collectPhase1AnnotationsSync(contexts, ctxOptions);
|
|
@@ -1333,6 +1339,8 @@ function runWithSync(parser, programName, contexts, options) {
|
|
|
1333
1339
|
* @param contexts Source contexts to use (priority: earlier overrides later).
|
|
1334
1340
|
* @param options Run options including args, help, version, etc.
|
|
1335
1341
|
* @returns Promise that resolves to the parsed result.
|
|
1342
|
+
* @throws {TypeError} If two or more contexts share the same
|
|
1343
|
+
* {@link SourceContext.id}.
|
|
1336
1344
|
* @since 0.10.0
|
|
1337
1345
|
*/
|
|
1338
1346
|
function runWithAsync(parser, programName, contexts, options) {
|
package/dist/usage.cjs
CHANGED
|
@@ -239,6 +239,9 @@ function formatUsage(programName, usage, options = {}) {
|
|
|
239
239
|
* nested exclusive terms into their parent exclusive term to avoid
|
|
240
240
|
* redundant nesting. For example, an exclusive term containing another
|
|
241
241
|
* exclusive term will have its nested terms flattened into the parent.
|
|
242
|
+
* Similarly, nested optional terms are collapsed:
|
|
243
|
+
* `optional(optional(X))` becomes `optional(X)` when the outer optional
|
|
244
|
+
* contains only a single inner optional term.
|
|
242
245
|
*
|
|
243
246
|
* 3. *Sorting*: Reorders terms to improve readability by placing:
|
|
244
247
|
* - Commands (subcommands) first
|
|
@@ -251,8 +254,8 @@ function formatUsage(programName, usage, options = {}) {
|
|
|
251
254
|
*
|
|
252
255
|
* @param usage The usage description to normalize.
|
|
253
256
|
* @returns A normalized usage description with degenerate terms removed,
|
|
254
|
-
* nested exclusive terms flattened, and remaining
|
|
255
|
-
* optimal readability.
|
|
257
|
+
* nested exclusive and optional terms flattened, and remaining
|
|
258
|
+
* terms sorted for optimal readability.
|
|
256
259
|
*/
|
|
257
260
|
function normalizeUsage(usage) {
|
|
258
261
|
const terms = usage.map(normalizeUsageTerm).filter(isNonDegenerateTerm);
|
|
@@ -266,11 +269,14 @@ function normalizeUsage(usage) {
|
|
|
266
269
|
return terms;
|
|
267
270
|
}
|
|
268
271
|
function normalizeUsageTerm(term) {
|
|
269
|
-
if (term.type === "optional")
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
272
|
+
if (term.type === "optional") {
|
|
273
|
+
const normalized = normalizeUsage(term.terms);
|
|
274
|
+
if (normalized.length === 1 && normalized[0].type === "optional") return normalized[0];
|
|
275
|
+
return {
|
|
276
|
+
type: "optional",
|
|
277
|
+
terms: normalized
|
|
278
|
+
};
|
|
279
|
+
} else if (term.type === "multiple") return {
|
|
274
280
|
type: "multiple",
|
|
275
281
|
terms: normalizeUsage(term.terms),
|
|
276
282
|
min: term.min
|
package/dist/usage.d.cts
CHANGED
|
@@ -379,6 +379,9 @@ declare function formatUsage(programName: string, usage: Usage, options?: UsageF
|
|
|
379
379
|
* nested exclusive terms into their parent exclusive term to avoid
|
|
380
380
|
* redundant nesting. For example, an exclusive term containing another
|
|
381
381
|
* exclusive term will have its nested terms flattened into the parent.
|
|
382
|
+
* Similarly, nested optional terms are collapsed:
|
|
383
|
+
* `optional(optional(X))` becomes `optional(X)` when the outer optional
|
|
384
|
+
* contains only a single inner optional term.
|
|
382
385
|
*
|
|
383
386
|
* 3. *Sorting*: Reorders terms to improve readability by placing:
|
|
384
387
|
* - Commands (subcommands) first
|
|
@@ -391,8 +394,8 @@ declare function formatUsage(programName: string, usage: Usage, options?: UsageF
|
|
|
391
394
|
*
|
|
392
395
|
* @param usage The usage description to normalize.
|
|
393
396
|
* @returns A normalized usage description with degenerate terms removed,
|
|
394
|
-
* nested exclusive terms flattened, and remaining
|
|
395
|
-
* optimal readability.
|
|
397
|
+
* nested exclusive and optional terms flattened, and remaining
|
|
398
|
+
* terms sorted for optimal readability.
|
|
396
399
|
*/
|
|
397
400
|
declare function normalizeUsage(usage: Usage): Usage;
|
|
398
401
|
/**
|
package/dist/usage.d.ts
CHANGED
|
@@ -379,6 +379,9 @@ declare function formatUsage(programName: string, usage: Usage, options?: UsageF
|
|
|
379
379
|
* nested exclusive terms into their parent exclusive term to avoid
|
|
380
380
|
* redundant nesting. For example, an exclusive term containing another
|
|
381
381
|
* exclusive term will have its nested terms flattened into the parent.
|
|
382
|
+
* Similarly, nested optional terms are collapsed:
|
|
383
|
+
* `optional(optional(X))` becomes `optional(X)` when the outer optional
|
|
384
|
+
* contains only a single inner optional term.
|
|
382
385
|
*
|
|
383
386
|
* 3. *Sorting*: Reorders terms to improve readability by placing:
|
|
384
387
|
* - Commands (subcommands) first
|
|
@@ -391,8 +394,8 @@ declare function formatUsage(programName: string, usage: Usage, options?: UsageF
|
|
|
391
394
|
*
|
|
392
395
|
* @param usage The usage description to normalize.
|
|
393
396
|
* @returns A normalized usage description with degenerate terms removed,
|
|
394
|
-
* nested exclusive terms flattened, and remaining
|
|
395
|
-
* optimal readability.
|
|
397
|
+
* nested exclusive and optional terms flattened, and remaining
|
|
398
|
+
* terms sorted for optimal readability.
|
|
396
399
|
*/
|
|
397
400
|
declare function normalizeUsage(usage: Usage): Usage;
|
|
398
401
|
/**
|
package/dist/usage.js
CHANGED
|
@@ -239,6 +239,9 @@ function formatUsage(programName, usage, options = {}) {
|
|
|
239
239
|
* nested exclusive terms into their parent exclusive term to avoid
|
|
240
240
|
* redundant nesting. For example, an exclusive term containing another
|
|
241
241
|
* exclusive term will have its nested terms flattened into the parent.
|
|
242
|
+
* Similarly, nested optional terms are collapsed:
|
|
243
|
+
* `optional(optional(X))` becomes `optional(X)` when the outer optional
|
|
244
|
+
* contains only a single inner optional term.
|
|
242
245
|
*
|
|
243
246
|
* 3. *Sorting*: Reorders terms to improve readability by placing:
|
|
244
247
|
* - Commands (subcommands) first
|
|
@@ -251,8 +254,8 @@ function formatUsage(programName, usage, options = {}) {
|
|
|
251
254
|
*
|
|
252
255
|
* @param usage The usage description to normalize.
|
|
253
256
|
* @returns A normalized usage description with degenerate terms removed,
|
|
254
|
-
* nested exclusive terms flattened, and remaining
|
|
255
|
-
* optimal readability.
|
|
257
|
+
* nested exclusive and optional terms flattened, and remaining
|
|
258
|
+
* terms sorted for optimal readability.
|
|
256
259
|
*/
|
|
257
260
|
function normalizeUsage(usage) {
|
|
258
261
|
const terms = usage.map(normalizeUsageTerm).filter(isNonDegenerateTerm);
|
|
@@ -266,11 +269,14 @@ function normalizeUsage(usage) {
|
|
|
266
269
|
return terms;
|
|
267
270
|
}
|
|
268
271
|
function normalizeUsageTerm(term) {
|
|
269
|
-
if (term.type === "optional")
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
272
|
+
if (term.type === "optional") {
|
|
273
|
+
const normalized = normalizeUsage(term.terms);
|
|
274
|
+
if (normalized.length === 1 && normalized[0].type === "optional") return normalized[0];
|
|
275
|
+
return {
|
|
276
|
+
type: "optional",
|
|
277
|
+
terms: normalized
|
|
278
|
+
};
|
|
279
|
+
} else if (term.type === "multiple") return {
|
|
274
280
|
type: "multiple",
|
|
275
281
|
terms: normalizeUsage(term.terms),
|
|
276
282
|
min: term.min
|
package/dist/validate.cjs
CHANGED
|
@@ -177,9 +177,25 @@ function validateLabel(label) {
|
|
|
177
177
|
if (/^\s+$/.test(label)) throw new TypeError(`Label must not be whitespace-only: "${escapeControlChars(label)}".`);
|
|
178
178
|
if (CONTROL_CHAR_RE.test(label)) throw new TypeError(`Label must not contain control characters: "${escapeControlChars(label)}".`);
|
|
179
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Validates that all source contexts have unique
|
|
182
|
+
* {@link import("./context.ts").SourceContext.id | id} values.
|
|
183
|
+
*
|
|
184
|
+
* @param contexts The source contexts to validate.
|
|
185
|
+
* @throws {TypeError} If two or more contexts share the same id.
|
|
186
|
+
* @since 1.0.0
|
|
187
|
+
*/
|
|
188
|
+
function validateContextIds(contexts) {
|
|
189
|
+
const seen = /* @__PURE__ */ new Set();
|
|
190
|
+
for (const context of contexts) {
|
|
191
|
+
if (seen.has(context.id)) throw new TypeError(`Duplicate SourceContext id: ${String(context.id)}`);
|
|
192
|
+
seen.add(context.id);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
180
195
|
|
|
181
196
|
//#endregion
|
|
182
197
|
exports.validateCommandNames = validateCommandNames;
|
|
198
|
+
exports.validateContextIds = validateContextIds;
|
|
183
199
|
exports.validateLabel = validateLabel;
|
|
184
200
|
exports.validateMetaNameCollisions = validateMetaNameCollisions;
|
|
185
201
|
exports.validateOptionNames = validateOptionNames;
|
package/dist/validate.js
CHANGED
|
@@ -176,6 +176,21 @@ function validateLabel(label) {
|
|
|
176
176
|
if (/^\s+$/.test(label)) throw new TypeError(`Label must not be whitespace-only: "${escapeControlChars(label)}".`);
|
|
177
177
|
if (CONTROL_CHAR_RE.test(label)) throw new TypeError(`Label must not contain control characters: "${escapeControlChars(label)}".`);
|
|
178
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Validates that all source contexts have unique
|
|
181
|
+
* {@link import("./context.ts").SourceContext.id | id} values.
|
|
182
|
+
*
|
|
183
|
+
* @param contexts The source contexts to validate.
|
|
184
|
+
* @throws {TypeError} If two or more contexts share the same id.
|
|
185
|
+
* @since 1.0.0
|
|
186
|
+
*/
|
|
187
|
+
function validateContextIds(contexts) {
|
|
188
|
+
const seen = /* @__PURE__ */ new Set();
|
|
189
|
+
for (const context of contexts) {
|
|
190
|
+
if (seen.has(context.id)) throw new TypeError(`Duplicate SourceContext id: ${String(context.id)}`);
|
|
191
|
+
seen.add(context.id);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
179
194
|
|
|
180
195
|
//#endregion
|
|
181
|
-
export { validateCommandNames, validateLabel, validateMetaNameCollisions, validateOptionNames, validateProgramName };
|
|
196
|
+
export { validateCommandNames, validateContextIds, validateLabel, validateMetaNameCollisions, validateOptionNames, validateProgramName };
|
package/dist/valueparser.cjs
CHANGED
|
@@ -1161,8 +1161,8 @@ function ipv4(options) {
|
|
|
1161
1161
|
metavar,
|
|
1162
1162
|
placeholder: allowZero ? "0.0.0.0" : allowLoopback ? "127.0.0.1" : "192.0.2.1",
|
|
1163
1163
|
parse(input) {
|
|
1164
|
-
const
|
|
1165
|
-
if (
|
|
1164
|
+
const octets = parseIpv4Octets(input);
|
|
1165
|
+
if (octets === null) {
|
|
1166
1166
|
const errorMsg = options?.errors?.invalidIpv4;
|
|
1167
1167
|
const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? require_message.message`Expected a valid IPv4 address, but got ${input}.`;
|
|
1168
1168
|
return {
|
|
@@ -1170,51 +1170,6 @@ function ipv4(options) {
|
|
|
1170
1170
|
error: msg
|
|
1171
1171
|
};
|
|
1172
1172
|
}
|
|
1173
|
-
const octets = [];
|
|
1174
|
-
for (const part of parts) {
|
|
1175
|
-
if (part.length === 0) {
|
|
1176
|
-
const errorMsg = options?.errors?.invalidIpv4;
|
|
1177
|
-
const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? require_message.message`Expected a valid IPv4 address, but got ${input}.`;
|
|
1178
|
-
return {
|
|
1179
|
-
success: false,
|
|
1180
|
-
error: msg
|
|
1181
|
-
};
|
|
1182
|
-
}
|
|
1183
|
-
if (part.trim() !== part) {
|
|
1184
|
-
const errorMsg = options?.errors?.invalidIpv4;
|
|
1185
|
-
const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? require_message.message`Expected a valid IPv4 address, but got ${input}.`;
|
|
1186
|
-
return {
|
|
1187
|
-
success: false,
|
|
1188
|
-
error: msg
|
|
1189
|
-
};
|
|
1190
|
-
}
|
|
1191
|
-
if (part.length > 1 && part[0] === "0") {
|
|
1192
|
-
const errorMsg = options?.errors?.invalidIpv4;
|
|
1193
|
-
const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? require_message.message`Expected a valid IPv4 address, but got ${input}.`;
|
|
1194
|
-
return {
|
|
1195
|
-
success: false,
|
|
1196
|
-
error: msg
|
|
1197
|
-
};
|
|
1198
|
-
}
|
|
1199
|
-
if (!/^[0-9]+$/.test(part)) {
|
|
1200
|
-
const errorMsg = options?.errors?.invalidIpv4;
|
|
1201
|
-
const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? require_message.message`Expected a valid IPv4 address, but got ${input}.`;
|
|
1202
|
-
return {
|
|
1203
|
-
success: false,
|
|
1204
|
-
error: msg
|
|
1205
|
-
};
|
|
1206
|
-
}
|
|
1207
|
-
const octet = Number(part);
|
|
1208
|
-
if (!Number.isInteger(octet) || octet < 0 || octet > 255) {
|
|
1209
|
-
const errorMsg = options?.errors?.invalidIpv4;
|
|
1210
|
-
const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? require_message.message`Expected a valid IPv4 address, but got ${input}.`;
|
|
1211
|
-
return {
|
|
1212
|
-
success: false,
|
|
1213
|
-
error: msg
|
|
1214
|
-
};
|
|
1215
|
-
}
|
|
1216
|
-
octets.push(octet);
|
|
1217
|
-
}
|
|
1218
1173
|
const ipAddress = octets.join(".");
|
|
1219
1174
|
if (!allowPrivate && isPrivateIp(octets)) {
|
|
1220
1175
|
const errorMsg = options?.errors?.privateNotAllowed;
|
|
@@ -2855,6 +2810,33 @@ function ipv6(options) {
|
|
|
2855
2810
|
return ipv6ParserObj;
|
|
2856
2811
|
}
|
|
2857
2812
|
/**
|
|
2813
|
+
* Parses a dotted-decimal IPv4 string into four validated octets.
|
|
2814
|
+
* Returns null if the input is not a valid strict IPv4 address
|
|
2815
|
+
* (exactly four decimal octets 0–255, no leading zeros, no whitespace,
|
|
2816
|
+
* no non-decimal characters).
|
|
2817
|
+
*/
|
|
2818
|
+
function parseIpv4Octets(input) {
|
|
2819
|
+
const parts = input.split(".");
|
|
2820
|
+
if (parts.length !== 4) return null;
|
|
2821
|
+
const octets = [
|
|
2822
|
+
0,
|
|
2823
|
+
0,
|
|
2824
|
+
0,
|
|
2825
|
+
0
|
|
2826
|
+
];
|
|
2827
|
+
for (let i = 0; i < 4; i++) {
|
|
2828
|
+
const part = parts[i];
|
|
2829
|
+
if (part.length === 0) return null;
|
|
2830
|
+
if (part.trim() !== part) return null;
|
|
2831
|
+
if (part.length > 1 && part[0] === "0") return null;
|
|
2832
|
+
if (!/^[0-9]+$/.test(part)) return null;
|
|
2833
|
+
const octet = Number(part);
|
|
2834
|
+
if (!Number.isInteger(octet) || octet < 0 || octet > 255) return null;
|
|
2835
|
+
octets[i] = octet;
|
|
2836
|
+
}
|
|
2837
|
+
return octets;
|
|
2838
|
+
}
|
|
2839
|
+
/**
|
|
2858
2840
|
* Parses and normalizes an IPv6 address to canonical form.
|
|
2859
2841
|
* Returns null if the input is not a valid IPv6 address.
|
|
2860
2842
|
*/
|
|
@@ -2864,10 +2846,8 @@ function parseAndNormalizeIpv6(input) {
|
|
|
2864
2846
|
if (ipv4MappedMatch) {
|
|
2865
2847
|
const ipv6Part = ipv4MappedMatch[1];
|
|
2866
2848
|
const ipv4Part = ipv4MappedMatch[2];
|
|
2867
|
-
const
|
|
2868
|
-
if (
|
|
2869
|
-
const octets = ipv4Octets.map((o) => parseInt(o, 10));
|
|
2870
|
-
if (octets.some((o) => isNaN(o) || o < 0 || o > 255)) return null;
|
|
2849
|
+
const octets = parseIpv4Octets(ipv4Part);
|
|
2850
|
+
if (octets === null) return null;
|
|
2871
2851
|
const group1 = octets[0] << 8 | octets[1];
|
|
2872
2852
|
const group2 = octets[2] << 8 | octets[3];
|
|
2873
2853
|
const fullAddress = `${ipv6Part}:${group1.toString(16)}:${group2.toString(16)}`;
|
package/dist/valueparser.js
CHANGED
|
@@ -1161,8 +1161,8 @@ function ipv4(options) {
|
|
|
1161
1161
|
metavar,
|
|
1162
1162
|
placeholder: allowZero ? "0.0.0.0" : allowLoopback ? "127.0.0.1" : "192.0.2.1",
|
|
1163
1163
|
parse(input) {
|
|
1164
|
-
const
|
|
1165
|
-
if (
|
|
1164
|
+
const octets = parseIpv4Octets(input);
|
|
1165
|
+
if (octets === null) {
|
|
1166
1166
|
const errorMsg = options?.errors?.invalidIpv4;
|
|
1167
1167
|
const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? message`Expected a valid IPv4 address, but got ${input}.`;
|
|
1168
1168
|
return {
|
|
@@ -1170,51 +1170,6 @@ function ipv4(options) {
|
|
|
1170
1170
|
error: msg
|
|
1171
1171
|
};
|
|
1172
1172
|
}
|
|
1173
|
-
const octets = [];
|
|
1174
|
-
for (const part of parts) {
|
|
1175
|
-
if (part.length === 0) {
|
|
1176
|
-
const errorMsg = options?.errors?.invalidIpv4;
|
|
1177
|
-
const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? message`Expected a valid IPv4 address, but got ${input}.`;
|
|
1178
|
-
return {
|
|
1179
|
-
success: false,
|
|
1180
|
-
error: msg
|
|
1181
|
-
};
|
|
1182
|
-
}
|
|
1183
|
-
if (part.trim() !== part) {
|
|
1184
|
-
const errorMsg = options?.errors?.invalidIpv4;
|
|
1185
|
-
const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? message`Expected a valid IPv4 address, but got ${input}.`;
|
|
1186
|
-
return {
|
|
1187
|
-
success: false,
|
|
1188
|
-
error: msg
|
|
1189
|
-
};
|
|
1190
|
-
}
|
|
1191
|
-
if (part.length > 1 && part[0] === "0") {
|
|
1192
|
-
const errorMsg = options?.errors?.invalidIpv4;
|
|
1193
|
-
const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? message`Expected a valid IPv4 address, but got ${input}.`;
|
|
1194
|
-
return {
|
|
1195
|
-
success: false,
|
|
1196
|
-
error: msg
|
|
1197
|
-
};
|
|
1198
|
-
}
|
|
1199
|
-
if (!/^[0-9]+$/.test(part)) {
|
|
1200
|
-
const errorMsg = options?.errors?.invalidIpv4;
|
|
1201
|
-
const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? message`Expected a valid IPv4 address, but got ${input}.`;
|
|
1202
|
-
return {
|
|
1203
|
-
success: false,
|
|
1204
|
-
error: msg
|
|
1205
|
-
};
|
|
1206
|
-
}
|
|
1207
|
-
const octet = Number(part);
|
|
1208
|
-
if (!Number.isInteger(octet) || octet < 0 || octet > 255) {
|
|
1209
|
-
const errorMsg = options?.errors?.invalidIpv4;
|
|
1210
|
-
const msg = typeof errorMsg === "function" ? errorMsg(input) : errorMsg ?? message`Expected a valid IPv4 address, but got ${input}.`;
|
|
1211
|
-
return {
|
|
1212
|
-
success: false,
|
|
1213
|
-
error: msg
|
|
1214
|
-
};
|
|
1215
|
-
}
|
|
1216
|
-
octets.push(octet);
|
|
1217
|
-
}
|
|
1218
1173
|
const ipAddress = octets.join(".");
|
|
1219
1174
|
if (!allowPrivate && isPrivateIp(octets)) {
|
|
1220
1175
|
const errorMsg = options?.errors?.privateNotAllowed;
|
|
@@ -2855,6 +2810,33 @@ function ipv6(options) {
|
|
|
2855
2810
|
return ipv6ParserObj;
|
|
2856
2811
|
}
|
|
2857
2812
|
/**
|
|
2813
|
+
* Parses a dotted-decimal IPv4 string into four validated octets.
|
|
2814
|
+
* Returns null if the input is not a valid strict IPv4 address
|
|
2815
|
+
* (exactly four decimal octets 0–255, no leading zeros, no whitespace,
|
|
2816
|
+
* no non-decimal characters).
|
|
2817
|
+
*/
|
|
2818
|
+
function parseIpv4Octets(input) {
|
|
2819
|
+
const parts = input.split(".");
|
|
2820
|
+
if (parts.length !== 4) return null;
|
|
2821
|
+
const octets = [
|
|
2822
|
+
0,
|
|
2823
|
+
0,
|
|
2824
|
+
0,
|
|
2825
|
+
0
|
|
2826
|
+
];
|
|
2827
|
+
for (let i = 0; i < 4; i++) {
|
|
2828
|
+
const part = parts[i];
|
|
2829
|
+
if (part.length === 0) return null;
|
|
2830
|
+
if (part.trim() !== part) return null;
|
|
2831
|
+
if (part.length > 1 && part[0] === "0") return null;
|
|
2832
|
+
if (!/^[0-9]+$/.test(part)) return null;
|
|
2833
|
+
const octet = Number(part);
|
|
2834
|
+
if (!Number.isInteger(octet) || octet < 0 || octet > 255) return null;
|
|
2835
|
+
octets[i] = octet;
|
|
2836
|
+
}
|
|
2837
|
+
return octets;
|
|
2838
|
+
}
|
|
2839
|
+
/**
|
|
2858
2840
|
* Parses and normalizes an IPv6 address to canonical form.
|
|
2859
2841
|
* Returns null if the input is not a valid IPv6 address.
|
|
2860
2842
|
*/
|
|
@@ -2864,10 +2846,8 @@ function parseAndNormalizeIpv6(input) {
|
|
|
2864
2846
|
if (ipv4MappedMatch) {
|
|
2865
2847
|
const ipv6Part = ipv4MappedMatch[1];
|
|
2866
2848
|
const ipv4Part = ipv4MappedMatch[2];
|
|
2867
|
-
const
|
|
2868
|
-
if (
|
|
2869
|
-
const octets = ipv4Octets.map((o) => parseInt(o, 10));
|
|
2870
|
-
if (octets.some((o) => isNaN(o) || o < 0 || o > 255)) return null;
|
|
2849
|
+
const octets = parseIpv4Octets(ipv4Part);
|
|
2850
|
+
if (octets === null) return null;
|
|
2871
2851
|
const group1 = octets[0] << 8 | octets[1];
|
|
2872
2852
|
const group2 = octets[2] << 8 | octets[3];
|
|
2873
2853
|
const fullAddress = `${ipv6Part}:${group1.toString(16)}:${group2.toString(16)}`;
|