This release is 23 versions behind 1.4.9 — the latest version of @fedify/fedify. Jump to latest
Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
Built and signed on GitHub Actions
An ActivityPub/fediverse server framework
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130// @ts-ignore TS7016 import { Router as InnerRouter } from "npm:uri-template-router@^0.0.16"; import { parseTemplate, type Template } from "npm:url-template@^3.1.1"; /** * Options for the {@link Router}. * @since 0.12.0 */ export interface RouterOptions { /** * Whether to ignore trailing slashes when matching paths. */ trailingSlashInsensitive?: boolean; } /** * The result of {@link Router.route} method. * @since 1.3.0 */ export interface RouterRouteResult { /** * The matched route name. */ name: string; /** * The URL template of the matched route. */ template: string; /** * The values extracted from the URL. */ values: Record<string, string>; } /** * URL router and constructor based on URI Template * ([RFC 6570](https://tools.ietf.org/html/rfc6570)). */ export class Router { #router: InnerRouter; #templates: Record<string, Template>; #templateStrings: Record<string, string>; #trailingSlashInsensitive: boolean; /** * Create a new {@link Router}. * @param options Options for the router. */ constructor(options: RouterOptions = {}) { this.#router = new InnerRouter(); this.#templates = {}; this.#templateStrings = {}; this.#trailingSlashInsensitive = options.trailingSlashInsensitive ?? false; } /** * Checks if a path name exists in the router. * @param name The name of the path. * @returns `true` if the path name exists, otherwise `false`. */ has(name: string): boolean { return name in this.#templates; } /** * Adds a new path rule to the router. * @param template The path pattern. * @param name The name of the path. * @returns The names of the variables in the path pattern. */ add(template: string, name: string): Set<string> { if (!template.startsWith("/")) { throw new RouterError("Path must start with a slash."); } const rule = this.#router.addTemplate(template, {}, name); this.#templates[name] = parseTemplate(template); this.#templateStrings[name] = template; return new Set(rule.variables.map((v: { varname: string }) => v.varname)); } /** * Resolves a path name and values from a URL, if any match. * @param url The URL to resolve. * @returns The name of the path and its values, if any match. Otherwise, * `null`. */ route(url: string): RouterRouteResult | null { let match = this.#router.resolveURI(url); if (match == null) { if (!this.#trailingSlashInsensitive) return null; url = url.endsWith("/") ? url.replace(/\/+$/, "") : `${url}/`; match = this.#router.resolveURI(url); if (match == null) return null; } return { name: match.matchValue, template: this.#templateStrings[match.matchValue], values: match.params, }; } /** * Constructs a URL/path from a path name and values. * @param name The name of the path. * @param values The values to expand the path with. * @returns The URL/path, if the name exists. Otherwise, `null`. */ build(name: string, values: Record<string, string>): string | null { if (name in this.#templates) { return this.#templates[name].expand(values); } return null; } } /** * An error thrown by the {@link Router}. */ export class RouterError extends Error { /** * Create a new {@link RouterError}. * @param message The error message. */ constructor(message: string) { super(message); this.name = "RouterError"; } }