@fedify/fedify
Version:
An ActivityPub server framework
115 lines (114 loc) • 3.42 kB
JavaScript
import "@js-temporal/polyfill";
import "urlpattern-polyfill";
globalThis.addEventListener = () => {};
import { cloneDeep } from "es-toolkit";
import { Router } from "uri-template-router";
import { parseTemplate } from "url-template";
//#region src/federation/router.ts
function cloneInnerRouter(router) {
const clone = new Router();
clone.nid = router.nid;
clone.fsm = cloneDeep(router.fsm);
clone.routeSet = new Set(router.routeSet);
clone.templateRouteMap = new Map(router.templateRouteMap);
clone.valueRouteMap = new Map(router.valueRouteMap);
clone.hierarchy = cloneDeep(router.hierarchy);
return clone;
}
/**
* URL router and constructor based on URI Template
* ([RFC 6570](https://tools.ietf.org/html/rfc6570)).
*/
var Router$1 = class Router$1 {
#router;
#templates;
#templateStrings;
/**
* Whether to ignore trailing slashes when matching paths.
* @since 1.6.0
*/
trailingSlashInsensitive;
/**
* Create a new {@link Router}.
* @param options Options for the router.
*/
constructor(options = {}) {
this.#router = new Router();
this.#templates = {};
this.#templateStrings = {};
this.trailingSlashInsensitive = options.trailingSlashInsensitive ?? false;
}
clone() {
const clone = new Router$1({ trailingSlashInsensitive: this.trailingSlashInsensitive });
clone.#router = cloneInnerRouter(this.#router);
clone.#templates = { ...this.#templates };
clone.#templateStrings = { ...this.#templateStrings };
return clone;
}
/**
* 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}.
*/
var RouterError = class extends Error {
/**
* Create a new {@link RouterError}.
* @param message The error message.
*/
constructor(message) {
super(message);
this.name = "RouterError";
}
};
//#endregion
export { RouterError as n, Router$1 as t };