@optique/core 1.3.0-dev.2585 → 1.3.0-dev.2589

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.
@@ -2756,6 +2756,18 @@ function url(options = {}) {
2756
2756
  */
2757
2757
  const IPV4_PATTERN = /^\d{1,3}(?:\.\d{1,3}){3}$/u;
2758
2758
  /**
2759
+ * Whether a URL scheme can yield a tuple origin. Opaque schemes such as
2760
+ * `file:`, `blob:`, `mailto:`, and `data:` cannot, and an origin parser can
2761
+ * never accept them, so they are rejected when listed in `allowedProtocols`.
2762
+ */
2763
+ function canProduceOrigin(protocol) {
2764
+ try {
2765
+ return new URL(`${protocol}//0.invalid`).origin !== "null";
2766
+ } catch {
2767
+ return false;
2768
+ }
2769
+ }
2770
+ /**
2759
2771
  * Creates a {@link ValueParser} for web origins.
2760
2772
  *
2761
2773
  * An origin is a scheme, a host, and an optional port, and nothing else. The
@@ -2804,6 +2816,7 @@ function origin(options = {}) {
2804
2816
  const rendered = typeof protocol === "string" ? JSON.stringify(protocol) : String(protocol);
2805
2817
  throw new TypeError(`Each allowed protocol must be a valid protocol ending with a colon (e.g., "https:"), got: ${rendered}.`);
2806
2818
  }
2819
+ if (!canProduceOrigin(protocol)) throw new TypeError(`Each allowed protocol must name a scheme that can produce an origin, but ${JSON.stringify(protocol)} cannot.`);
2807
2820
  const normalized = protocol.toLowerCase();
2808
2821
  if (seen.has(normalized)) continue;
2809
2822
  seen.add(normalized);
@@ -2870,7 +2883,7 @@ function origin(options = {}) {
2870
2883
  success: false,
2871
2884
  error: require_message.message`An origin must not contain credentials: ${url$1.origin}`
2872
2885
  };
2873
- if (extraComponents === "reject" && (url$1.pathname !== "/" || url$1.search !== "" || url$1.hash !== "")) return {
2886
+ if (extraComponents === "reject" && (url$1.pathname !== "/" || url$1.search !== "" || url$1.hash !== "" || url$1.href !== new URL(url$1.origin).href)) return {
2874
2887
  success: false,
2875
2888
  error: require_message.message`Expected only an origin, but got extra components on ${url$1.origin}.`
2876
2889
  };
@@ -1191,6 +1191,8 @@ interface UrlOptions {
1191
1191
  declare function url(options?: UrlOptions): ValueParser<"sync", URL>;
1192
1192
  /**
1193
1193
  * Options for creating an {@link origin} parser.
1194
+ *
1195
+ * @since 1.3.0
1194
1196
  */
1195
1197
  interface OriginOptions {
1196
1198
  /**
@@ -1203,7 +1205,9 @@ interface OriginOptions {
1203
1205
  /**
1204
1206
  * List of allowed URL protocols (e.g., `["http:", "https:"]`).
1205
1207
  * If specified, the parsed URL must use one of these protocols.
1206
- * Protocol names should include the trailing colon (e.g., `"https:"`).
1208
+ * Protocol names should include the trailing colon (e.g., `"https:"`), and
1209
+ * each must name a scheme that can produce an origin; opaque schemes such as
1210
+ * `file:` and `blob:` are rejected at construction.
1207
1211
  * If not specified, any protocol with a tuple origin is allowed.
1208
1212
  */
1209
1213
  readonly allowedProtocols?: readonly string[];
@@ -1212,8 +1216,8 @@ interface OriginOptions {
1212
1216
  *
1213
1217
  * - `"strip"`: return just the origin, discarding any path, query, or
1214
1218
  * fragment.
1215
- * - `"reject"`: fail when the input has a path other than `/`, a query, or
1216
- * a fragment.
1219
+ * - `"reject"`: fail when the input has a path other than `/`, or a query
1220
+ * or fragment delimiter (including an empty `?` or `#`).
1217
1221
  *
1218
1222
  * Credentials are always rejected, under either setting.
1219
1223
  * @default `"strip"`
@@ -1191,6 +1191,8 @@ interface UrlOptions {
1191
1191
  declare function url(options?: UrlOptions): ValueParser<"sync", URL>;
1192
1192
  /**
1193
1193
  * Options for creating an {@link origin} parser.
1194
+ *
1195
+ * @since 1.3.0
1194
1196
  */
1195
1197
  interface OriginOptions {
1196
1198
  /**
@@ -1203,7 +1205,9 @@ interface OriginOptions {
1203
1205
  /**
1204
1206
  * List of allowed URL protocols (e.g., `["http:", "https:"]`).
1205
1207
  * If specified, the parsed URL must use one of these protocols.
1206
- * Protocol names should include the trailing colon (e.g., `"https:"`).
1208
+ * Protocol names should include the trailing colon (e.g., `"https:"`), and
1209
+ * each must name a scheme that can produce an origin; opaque schemes such as
1210
+ * `file:` and `blob:` are rejected at construction.
1207
1211
  * If not specified, any protocol with a tuple origin is allowed.
1208
1212
  */
1209
1213
  readonly allowedProtocols?: readonly string[];
@@ -1212,8 +1216,8 @@ interface OriginOptions {
1212
1216
  *
1213
1217
  * - `"strip"`: return just the origin, discarding any path, query, or
1214
1218
  * fragment.
1215
- * - `"reject"`: fail when the input has a path other than `/`, a query, or
1216
- * a fragment.
1219
+ * - `"reject"`: fail when the input has a path other than `/`, or a query
1220
+ * or fragment delimiter (including an empty `?` or `#`).
1217
1221
  *
1218
1222
  * Credentials are always rejected, under either setting.
1219
1223
  * @default `"strip"`
@@ -2756,6 +2756,18 @@ function url(options = {}) {
2756
2756
  */
2757
2757
  const IPV4_PATTERN = /^\d{1,3}(?:\.\d{1,3}){3}$/u;
2758
2758
  /**
2759
+ * Whether a URL scheme can yield a tuple origin. Opaque schemes such as
2760
+ * `file:`, `blob:`, `mailto:`, and `data:` cannot, and an origin parser can
2761
+ * never accept them, so they are rejected when listed in `allowedProtocols`.
2762
+ */
2763
+ function canProduceOrigin(protocol) {
2764
+ try {
2765
+ return new URL(`${protocol}//0.invalid`).origin !== "null";
2766
+ } catch {
2767
+ return false;
2768
+ }
2769
+ }
2770
+ /**
2759
2771
  * Creates a {@link ValueParser} for web origins.
2760
2772
  *
2761
2773
  * An origin is a scheme, a host, and an optional port, and nothing else. The
@@ -2804,6 +2816,7 @@ function origin(options = {}) {
2804
2816
  const rendered = typeof protocol === "string" ? JSON.stringify(protocol) : String(protocol);
2805
2817
  throw new TypeError(`Each allowed protocol must be a valid protocol ending with a colon (e.g., "https:"), got: ${rendered}.`);
2806
2818
  }
2819
+ if (!canProduceOrigin(protocol)) throw new TypeError(`Each allowed protocol must name a scheme that can produce an origin, but ${JSON.stringify(protocol)} cannot.`);
2807
2820
  const normalized = protocol.toLowerCase();
2808
2821
  if (seen.has(normalized)) continue;
2809
2822
  seen.add(normalized);
@@ -2870,7 +2883,7 @@ function origin(options = {}) {
2870
2883
  success: false,
2871
2884
  error: message`An origin must not contain credentials: ${url$1.origin}`
2872
2885
  };
2873
- if (extraComponents === "reject" && (url$1.pathname !== "/" || url$1.search !== "" || url$1.hash !== "")) return {
2886
+ if (extraComponents === "reject" && (url$1.pathname !== "/" || url$1.search !== "" || url$1.hash !== "" || url$1.href !== new URL(url$1.origin).href)) return {
2874
2887
  success: false,
2875
2888
  error: message`Expected only an origin, but got extra components on ${url$1.origin}.`
2876
2889
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.3.0-dev.2585",
3
+ "version": "1.3.0-dev.2589",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -237,7 +237,7 @@
237
237
  },
238
238
  "sideEffects": false,
239
239
  "devDependencies": {
240
- "@optique/env": "1.3.0-dev.2585+77a42c40",
240
+ "@optique/env": "1.3.0-dev.2589+3608b288",
241
241
  "@types/node": "^24.0.0",
242
242
  "fast-check": "^4.7.0",
243
243
  "tsdown": "^0.13.0",