@optique/core 1.0.0-dev.1458 → 1.0.0-dev.1461

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.
@@ -2636,6 +2636,90 @@ function compressIpv6(groups) {
2636
2636
  else return before.join(":") + "::" + after.join(":");
2637
2637
  }
2638
2638
  /**
2639
+ * Extracts IPv4 octets from an IPv4-mapped IPv6 address.
2640
+ * Returns null if the address is not an IPv4-mapped address
2641
+ * (i.e., not in the `::ffff:x.x.x.x` range).
2642
+ */
2643
+ function extractIpv4FromMapped(normalizedIpv6) {
2644
+ const groups = expandIpv6(normalizedIpv6);
2645
+ if (groups === null) return null;
2646
+ for (let i = 0; i < 5; i++) if (parseInt(groups[i], 16) !== 0) return null;
2647
+ if (parseInt(groups[5], 16) !== 65535) return null;
2648
+ const high = parseInt(groups[6], 16);
2649
+ const low = parseInt(groups[7], 16);
2650
+ return [
2651
+ high >> 8 & 255,
2652
+ high & 255,
2653
+ low >> 8 & 255,
2654
+ low & 255
2655
+ ];
2656
+ }
2657
+ /**
2658
+ * Checks IPv4 restrictions against octets extracted from an IPv4-mapped
2659
+ * IPv6 address. The check uses the base address, consistent with how
2660
+ * the `ipv4()` parser validates the address part of a regular IPv4 CIDR.
2661
+ *
2662
+ * Returns an error result if a restriction is violated, or null if all
2663
+ * checks pass.
2664
+ */
2665
+ function checkIpv4MappedRestrictions(octets, normalizedIpv6, ipv4Opts, errors) {
2666
+ const allowPrivate = ipv4Opts?.allowPrivate ?? true;
2667
+ const allowLoopback = ipv4Opts?.allowLoopback ?? true;
2668
+ const allowLinkLocal = ipv4Opts?.allowLinkLocal ?? true;
2669
+ const allowMulticast = ipv4Opts?.allowMulticast ?? true;
2670
+ const allowBroadcast = ipv4Opts?.allowBroadcast ?? true;
2671
+ const allowZero = ipv4Opts?.allowZero ?? true;
2672
+ if (!allowPrivate && isPrivateIp(octets)) {
2673
+ const errorMsg = errors?.privateNotAllowed;
2674
+ const msg = typeof errorMsg === "function" ? errorMsg(normalizedIpv6) : errorMsg ?? require_message.message`${normalizedIpv6} is a private IP address.`;
2675
+ return {
2676
+ success: false,
2677
+ error: msg
2678
+ };
2679
+ }
2680
+ if (!allowLoopback && isLoopbackIp(octets)) {
2681
+ const errorMsg = errors?.loopbackNotAllowed;
2682
+ const msg = typeof errorMsg === "function" ? errorMsg(normalizedIpv6) : errorMsg ?? require_message.message`${normalizedIpv6} is a loopback address.`;
2683
+ return {
2684
+ success: false,
2685
+ error: msg
2686
+ };
2687
+ }
2688
+ if (!allowLinkLocal && isLinkLocalIp(octets)) {
2689
+ const errorMsg = errors?.linkLocalNotAllowed;
2690
+ const msg = typeof errorMsg === "function" ? errorMsg(normalizedIpv6) : errorMsg ?? require_message.message`${normalizedIpv6} is a link-local address.`;
2691
+ return {
2692
+ success: false,
2693
+ error: msg
2694
+ };
2695
+ }
2696
+ if (!allowMulticast && isMulticastIp(octets)) {
2697
+ const errorMsg = errors?.multicastNotAllowed;
2698
+ const msg = typeof errorMsg === "function" ? errorMsg(normalizedIpv6) : errorMsg ?? require_message.message`${normalizedIpv6} is a multicast address.`;
2699
+ return {
2700
+ success: false,
2701
+ error: msg
2702
+ };
2703
+ }
2704
+ if (!allowBroadcast && isBroadcastIp(octets)) {
2705
+ const errorMsg = errors?.broadcastNotAllowed;
2706
+ const msg = typeof errorMsg === "function" ? errorMsg(normalizedIpv6) : errorMsg ?? require_message.message`${normalizedIpv6} is the broadcast address.`;
2707
+ return {
2708
+ success: false,
2709
+ error: msg
2710
+ };
2711
+ }
2712
+ if (!allowZero && isZeroIp(octets)) {
2713
+ const errorMsg = errors?.zeroNotAllowed;
2714
+ const msg = typeof errorMsg === "function" ? errorMsg(normalizedIpv6) : errorMsg ?? require_message.message`${normalizedIpv6} is the zero address.`;
2715
+ return {
2716
+ success: false,
2717
+ error: msg
2718
+ };
2719
+ }
2720
+ return null;
2721
+ }
2722
+ /**
2639
2723
  * Creates a value parser that accepts both IPv4 and IPv6 addresses.
2640
2724
  *
2641
2725
  * By default, accepts both IPv4 and IPv6 addresses. Use the `version` option
@@ -2688,6 +2772,22 @@ function ip(options) {
2688
2772
  zeroNotAllowed: errors?.zeroNotAllowed
2689
2773
  }
2690
2774
  }) : null;
2775
+ const mappedIpv4Opts = version === "both" ? {
2776
+ allowPrivate: options?.ipv4?.allowPrivate,
2777
+ allowLoopback: options?.ipv4?.allowLoopback,
2778
+ allowLinkLocal: options?.ipv4?.allowLinkLocal,
2779
+ allowMulticast: options?.ipv4?.allowMulticast,
2780
+ allowBroadcast: options?.ipv4?.allowBroadcast,
2781
+ allowZero: options?.ipv4?.allowZero
2782
+ } : void 0;
2783
+ const mappedIpv4Errors = version === "both" ? {
2784
+ privateNotAllowed: errors?.privateNotAllowed,
2785
+ loopbackNotAllowed: errors?.loopbackNotAllowed,
2786
+ linkLocalNotAllowed: errors?.linkLocalNotAllowed,
2787
+ multicastNotAllowed: errors?.multicastNotAllowed,
2788
+ broadcastNotAllowed: errors?.broadcastNotAllowed,
2789
+ zeroNotAllowed: errors?.zeroNotAllowed
2790
+ } : void 0;
2691
2791
  return {
2692
2792
  $mode: "sync",
2693
2793
  metavar,
@@ -2702,7 +2802,16 @@ function ip(options) {
2702
2802
  }
2703
2803
  if (ipv6Parser !== null) {
2704
2804
  const result = ipv6Parser.parse(input);
2705
- if (result.success) return result;
2805
+ if (result.success) {
2806
+ if (version === "both") {
2807
+ const mappedOctets = extractIpv4FromMapped(result.value);
2808
+ if (mappedOctets !== null) {
2809
+ const restrictionError = checkIpv4MappedRestrictions(mappedOctets, result.value, mappedIpv4Opts, mappedIpv4Errors);
2810
+ if (restrictionError !== null) return restrictionError;
2811
+ }
2812
+ }
2813
+ return result;
2814
+ }
2706
2815
  ipv6Error = result;
2707
2816
  if (version === 6) return result;
2708
2817
  }
@@ -2804,6 +2913,22 @@ function cidr(options) {
2804
2913
  uniqueLocalNotAllowed: errors?.uniqueLocalNotAllowed
2805
2914
  }
2806
2915
  }) : null;
2916
+ const mappedIpv4Opts = version === "both" ? {
2917
+ allowPrivate: options?.ipv4?.allowPrivate,
2918
+ allowLoopback: options?.ipv4?.allowLoopback,
2919
+ allowLinkLocal: options?.ipv4?.allowLinkLocal,
2920
+ allowMulticast: options?.ipv4?.allowMulticast,
2921
+ allowBroadcast: options?.ipv4?.allowBroadcast,
2922
+ allowZero: options?.ipv4?.allowZero
2923
+ } : void 0;
2924
+ const mappedIpv4Errors = version === "both" ? {
2925
+ privateNotAllowed: errors?.privateNotAllowed,
2926
+ loopbackNotAllowed: errors?.loopbackNotAllowed,
2927
+ linkLocalNotAllowed: errors?.linkLocalNotAllowed,
2928
+ multicastNotAllowed: errors?.multicastNotAllowed,
2929
+ broadcastNotAllowed: errors?.broadcastNotAllowed,
2930
+ zeroNotAllowed: errors?.zeroNotAllowed
2931
+ } : void 0;
2807
2932
  return {
2808
2933
  $mode: "sync",
2809
2934
  metavar,
@@ -3148,6 +3273,13 @@ function cidr(options) {
3148
3273
  error: msg
3149
3274
  };
3150
3275
  }
3276
+ if (version === "both" && ipVersion === 6 && normalizedIp !== null) {
3277
+ const mappedOctets = extractIpv4FromMapped(normalizedIp);
3278
+ if (mappedOctets !== null) {
3279
+ const restrictionError = checkIpv4MappedRestrictions(mappedOctets, normalizedIp, mappedIpv4Opts, mappedIpv4Errors);
3280
+ if (restrictionError !== null) return restrictionError;
3281
+ }
3282
+ }
3151
3283
  return {
3152
3284
  success: true,
3153
3285
  value: {
@@ -2636,6 +2636,90 @@ function compressIpv6(groups) {
2636
2636
  else return before.join(":") + "::" + after.join(":");
2637
2637
  }
2638
2638
  /**
2639
+ * Extracts IPv4 octets from an IPv4-mapped IPv6 address.
2640
+ * Returns null if the address is not an IPv4-mapped address
2641
+ * (i.e., not in the `::ffff:x.x.x.x` range).
2642
+ */
2643
+ function extractIpv4FromMapped(normalizedIpv6) {
2644
+ const groups = expandIpv6(normalizedIpv6);
2645
+ if (groups === null) return null;
2646
+ for (let i = 0; i < 5; i++) if (parseInt(groups[i], 16) !== 0) return null;
2647
+ if (parseInt(groups[5], 16) !== 65535) return null;
2648
+ const high = parseInt(groups[6], 16);
2649
+ const low = parseInt(groups[7], 16);
2650
+ return [
2651
+ high >> 8 & 255,
2652
+ high & 255,
2653
+ low >> 8 & 255,
2654
+ low & 255
2655
+ ];
2656
+ }
2657
+ /**
2658
+ * Checks IPv4 restrictions against octets extracted from an IPv4-mapped
2659
+ * IPv6 address. The check uses the base address, consistent with how
2660
+ * the `ipv4()` parser validates the address part of a regular IPv4 CIDR.
2661
+ *
2662
+ * Returns an error result if a restriction is violated, or null if all
2663
+ * checks pass.
2664
+ */
2665
+ function checkIpv4MappedRestrictions(octets, normalizedIpv6, ipv4Opts, errors) {
2666
+ const allowPrivate = ipv4Opts?.allowPrivate ?? true;
2667
+ const allowLoopback = ipv4Opts?.allowLoopback ?? true;
2668
+ const allowLinkLocal = ipv4Opts?.allowLinkLocal ?? true;
2669
+ const allowMulticast = ipv4Opts?.allowMulticast ?? true;
2670
+ const allowBroadcast = ipv4Opts?.allowBroadcast ?? true;
2671
+ const allowZero = ipv4Opts?.allowZero ?? true;
2672
+ if (!allowPrivate && isPrivateIp(octets)) {
2673
+ const errorMsg = errors?.privateNotAllowed;
2674
+ const msg = typeof errorMsg === "function" ? errorMsg(normalizedIpv6) : errorMsg ?? message`${normalizedIpv6} is a private IP address.`;
2675
+ return {
2676
+ success: false,
2677
+ error: msg
2678
+ };
2679
+ }
2680
+ if (!allowLoopback && isLoopbackIp(octets)) {
2681
+ const errorMsg = errors?.loopbackNotAllowed;
2682
+ const msg = typeof errorMsg === "function" ? errorMsg(normalizedIpv6) : errorMsg ?? message`${normalizedIpv6} is a loopback address.`;
2683
+ return {
2684
+ success: false,
2685
+ error: msg
2686
+ };
2687
+ }
2688
+ if (!allowLinkLocal && isLinkLocalIp(octets)) {
2689
+ const errorMsg = errors?.linkLocalNotAllowed;
2690
+ const msg = typeof errorMsg === "function" ? errorMsg(normalizedIpv6) : errorMsg ?? message`${normalizedIpv6} is a link-local address.`;
2691
+ return {
2692
+ success: false,
2693
+ error: msg
2694
+ };
2695
+ }
2696
+ if (!allowMulticast && isMulticastIp(octets)) {
2697
+ const errorMsg = errors?.multicastNotAllowed;
2698
+ const msg = typeof errorMsg === "function" ? errorMsg(normalizedIpv6) : errorMsg ?? message`${normalizedIpv6} is a multicast address.`;
2699
+ return {
2700
+ success: false,
2701
+ error: msg
2702
+ };
2703
+ }
2704
+ if (!allowBroadcast && isBroadcastIp(octets)) {
2705
+ const errorMsg = errors?.broadcastNotAllowed;
2706
+ const msg = typeof errorMsg === "function" ? errorMsg(normalizedIpv6) : errorMsg ?? message`${normalizedIpv6} is the broadcast address.`;
2707
+ return {
2708
+ success: false,
2709
+ error: msg
2710
+ };
2711
+ }
2712
+ if (!allowZero && isZeroIp(octets)) {
2713
+ const errorMsg = errors?.zeroNotAllowed;
2714
+ const msg = typeof errorMsg === "function" ? errorMsg(normalizedIpv6) : errorMsg ?? message`${normalizedIpv6} is the zero address.`;
2715
+ return {
2716
+ success: false,
2717
+ error: msg
2718
+ };
2719
+ }
2720
+ return null;
2721
+ }
2722
+ /**
2639
2723
  * Creates a value parser that accepts both IPv4 and IPv6 addresses.
2640
2724
  *
2641
2725
  * By default, accepts both IPv4 and IPv6 addresses. Use the `version` option
@@ -2688,6 +2772,22 @@ function ip(options) {
2688
2772
  zeroNotAllowed: errors?.zeroNotAllowed
2689
2773
  }
2690
2774
  }) : null;
2775
+ const mappedIpv4Opts = version === "both" ? {
2776
+ allowPrivate: options?.ipv4?.allowPrivate,
2777
+ allowLoopback: options?.ipv4?.allowLoopback,
2778
+ allowLinkLocal: options?.ipv4?.allowLinkLocal,
2779
+ allowMulticast: options?.ipv4?.allowMulticast,
2780
+ allowBroadcast: options?.ipv4?.allowBroadcast,
2781
+ allowZero: options?.ipv4?.allowZero
2782
+ } : void 0;
2783
+ const mappedIpv4Errors = version === "both" ? {
2784
+ privateNotAllowed: errors?.privateNotAllowed,
2785
+ loopbackNotAllowed: errors?.loopbackNotAllowed,
2786
+ linkLocalNotAllowed: errors?.linkLocalNotAllowed,
2787
+ multicastNotAllowed: errors?.multicastNotAllowed,
2788
+ broadcastNotAllowed: errors?.broadcastNotAllowed,
2789
+ zeroNotAllowed: errors?.zeroNotAllowed
2790
+ } : void 0;
2691
2791
  return {
2692
2792
  $mode: "sync",
2693
2793
  metavar,
@@ -2702,7 +2802,16 @@ function ip(options) {
2702
2802
  }
2703
2803
  if (ipv6Parser !== null) {
2704
2804
  const result = ipv6Parser.parse(input);
2705
- if (result.success) return result;
2805
+ if (result.success) {
2806
+ if (version === "both") {
2807
+ const mappedOctets = extractIpv4FromMapped(result.value);
2808
+ if (mappedOctets !== null) {
2809
+ const restrictionError = checkIpv4MappedRestrictions(mappedOctets, result.value, mappedIpv4Opts, mappedIpv4Errors);
2810
+ if (restrictionError !== null) return restrictionError;
2811
+ }
2812
+ }
2813
+ return result;
2814
+ }
2706
2815
  ipv6Error = result;
2707
2816
  if (version === 6) return result;
2708
2817
  }
@@ -2804,6 +2913,22 @@ function cidr(options) {
2804
2913
  uniqueLocalNotAllowed: errors?.uniqueLocalNotAllowed
2805
2914
  }
2806
2915
  }) : null;
2916
+ const mappedIpv4Opts = version === "both" ? {
2917
+ allowPrivate: options?.ipv4?.allowPrivate,
2918
+ allowLoopback: options?.ipv4?.allowLoopback,
2919
+ allowLinkLocal: options?.ipv4?.allowLinkLocal,
2920
+ allowMulticast: options?.ipv4?.allowMulticast,
2921
+ allowBroadcast: options?.ipv4?.allowBroadcast,
2922
+ allowZero: options?.ipv4?.allowZero
2923
+ } : void 0;
2924
+ const mappedIpv4Errors = version === "both" ? {
2925
+ privateNotAllowed: errors?.privateNotAllowed,
2926
+ loopbackNotAllowed: errors?.loopbackNotAllowed,
2927
+ linkLocalNotAllowed: errors?.linkLocalNotAllowed,
2928
+ multicastNotAllowed: errors?.multicastNotAllowed,
2929
+ broadcastNotAllowed: errors?.broadcastNotAllowed,
2930
+ zeroNotAllowed: errors?.zeroNotAllowed
2931
+ } : void 0;
2807
2932
  return {
2808
2933
  $mode: "sync",
2809
2934
  metavar,
@@ -3148,6 +3273,13 @@ function cidr(options) {
3148
3273
  error: msg
3149
3274
  };
3150
3275
  }
3276
+ if (version === "both" && ipVersion === 6 && normalizedIp !== null) {
3277
+ const mappedOctets = extractIpv4FromMapped(normalizedIp);
3278
+ if (mappedOctets !== null) {
3279
+ const restrictionError = checkIpv4MappedRestrictions(mappedOctets, normalizedIp, mappedIpv4Opts, mappedIpv4Errors);
3280
+ if (restrictionError !== null) return restrictionError;
3281
+ }
3282
+ }
3151
3283
  return {
3152
3284
  success: true,
3153
3285
  value: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.0.0-dev.1458+3f4a79f6",
3
+ "version": "1.0.0-dev.1461+fd58e837",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",