UNPKG

@fedify/fedify

Version:

An ActivityPub server framework

94 lines (93 loc) 2.94 kB
// @ts-ignore TS7016 import { Router as InnerRouter } from "uri-template-router"; import { parseTemplate } from "url-template"; /** * URL router and constructor based on URI Template * ([RFC 6570](https://tools.ietf.org/html/rfc6570)). */ export class Router { #router; #templates; #templateStrings; #trailingSlashInsensitive; /** * Create a new {@link Router}. * @param options Options for the router. */ constructor(options = {}) { 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) { 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, name) { 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) => 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) { 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, values) { 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) { super(message); this.name = "RouterError"; } }