shelving 1.167.2 → 1.168.0

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/util/format.d.ts CHANGED
@@ -2,7 +2,7 @@ import { type ImmutableArray } from "./array.js";
2
2
  import { type PossibleDate } from "./date.js";
3
3
  import { type Duration } from "./duration.js";
4
4
  import { type ImmutableObject } from "./object.js";
5
- import { type PossibleURL } from "./url.js";
5
+ import { type PossibleURI } from "./uri.js";
6
6
  /** Options we use for number formatting. */
7
7
  export type NumberOptions = Omit<Intl.NumberFormatOptions, "style" | "unit" | "unitDisplay" | "currency" | "currencyDisplay" | "currencySign">;
8
8
  /** Format a number (based on the user's browser language settings). */
@@ -73,7 +73,7 @@ export declare function formatTime(time?: PossibleDate, options?: Intl.DateTimeF
73
73
  /** Format a datetime in the browser locale (no seconds by default). */
74
74
  export declare function formatDateTime(date: PossibleDate, options?: Intl.DateTimeFormatOptions): string;
75
75
  /** Format a URL as a user-friendly string, e.g. `http://shax.com/test?uid=129483` → `shax.com/test` */
76
- export declare function formatURL(possible: PossibleURL, base?: PossibleURL): string;
76
+ export declare function formatURI(possible: PossibleURI): string;
77
77
  /**
78
78
  * Convert any unknown value into a friendly string for user-facing use.
79
79
  * - Strings return the string.
package/util/format.js CHANGED
@@ -5,7 +5,7 @@ import { getBestTimeUnit, getMilliseconds } from "./duration.js";
5
5
  import { getPercent } from "./number.js";
6
6
  import { isObject } from "./object.js";
7
7
  import { TIME_UNITS } from "./units.js";
8
- import { requireURL } from "./url.js";
8
+ import { isURI, requireURI } from "./uri.js";
9
9
  /** Format a number (based on the user's browser language settings). */
10
10
  export function formatNumber(num, options) {
11
11
  return Intl.NumberFormat(undefined, options).format(num);
@@ -110,9 +110,9 @@ export function formatDateTime(date, options) {
110
110
  });
111
111
  }
112
112
  /** Format a URL as a user-friendly string, e.g. `http://shax.com/test?uid=129483` → `shax.com/test` */
113
- export function formatURL(possible, base) {
114
- const { host, pathname } = requireURL(possible, base, formatURL);
115
- return `${host}${pathname.length > 1 ? pathname : ""}`;
113
+ export function formatURI(possible) {
114
+ const { host, pathname } = requireURI(possible, formatURI);
115
+ return `${host}${pathname !== "/" ? pathname : ""}`;
116
116
  }
117
117
  /**
118
118
  * Convert any unknown value into a friendly string for user-facing use.
@@ -146,6 +146,8 @@ export function formatValue(value) {
146
146
  return formatArray(value);
147
147
  if (isObject(value))
148
148
  return formatObject(value);
149
+ if (isURI(value))
150
+ return formatURI(value);
149
151
  return "Unknown";
150
152
  }
151
153
  /**
package/util/index.d.ts CHANGED
@@ -34,7 +34,6 @@ export * from "./iterate.js";
34
34
  export * from "./jsx.js";
35
35
  export * from "./jwt.js";
36
36
  export * from "./lazy.js";
37
- export * from "./link.js";
38
37
  export * from "./map.js";
39
38
  export * from "./merge.js";
40
39
  export * from "./null.js";
@@ -56,6 +55,7 @@ export * from "./transform.js";
56
55
  export * from "./undefined.js";
57
56
  export * from "./units.js";
58
57
  export * from "./update.js";
58
+ export * from "./uri.js";
59
59
  export * from "./url.js";
60
60
  export * from "./uuid.js";
61
61
  export * from "./validate.js";
package/util/index.js CHANGED
@@ -34,7 +34,6 @@ export * from "./iterate.js";
34
34
  export * from "./jsx.js";
35
35
  export * from "./jwt.js";
36
36
  export * from "./lazy.js";
37
- export * from "./link.js";
38
37
  export * from "./map.js";
39
38
  export * from "./merge.js";
40
39
  export * from "./null.js";
@@ -56,6 +55,7 @@ export * from "./transform.js";
56
55
  export * from "./undefined.js";
57
56
  export * from "./units.js";
58
57
  export * from "./update.js";
58
+ export * from "./uri.js";
59
59
  export * from "./url.js";
60
60
  export * from "./uuid.js";
61
61
  export * from "./validate.js";
package/util/uri.d.ts ADDED
@@ -0,0 +1,94 @@
1
+ import type { ImmutableArray } from "./array.js";
2
+ import { type ImmutableDictionary } from "./dictionary.js";
3
+ import type { AnyCaller } from "./function.js";
4
+ import { type Nullish } from "./null.js";
5
+ import type { URL, URLString } from "./url.js";
6
+ /**
7
+ * Valid URI string is anything following `protocol:resource` format, e.g. `urn:isbn:0451450523` or `http://example.com/path/to/resource`
8
+ *
9
+ * URI and URL differences:
10
+ * - According to RFC 3986, URLs are a subset of URIs that have a hierarchical path component, e.g. `http://example.com/path`.
11
+ * - The `//` at the start of a URL indicates that it has a hierarchical path component, so this makes it a URL.
12
+ * - The absence of `//` indicates a non-hierarchical URI.
13
+ * - URLs can be considered as "hierarchical URIs".
14
+ * - All URLs are also URIs, but not all URIs are URLs.
15
+ */
16
+ export type URIString = `${string}:${string}`;
17
+ /**
18
+ * Object that describes a valid URI, e.g. `urn:isbn:0451450523` or `http://example.com/path/to/resource`
19
+ * - Improves the builtin Javascript `URL` class to more accurately type its properties.
20
+ *
21
+ * URI and URL differences:
22
+ * - According to RFC 3986, URLs are a subset of URIs that have a hierarchical path component, e.g. `http://example.com/path`.
23
+ * - The `//` at the start of a URL indicates that it has a hierarchical path component, so this makes it a URL.
24
+ * - The absence of `//` indicates a non-hierarchical URI.
25
+ * - URLs can be considered as "hierarchical URIs".
26
+ * - All URLs are also URIs, but not all URIs are URLs.
27
+ *
28
+ * Javascript URL problems:
29
+ * - Javascript `URL` instance can actually represent any kind of URI (not just URLs).
30
+ * - It's more "correct" terminology to use `URI` to refer to what the Javascript `URL` class represents.
31
+ * - You can tell the difference because a URL will have a non-empty `host` property, whereas URIs will never have a `host` (it will be `""` empty string).
32
+ */
33
+ export interface URI extends globalThis.URL {
34
+ protocol: URIScheme;
35
+ href: URIString;
36
+ }
37
+ /**
38
+ * Construct a correctly-typed `URI` object.
39
+ * - This is a more correctly typed version of the builtin Javascript `URI` constructor.
40
+ * - Requires a URI string, URI object, or path as input, and optionally a base URI.
41
+ * - If a path is provided as input, a base URI _must_ also be provided.
42
+ * - The returned type is
43
+ */
44
+ export interface URIConstructor {
45
+ new (input: URIString | URI): URI;
46
+ }
47
+ export declare const URI: URIConstructor;
48
+ /** Values that can be converted to a URI instance. */
49
+ export type PossibleURI = string | globalThis.URL;
50
+ /** Is an unknown value a URI object? */
51
+ export declare function isURI(value: unknown): value is URI;
52
+ /** Assert that an unknown value is a URI object. */
53
+ export declare function assertURI(value: unknown, caller?: AnyCaller): asserts value is URI;
54
+ /** Convert a possible URI to a URI, or return `undefined` if conversion fails. */
55
+ export declare function getURI(possible: Nullish<PossibleURI>): URI | undefined;
56
+ /** Convert a possible URI to a URI, or throw `RequiredError` if conversion fails. */
57
+ export declare function requireURI(possible: PossibleURI, caller?: AnyCaller): URI;
58
+ /** Convert a possible URI to a URI string, or return `undefined` if conversion fails. */
59
+ export declare function getURIString(possible: Nullish<PossibleURI>): URIString | undefined;
60
+ /** Convert a possible URI to a URI string, or throw `RequiredError` if conversion fails. */
61
+ export declare function requireURIString(possible: PossibleURI, caller?: AnyCaller): URIString | undefined;
62
+ /** Type for a set of named URL parameters. */
63
+ export type URIParams = ImmutableDictionary<string>;
64
+ /** Type for things that can be converted to named URI parameters. */
65
+ export type PossibleURIParams = PossibleURI | URLSearchParams | ImmutableDictionary<unknown>;
66
+ /** Get a set of params for a URI as a dictionary. */
67
+ export declare function getURIParams(input: PossibleURIParams, caller?: AnyCaller): URIParams;
68
+ /** Get a single named param from a URI. */
69
+ export declare function getURIParam(input: PossibleURIParams, key: string): string | undefined;
70
+ /** Get a single named param from a URI. */
71
+ export declare function requireURIParam(input: PossibleURIParams, key: string, caller?: AnyCaller): string;
72
+ /**
73
+ * Return a URI with a new param set (or same URI if no changes were made).
74
+ * - Throws `ValueError` if the value could not be converted to a string.
75
+ */
76
+ export declare function withURIParam(url: URL | URLString, key: string, value: unknown, caller?: AnyCaller): URL;
77
+ export declare function withURIParam(url: PossibleURI, key: string, value: unknown, caller?: AnyCaller): URI;
78
+ /**
79
+ * Return a URI with several new params set (or same URI if no changes were made).
80
+ * - Throws `ValueError` if any of the values could not be converted to strings.
81
+ */
82
+ export declare function withURIParams(url: URL | URLString, params: PossibleURIParams, caller?: AnyCaller): URL;
83
+ export declare function withURIParams(url: PossibleURI, params: PossibleURIParams, caller?: AnyCaller): URI;
84
+ /** Return a URI without one or more params (or same URI if no changes were made). */
85
+ export declare function omitURIParams(url: URL | URLString, ...keys: string[]): URL;
86
+ export declare function omitURIParams(url: PossibleURI, ...keys: string[]): URI;
87
+ /** Return a URI without a param (or same URI if no changes were made). */
88
+ export declare const omitURIParam: (url: PossibleURI, key: string) => URI;
89
+ /** A single schema for a URL. */
90
+ export type URIScheme = `${string}:`;
91
+ /** List of allowed URI schemes. */
92
+ export type URISchemes = ImmutableArray<URIScheme>;
93
+ /** Valid HTTP schemes for a URI. */
94
+ export declare const HTTP_SCHEMES: URISchemes;
package/util/uri.js ADDED
@@ -0,0 +1,119 @@
1
+ import { RequiredError } from "../error/RequiredError.js";
2
+ import { ValueError } from "../error/ValueError.js";
3
+ import { getDictionaryItems, isDictionary } from "./dictionary.js";
4
+ import { notNullish } from "./null.js";
5
+ import { getString, isString } from "./string.js";
6
+ export const URI = globalThis.URL;
7
+ /** Is an unknown value a URI object? */
8
+ export function isURI(value) {
9
+ return value instanceof URI;
10
+ }
11
+ /** Assert that an unknown value is a URI object. */
12
+ export function assertURI(value, caller = assertURI) {
13
+ if (!isURI(value))
14
+ throw new RequiredError("Invalid URI", { received: value, caller });
15
+ }
16
+ /** Convert a possible URI to a URI, or return `undefined` if conversion fails. */
17
+ export function getURI(possible) {
18
+ if (notNullish(possible)) {
19
+ if (isURI(possible))
20
+ return possible;
21
+ try {
22
+ return new globalThis.URL(possible);
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ }
29
+ /** Convert a possible URI to a URI, or throw `RequiredError` if conversion fails. */
30
+ export function requireURI(possible, caller = requireURI) {
31
+ const url = getURI(possible);
32
+ assertURI(url, caller);
33
+ return url;
34
+ }
35
+ /** Convert a possible URI to a URI string, or return `undefined` if conversion fails. */
36
+ export function getURIString(possible) {
37
+ return getURI(possible)?.href;
38
+ }
39
+ /** Convert a possible URI to a URI string, or throw `RequiredError` if conversion fails. */
40
+ export function requireURIString(possible, caller = requireURIString) {
41
+ return requireURI(possible, caller).href;
42
+ }
43
+ /**
44
+ * Get a set of entries for a set of possible URI params.
45
+ *
46
+ * Note: Not as simple as just converting with `Object.fromEntries()`:
47
+ * 1. When `URLSearchParams` contains multiple values for the same key, calling `params.get()` will return the _first_ value.
48
+ * 2. So when converting this to a simple data object, only one value per key can be represented, but it needs to be the _first_ one.
49
+ * 3. Since we're looping through anyway, we also take the time to convert values to strings, so we can accept a wider range of input types.
50
+ */
51
+ function* getURIEntries(input, caller = getURIParams) {
52
+ if (input instanceof URLSearchParams) {
53
+ yield* input;
54
+ }
55
+ else if (isString(input) || input instanceof globalThis.URL) {
56
+ yield* requireURI(input, caller).searchParams;
57
+ }
58
+ else {
59
+ const done = [];
60
+ for (const [key, value] of getDictionaryItems(input)) {
61
+ if (done.includes(key))
62
+ continue;
63
+ done.push(key);
64
+ const str = getString(value);
65
+ if (str === undefined)
66
+ throw new ValueError(`URI param "${key}" must be string`, { received: value, caller });
67
+ yield [key, str];
68
+ }
69
+ }
70
+ }
71
+ /** Get a set of params for a URI as a dictionary. */
72
+ export function getURIParams(input, caller = getURIParams) {
73
+ const output = {};
74
+ for (const [key, str] of getURIEntries(input, caller))
75
+ output[key] = str;
76
+ return output;
77
+ }
78
+ /** Get a single named param from a URI. */
79
+ export function getURIParam(input, key) {
80
+ if (input instanceof URLSearchParams)
81
+ return input.get(key) || undefined;
82
+ if (isDictionary(input))
83
+ return getString(input[key]);
84
+ return getURIParams(input)[key];
85
+ }
86
+ /** Get a single named param from a URI. */
87
+ export function requireURIParam(input, key, caller = requireURIParam) {
88
+ const value = getURIParam(input, key);
89
+ if (value === undefined)
90
+ throw new RequiredError(`URI param "${key}" is required`, { received: input, caller });
91
+ return value;
92
+ }
93
+ export function withURIParam(url, key, value, caller = withURIParam) {
94
+ const input = requireURI(url, caller);
95
+ const output = new URI(input);
96
+ const str = getString(value);
97
+ if (str === undefined)
98
+ throw new ValueError(`URI param "${key}" must be string`, { received: value, caller });
99
+ output.searchParams.set(key, str);
100
+ return input.href === output.href ? input : output;
101
+ }
102
+ export function withURIParams(url, params, caller = withURIParams) {
103
+ const input = requireURI(url, caller);
104
+ const output = new URI(input);
105
+ for (const [key, str] of getURIEntries(params, caller))
106
+ output.searchParams.set(key, str);
107
+ return input.href === output.href ? input : output;
108
+ }
109
+ export function omitURIParams(url, ...keys) {
110
+ const input = requireURI(url, omitURIParams);
111
+ const output = new URI(input);
112
+ for (const key of keys)
113
+ output.searchParams.delete(key);
114
+ return input.href === output.href ? input : output;
115
+ }
116
+ /** Return a URI without a param (or same URI if no changes were made). */
117
+ export const omitURIParam = omitURIParams;
118
+ /** Valid HTTP schemes for a URI. */
119
+ export const HTTP_SCHEMES = ["http:", "https:"];
package/util/url.d.ts CHANGED
@@ -1,46 +1,56 @@
1
- import type { DictionaryItem, ImmutableDictionary } from "./dictionary.js";
2
1
  import type { AnyCaller } from "./function.js";
3
2
  import { type Nullish } from "./null.js";
4
- /** Values that can be converted to a URL instance. */
5
- export type PossibleURL = string | URL;
6
- /** Is an unknown value a URL object? */
7
- export declare function isURL(value: unknown): value is URL;
8
- /** Assert that an unknown value is a URL object. */
9
- export declare function assertURL(value: unknown, caller?: AnyCaller): asserts value is URL;
10
- /** Convert a possible URL to a URL, or return `undefined` if conversion fails. */
11
- export declare function getURL(possible: Nullish<PossibleURL>, base?: PossibleURL | undefined): URL | undefined;
12
- /** Convert a possible URL to a URL, or throw `RequiredError` if conversion fails. */
13
- export declare function requireURL(possible: PossibleURL, base?: PossibleURL, caller?: AnyCaller): URL;
14
- /** Type for a set of named URL parameters. */
15
- export type URLParams = ImmutableDictionary<string>;
16
- /** Type for things that can be converted to named URL parameters. */
17
- export type PossibleURLParams = PossibleURL | URLSearchParams | ImmutableDictionary<unknown>;
3
+ import type { AbsolutePath, Path } from "./path.js";
4
+ import type { URI } from "./uri.js";
5
+ /**
6
+ * A URL string has a protocol and a `//`.
7
+ * - The `//` at the start of a URL indicates that it has a hierarchical path component, so this makes it a URL.
8
+ * - URLs have a concept of "absolute" or "relative" URLs, since they have a path.
9
+ */
10
+ export type URLString = `${string}://${string}`;
18
11
  /**
19
- * Get a set of entries for a set of possible URL params.
12
+ * Object that describes a valid URL, e.g. `http://example.com/path/to/resource`
13
+ * - Improves the builtin Javascript `URL` class to more accurately type its properties.
20
14
  *
21
- * Note: Not as simple as just converting with `Object.fromEntries()`:
22
- * 1. When `URLSearchParams` contains multiple values for the same key, calling `params.get()` will return the _first_ value.
23
- * 2. So when converting this to a simple data object, only one value per key can be represented, but it needs to be the _first_ one.
24
- * 3. Since we're looping through anyway, we also take the time to convert values to strings, so we can accept a wider range of input types.
15
+ * URI and URL differences:
16
+ * - According to RFC 3986, URLs are a subset of URIs that have a hierarchical path component, e.g. `http://example.com/path`.
17
+ * - The `//` at the start of a URL indicates that it has a hierarchical path component, so this makes it a URL.
18
+ * - The absence of `//` indicates a non-hierarchical URI.
19
+ * - URLs can be considered as "hierarchical URIs".
20
+ * - All URLs are also URIs, but not all URIs are URLs.
21
+ *
22
+ * Javascript URL problems:
23
+ * - Javascript `URL` instance can actually represent any kind of URI (not just URLs).
24
+ * - It's more "correct" terminology to use `URI` to refer to what the Javascript `URL` class represents.
25
+ * - You can tell the difference because a URL will have a non-empty `host` property, whereas URIs will never have a `host` (it will be `""` empty string).
25
26
  */
26
- export declare function getURLEntries(input: PossibleURLParams, caller?: AnyCaller): Iterable<DictionaryItem<string>>;
27
- /** Get a set of params for a URL as a dictionary. */
28
- export declare function getURLParams(input: PossibleURLParams, caller?: AnyCaller): URLParams;
29
- /** Get a single named param from a URL. */
30
- export declare function getURLParam(input: PossibleURLParams, key: string): string | undefined;
31
- /** Get a single named param from a URL. */
32
- export declare function requireURLParam(input: PossibleURLParams, key: string, caller?: AnyCaller): string;
27
+ export interface URL extends URI {
28
+ href: URLString;
29
+ origin: URLString;
30
+ pathname: AbsolutePath;
31
+ }
33
32
  /**
34
- * Return a URL with a new param set (or same URL if no changes were made).
35
- * - Throws `ValueError` if the value could not be converted to a string.
33
+ * Construct a correctly-typed `URL` object.
34
+ * - This is a more correctly typed version of the builtin Javascript `URL` constructor.
35
+ * - Requires a URL string, URL object, or path as input, and optionally a base URL.
36
+ * - If a path is provided as input, a base URL _must_ also be provided.
37
+ * - The returned type is
36
38
  */
37
- export declare function withURLParam(url: PossibleURL, key: string, value: unknown, caller?: AnyCaller): URL;
39
+ export interface URLConstructor {
40
+ new (input: URLString | URL, base?: URLString | URL): URL;
41
+ new (input: URLString | URL | Path, base: URLString | URL): URL;
42
+ }
43
+ export declare const URL: URLConstructor;
44
+ /** Values that can be converted to a URL instance. */
45
+ export type PossibleURL = string | globalThis.URL;
38
46
  /**
39
- * Return a URL with several new params set (or same URL if no changes were made).
40
- * - Throws `ValueError` if any of the values could not be converted to strings.
47
+ * Is an unknown value a URL object?
48
+ * - Must be a `URL` instance and its origin must start with `scheme://`
41
49
  */
42
- export declare function withURLParams(url: PossibleURL, params: PossibleURLParams, caller?: AnyCaller): URL;
43
- /** Return a URL without one or more params (or same URL if no changes were made). */
44
- export declare function omitURLParams(url: PossibleURL, ...keys: string[]): URL;
45
- /** Return a URL without a param (or same URL if no changes were made). */
46
- export declare const omitURLParam: (url: PossibleURL, key: string) => URL;
50
+ export declare function isURL(value: unknown): value is URL;
51
+ /** Assert that an unknown value is a URL object. */
52
+ export declare function assertURL(value: unknown, caller?: AnyCaller): asserts value is URL;
53
+ /** Convert a possible URL to a URL, or return `undefined` if conversion fails. */
54
+ export declare function getURL(possible: Nullish<PossibleURL>, base?: PossibleURL | undefined): URL | undefined;
55
+ /** Convert a possible URL to a URL, or throw `RequiredError` if conversion fails. */
56
+ export declare function requireURL(possible: PossibleURL, base?: PossibleURL | undefined, caller?: AnyCaller): URL;
package/util/url.js CHANGED
@@ -1,11 +1,15 @@
1
1
  import { RequiredError } from "../error/RequiredError.js";
2
- import { ValueError } from "../error/ValueError.js";
3
- import { getDictionaryItems, isDictionary } from "./dictionary.js";
4
2
  import { notNullish } from "./null.js";
5
- import { getString, isString } from "./string.js";
6
- /** Is an unknown value a URL object? */
3
+ export const URL = globalThis.URL;
4
+ /**
5
+ * Is an unknown value a URL object?
6
+ * - Must be a `URL` instance and its origin must start with `scheme://`
7
+ */
7
8
  export function isURL(value) {
8
- return value instanceof URL;
9
+ return value instanceof globalThis.URL && _isValidURL(value);
10
+ }
11
+ function _isValidURL(url) {
12
+ return url.href.startsWith(`${url.protocol}//`);
9
13
  }
10
14
  /** Assert that an unknown value is a URL object. */
11
15
  export function assertURL(value, caller = assertURL) {
@@ -15,13 +19,19 @@ export function assertURL(value, caller = assertURL) {
15
19
  /** Convert a possible URL to a URL, or return `undefined` if conversion fails. */
16
20
  export function getURL(possible, base = _BASE) {
17
21
  if (notNullish(possible)) {
18
- if (isURL(possible))
19
- return possible;
20
- try {
21
- return new URL(possible, base);
22
+ if (possible instanceof globalThis.URL) {
23
+ if (_isValidURL(possible))
24
+ return possible;
22
25
  }
23
- catch {
24
- return undefined;
26
+ else {
27
+ try {
28
+ const url = new globalThis.URL(possible, base);
29
+ if (_isValidURL(url))
30
+ return url;
31
+ }
32
+ catch {
33
+ //
34
+ }
25
35
  }
26
36
  }
27
37
  }
@@ -32,87 +42,3 @@ export function requireURL(possible, base, caller = requireURL) {
32
42
  assertURL(url, caller);
33
43
  return url;
34
44
  }
35
- /**
36
- * Get a set of entries for a set of possible URL params.
37
- *
38
- * Note: Not as simple as just converting with `Object.fromEntries()`:
39
- * 1. When `URLSearchParams` contains multiple values for the same key, calling `params.get()` will return the _first_ value.
40
- * 2. So when converting this to a simple data object, only one value per key can be represented, but it needs to be the _first_ one.
41
- * 3. Since we're looping through anyway, we also take the time to convert values to strings, so we can accept a wider range of input types.
42
- */
43
- export function* getURLEntries(input, caller = getURLParams) {
44
- if (input instanceof URLSearchParams) {
45
- yield* input;
46
- }
47
- else if (isString(input) || isURL(input)) {
48
- yield* requireURL(input, undefined, caller).searchParams;
49
- }
50
- else {
51
- const done = [];
52
- for (const [key, value] of getDictionaryItems(input)) {
53
- if (done.includes(key))
54
- continue;
55
- done.push(key);
56
- const str = getString(value);
57
- if (str === undefined)
58
- throw new ValueError(`URL param "${key}" must be string`, { received: value, caller });
59
- yield [key, str];
60
- }
61
- }
62
- }
63
- /** Get a set of params for a URL as a dictionary. */
64
- export function getURLParams(input, caller = getURLParams) {
65
- const output = {};
66
- for (const [key, str] of getURLEntries(input, caller))
67
- output[key] = str;
68
- return output;
69
- }
70
- /** Get a single named param from a URL. */
71
- export function getURLParam(input, key) {
72
- if (input instanceof URLSearchParams)
73
- return input.get(key) || undefined;
74
- if (isDictionary(input))
75
- return getString(input[key]);
76
- return getURLParams(input)[key];
77
- }
78
- /** Get a single named param from a URL. */
79
- export function requireURLParam(input, key, caller = requireURLParam) {
80
- const value = getURLParam(input, key);
81
- if (value === undefined)
82
- throw new RequiredError(`URL param "${key}" is required`, { received: input, caller });
83
- return value;
84
- }
85
- /**
86
- * Return a URL with a new param set (or same URL if no changes were made).
87
- * - Throws `ValueError` if the value could not be converted to a string.
88
- */
89
- export function withURLParam(url, key, value, caller = withURLParam) {
90
- const input = requireURL(url);
91
- const output = new URL(input);
92
- const str = getString(value);
93
- if (str === undefined)
94
- throw new ValueError(`URL param "${key}" must be string`, { received: value, caller });
95
- output.searchParams.set(key, str);
96
- return input.href === output.href ? input : output;
97
- }
98
- /**
99
- * Return a URL with several new params set (or same URL if no changes were made).
100
- * - Throws `ValueError` if any of the values could not be converted to strings.
101
- */
102
- export function withURLParams(url, params, caller = withURLParams) {
103
- const input = requireURL(url);
104
- const output = new URL(input);
105
- for (const [key, str] of getURLEntries(params, caller))
106
- output.searchParams.set(key, str);
107
- return input.href === output.href ? input : output;
108
- }
109
- /** Return a URL without one or more params (or same URL if no changes were made). */
110
- export function omitURLParams(url, ...keys) {
111
- const input = requireURL(url);
112
- const output = new URL(input);
113
- for (const key of keys)
114
- output.searchParams.delete(key);
115
- return input.href === output.href ? input : output;
116
- }
117
- /** Return a URL without a param (or same URL if no changes were made). */
118
- export const omitURLParam = omitURLParams;
@@ -1,27 +0,0 @@
1
- import type { ImmutableArray } from "../util/array.js";
2
- import { type AbsoluteLink } from "../util/link.js";
3
- import type { StringSchemaOptions } from "./StringSchema.js";
4
- import { StringSchema } from "./StringSchema.js";
5
- /** Allowed options for `LinkSchema` */
6
- export interface LinkSchemaOptions extends Omit<StringSchemaOptions, "input" | "min" | "max" | "multiline"> {
7
- readonly base?: AbsoluteLink | undefined;
8
- readonly schemes?: ImmutableArray<string> | undefined;
9
- readonly hosts?: ImmutableArray<string> | undefined;
10
- }
11
- /**
12
- * Type of `StringSchema` that defines a valid URL link.
13
- * - Checks URL scheme against a whitelist (always), and checks URL domain against a whitelist (optional).
14
- * - URLs are limited to 512 characters, but generally these won't be data: URIs so this is a reasonable limit.
15
- * - Falsy values are converted to `""` empty string.
16
- */
17
- export declare class LinkSchema extends StringSchema {
18
- readonly base: AbsoluteLink | undefined;
19
- readonly schemes: ImmutableArray<string> | undefined;
20
- readonly hosts: ImmutableArray<string> | undefined;
21
- constructor({ one, title, base, schemes, hosts, ...options }: LinkSchemaOptions);
22
- validate(unsafeValue: unknown): AbsoluteLink;
23
- }
24
- /** Valid link, e.g. `https://www.google.com` */
25
- export declare const LINK: LinkSchema;
26
- /** Valid link, e.g. `https://www.google.com`, or `null` */
27
- export declare const NULLABLE_LINK: import("./NullableSchema.js").NullableSchema<`${string}:${string}`>;
package/util/link.d.ts DELETED
@@ -1,71 +0,0 @@
1
- import type { ImmutableArray } from "./array.js";
2
- import type { AnyCaller } from "./function.js";
3
- import type { Nullish } from "./null.js";
4
- import type { Path } from "./path.js";
5
- import { type PossibleURL } from "./url.js";
6
- /**
7
- * URL string with a `scheme:` at the start and a path component, e.g. `https://` or `file://`
8
- * - The `//` in a URI indicates that type of URI has a path heirarchy indicated by the first `/` after the `://`
9
- * - If the URI has no explicit `/path` component it will always be assumed to be `/`
10
- * - Some URIs are non-heirarchical, e.g. `mailto:me@gmail.com`
11
- */
12
- export type AbsoluteLink = `${string}:${string}`;
13
- /**
14
- * Relative link starts with `./` or `../` or `/`
15
- * @example "/a/b/c"
16
- * @example "./d/e/f"
17
- * @example "../g/h/i"
18
- */
19
- export type RelativeLink = Path;
20
- /**
21
- * Either an absolute URL string or a relative URL string.
22
- * @example "http://www.google.com"
23
- * @example "/a/b/c"
24
- * @example "./d/e/f"
25
- * @example "../g/h/i"
26
- */
27
- export type Link = AbsoluteLink | RelativeLink;
28
- /**
29
- * List of allowed schemes for a link.
30
- * @example ["http:", "https:", "mailto:"]
31
- */
32
- export type LinkSchemes = ImmutableArray<string>;
33
- /**
34
- * List of allowed hosts for a link.
35
- * @example ["google.com", "www.google.com"]
36
- */
37
- export type LinkHosts = ImmutableArray<string>;
38
- /**
39
- * Absolute URL is a URL that has an `href` that is a `Link` element and a `pathname` that is an `AbsolutePath` property.
40
- * - e.g. `http://x.com/a/b` is a link URL because `.pathname` will be `/a/b`
41
- * - e.g. `http://x.com` is a link URL because `.pathname` will be `/`
42
- * - e.g. `mailto:me@gmail.com` is _not_ an absolute URL because `.pathname` will be `"me@gmail.com"` which does not start with `/` slash.
43
- */
44
- export type AbsoluteLinkURL = URL & {
45
- href: AbsoluteLink;
46
- };
47
- /**
48
- * Is an unknown value a link URL?
49
- * - A valid link URL is a `URL` instance with a scheme matching the `schemes` array, and `host` matching the optional `hosts` array.
50
- */
51
- export declare function isLinkURL(value: unknown, schemes?: LinkSchemes, hosts?: LinkHosts): value is AbsoluteLinkURL;
52
- /**
53
- * Convert a possible URL to a link URL, or return `undefined` if conversion fails.
54
- * - A valid link URL is a `URL` instance with a scheme matching the `schemes` array, and `host` matching the optional `hosts` array.
55
- */
56
- export declare function getLinkURL(value: Nullish<PossibleURL>, base?: AbsoluteLinkURL | AbsoluteLink, schemes?: LinkSchemes, hosts?: LinkHosts): AbsoluteLinkURL | undefined;
57
- /**
58
- * Convert a possible URL to a link URL, or throw `RequiredError` if conversion fails.
59
- * - A valid link URL is a `URL` instance with a scheme matching the `schemes` array, and `host` matching the optional `hosts` array.
60
- */
61
- export declare function requireLinkURL(value: PossibleURL, base?: AbsoluteLinkURL | AbsoluteLink, schemes?: LinkSchemes, hosts?: LinkHosts, caller?: AnyCaller): AbsoluteLinkURL;
62
- /**
63
- * Convert a possible URL to an link URL string, or return `undefined` if conversion fails.
64
- * - A valid link URL string is an absolute URL string with a scheme matching the `schemes` array, and `host` matching the optional `hosts` array.
65
- */
66
- export declare function getLink(value: Nullish<PossibleURL>, base?: AbsoluteLinkURL | AbsoluteLink, schemes?: LinkSchemes, hosts?: LinkHosts): AbsoluteLink | undefined;
67
- /**
68
- * Convert a possible URL to an link URL string, or throw `RequiredError` if conversion fails.
69
- * - A valid link URL string is an absolute URL string with a scheme matching the `schemes` array, and `host` matching the optional `hosts` array.
70
- */
71
- export declare function requireLink(value: PossibleURL, base?: AbsoluteLinkURL | AbsoluteLink, schemes?: LinkSchemes, hosts?: LinkHosts, caller?: AnyCaller): AbsoluteLink;