Unverified Commit 94f077ee authored by Hong Minhee's avatar Hong Minhee
Browse files

Fix FederationBuilder.build() multiple calls error

Implement proper cloning of mutable state to prevent errors when
build() is called multiple times on the same builder instance.
parent 7f77f903
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ To be released. Note that 1.6.0 was skipped due to a mistake in the versioning.

 -  Added `Router.trailingSlashInsensitive` property.

 -  Added `Router.clone()` method.

 -  Implemented HTTP Message Signatures ([RFC 9421]) with double-knocking.
    Currently, it only works with RSA-PKCS#1-v1.5.  [[#208]]

+2 −0
Original line number Diff line number Diff line
@@ -102,6 +102,8 @@ test("FederationBuilder", async (t) => {
        impl.router.build("inbox", { identifier: "user1" }),
        "/users/user1/inbox",
      );

      await builder.build({ kv }); // Ensure build can be called multiple times
    },
  );

+32 −12
Original line number Diff line number Diff line
@@ -104,26 +104,46 @@ export class FederationBuilderImpl<TContextData>
    const { FederationImpl } = await import("./middleware.ts");
    const f = new FederationImpl(options);

    // In order to ensure `build()` can be called multiple times and
    // each instance does not share their state, we clone everything
    // that is mutable.  This includes the router and callbacks.

    // Assign the existing router instance but preserve the settings
    // Keep the original trailingSlashInsensitive configuration
    const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
    f.router = this.router;
    f.router = this.router.clone();
    f.router.trailingSlashInsensitive = trailingSlashInsensitiveValue;
    f._initializeRouter();

    f.actorCallbacks = this.actorCallbacks;
    f.actorCallbacks = this.actorCallbacks == null
      ? undefined
      : { ...this.actorCallbacks };
    f.nodeInfoDispatcher = this.nodeInfoDispatcher;
    f.objectCallbacks = this.objectCallbacks;
    f.objectTypeIds = this.objectTypeIds;
    f.objectCallbacks = { ...this.objectCallbacks };
    f.objectTypeIds = { ...this.objectTypeIds };
    f.inboxPath = this.inboxPath;
    f.inboxCallbacks = this.inboxCallbacks;
    f.outboxCallbacks = this.outboxCallbacks;
    f.followingCallbacks = this.followingCallbacks;
    f.followersCallbacks = this.followersCallbacks;
    f.likedCallbacks = this.likedCallbacks;
    f.featuredCallbacks = this.featuredCallbacks;
    f.featuredTagsCallbacks = this.featuredTagsCallbacks;
    f.inboxListeners = this.inboxListeners;
    f.inboxCallbacks = this.inboxCallbacks == null
      ? undefined
      : { ...this.inboxCallbacks };
    f.outboxCallbacks = this.outboxCallbacks == null
      ? undefined
      : { ...this.outboxCallbacks };
    f.followingCallbacks = this.followingCallbacks == null
      ? undefined
      : { ...this.followingCallbacks };
    f.followersCallbacks = this.followersCallbacks == null
      ? undefined
      : { ...this.followersCallbacks };
    f.likedCallbacks = this.likedCallbacks == null
      ? undefined
      : { ...this.likedCallbacks };
    f.featuredCallbacks = this.featuredCallbacks == null
      ? undefined
      : { ...this.featuredCallbacks };
    f.featuredTagsCallbacks = this.featuredTagsCallbacks == null
      ? undefined
      : { ...this.featuredTagsCallbacks };
    f.inboxListeners = this.inboxListeners?.clone();
    f.inboxErrorHandler = this.inboxErrorHandler;
    f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
    return f;
+6 −0
Original line number Diff line number Diff line
@@ -27,6 +27,12 @@ export class InboxListenerSet<TContextData> {
    this.#listeners = new Map();
  }

  clone(): InboxListenerSet<TContextData> {
    const clone = new InboxListenerSet<TContextData>();
    clone.#listeners = new Map(this.#listeners);
    return clone;
  }

  add<TActivity extends Activity>(
    // deno-lint-ignore no-explicit-any
    type: new (...args: any[]) => TActivity,
+16 −1
Original line number Diff line number Diff line
import { assertEquals, assertThrows } from "@std/assert";
import { assert, assertEquals, assertFalse, assertThrows } from "@std/assert";
import { test } from "../testing/mod.ts";
import { Router, RouterError, type RouterOptions } from "./router.ts";

@@ -13,6 +13,21 @@ function setUp(options: RouterOptions = {}): Router {
  return router;
}

test("Router.clone()", () => {
  const original = setUp();
  const clone = original.clone();
  clone.add("/users/{name}/friends", "friends");

  assert(clone.has("friends"));
  assertEquals(clone.route("/users/alice/friends"), {
    name: "friends",
    template: "/users/{name}/friends",
    values: { name: "alice" },
  });
  assertFalse(original.has("friends"));
  assertEquals(original.route("/users/alice/friends"), null);
});

test("Router.add()", () => {
  const router = new Router();
  assertEquals(router.add("/users", "users"), new Set());
Loading