@optique/core 1.0.0-dev.1200 → 1.0.0-dev.1209

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.
@@ -797,9 +797,16 @@ function locale(options = {}) {
797
797
  *
798
798
  * This parser validates that the input is a well-formed UUID string in the
799
799
  * standard format: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` where each `x`
800
- * is a hexadecimal digit. The parser can optionally restrict to specific
801
- * UUID versions.
800
+ * is a hexadecimal digit.
802
801
  *
802
+ * By default, the parser enforces strict [RFC 9562] validation: it requires
803
+ * a standardized version digit (1 through 8) and the RFC 9562 variant bits
804
+ * (`10xx`). The nil and max UUIDs are accepted as special standard values.
805
+ * Set `strict: false` to disable the default RFC 9562 version/variant
806
+ * checks. An explicit {@link UuidOptions.allowedVersions} list still
807
+ * constrains the version nibble even in lenient mode.
808
+ *
809
+ * [RFC 9562]: https://www.rfc-editor.org/rfc/rfc9562
803
810
  * @param options Configuration options for the UUID parser.
804
811
  * @returns A {@link ValueParser} that converts string input to {@link Uuid}
805
812
  * strings.
@@ -808,9 +815,12 @@ function uuid(options = {}) {
808
815
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
809
816
  const metavar = options.metavar ?? "UUID";
810
817
  require_nonempty.ensureNonEmptyString(metavar);
818
+ checkBooleanOption(options, "strict");
819
+ const strict = options.strict !== false;
811
820
  const allowedVersions = options.allowedVersions != null ? Object.freeze([...options.allowedVersions]) : null;
812
821
  const invalidUuid = options.errors?.invalidUuid;
813
822
  const disallowedVersion = options.errors?.disallowedVersion;
823
+ const invalidVariant = options.errors?.invalidVariant;
814
824
  return {
815
825
  $mode: "sync",
816
826
  metavar,
@@ -819,9 +829,14 @@ function uuid(options = {}) {
819
829
  success: false,
820
830
  error: invalidUuid ? typeof invalidUuid === "function" ? invalidUuid(input) : invalidUuid : require_message.message`Expected a valid UUID in format ${"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}, but got ${input}.`
821
831
  };
832
+ const lower = input.toLowerCase();
833
+ if (lower === "00000000-0000-0000-0000-000000000000" || lower === "ffffffff-ffff-ffff-ffff-ffffffffffff") return {
834
+ success: true,
835
+ value: input
836
+ };
837
+ const versionChar = input.charAt(14);
838
+ const version = parseInt(versionChar, 16);
822
839
  if (allowedVersions != null && allowedVersions.length > 0) {
823
- const versionChar = input.charAt(14);
824
- const version = parseInt(versionChar, 16);
825
840
  if (!allowedVersions.includes(version)) return {
826
841
  success: false,
827
842
  error: disallowedVersion ? typeof disallowedVersion === "function" ? disallowedVersion(version, allowedVersions) : disallowedVersion : (() => {
@@ -834,6 +849,25 @@ function uuid(options = {}) {
834
849
  return require_message.message`Expected UUID version ${expectedVersions}, but got version ${version.toLocaleString("en")}.`;
835
850
  })()
836
851
  };
852
+ } else if (strict && (version < 1 || version > 8)) return {
853
+ success: false,
854
+ error: disallowedVersion ? typeof disallowedVersion === "function" ? disallowedVersion(version, [
855
+ 1,
856
+ 2,
857
+ 3,
858
+ 4,
859
+ 5,
860
+ 6,
861
+ 7,
862
+ 8
863
+ ]) : disallowedVersion : require_message.message`Expected UUID version 1 through 8, but got version ${version.toLocaleString("en")}.`
864
+ };
865
+ if (strict) {
866
+ const variantChar = input.charAt(19).toLowerCase();
867
+ if (variantChar !== "8" && variantChar !== "9" && variantChar !== "a" && variantChar !== "b") return {
868
+ success: false,
869
+ error: invalidVariant ? typeof invalidVariant === "function" ? invalidVariant(input) : invalidVariant : require_message.message`Expected RFC 9562 variant (8, 9, a, or b at position 20), but got ${variantChar} in ${input}.`
870
+ };
837
871
  }
838
872
  return {
839
873
  success: true,
@@ -563,9 +563,33 @@ interface UuidOptions {
563
563
  /**
564
564
  * List of allowed UUID versions (e.g., `[4, 5]` for UUIDs version 4 and 5).
565
565
  * If specified, the parser will validate that the UUID matches one of the
566
- * allowed versions. If not specified, any valid UUID format is accepted.
566
+ * allowed versions. If not specified, the accepted versions depend on
567
+ * the {@link strict} option.
567
568
  */
568
569
  readonly allowedVersions?: readonly number[];
570
+ /**
571
+ * Whether to enforce strict [RFC 9562] validation. When `true` (the
572
+ * default), the parser validates that the version digit is one of the
573
+ * currently standardized versions (1 through 8) and that the variant bits
574
+ * follow the RFC 9562 layout (`10xx`, i.e., hex digits `8`, `9`, `a`,
575
+ * or `b` at position 20 of the UUID string).
576
+ *
577
+ * The nil UUID (`00000000-0000-0000-0000-000000000000`) and max UUID
578
+ * (`ffffffff-ffff-ffff-ffff-ffffffffffff`) are accepted as special
579
+ * standard values regardless of this setting.
580
+ *
581
+ * When `false`, the parser only validates the UUID format without
582
+ * checking version or variant fields.
583
+ *
584
+ * When {@link allowedVersions} is provided, it takes precedence over the
585
+ * strict version check, but variant bit validation still applies if
586
+ * `strict` is `true`.
587
+ *
588
+ * [RFC 9562]: https://www.rfc-editor.org/rfc/rfc9562
589
+ * @default true
590
+ * @since 1.0.0
591
+ */
592
+ readonly strict?: boolean;
569
593
  /**
570
594
  * Custom error messages for UUID parsing failures.
571
595
  * @since 0.5.0
@@ -583,6 +607,12 @@ interface UuidOptions {
583
607
  * @since 0.5.0
584
608
  */
585
609
  disallowedVersion?: Message | ((version: number, allowedVersions: readonly number[]) => Message);
610
+ /**
611
+ * Custom error message when UUID variant bits are not RFC 9562 compliant.
612
+ * Can be a static message or a function that receives the input.
613
+ * @since 1.0.0
614
+ */
615
+ invalidVariant?: Message | ((input: string) => Message);
586
616
  };
587
617
  }
588
618
  /**
@@ -590,9 +620,16 @@ interface UuidOptions {
590
620
  *
591
621
  * This parser validates that the input is a well-formed UUID string in the
592
622
  * standard format: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` where each `x`
593
- * is a hexadecimal digit. The parser can optionally restrict to specific
594
- * UUID versions.
623
+ * is a hexadecimal digit.
624
+ *
625
+ * By default, the parser enforces strict [RFC 9562] validation: it requires
626
+ * a standardized version digit (1 through 8) and the RFC 9562 variant bits
627
+ * (`10xx`). The nil and max UUIDs are accepted as special standard values.
628
+ * Set `strict: false` to disable the default RFC 9562 version/variant
629
+ * checks. An explicit {@link UuidOptions.allowedVersions} list still
630
+ * constrains the version nibble even in lenient mode.
595
631
  *
632
+ * [RFC 9562]: https://www.rfc-editor.org/rfc/rfc9562
596
633
  * @param options Configuration options for the UUID parser.
597
634
  * @returns A {@link ValueParser} that converts string input to {@link Uuid}
598
635
  * strings.
@@ -563,9 +563,33 @@ interface UuidOptions {
563
563
  /**
564
564
  * List of allowed UUID versions (e.g., `[4, 5]` for UUIDs version 4 and 5).
565
565
  * If specified, the parser will validate that the UUID matches one of the
566
- * allowed versions. If not specified, any valid UUID format is accepted.
566
+ * allowed versions. If not specified, the accepted versions depend on
567
+ * the {@link strict} option.
567
568
  */
568
569
  readonly allowedVersions?: readonly number[];
570
+ /**
571
+ * Whether to enforce strict [RFC 9562] validation. When `true` (the
572
+ * default), the parser validates that the version digit is one of the
573
+ * currently standardized versions (1 through 8) and that the variant bits
574
+ * follow the RFC 9562 layout (`10xx`, i.e., hex digits `8`, `9`, `a`,
575
+ * or `b` at position 20 of the UUID string).
576
+ *
577
+ * The nil UUID (`00000000-0000-0000-0000-000000000000`) and max UUID
578
+ * (`ffffffff-ffff-ffff-ffff-ffffffffffff`) are accepted as special
579
+ * standard values regardless of this setting.
580
+ *
581
+ * When `false`, the parser only validates the UUID format without
582
+ * checking version or variant fields.
583
+ *
584
+ * When {@link allowedVersions} is provided, it takes precedence over the
585
+ * strict version check, but variant bit validation still applies if
586
+ * `strict` is `true`.
587
+ *
588
+ * [RFC 9562]: https://www.rfc-editor.org/rfc/rfc9562
589
+ * @default true
590
+ * @since 1.0.0
591
+ */
592
+ readonly strict?: boolean;
569
593
  /**
570
594
  * Custom error messages for UUID parsing failures.
571
595
  * @since 0.5.0
@@ -583,6 +607,12 @@ interface UuidOptions {
583
607
  * @since 0.5.0
584
608
  */
585
609
  disallowedVersion?: Message | ((version: number, allowedVersions: readonly number[]) => Message);
610
+ /**
611
+ * Custom error message when UUID variant bits are not RFC 9562 compliant.
612
+ * Can be a static message or a function that receives the input.
613
+ * @since 1.0.0
614
+ */
615
+ invalidVariant?: Message | ((input: string) => Message);
586
616
  };
587
617
  }
588
618
  /**
@@ -590,9 +620,16 @@ interface UuidOptions {
590
620
  *
591
621
  * This parser validates that the input is a well-formed UUID string in the
592
622
  * standard format: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` where each `x`
593
- * is a hexadecimal digit. The parser can optionally restrict to specific
594
- * UUID versions.
623
+ * is a hexadecimal digit.
624
+ *
625
+ * By default, the parser enforces strict [RFC 9562] validation: it requires
626
+ * a standardized version digit (1 through 8) and the RFC 9562 variant bits
627
+ * (`10xx`). The nil and max UUIDs are accepted as special standard values.
628
+ * Set `strict: false` to disable the default RFC 9562 version/variant
629
+ * checks. An explicit {@link UuidOptions.allowedVersions} list still
630
+ * constrains the version nibble even in lenient mode.
595
631
  *
632
+ * [RFC 9562]: https://www.rfc-editor.org/rfc/rfc9562
596
633
  * @param options Configuration options for the UUID parser.
597
634
  * @returns A {@link ValueParser} that converts string input to {@link Uuid}
598
635
  * strings.
@@ -797,9 +797,16 @@ function locale(options = {}) {
797
797
  *
798
798
  * This parser validates that the input is a well-formed UUID string in the
799
799
  * standard format: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` where each `x`
800
- * is a hexadecimal digit. The parser can optionally restrict to specific
801
- * UUID versions.
800
+ * is a hexadecimal digit.
802
801
  *
802
+ * By default, the parser enforces strict [RFC 9562] validation: it requires
803
+ * a standardized version digit (1 through 8) and the RFC 9562 variant bits
804
+ * (`10xx`). The nil and max UUIDs are accepted as special standard values.
805
+ * Set `strict: false` to disable the default RFC 9562 version/variant
806
+ * checks. An explicit {@link UuidOptions.allowedVersions} list still
807
+ * constrains the version nibble even in lenient mode.
808
+ *
809
+ * [RFC 9562]: https://www.rfc-editor.org/rfc/rfc9562
803
810
  * @param options Configuration options for the UUID parser.
804
811
  * @returns A {@link ValueParser} that converts string input to {@link Uuid}
805
812
  * strings.
@@ -808,9 +815,12 @@ function uuid(options = {}) {
808
815
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
809
816
  const metavar = options.metavar ?? "UUID";
810
817
  ensureNonEmptyString(metavar);
818
+ checkBooleanOption(options, "strict");
819
+ const strict = options.strict !== false;
811
820
  const allowedVersions = options.allowedVersions != null ? Object.freeze([...options.allowedVersions]) : null;
812
821
  const invalidUuid = options.errors?.invalidUuid;
813
822
  const disallowedVersion = options.errors?.disallowedVersion;
823
+ const invalidVariant = options.errors?.invalidVariant;
814
824
  return {
815
825
  $mode: "sync",
816
826
  metavar,
@@ -819,9 +829,14 @@ function uuid(options = {}) {
819
829
  success: false,
820
830
  error: invalidUuid ? typeof invalidUuid === "function" ? invalidUuid(input) : invalidUuid : message`Expected a valid UUID in format ${"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}, but got ${input}.`
821
831
  };
832
+ const lower = input.toLowerCase();
833
+ if (lower === "00000000-0000-0000-0000-000000000000" || lower === "ffffffff-ffff-ffff-ffff-ffffffffffff") return {
834
+ success: true,
835
+ value: input
836
+ };
837
+ const versionChar = input.charAt(14);
838
+ const version = parseInt(versionChar, 16);
822
839
  if (allowedVersions != null && allowedVersions.length > 0) {
823
- const versionChar = input.charAt(14);
824
- const version = parseInt(versionChar, 16);
825
840
  if (!allowedVersions.includes(version)) return {
826
841
  success: false,
827
842
  error: disallowedVersion ? typeof disallowedVersion === "function" ? disallowedVersion(version, allowedVersions) : disallowedVersion : (() => {
@@ -834,6 +849,25 @@ function uuid(options = {}) {
834
849
  return message`Expected UUID version ${expectedVersions}, but got version ${version.toLocaleString("en")}.`;
835
850
  })()
836
851
  };
852
+ } else if (strict && (version < 1 || version > 8)) return {
853
+ success: false,
854
+ error: disallowedVersion ? typeof disallowedVersion === "function" ? disallowedVersion(version, [
855
+ 1,
856
+ 2,
857
+ 3,
858
+ 4,
859
+ 5,
860
+ 6,
861
+ 7,
862
+ 8
863
+ ]) : disallowedVersion : message`Expected UUID version 1 through 8, but got version ${version.toLocaleString("en")}.`
864
+ };
865
+ if (strict) {
866
+ const variantChar = input.charAt(19).toLowerCase();
867
+ if (variantChar !== "8" && variantChar !== "9" && variantChar !== "a" && variantChar !== "b") return {
868
+ success: false,
869
+ error: invalidVariant ? typeof invalidVariant === "function" ? invalidVariant(input) : invalidVariant : message`Expected RFC 9562 variant (8, 9, a, or b at position 20), but got ${variantChar} in ${input}.`
870
+ };
837
871
  }
838
872
  return {
839
873
  success: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.0.0-dev.1200+810bacf1",
3
+ "version": "1.0.0-dev.1209+d4c6ddee",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",