Loading CHANGES.md +9 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,14 @@ To be released. interface. - Added `Federation.startQueue()` method. - Made the router able to be insensitive to trailing slashes in the URL paths. [[#81]] - Added `trailingSlashInsensitive` option to `CreateFederationOptions` interface. - Added `RouterOptions` interface. - Added an optional parameter to `new Router()` constructor. - Added `ChatMessage` class to Activity Vocabulary API. [[#85]] - Added `Move` class to Activity Vocabulary API. [[#65], [#92] by Lee Dogeon] Loading Loading @@ -86,6 +94,7 @@ To be released. [#53]: https://github.com/dahlia/fedify/issues/53 [#66]: https://github.com/dahlia/fedify/issues/66 [#70]: https://github.com/dahlia/fedify/issues/70 [#81]: https://github.com/dahlia/fedify/issues/81 [#85]: https://github.com/dahlia/fedify/issues/85 [#92]: https://github.com/dahlia/fedify/pull/92 Loading docs/manual/federation.md +11 −1 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ const federation = createFederation<void>({ Constructor parameters ---------------------- The `Federation` constructor function takes an object with the following The `createFederation()` function takes an object with the following properties. Some of them are required: ### `kv` Loading Loading @@ -203,6 +203,16 @@ the retry policy by providing a custom function that satisfies the `RetryPolicy` type. Or you can adjust the parameters of the built-in `createExponentialBackoffRetryPolicy()` function. ### `trailingSlashInsensitive` *This API is available since Fedify 0.12.0.* Whether the router should be insensitive to trailing slashes in the URL paths. For example, if this option is `true`, `/foo` and `/foo/` are treated as the same path. Turned off by default. How the `Federation` object recognizes the domain name ------------------------------------------------------ Loading federation/middleware.ts +11 −1 Original line number Diff line number Diff line Loading @@ -153,6 +153,14 @@ export interface CreateFederationOptions { * @since 0.12.0 */ inboxRetryPolicy?: RetryPolicy; /** * Whether the router should be insensitive to trailing slashes in the URL * paths. For example, if this option is `true`, `/foo` and `/foo/` are * treated as the same path. Turned off by default. * @since 0.12.0 */ trailingSlashInsensitive?: boolean; } /** Loading Loading @@ -326,7 +334,9 @@ export class Federation<TContextData> { this.#queue = options.queue; this.#queueStarted = false; this.#manuallyStartQueue = options.manuallyStartQueue ?? false; this.#router = new Router(); this.#router = new Router({ trailingSlashInsensitive: options.trailingSlashInsensitive, }); this.#router.add("/.well-known/webfinger", "webfinger"); this.#router.add("/.well-known/nodeinfo", "nodeInfoJrd"); this.#objectCallbacks = {}; Loading federation/router.test.ts +32 −8 Original line number Diff line number Diff line import { assertEquals, assertThrows } from "@std/assert"; import { test } from "../testing/mod.ts"; import { Router, RouterError } from "./router.ts"; import { Router, RouterError, type RouterOptions } from "./router.ts"; function setUp(): Router { const router = new Router(); function setUp(options: RouterOptions = {}): Router { const router = new Router(options); router.add("/users/{name}", "user"); router.add("/users/{name}/{postId}", "post"); router.add( "/users/{name}/posts/{postId}" + (options.trailingSlashInsensitive ? "/" : ""), "post", ); return router; } Loading @@ -14,7 +18,7 @@ test("Router.add()", () => { assertEquals(router.add("/users", "users"), new Set()); assertEquals(router.add("/users/{name}", "user"), new Set(["name"])); assertEquals( router.add("/users/{name}/{postId}", "post"), router.add("/users/{name}/posts/{postId}", "post"), new Set([ "name", "postId", Loading @@ -24,15 +28,35 @@ test("Router.add()", () => { }); test("Router.route()", () => { const router = setUp(); let router = setUp(); assertEquals(router.route("/users/alice"), { name: "user", values: { name: "alice" }, }); assertEquals(router.route("/users/alice/123"), { assertEquals(router.route("/users/bob/"), null); assertEquals(router.route("/users/alice/posts/123"), { name: "post", values: { name: "alice", postId: "123" }, }); assertEquals(router.route("/users/bob/posts/456/"), null); router = setUp({ trailingSlashInsensitive: true }); assertEquals(router.route("/users/alice"), { name: "user", values: { name: "alice" }, }); assertEquals(router.route("/users/bob/"), { name: "user", values: { name: "bob" }, }); assertEquals(router.route("/users/alice/posts/123"), { name: "post", values: { name: "alice", postId: "123" }, }); assertEquals(router.route("/users/bob/posts/456/"), { name: "post", values: { name: "bob", postId: "456" }, }); }); test("Router.build()", () => { Loading @@ -40,6 +64,6 @@ test("Router.build()", () => { assertEquals(router.build("user", { name: "alice" }), "/users/alice"); assertEquals( router.build("post", { name: "alice", postId: "123" }), "/users/alice/123", "/users/alice/posts/123", ); }); federation/router.ts +22 −3 Original line number Diff line number Diff line Loading @@ -2,6 +2,17 @@ import { Router as InnerRouter } from "uri-template-router"; import { parseTemplate, type Template } from "url-template"; /** * Options for the {@link Router}. * @since 0.12.0 */ export interface RouterOptions { /** * Whether to ignore trailing slashes when matching paths. */ trailingSlashInsensitive?: boolean; } /** * URL router and constructor based on URI Template * ([RFC 6570](https://tools.ietf.org/html/rfc6570)). Loading @@ -9,13 +20,16 @@ import { parseTemplate, type Template } from "url-template"; export class Router { #router: InnerRouter; #templates: Record<string, Template>; #trailingSlashInsensitive: boolean; /** * Create a new {@link Router}. * @param options Options for the router. */ constructor() { constructor(options: RouterOptions = {}) { this.#router = new InnerRouter(); this.#templates = {}; this.#trailingSlashInsensitive = options.trailingSlashInsensitive ?? false; } /** Loading Loading @@ -49,8 +63,13 @@ export class Router { * `null`. */ route(url: string): { name: string; values: Record<string, string> } | null { const match = this.#router.resolveURI(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, values: match.params, Loading Loading
CHANGES.md +9 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,14 @@ To be released. interface. - Added `Federation.startQueue()` method. - Made the router able to be insensitive to trailing slashes in the URL paths. [[#81]] - Added `trailingSlashInsensitive` option to `CreateFederationOptions` interface. - Added `RouterOptions` interface. - Added an optional parameter to `new Router()` constructor. - Added `ChatMessage` class to Activity Vocabulary API. [[#85]] - Added `Move` class to Activity Vocabulary API. [[#65], [#92] by Lee Dogeon] Loading Loading @@ -86,6 +94,7 @@ To be released. [#53]: https://github.com/dahlia/fedify/issues/53 [#66]: https://github.com/dahlia/fedify/issues/66 [#70]: https://github.com/dahlia/fedify/issues/70 [#81]: https://github.com/dahlia/fedify/issues/81 [#85]: https://github.com/dahlia/fedify/issues/85 [#92]: https://github.com/dahlia/fedify/pull/92 Loading
docs/manual/federation.md +11 −1 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ const federation = createFederation<void>({ Constructor parameters ---------------------- The `Federation` constructor function takes an object with the following The `createFederation()` function takes an object with the following properties. Some of them are required: ### `kv` Loading Loading @@ -203,6 +203,16 @@ the retry policy by providing a custom function that satisfies the `RetryPolicy` type. Or you can adjust the parameters of the built-in `createExponentialBackoffRetryPolicy()` function. ### `trailingSlashInsensitive` *This API is available since Fedify 0.12.0.* Whether the router should be insensitive to trailing slashes in the URL paths. For example, if this option is `true`, `/foo` and `/foo/` are treated as the same path. Turned off by default. How the `Federation` object recognizes the domain name ------------------------------------------------------ Loading
federation/middleware.ts +11 −1 Original line number Diff line number Diff line Loading @@ -153,6 +153,14 @@ export interface CreateFederationOptions { * @since 0.12.0 */ inboxRetryPolicy?: RetryPolicy; /** * Whether the router should be insensitive to trailing slashes in the URL * paths. For example, if this option is `true`, `/foo` and `/foo/` are * treated as the same path. Turned off by default. * @since 0.12.0 */ trailingSlashInsensitive?: boolean; } /** Loading Loading @@ -326,7 +334,9 @@ export class Federation<TContextData> { this.#queue = options.queue; this.#queueStarted = false; this.#manuallyStartQueue = options.manuallyStartQueue ?? false; this.#router = new Router(); this.#router = new Router({ trailingSlashInsensitive: options.trailingSlashInsensitive, }); this.#router.add("/.well-known/webfinger", "webfinger"); this.#router.add("/.well-known/nodeinfo", "nodeInfoJrd"); this.#objectCallbacks = {}; Loading
federation/router.test.ts +32 −8 Original line number Diff line number Diff line import { assertEquals, assertThrows } from "@std/assert"; import { test } from "../testing/mod.ts"; import { Router, RouterError } from "./router.ts"; import { Router, RouterError, type RouterOptions } from "./router.ts"; function setUp(): Router { const router = new Router(); function setUp(options: RouterOptions = {}): Router { const router = new Router(options); router.add("/users/{name}", "user"); router.add("/users/{name}/{postId}", "post"); router.add( "/users/{name}/posts/{postId}" + (options.trailingSlashInsensitive ? "/" : ""), "post", ); return router; } Loading @@ -14,7 +18,7 @@ test("Router.add()", () => { assertEquals(router.add("/users", "users"), new Set()); assertEquals(router.add("/users/{name}", "user"), new Set(["name"])); assertEquals( router.add("/users/{name}/{postId}", "post"), router.add("/users/{name}/posts/{postId}", "post"), new Set([ "name", "postId", Loading @@ -24,15 +28,35 @@ test("Router.add()", () => { }); test("Router.route()", () => { const router = setUp(); let router = setUp(); assertEquals(router.route("/users/alice"), { name: "user", values: { name: "alice" }, }); assertEquals(router.route("/users/alice/123"), { assertEquals(router.route("/users/bob/"), null); assertEquals(router.route("/users/alice/posts/123"), { name: "post", values: { name: "alice", postId: "123" }, }); assertEquals(router.route("/users/bob/posts/456/"), null); router = setUp({ trailingSlashInsensitive: true }); assertEquals(router.route("/users/alice"), { name: "user", values: { name: "alice" }, }); assertEquals(router.route("/users/bob/"), { name: "user", values: { name: "bob" }, }); assertEquals(router.route("/users/alice/posts/123"), { name: "post", values: { name: "alice", postId: "123" }, }); assertEquals(router.route("/users/bob/posts/456/"), { name: "post", values: { name: "bob", postId: "456" }, }); }); test("Router.build()", () => { Loading @@ -40,6 +64,6 @@ test("Router.build()", () => { assertEquals(router.build("user", { name: "alice" }), "/users/alice"); assertEquals( router.build("post", { name: "alice", postId: "123" }), "/users/alice/123", "/users/alice/posts/123", ); });
federation/router.ts +22 −3 Original line number Diff line number Diff line Loading @@ -2,6 +2,17 @@ import { Router as InnerRouter } from "uri-template-router"; import { parseTemplate, type Template } from "url-template"; /** * Options for the {@link Router}. * @since 0.12.0 */ export interface RouterOptions { /** * Whether to ignore trailing slashes when matching paths. */ trailingSlashInsensitive?: boolean; } /** * URL router and constructor based on URI Template * ([RFC 6570](https://tools.ietf.org/html/rfc6570)). Loading @@ -9,13 +20,16 @@ import { parseTemplate, type Template } from "url-template"; export class Router { #router: InnerRouter; #templates: Record<string, Template>; #trailingSlashInsensitive: boolean; /** * Create a new {@link Router}. * @param options Options for the router. */ constructor() { constructor(options: RouterOptions = {}) { this.#router = new InnerRouter(); this.#templates = {}; this.#trailingSlashInsensitive = options.trailingSlashInsensitive ?? false; } /** Loading Loading @@ -49,8 +63,13 @@ export class Router { * `null`. */ route(url: string): { name: string; values: Record<string, string> } | null { const match = this.#router.resolveURI(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, values: match.params, Loading