shelving
Version:
Toolkit for using data in JavaScript.
112 lines (111 loc) • 6.28 kB
TypeScript
import type { AnyCaller } from "./function.js";
import type { Nullish } from "./null.js";
import type { AbsolutePath } from "./path.js";
import type { ImmutableURI } from "./uri.js";
/**
* A URL string has a protocol and a `//`.
* - The `//` at the start of a URL indicates that it has a hierarchical path component, so this makes it a URL.
* - URLs have a concept of "absolute" or "relative" URLs, since they have a path.
*/
export type URLString = `${string}://${string}`;
/**
* Construct a correctly-typed `URL` object.
* - This is a more correctly typed version of the builtin Javascript `URL` constructor.
* - Requires a URL string, URL object, or path as input, and optionally a base URL.
* - If a path is provided as input, a base URL _must_ also be provided.
* - The returned type is
*/
export interface ImmutableURLConstructor {
new (input: URLString | ImmutableURL, base?: URLString | ImmutableURL): ImmutableURL;
new (input: URLString | ImmutableURL | string, base: URLString | ImmutableURL): ImmutableURL;
}
/**
* Object that describes a valid URL, e.g. `http://example.com/path/to/resource`
* - Improves the builtin Javascript `URL` class to more accurately type its properties.
*
* URI and URL differences:
* - According to RFC 3986, URLs are a subset of URIs that have a hierarchical path component, e.g. `http://example.com/path`.
* - The `//` at the start of a URL indicates that it has a hierarchical path component, so this makes it a URL.
* - The absence of `//` indicates a non-hierarchical URI.
* - URLs can be considered as "hierarchical URIs".
* - All URLs are also URIs, but not all URIs are URLs.
*
* Javascript URL problems:
* - Javascript `URL` instance can actually represent any kind of URI (not just URLs).
* - It's more "correct" terminology to use `URI` to refer to what the Javascript `URL` class represents.
* - 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).
* - Javascript URLs are mutable which can lead to subtle bugs.
*/
export interface ImmutableURL extends ImmutableURI {
readonly href: URLString;
readonly origin: URLString;
readonly pathname: AbsolutePath;
}
export declare const ImmutableURL: ImmutableURLConstructor;
/** Values that can be converted to a URL instance. */
export type PossibleURL = string | URL;
/**
* Is an unknown value a URL object?
* - Must be a `URL` instance and its origin must start with `scheme://`
*/
export declare function isURL(value: unknown): value is ImmutableURL;
/** Assert that an unknown value is a URL object. */
export declare function assertURL(value: unknown, caller?: AnyCaller): asserts value is ImmutableURL;
/**
* Resolve a possible URI relative to a base, or return `undefined` if conversion fails.
* - Returns any kind of URI — not just hierarchical `scheme://host` URLs. Use `getURL()` when a true URL is specifically required.
* - A `URL` instance is returned as-is (already absolute, base ignored).
*
* Note: the base is normalised with `getBaseURL()`, so it is always treated as if it ends in a slash.
* - e.g. if `base` is `http://p.com/a/b/c` the path resolves relative to `c/` as if a trailing slash was present.
* - This differs from the default behaviour of `new URL()`, but is the more natural expected result.
*/
export declare function getBasedURI(input: Nullish<PossibleURL>, base?: PossibleURL): ImmutableURI | undefined;
/**
* Resolve a possible URL relative to a base URL, or return `undefined` if conversion fails.
* - Like `getBasedURI()` but only succeeds for true `scheme://host` URLs — other URIs (e.g. `mailto:`) return `undefined`.
*/
export declare function getURL(target: Nullish<PossibleURL>, base?: PossibleURL): ImmutableURL | undefined;
/** Convert a possible URL to a URL, or throw `RequiredError` if conversion fails. */
export declare function requireURL(target: PossibleURL, base?: PossibleURL, caller?: AnyCaller): ImmutableURL;
/**
* Resolve and match a target URL/path against a base URL and return the remaining path.
*
* - Need to be valid _URLs_ not just _URIs_, i.e. needs to have `protocol://` at the start.
* - Origins need to match, i.e. `http://localhost` !== `http://localhost:4020`
* - Relative targets are resolved against the normalized base URL.
*
* @param target URL to match against `base` — if this is a relative path it will be resolved against `base`
*
* @returns Absolute path starting with `/`, or `undefined` for origin mismatches or non-matching paths.
*/
export declare function matchURLPrefix(target: PossibleURL | undefined, base: PossibleURL | undefined, caller?: AnyCaller): AbsolutePath | undefined;
/**
* Is a target URL active relative to a base URL?
* - Active means `target` and `base` resolve to the exact same URL (same origin, same path).
* - Origin mismatches return `false`.
*
* @param target URL whose status to test — relative paths resolve against `base`.
* @param base Base URL to test against.
*/
export declare function isURLActive(target: PossibleURL | undefined, base: PossibleURL | undefined, caller?: AnyCaller): boolean;
/**
* Is a target URL proud relative to a base URL?
* - Proud means `target` is `base` or a descendant of `base` — i.e. `base` is at or above `target` in the URL hierarchy.
* - Useful for marking a menu item as "current branch" when the user is somewhere deeper in its sub-tree.
* - Origin mismatches return `false`.
*
* @param target URL whose status to test — relative paths resolve against `base`.
* @param base Base URL to test against.
*/
export declare function isURLProud(target: PossibleURL | undefined, base: PossibleURL | undefined, caller?: AnyCaller): boolean;
/** BaseURL is a URL with a guaranteed trailing slash on pathname. */
export interface BaseURL extends ImmutableURL {
readonly pathname: `/` | `/${string}/`;
}
/** Is an unknown value a valid Base URL. */
export declare function isBaseURL(value: PossibleURL): value is BaseURL;
/** Get a Base URL. */
export declare function getBaseURL(input: Nullish<PossibleURL>): BaseURL | undefined;
/** Require a Base URL. */
export declare function requireBaseURL(value: PossibleURL, caller: AnyCaller): BaseURL;