
      import { Temporal } from "@js-temporal/polyfill";
      import { URLPattern } from "urlpattern-polyfill";
      globalThis.addEventListener = () => {};
    
import { Activity, Announce, Create, Invite, Multikey, Note, Object as Object$1, Offer, Person, fetchDocumentLoader, getTypeId } from "../type-BSR-vRXo.js";
import { assertEquals } from "../assert_equals-DSbWqCm3.js";
import { assert } from "../assert-MZs1qjMx.js";
import { assertInstanceOf } from "../assert_instance_of-DHz7EHNU.js";
import { MemoryKvStore } from "../kv-CRZrzyXm.js";
import { ContextImpl, FederationImpl, InboxContextImpl, KvSpecDeterminer, createFederation } from "../middleware-DxS7Csvz.js";
import "../client-KUXReLjS.js";
import { RouterError, lookupObject } from "../lookup-q8DeFAjR.js";
import "../types-BSuWJsOm.js";
import "../actor-By5NMImS.js";
import "../key-B057bXm0.js";
import { signRequest, verifyRequest } from "../http-CCydyMoV.js";
import { getAuthenticatedDocumentLoader } from "../authdocloader-lhbaj4KA.js";
import { detachSignature, signJsonLd, verifyJsonLd } from "../ld-CxJe2x7_.js";
import { doesActorOwnKey } from "../owner-jwXilm3L.js";
import { signObject, verifyObject } from "../proof-CHi36V0T.js";
import "../inbox-DH8TXk1f.js";
import "../builder-B6AupxqD.js";
import "../collection-CcnIw1qY.js";
import "../keycache-CB_VjDe0.js";
import "../negotiation-5NPJL6zp.js";
import "../retry-D4GJ670a.js";
import "../send-CwVrwo7j.js";
import { mockDocumentLoader, test } from "../testing-Cqwzq9nj.js";
import { assertStrictEquals } from "../std__assert-X-_kMxKM.js";
import { assertFalse, assertRejects } from "../assert_rejects-DiIiJbZn.js";
import "../assert_is_error-BPGph1Jx.js";
import { assertNotEquals } from "../assert_not_equals-f3m3epl3.js";
import { assertThrows } from "../assert_throws-BOO88avQ.js";
import { ed25519Multikey, ed25519PrivateKey, ed25519PublicKey, rsaPrivateKey2, rsaPrivateKey3, rsaPublicKey2, rsaPublicKey3 } from "../keys-Dz9o2VwW.js";
import { esm_default } from "../esm-CIFbziGC.js";

//#region src/testing/fixtures/example.com/create.json
var __context$2 = "https://www.w3.org/ns/activitystreams";
var type$2 = "Create";
var id$2 = "https://example.com/create";
var actor = "https://example.com/person";
var create_default = {
	"@context": __context$2,
	type: type$2,
	id: id$2,
	actor
};

//#endregion
//#region src/testing/fixtures/example.com/person.json
var __context$1 = ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"];
var id$1 = "https://example.com/person";
var type$1 = "Person";
var name$1 = "John Doe";
var publicKey$1 = [{
	"id": "https://example.com/key",
	"owner": "https://example.com/person",
	"type": "CryptographicKey",
	"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyIB9rotX8G6r6/6toT+x\n24BUiQ/HaPH1Em9dOt4c94s+OPFoEdH7DY7Iym9A8LlH4JaGF8KD38bLHWe1S4x0\njV3gHJKhK7veJfGZCKUENcQecBZ+YWUs5HWvUIX1vVB//0luHrg6BQKGOrSOE+WI\nAxyr0qsWCFfZzQrvSnUD2yvg1arJX2xhms14uxoRd5Kg9efKSCmmQaNEapicARUm\nFWrIEpGFa/nUUnqimssAGw1eZFqf3wA4TjhsuARBhGaJtv/3KEa016eMZxy3kDlO\njZnXZTaTgWkXdodwUvy8563fes3Al6BlcS2iJ9qbtha8rSm0FHqoUKH73JsLPKQI\nwQIDAQAB\n-----END PUBLIC KEY-----"
}, {
	"id": "https://example.com/key2",
	"type": "CryptographicKey",
	"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoRmBtnxbdFutoRd1GLGw\nwGTrsqlRRWUe11hHQaoRLGf5LwQ0tIc6I9q+dynliw+2kxYsLn9SH2je6HcTYOol\ngW7F/cOWXZQN04b+OiYcU1ConAhLjmn4k1uKawJ614y0ScPNd8PQ+CljsnlPxbq9\nofaCMe2BV3B6y09aCuGFJ0nxn1/ubjmIBIWWFTAznoz1J9BhJDGyt3IO3ABy3f9z\nDVlR32L/n5VIkXnxkjUKdzMAOzYb62kuKOp1iznRTPrV71SNtivJMwSh/LVgBrmZ\njtIn/oim+KyX/fdLU3tQ7VClyqmJzyAjccOH6Qj6nFTPh+vX07gqN8IlLT2uye4w\nawIDAQAB\n-----END PUBLIC KEY-----"
}];
var person_default = {
	"@context": __context$1,
	id: id$1,
	type: type$1,
	name: name$1,
	publicKey: publicKey$1
};

//#endregion
//#region src/testing/fixtures/example.com/person2.json
var __context = [
	"https://www.w3.org/ns/activitystreams",
	"https://w3id.org/security/v1",
	"https://w3id.org/security/multikey/v1",
	"https://w3id.org/security/data-integrity/v1",
	"https://www.w3.org/ns/did/v1"
];
var id = "https://example.com/person2";
var type = "Person";
var name = "Jane Doe";
var publicKey = [{
	"id": "https://example.com/person2#key3",
	"type": "CryptographicKey",
	"owner": "https://example.com/person2",
	"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4GUqWgdiYlN3Su5Gr4l6\ni+xRS8gDDVKZ718vpGk6eIpvqs33q430nRbHIzbHRXRaAhc/1++rUBcK0V4/kjZl\nCSzVtRgGU6HMkmjcD+uE56a8XbTczfltbEDj7afoEuB2F3UhQEWrSz+QJ29DPXaL\nMIa1Yv61NR2vxGqNbdtoMjDORMBYtg77CYbcFkiJHw65PDa7+f/yjLxuCRPye5L7\nhncN0UZuuFoRJmHNRLSg5omBad9WTvQXmSyXEhEdk9fHwlI022AqAzlWbT79hldc\nDSKGGLLbQIs1c3JZIG8G5i6Uh5Vy0Z7tSNBcxbhqoI9i9je4f/x/OPIVc19f04BE\n1LgWuHsftZzRgW9Sdqz53W83XxVdxlyHeywXOnstSWT11f8dkLyQUcHKTH+E6urb\nH+aiPLiRpYK8W7D9KTQA9kZ5JXaEuveBd5vJX7wakhbzAn8pWJU7GYIHNY38Ycok\nmivkU5pY8S2cKFMwY0b7ade3MComlir5P3ZYSjF+n6gRVsT96P+9mNfCu9gXt/f8\nXCyjKlH89kGwuJ7HhR8CuVdm0l+jYozVt6GsDy0hHYyn79NCCAEzP7ZbhBMR0T5V\nrkl+TIGXoJH9WFiz4VxO+NnglF6dNQjDS5IzYLoFRXIK1f3cmQiEB4FZmL70l9HL\nrgwR+Xys83xia79OqFDRezMCAwEAAQ==\n-----END PUBLIC KEY-----\n"
}, {
	"id": "https://example.com/person2#key4",
	"type": "CryptographicKey",
	"owner": "https://example.com/person2",
	"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEALR8epAGDe+cVq5p2Tx49CCfphpk1rNhkNoY9i+XEUfg=\n-----END PUBLIC KEY-----\n"
}];
var assertionMethod = [{
	"id": "https://example.com/peson2#key3",
	"type": "Multikey",
	"controller": "https://example.com/person2",
	"publicKeyMultibase": "zgghBUVkqmWS8e1j4aN2yowLAEkJC6wowB9wWmLRACYCok7UzstWcTBp3waKiDUM7wqL9bbBD9W9FvNaXEK2KPCZ9ffhvd5dxChJL9bdPQSrMwa28FEYMGDtcF1uocrYNmZm2dBBMaWrCu8U3s4PpVVhn4hsWDL8GLuE466pkJs9Hy8xmECoaaVgAZLiYDw2gwrjHDiX2i7aDHKfE7aSZWUWmC8nAGNZ7DX5pXoyXK3pxuaCWxNxXwPmaFwgKDyy9uhtBJ8znp9NZXkXHBTQe5uAi8GFwHY5asvqCmYPrAGWxcT6pdbZaJHdWkM7nw6apBHfakKs42oMqdBoJ2WkkresoT1qHrX2GW7gNP9PLtveF4vfEd6cwgHKQCdYgayG3muGfZiPvML75cyfkNrjkctvuQUfMxY9umbd2TG3V3mPnLrvQnqHpuRMZYtCn3nX1qfZaqFhTwT4NFPqVNLqvgR6k9vcuGXn6Ndaumhd5xtTK64jk3e2gPBit9iq6MrFUSoxNsbTty4kqcHAodtkK8CMSxUxbFP1kK3nyy8ZfeMgDCts1KboBcT2m5FMpQpYxKtNBfvhTuyeDDC34uhbY8itmTAnDwSr5mKrniwwDUGPZFejda51TYs1N9D9Ejzaw5Mvr8qN6wahHmsDBWTbWwV6YKVMD1MjAhJBUopWJWB5x6mEBAX25MssKfAEhJyDtqYWjq63uQHUJCsPJp"
}, {
	"id": "https://example.com/person2#key4",
	"type": "Multikey",
	"controller": "https://example.com/person2",
	"publicKeyMultibase": "z6MkhVPuyvgG1RkMv67azDqDCDERPXVrUg1i3qchXY5EACE3"
}];
var person2_default = {
	"@context": __context,
	id,
	type,
	name,
	publicKey,
	assertionMethod
};

//#endregion
//#region src/federation/middleware.test.ts
test("createFederation()", async (t) => {
	const kv = new MemoryKvStore();
	await t.step("allowPrivateAddress", () => {
		assertThrows(() => createFederation({
			kv,
			contextLoader: mockDocumentLoader,
			allowPrivateAddress: true
		}), TypeError);
		assertThrows(() => createFederation({
			kv,
			authenticatedDocumentLoaderFactory: () => mockDocumentLoader,
			allowPrivateAddress: true
		}), TypeError);
	});
	await t.step("origin", () => {
		const f = createFederation({
			kv,
			origin: "http://example.com:8080"
		});
		assertInstanceOf(f, FederationImpl);
		assertEquals(f.origin, {
			handleHost: "example.com:8080",
			webOrigin: "http://example.com:8080"
		});
		assertThrows(() => createFederation({
			kv,
			origin: "example.com"
		}), TypeError);
		assertThrows(() => createFederation({
			kv,
			origin: "ftp://example.com"
		}), TypeError);
		assertThrows(() => createFederation({
			kv,
			origin: "https://example.com/foo"
		}), TypeError);
		assertThrows(() => createFederation({
			kv,
			origin: "https://example.com/?foo"
		}), TypeError);
		assertThrows(() => createFederation({
			kv,
			origin: "https://example.com/#foo"
		}), TypeError);
		const f2 = createFederation({
			kv,
			origin: {
				handleHost: "example.com:8080",
				webOrigin: "https://ap.example.com"
			}
		});
		assertInstanceOf(f2, FederationImpl);
		assertEquals(f2.origin, {
			handleHost: "example.com:8080",
			webOrigin: "https://ap.example.com"
		});
		assertThrows(() => createFederation({
			kv,
			origin: {
				handleHost: "https://example.com",
				webOrigin: "https://example.com"
			}
		}), TypeError);
		assertThrows(() => createFederation({
			kv,
			origin: {
				handleHost: "example.com/",
				webOrigin: "https://example.com"
			}
		}), TypeError);
		assertThrows(() => createFederation({
			kv,
			origin: {
				handleHost: "example.com",
				webOrigin: "example.com"
			}
		}), TypeError);
		assertThrows(() => createFederation({
			kv,
			origin: {
				handleHost: "example.com",
				webOrigin: "ftp://example.com"
			}
		}), TypeError);
		assertThrows(() => createFederation({
			kv,
			origin: {
				handleHost: "example.com",
				webOrigin: "https://example.com/foo"
			}
		}), TypeError);
		assertThrows(() => createFederation({
			kv,
			origin: {
				handleHost: "example.com",
				webOrigin: "https://example.com/?foo"
			}
		}), TypeError);
		assertThrows(() => createFederation({
			kv,
			origin: {
				handleHost: "example.com",
				webOrigin: "https://example.com/#foo"
			}
		}), TypeError);
	});
});
test({
	name: "Federation.createContext()",
	permissions: {
		env: true,
		read: true
	},
	async fn(t) {
		const kv = new MemoryKvStore();
		esm_default.spyGlobal();
		esm_default.get("https://example.com/auth-check", async (cl) => {
			const v = await verifyRequest(cl.request, {
				contextLoader: mockDocumentLoader,
				documentLoader: mockDocumentLoader,
				currentTime: Temporal.Now.instant()
			});
			return new Response(JSON.stringify(v != null), { headers: { "Content-Type": "application/json" } });
		});
		await t.step("Context", async () => {
			const rejectingLoader = (_url) => Promise.reject(/* @__PURE__ */ new Error("Not found"));
			const federation = createFederation({
				kv,
				documentLoaderFactory: () => rejectingLoader,
				contextLoaderFactory: () => mockDocumentLoader
			});
			let ctx = federation.createContext(new URL("https://example.com:1234/"), 123);
			assertEquals(ctx.data, 123);
			assertEquals(ctx.origin, "https://example.com:1234");
			assertEquals(ctx.canonicalOrigin, "https://example.com:1234");
			assertEquals(ctx.host, "example.com:1234");
			assertEquals(ctx.hostname, "example.com");
			assertStrictEquals(ctx.documentLoader, rejectingLoader);
			assertStrictEquals(ctx.contextLoader, mockDocumentLoader);
			assertStrictEquals(ctx.federation, federation);
			assertThrows(() => ctx.getNodeInfoUri(), RouterError);
			assertThrows(() => ctx.getActorUri("handle"), RouterError);
			assertThrows(() => ctx.getObjectUri(Note, {
				handle: "handle",
				id: "id"
			}), RouterError);
			assertThrows(() => ctx.getInboxUri(), RouterError);
			assertThrows(() => ctx.getInboxUri("handle"), RouterError);
			assertThrows(() => ctx.getOutboxUri("handle"), RouterError);
			assertThrows(() => ctx.getFollowingUri("handle"), RouterError);
			assertThrows(() => ctx.getFollowersUri("handle"), RouterError);
			assertThrows(() => ctx.getLikedUri("handle"), RouterError);
			assertThrows(() => ctx.getFeaturedUri("handle"), RouterError);
			assertThrows(() => ctx.getFeaturedTagsUri("handle"), RouterError);
			assertThrows(() => ctx.getCollectionUri("test", { id: "123" }), RouterError);
			assertEquals(ctx.parseUri(new URL("https://example.com/")), null);
			assertEquals(ctx.parseUri(null), null);
			assertEquals(await ctx.getActorKeyPairs("handle"), []);
			await assertRejects(() => ctx.getDocumentLoader({ identifier: "handle" }), Error, "No actor key pairs dispatcher registered");
			await assertRejects(() => ctx.sendActivity({ identifier: "handle" }, [], new Create({})), Error, "No actor key pairs dispatcher registered");
			federation.setNodeInfoDispatcher("/nodeinfo/2.1", () => ({
				software: {
					name: "Example",
					version: "1.2.3"
				},
				protocols: ["activitypub"],
				usage: {
					users: {},
					localPosts: 123,
					localComments: 456
				}
			}));
			ctx = federation.createContext(new URL("https://example.com/"), 123);
			assertEquals(ctx.getNodeInfoUri(), new URL("https://example.com/nodeinfo/2.1"));
			federation.setActorDispatcher("/users/{identifier}", () => new Person({})).setKeyPairsDispatcher(() => [{
				privateKey: rsaPrivateKey2,
				publicKey: rsaPublicKey2.publicKey
			}, {
				privateKey: ed25519PrivateKey,
				publicKey: ed25519PublicKey.publicKey
			}]).mapHandle((_, username) => username === "HANDLE" ? "handle" : null);
			ctx = federation.createContext(new URL("https://example.com/"), 123);
			assertEquals(ctx.getActorUri("handle"), new URL("https://example.com/users/handle"));
			assertEquals(ctx.parseUri(new URL("https://example.com/")), null);
			assertEquals(ctx.parseUri(new URL("https://example.com/users/handle")), {
				type: "actor",
				identifier: "handle",
				handle: "handle"
			});
			assertEquals(ctx.parseUri(null), null);
			assertEquals(await ctx.getActorKeyPairs("handle"), [{
				keyId: new URL("https://example.com/users/handle#main-key"),
				privateKey: rsaPrivateKey2,
				publicKey: rsaPublicKey2.publicKey,
				cryptographicKey: rsaPublicKey2.clone({
					id: new URL("https://example.com/users/handle#main-key"),
					owner: new URL("https://example.com/users/handle")
				}),
				multikey: new Multikey({
					id: new URL("https://example.com/users/handle#main-key"),
					controller: new URL("https://example.com/users/handle"),
					publicKey: rsaPublicKey2.publicKey
				})
			}, {
				keyId: new URL("https://example.com/users/handle#key-2"),
				privateKey: ed25519PrivateKey,
				publicKey: ed25519PublicKey.publicKey,
				cryptographicKey: ed25519PublicKey.clone({
					id: new URL("https://example.com/users/handle#key-2"),
					owner: new URL("https://example.com/users/handle")
				}),
				multikey: new Multikey({
					id: new URL("https://example.com/users/handle#key-2"),
					controller: new URL("https://example.com/users/handle"),
					publicKey: ed25519PublicKey.publicKey
				})
			}]);
			const loader = await ctx.getDocumentLoader({ identifier: "handle" });
			assertEquals(await loader("https://example.com/auth-check"), {
				contextUrl: null,
				documentUrl: "https://example.com/auth-check",
				document: true
			});
			const loader2 = await ctx.getDocumentLoader({ username: "HANDLE" });
			assertEquals(await loader2("https://example.com/auth-check"), {
				contextUrl: null,
				documentUrl: "https://example.com/auth-check",
				document: true
			});
			const loader3 = ctx.getDocumentLoader({
				keyId: new URL("https://example.com/key2"),
				privateKey: rsaPrivateKey2
			});
			assertEquals(await loader3("https://example.com/auth-check"), {
				contextUrl: null,
				documentUrl: "https://example.com/auth-check",
				document: true
			});
			assertEquals(await ctx.lookupObject("https://example.com/object"), null);
			await assertRejects(() => ctx.sendActivity({ identifier: "handle" }, [], new Create({})), TypeError, "The activity to send must have at least one actor property.");
			await ctx.sendActivity({ identifier: "handle" }, [], new Create({ actor: new URL("https://example.com/users/handle") }));
			esm_default.get("https://example.com/object", () => new Response(JSON.stringify({
				"@context": "https://www.w3.org/ns/activitystreams",
				type: "Object",
				id: "https://example.com/object",
				name: "Fetched object"
			}), { headers: { "Content-Type": "application/activity+json" } }));
			const federation2 = createFederation({
				kv,
				documentLoaderFactory: () => fetchDocumentLoader,
				contextLoaderFactory: () => mockDocumentLoader
			});
			const ctx2 = federation2.createContext(new URL("https://example.com/"), 123);
			assertEquals(await ctx2.lookupObject("https://example.com/object"), new Object$1({
				id: new URL("https://example.com/object"),
				name: "Fetched object"
			}));
			federation.setObjectDispatcher(Note, "/users/{identifier}/notes/{id}", (_ctx, values) => {
				return new Note({ summary: `Note ${values.id} by ${values.identifier}` });
			});
			ctx = federation.createContext(new URL("https://example.com/"), 123);
			assertEquals(ctx.getObjectUri(Note, {
				identifier: "john",
				id: "123"
			}), new URL("https://example.com/users/john/notes/123"));
			assertEquals(ctx.parseUri(new URL("https://example.com/users/john/notes/123")), {
				type: "object",
				class: Note,
				typeId: new URL("https://www.w3.org/ns/activitystreams#Note"),
				values: {
					identifier: "john",
					id: "123"
				}
			});
			assertEquals(ctx.parseUri(null), null);
			federation.setInboxListeners("/users/{identifier}/inbox", "/inbox");
			ctx = federation.createContext(new URL("https://example.com/"), 123);
			assertEquals(ctx.getInboxUri(), new URL("https://example.com/inbox"));
			assertEquals(ctx.getInboxUri("handle"), new URL("https://example.com/users/handle/inbox"));
			assertEquals(ctx.parseUri(new URL("https://example.com/inbox")), {
				type: "inbox",
				identifier: void 0,
				handle: void 0
			});
			assertEquals(ctx.parseUri(new URL("https://example.com/users/handle/inbox")), {
				type: "inbox",
				identifier: "handle",
				handle: "handle"
			});
			assertEquals(ctx.parseUri(null), null);
			federation.setOutboxDispatcher("/users/{identifier}/outbox", () => ({ items: [] }));
			ctx = federation.createContext(new URL("https://example.com/"), 123);
			assertEquals(ctx.getOutboxUri("handle"), new URL("https://example.com/users/handle/outbox"));
			assertEquals(ctx.parseUri(new URL("https://example.com/users/handle/outbox")), {
				type: "outbox",
				identifier: "handle",
				handle: "handle"
			});
			assertEquals(ctx.parseUri(null), null);
			federation.setFollowingDispatcher("/users/{identifier}/following", () => ({ items: [] }));
			ctx = federation.createContext(new URL("https://example.com/"), 123);
			assertEquals(ctx.getFollowingUri("handle"), new URL("https://example.com/users/handle/following"));
			assertEquals(ctx.parseUri(new URL("https://example.com/users/handle/following")), {
				type: "following",
				identifier: "handle",
				handle: "handle"
			});
			assertEquals(ctx.parseUri(null), null);
			federation.setFollowersDispatcher("/users/{identifier}/followers", () => ({ items: [] }));
			ctx = federation.createContext(new URL("https://example.com/"), 123);
			assertEquals(ctx.getFollowersUri("handle"), new URL("https://example.com/users/handle/followers"));
			assertEquals(ctx.parseUri(new URL("https://example.com/users/handle/followers")), {
				type: "followers",
				identifier: "handle",
				handle: "handle"
			});
			assertEquals(ctx.parseUri(null), null);
			federation.setLikedDispatcher("/users/{identifier}/liked", () => ({ items: [] }));
			ctx = federation.createContext(new URL("https://example.com/"), 123);
			assertEquals(ctx.getLikedUri("handle"), new URL("https://example.com/users/handle/liked"));
			assertEquals(ctx.parseUri(new URL("https://example.com/users/handle/liked")), {
				type: "liked",
				identifier: "handle",
				handle: "handle"
			});
			assertEquals(ctx.parseUri(null), null);
			federation.setFeaturedDispatcher("/users/{identifier}/featured", () => ({ items: [] }));
			ctx = federation.createContext(new URL("https://example.com/"), 123);
			assertEquals(ctx.getFeaturedUri("handle"), new URL("https://example.com/users/handle/featured"));
			assertEquals(ctx.parseUri(new URL("https://example.com/users/handle/featured")), {
				type: "featured",
				identifier: "handle",
				handle: "handle"
			});
			assertEquals(ctx.parseUri(null), null);
			federation.setFeaturedTagsDispatcher("/users/{identifier}/tags", () => ({ items: [] }));
			ctx = federation.createContext(new URL("https://example.com/"), 123);
			assertEquals(ctx.getFeaturedTagsUri("handle"), new URL("https://example.com/users/handle/tags"));
			assertEquals(ctx.parseUri(new URL("https://example.com/users/handle/tags")), {
				type: "featuredTags",
				identifier: "handle",
				handle: "handle"
			});
			assertEquals(ctx.parseUri(null), null);
		});
		await t.step("Context with origin", () => {
			const federation = createFederation({
				kv,
				origin: "https://ap.example.com",
				documentLoaderFactory: () => mockDocumentLoader,
				contextLoader: mockDocumentLoader
			});
			const ctx = federation.createContext(new URL("https://example.com:1234/"));
			assertEquals(ctx.origin, "https://example.com:1234");
			assertEquals(ctx.canonicalOrigin, "https://ap.example.com");
			assertEquals(ctx.host, "example.com:1234");
			assertEquals(ctx.hostname, "example.com");
			federation.setNodeInfoDispatcher("/nodeinfo/2.1", () => ({
				software: {
					name: "Example",
					version: "1.2.3"
				},
				protocols: ["activitypub"],
				usage: {
					users: {},
					localPosts: 123,
					localComments: 456
				}
			}));
			assertEquals(ctx.getNodeInfoUri(), new URL("https://ap.example.com/nodeinfo/2.1"));
			federation.setActorDispatcher("/users/{identifier}", () => new Person({}));
			assertEquals(ctx.getActorUri("handle"), new URL("https://ap.example.com/users/handle"));
			assertEquals(ctx.parseUri(new URL("https://ap.example.com/users/handle")), {
				type: "actor",
				handle: "handle",
				identifier: "handle"
			});
			assertEquals(ctx.parseUri(new URL("https://example.com:1234/users/handle")), {
				type: "actor",
				handle: "handle",
				identifier: "handle"
			});
			federation.setObjectDispatcher(Note, "/users/{identifier}/notes/{id}", (_ctx, values) => {
				return new Note({ summary: `Note ${values.id} by ${values.identifier}` });
			});
			assertEquals(ctx.getObjectUri(Note, {
				identifier: "john",
				id: "123"
			}), new URL("https://ap.example.com/users/john/notes/123"));
			assertEquals(ctx.parseUri(new URL("https://ap.example.com/users/john/notes/123")), {
				type: "object",
				class: Note,
				typeId: new URL("https://www.w3.org/ns/activitystreams#Note"),
				values: {
					identifier: "john",
					id: "123"
				}
			});
			assertEquals(ctx.parseUri(new URL("https://example.com:1234/users/john/notes/123")), {
				type: "object",
				class: Note,
				typeId: new URL("https://www.w3.org/ns/activitystreams#Note"),
				values: {
					identifier: "john",
					id: "123"
				}
			});
			federation.setInboxListeners("/users/{identifier}/inbox", "/inbox");
			assertEquals(ctx.getInboxUri(), new URL("https://ap.example.com/inbox"));
			assertEquals(ctx.getInboxUri("handle"), new URL("https://ap.example.com/users/handle/inbox"));
			assertEquals(ctx.parseUri(new URL("https://ap.example.com/inbox")), {
				type: "inbox",
				handle: void 0,
				identifier: void 0
			});
			assertEquals(ctx.parseUri(new URL("https://example.com:1234/inbox")), {
				type: "inbox",
				handle: void 0,
				identifier: void 0
			});
			assertEquals(ctx.parseUri(new URL("https://ap.example.com/users/handle/inbox")), {
				type: "inbox",
				handle: "handle",
				identifier: "handle"
			});
			assertEquals(ctx.parseUri(new URL("https://example.com:1234/users/handle/inbox")), {
				type: "inbox",
				handle: "handle",
				identifier: "handle"
			});
			federation.setOutboxDispatcher("/users/{identifier}/outbox", () => ({ items: [] }));
			assertEquals(ctx.getOutboxUri("handle"), new URL("https://ap.example.com/users/handle/outbox"));
			assertEquals(ctx.parseUri(new URL("https://ap.example.com/users/handle/outbox")), {
				type: "outbox",
				handle: "handle",
				identifier: "handle"
			});
			assertEquals(ctx.parseUri(new URL("https://example.com:1234/users/handle/outbox")), {
				type: "outbox",
				handle: "handle",
				identifier: "handle"
			});
			federation.setFollowingDispatcher("/users/{identifier}/following", () => ({ items: [] }));
			assertEquals(ctx.getFollowingUri("handle"), new URL("https://ap.example.com/users/handle/following"));
			assertEquals(ctx.parseUri(new URL("https://ap.example.com/users/handle/following")), {
				type: "following",
				handle: "handle",
				identifier: "handle"
			});
			assertEquals(ctx.parseUri(new URL("https://example.com:1234/users/handle/following")), {
				type: "following",
				handle: "handle",
				identifier: "handle"
			});
			federation.setFollowersDispatcher("/users/{identifier}/followers", () => ({ items: [] }));
			assertEquals(ctx.getFollowersUri("handle"), new URL("https://ap.example.com/users/handle/followers"));
			assertEquals(ctx.parseUri(new URL("https://ap.example.com/users/handle/followers")), {
				type: "followers",
				handle: "handle",
				identifier: "handle"
			});
			assertEquals(ctx.parseUri(new URL("https://example.com:1234/users/handle/followers")), {
				type: "followers",
				handle: "handle",
				identifier: "handle"
			});
			federation.setLikedDispatcher("/users/{identifier}/liked", () => ({ items: [] }));
			assertEquals(ctx.getLikedUri("handle"), new URL("https://ap.example.com/users/handle/liked"));
			assertEquals(ctx.parseUri(new URL("https://ap.example.com/users/handle/liked")), {
				type: "liked",
				handle: "handle",
				identifier: "handle"
			});
			assertEquals(ctx.parseUri(new URL("https://example.com:1234/users/handle/liked")), {
				type: "liked",
				handle: "handle",
				identifier: "handle"
			});
			federation.setFeaturedDispatcher("/users/{identifier}/featured", () => ({ items: [] }));
			assertEquals(ctx.getFeaturedUri("handle"), new URL("https://ap.example.com/users/handle/featured"));
			assertEquals(ctx.parseUri(new URL("https://ap.example.com/users/handle/featured")), {
				type: "featured",
				handle: "handle",
				identifier: "handle"
			});
			assertEquals(ctx.parseUri(new URL("https://example.com:1234/users/handle/featured")), {
				type: "featured",
				handle: "handle",
				identifier: "handle"
			});
			federation.setFeaturedTagsDispatcher("/users/{identifier}/tags", () => ({ items: [] }));
			assertEquals(ctx.getFeaturedTagsUri("handle"), new URL("https://ap.example.com/users/handle/tags"));
			assertEquals(ctx.parseUri(new URL("https://ap.example.com/users/handle/tags")), {
				type: "featuredTags",
				handle: "handle",
				identifier: "handle"
			});
			assertEquals(ctx.parseUri(new URL("https://example.com:1234/users/handle/tags")), {
				type: "featuredTags",
				handle: "handle",
				identifier: "handle"
			});
		});
		await t.step("Context.clone()", () => {
			const federation = createFederation({ kv });
			const ctx = federation.createContext(new URL("https://example.com/"), 123);
			const clone = ctx.clone(456);
			assertStrictEquals(clone.canonicalOrigin, ctx.canonicalOrigin);
			assertStrictEquals(clone.origin, ctx.origin);
			assertEquals(clone.data, 456);
			assertEquals(clone.host, ctx.host);
			assertEquals(clone.hostname, ctx.hostname);
			assertStrictEquals(clone.documentLoader, ctx.documentLoader);
			assertStrictEquals(clone.contextLoader, ctx.contextLoader);
			assertStrictEquals(clone.federation, ctx.federation);
		});
		esm_default.get("https://example.com/.well-known/nodeinfo", (cl) => {
			const headers = cl.options.headers ?? {};
			assertEquals(new Headers(headers).get("User-Agent"), "CustomUserAgent/1.2.3");
			return new Response(JSON.stringify({ links: [{
				rel: "http://nodeinfo.diaspora.software/ns/schema/2.1",
				href: "https://example.com/nodeinfo/2.1"
			}] }));
		});
		esm_default.get("https://example.com/nodeinfo/2.1", (cl) => {
			const headers = cl.options.headers ?? {};
			assertEquals(new Headers(headers).get("User-Agent"), "CustomUserAgent/1.2.3");
			return new Response(JSON.stringify({
				software: {
					name: "foo",
					version: "1.2.3"
				},
				protocols: ["activitypub", "diaspora"],
				usage: {
					users: {},
					localPosts: 123,
					localComments: 456
				}
			}));
		});
		await t.step("Context.lookupNodeInfo()", async () => {
			const federation = createFederation({
				kv,
				userAgent: "CustomUserAgent/1.2.3"
			});
			const ctx = federation.createContext(new URL("https://example.com/"), 123);
			const nodeInfo = await ctx.lookupNodeInfo("https://example.com/");
			assertEquals(nodeInfo, {
				software: {
					name: "foo",
					version: "1.2.3"
				},
				protocols: ["activitypub", "diaspora"],
				usage: {
					users: {},
					localPosts: 123,
					localComments: 456
				}
			});
			const rawNodeInfo = await ctx.lookupNodeInfo("https://example.com/", { parse: "none" });
			assertEquals(rawNodeInfo, {
				software: {
					name: "foo",
					version: "1.2.3"
				},
				protocols: ["activitypub", "diaspora"],
				usage: {
					users: {},
					localPosts: 123,
					localComments: 456
				}
			});
		});
		await t.step("RequestContext", async () => {
			const federation = createFederation({
				kv,
				documentLoaderFactory: () => mockDocumentLoader
			});
			const req = new Request("https://example.com/", { headers: { "accept": "application/ld+json" } });
			const ctx = federation.createContext(req, 123);
			assertEquals(ctx.request, req);
			assertEquals(ctx.url, new URL("https://example.com/"));
			assertEquals(ctx.origin, "https://example.com");
			assertEquals(ctx.host, "example.com");
			assertEquals(ctx.hostname, "example.com");
			assertEquals(ctx.data, 123);
			await assertRejects(() => ctx.getActor("someone"), Error);
			await assertRejects(() => ctx.getObject(Note, {
				handle: "someone",
				id: "123"
			}), Error);
			assertEquals(await ctx.getSignedKey(), null);
			assertEquals(await ctx.getSignedKeyOwner(), null);
			assertEquals(await ctx.getSignedKey(), null);
			assertEquals(await ctx.getSignedKeyOwner(), null);
			await assertRejects(() => ctx.getActor("someone"), Error, "No actor dispatcher registered");
			const signedReq = await signRequest(new Request("https://example.com/", { headers: { "accept": "application/ld+json" } }), rsaPrivateKey2, rsaPublicKey2.id);
			const signedCtx = federation.createContext(signedReq, 456);
			assertEquals(signedCtx.request, signedReq);
			assertEquals(signedCtx.url, new URL("https://example.com/"));
			assertEquals(signedCtx.data, 456);
			assertEquals(await signedCtx.getSignedKey(), rsaPublicKey2);
			assertEquals(await signedCtx.getSignedKeyOwner(), null);
			assertEquals(await signedCtx.getSignedKey(), rsaPublicKey2);
			assertEquals(await signedCtx.getSignedKeyOwner(), null);
			const signedReq2 = await signRequest(new Request("https://example.com/", { headers: { "accept": "application/ld+json" } }), rsaPrivateKey3, rsaPublicKey3.id);
			const signedCtx2 = federation.createContext(signedReq2, 456);
			assertEquals(signedCtx2.request, signedReq2);
			assertEquals(signedCtx2.url, new URL("https://example.com/"));
			assertEquals(signedCtx2.data, 456);
			assertEquals(await signedCtx2.getSignedKey(), rsaPublicKey3);
			const expectedOwner = await lookupObject("https://example.com/person2", {
				documentLoader: mockDocumentLoader,
				contextLoader: mockDocumentLoader
			});
			assertEquals(await signedCtx2.getSignedKeyOwner(), expectedOwner);
			assertEquals(await signedCtx2.getSignedKey(), rsaPublicKey3);
			assertEquals(await signedCtx2.getSignedKeyOwner(), expectedOwner);
			federation.setActorDispatcher("/users/{identifier}", (_ctx, identifier) => new Person({ preferredUsername: identifier }));
			const ctx2 = federation.createContext(req, 789);
			assertEquals(ctx2.request, req);
			assertEquals(ctx2.url, new URL("https://example.com/"));
			assertEquals(ctx2.data, 789);
			assertEquals(await ctx2.getActor("john"), new Person({ preferredUsername: "john" }));
			federation.setObjectDispatcher(Note, "/users/{identifier}/notes/{id}", (_ctx, values) => {
				return new Note({ summary: `Note ${values.id} by ${values.identifier}` });
			});
			const ctx3 = federation.createContext(req, 123);
			assertEquals(ctx3.request, req);
			assertEquals(ctx3.url, new URL("https://example.com/"));
			assertEquals(ctx3.data, 123);
			assertEquals(await ctx2.getObject(Note, {
				identifier: "john",
				id: "123"
			}), new Note({ summary: "Note 123 by john" }));
		});
		await t.step("RequestContext.clone()", () => {
			const federation = createFederation({ kv });
			const req = new Request("https://example.com/", { headers: { "accept": "application/ld+json" } });
			const ctx = federation.createContext(req, 123);
			const clone = ctx.clone(456);
			assertStrictEquals(clone.request, ctx.request);
			assertEquals(clone.url, ctx.url);
			assertEquals(clone.data, 456);
			assertEquals(clone.origin, ctx.origin);
			assertEquals(clone.host, ctx.host);
			assertEquals(clone.hostname, ctx.hostname);
			assertStrictEquals(clone.documentLoader, ctx.documentLoader);
			assertStrictEquals(clone.contextLoader, ctx.contextLoader);
			assertStrictEquals(clone.federation, ctx.federation);
		});
		esm_default.hardReset();
	}
});
test("Federation.fetch()", async (t) => {
	esm_default.spyGlobal();
	esm_default.get("https://example.com/key2", {
		headers: { "Content-Type": "application/activity+json" },
		body: await rsaPublicKey2.toJsonLd({ contextLoader: mockDocumentLoader })
	});
	esm_default.get("begin:https://example.com/person", {
		headers: { "Content-Type": "application/activity+json" },
		body: person_default
	});
	const createTestContext = () => {
		const kv = new MemoryKvStore();
		const inbox = [];
		const dispatches = [];
		const federation = createFederation({
			kv,
			documentLoaderFactory: () => mockDocumentLoader,
			authenticatedDocumentLoaderFactory(identity) {
				const docLoader = getAuthenticatedDocumentLoader(identity);
				return (url) => {
					const urlObj = new URL(url);
					if (urlObj.host === "example.com") return docLoader(url);
					return mockDocumentLoader(url);
				};
			}
		});
		federation.setActorDispatcher("/users/{identifier}", (ctx, identifier) => {
			dispatches.push(identifier);
			return new Person({
				id: ctx.getActorUri(identifier),
				inbox: ctx.getInboxUri(identifier),
				preferredUsername: identifier
			});
		}).setKeyPairsDispatcher(() => {
			return [{
				privateKey: rsaPrivateKey2,
				publicKey: rsaPublicKey2.publicKey
			}];
		});
		federation.setInboxDispatcher("/users/{identifier}/inbox", () => {
			return { items: [] };
		});
		federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, (_ctx, activity) => {
			inbox.push(activity.id.toString());
			return;
		});
		return {
			federation,
			inbox,
			dispatches
		};
	};
	await t.step("POST without accepts header", async () => {
		const { federation, inbox } = createTestContext();
		const response = await federation.fetch(new Request("https://example.com/inbox", { method: "POST" }), { contextData: void 0 });
		assertEquals(inbox, []);
		assertEquals(response.status, 406);
	});
	await t.step("GET without accepts header", async () => {
		const { federation, dispatches } = createTestContext();
		const response = await federation.fetch(new Request("https://example.com/users/actor", { method: "GET" }), { contextData: void 0 });
		assertEquals(dispatches, []);
		assertEquals(response.status, 406);
	});
	await t.step("POST with application/json", async () => {
		const { federation, inbox } = createTestContext();
		const request = await signRequest(new Request("https://example.com/users/json/inbox", {
			method: "POST",
			headers: {
				"Accept": "application/json",
				"Content-Type": "application/json"
			},
			body: JSON.stringify(create_default)
		}), rsaPrivateKey2, rsaPublicKey2.id);
		const response = await federation.fetch(request, { contextData: void 0 });
		assertEquals(response.status, 202);
		assertEquals(inbox.length, 1, "Expected one item in the inbox, json");
		assertEquals(inbox[0], create_default.id);
	});
	await t.step("GET with application/json", async () => {
		const { federation, dispatches } = createTestContext();
		const response = await federation.fetch(new Request("https://example.com/users/json", {
			method: "GET",
			headers: { "Accept": "application/json" }
		}), { contextData: void 0 });
		assertEquals(dispatches, ["json"]);
		assertEquals(response.status, 200);
	});
	await t.step("POST with application/ld+json", async () => {
		const { federation, inbox } = createTestContext();
		const request = await signRequest(new Request("https://example.com/users/ld/inbox", {
			method: "POST",
			headers: {
				"Accept": "application/ld+json",
				"Content-Type": "application/activity+json"
			},
			body: JSON.stringify(create_default)
		}), rsaPrivateKey2, rsaPublicKey2.id);
		const response = await federation.fetch(request, { contextData: void 0 });
		assertEquals(response.status, 202);
		assertEquals(inbox.length, 1, "Expected one inbox activity, ld+json");
		assertEquals(inbox[0], create_default.id);
	});
	await t.step("GET with application/ld+json", async () => {
		const { federation, dispatches } = createTestContext();
		const request = new Request("https://example.com/users/ld", {
			method: "GET",
			headers: { "Accept": "application/ld+json" }
		});
		const response = await federation.fetch(request, { contextData: void 0 });
		assertEquals(dispatches, ["ld"]);
		assertEquals(response.status, 200);
	});
	await t.step("POST with application/activity+json", async () => {
		const { federation, inbox } = createTestContext();
		const request = await signRequest(new Request("https://example.com/users/activity/inbox", {
			method: "POST",
			headers: {
				"Accept": "application/activity+json",
				"Content-Type": "application/activity+json"
			},
			body: JSON.stringify(create_default)
		}), rsaPrivateKey2, rsaPublicKey2.id);
		const response = await federation.fetch(request, { contextData: void 0 });
		assertEquals(response.status, 202);
		assertEquals(inbox.length, 1);
		assertEquals(inbox[0], create_default.id);
	});
	await t.step("GET with application/activity+json", async () => {
		const { federation, dispatches } = createTestContext();
		const request = new Request("https://example.com/users/activity", {
			method: "GET",
			headers: { "Accept": "application/ld+json" }
		});
		const response = await federation.fetch(request, { contextData: void 0 });
		assertEquals(dispatches, ["activity"]);
		assertEquals(response.status, 200);
	});
	await t.step("onNotAcceptable with POST", async () => {
		const { federation } = createTestContext();
		let notAcceptableCalled = false;
		const response = await federation.fetch(new Request("https://example.com/users/html/inbox", {
			method: "POST",
			headers: { "Accept": "text/html" }
		}), {
			contextData: void 0,
			onNotAcceptable: () => {
				notAcceptableCalled = true;
				return new Response("handled by onNotAcceptable", { status: 200 });
			}
		});
		assertEquals(notAcceptableCalled, true);
		assertEquals(response.status, 200);
		assertEquals(await response.text(), "handled by onNotAcceptable");
	});
	await t.step("onNotAcceptable with GET", async () => {
		const { federation } = createTestContext();
		let notAcceptableCalled = false;
		const response = await federation.fetch(new Request("https://example.com/users/html", {
			method: "GET",
			headers: { "Accept": "text/html" }
		}), {
			contextData: void 0,
			onNotAcceptable: () => {
				notAcceptableCalled = true;
				return new Response("handled by onNotAcceptable", { status: 200 });
			}
		});
		assertEquals(notAcceptableCalled, true);
		assertEquals(response.status, 200);
		assertEquals(await response.text(), "handled by onNotAcceptable");
	});
	esm_default.hardReset();
});
test("Federation.setInboxListeners()", async (t) => {
	const kv = new MemoryKvStore();
	esm_default.spyGlobal();
	esm_default.get("https://example.com/key2", {
		headers: { "Content-Type": "application/activity+json" },
		body: await rsaPublicKey2.toJsonLd({ contextLoader: mockDocumentLoader })
	});
	esm_default.get("begin:https://example.com/person2", {
		headers: { "Content-Type": "application/activity+json" },
		body: person2_default
	});
	esm_default.get("begin:https://example.com/person", {
		headers: { "Content-Type": "application/activity+json" },
		body: person_default
	});
	await t.step("path match", () => {
		const federation = createFederation({
			kv,
			documentLoaderFactory: () => mockDocumentLoader
		});
		federation.setInboxDispatcher("/users/{identifier}/inbox", () => ({ items: [] }));
		assertThrows(() => federation.setInboxListeners("/users/{identifier}/inbox2"), RouterError);
	});
	await t.step("wrong variables in path", () => {
		const federation = createFederation({
			kv,
			documentLoaderFactory: () => mockDocumentLoader
		});
		assertThrows(() => federation.setInboxListeners("/users/inbox"), RouterError);
		assertThrows(() => federation.setInboxListeners("/users/{identifier}/inbox/{id2}"), RouterError);
		assertThrows(() => federation.setInboxListeners("/users/{identifier}/inbox/{handle}"), RouterError);
		assertThrows(() => federation.setInboxListeners("/users/{identifier2}/inbox"), RouterError);
	});
	await t.step("on()", async () => {
		const authenticatedRequests = [];
		const federation = createFederation({
			kv,
			documentLoaderFactory: () => mockDocumentLoader,
			authenticatedDocumentLoaderFactory(identity) {
				const docLoader = getAuthenticatedDocumentLoader(identity);
				return (url) => {
					const urlObj = new URL(url);
					authenticatedRequests.push([url, identity.keyId.href]);
					if (urlObj.host === "example.com") return docLoader(url);
					return mockDocumentLoader(url);
				};
			}
		});
		const inbox = [];
		federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, (ctx, create) => {
			inbox.push([ctx, create]);
		});
		let response = await federation.fetch(new Request("https://example.com/inbox", {
			method: "POST",
			headers: { "accept": "application/ld+json" }
		}), { contextData: void 0 });
		assertEquals(inbox, []);
		assertEquals(response.status, 404);
		federation.setActorDispatcher("/users/{identifier}", (_, identifier) => identifier === "john" ? new Person({}) : null).setKeyPairsDispatcher(() => [{
			privateKey: rsaPrivateKey2,
			publicKey: rsaPublicKey2.publicKey
		}]);
		const options = {
			documentLoader: mockDocumentLoader,
			contextLoader: mockDocumentLoader
		};
		const activity = () => new Create({
			id: new URL("https://example.com/activities/" + crypto.randomUUID()),
			actor: new URL("https://example.com/person2")
		});
		response = await federation.fetch(new Request("https://example.com/inbox", {
			method: "POST",
			body: JSON.stringify(await activity().toJsonLd(options)),
			headers: {
				"accept": "application/ld+json",
				"content-type": "application/ld+json"
			}
		}), { contextData: void 0 });
		assertEquals(inbox, []);
		assertEquals(response.status, 401);
		response = await federation.fetch(new Request("https://example.com/users/no-one/inbox", {
			method: "POST",
			headers: { "accept": "application/ld+json" }
		}), { contextData: void 0 });
		assertEquals(inbox, []);
		assertEquals(response.status, 404);
		response = await federation.fetch(new Request("https://example.com/users/john/inbox", {
			method: "POST",
			body: JSON.stringify(await activity().toJsonLd(options)),
			headers: {
				"accept": "application/ld+json",
				"content-type": "application/ld+json"
			}
		}), { contextData: void 0 });
		assertEquals(inbox, []);
		assertEquals(response.status, 401);
		const activityPayload = await activity().toJsonLd(options);
		let request = new Request("https://example.com/users/john/inbox", {
			method: "POST",
			headers: {
				"Content-Type": "application/activity+json",
				accept: "application/ld+json"
			},
			body: JSON.stringify(activityPayload)
		});
		request = await signRequest(request, rsaPrivateKey3, new URL("https://example.com/person2#key3"));
		response = await federation.fetch(request, { contextData: void 0 });
		assertEquals(inbox.length, 1);
		assertEquals(inbox[0][1].actorId, new URL("https://example.com/person2"));
		assertEquals(response.status, 202);
		while (authenticatedRequests.length > 0) authenticatedRequests.shift();
		assertEquals(authenticatedRequests, []);
		await inbox[0][0].documentLoader("https://example.com/person");
		assertEquals(authenticatedRequests, [["https://example.com/person", "https://example.com/users/john#main-key"]]);
		response = await federation.fetch(request, { contextData: void 0 });
		assertEquals(inbox.length, 1);
		inbox.shift();
		request = new Request("https://another.host/users/john/inbox", {
			method: "POST",
			headers: {
				"Content-Type": "application/activity+json",
				"accept": "application/ld+json"
			},
			body: JSON.stringify(activityPayload)
		});
		request = await signRequest(request, rsaPrivateKey3, new URL("https://example.com/person2#key3"));
		response = await federation.fetch(request, { contextData: void 0 });
		assertEquals(inbox.length, 1);
		assertEquals(inbox[0][1].actorId, new URL("https://example.com/person2"));
		assertEquals(response.status, 202);
		while (authenticatedRequests.length > 0) authenticatedRequests.shift();
		assertEquals(authenticatedRequests, []);
		await inbox[0][0].documentLoader("https://example.com/person");
		assertEquals(authenticatedRequests, [["https://example.com/person", "https://another.host/users/john#main-key"]]);
		inbox.shift();
		request = new Request("https://example.com/inbox", {
			method: "POST",
			headers: {
				"Content-Type": "application/activity+json",
				"accept": "application/ld+json"
			},
			body: JSON.stringify(await activity().toJsonLd(options))
		});
		request = await signRequest(request, rsaPrivateKey3, new URL("https://example.com/person2#key3"));
		response = await federation.fetch(request, { contextData: void 0 });
		assertEquals(inbox.length, 1);
		assertEquals(inbox[0][1].actorId, new URL("https://example.com/person2"));
		assertEquals(response.status, 202);
		while (authenticatedRequests.length > 0) authenticatedRequests.shift();
		assertEquals(authenticatedRequests, []);
		await inbox[0][0].documentLoader("https://example.com/person");
		assertEquals(authenticatedRequests, []);
		inbox.shift();
		request = new Request("https://example.com/users/john/inbox", {
			method: "POST",
			headers: {
				"Content-Type": "application/activity+json",
				"accept": "application/ld+json"
			},
			body: JSON.stringify(await (await signObject(activity(), ed25519PrivateKey, ed25519Multikey.id, options)).toJsonLd(options))
		});
		response = await federation.fetch(request, { contextData: void 0 });
		assertEquals(inbox.length, 1);
		assertEquals(inbox[0][1].actorId, new URL("https://example.com/person2"));
		assertEquals(response.status, 202);
		while (authenticatedRequests.length > 0) authenticatedRequests.shift();
		assertEquals(authenticatedRequests, []);
		await inbox[0][0].documentLoader("https://example.com/person");
		assertEquals(authenticatedRequests, [["https://example.com/person", "https://example.com/users/john#main-key"]]);
	});
	await t.step("onError()", async () => {
		const federation = createFederation({
			kv,
			documentLoaderFactory: () => mockDocumentLoader,
			authenticatedDocumentLoaderFactory(identity) {
				const docLoader = getAuthenticatedDocumentLoader(identity);
				return (url) => {
					const urlObj = new URL(url);
					if (urlObj.host === "example.com") return docLoader(url);
					return mockDocumentLoader(url);
				};
			}
		});
		federation.setActorDispatcher("/users/{identifier}", (_, identifier) => identifier === "john" ? new Person({}) : null).setKeyPairsDispatcher(() => [{
			privateKey: rsaPrivateKey2,
			publicKey: rsaPublicKey2.publicKey
		}]);
		const error = /* @__PURE__ */ new Error("test");
		const errors = [];
		federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, () => {
			throw error;
		}).onError((_, e) => {
			errors.push(e);
		});
		const activity = new Create({ actor: new URL("https://example.com/person") });
		let request = new Request("https://example.com/users/john/inbox", {
			method: "POST",
			headers: {
				"Content-Type": "application/activity+json",
				"Accept": "application/activity+json"
			},
			body: JSON.stringify(await activity.toJsonLd({ contextLoader: mockDocumentLoader }))
		});
		request = await signRequest(request, rsaPrivateKey2, new URL("https://example.com/key2"));
		const response = await federation.fetch(request, { contextData: void 0 });
		assertEquals(errors.length, 1);
		assertEquals(errors[0], error);
		assertEquals(response.status, 500);
	});
	esm_default.hardReset();
});
test("Federation.setInboxDispatcher()", async (t) => {
	const kv = new MemoryKvStore();
	await t.step("path match", () => {
		const federation = createFederation({
			kv,
			documentLoaderFactory: () => mockDocumentLoader
		});
		federation.setInboxListeners("/users/{identifier}/inbox");
		assertThrows(() => federation.setInboxDispatcher("/users/{identifier}/inbox2", () => ({ items: [] })), RouterError);
	});
	await t.step("path match", () => {
		const federation = createFederation({
			kv,
			documentLoaderFactory: () => mockDocumentLoader
		});
		federation.setInboxListeners("/users/{identifier}/inbox");
		federation.setInboxDispatcher("/users/{identifier}/inbox", () => ({ items: [] }));
	});
	await t.step("wrong variables in path", () => {
		const federation = createFederation({
			kv,
			documentLoaderFactory: () => mockDocumentLoader
		});
		assertThrows(() => federation.setInboxDispatcher("/users/inbox", () => ({ items: [] })), RouterError);
		assertThrows(() => federation.setInboxDispatcher("/users/{identifier}/inbox/{identifier2}", () => ({ items: [] })), RouterError);
		assertThrows(() => federation.setInboxDispatcher("/users/{identifier2}/inbox", () => ({ items: [] })), RouterError);
	});
});
test("FederationImpl.sendActivity()", async (t) => {
	esm_default.spyGlobal();
	let verified = null;
	let request = null;
	esm_default.post("https://example.com/inbox", async (cl) => {
		verified = [];
		request = cl.request.clone();
		const options = {
			documentLoader: mockDocumentLoader,
			contextLoader: mockDocumentLoader
		};
		let json = await cl.request.json();
		if (await verifyJsonLd(json, options)) verified.push("ld");
		json = detachSignature(json);
		let activity = await verifyObject(Activity, json, options);
		if (activity == null) activity = await Activity.fromJsonLd(json, options);
		else verified.push("proof");
		const key = await verifyRequest(request, options);
		if (key != null && await doesActorOwnKey(activity, key, options)) verified.push("http");
		if (verified.length > 0) return new Response(null, { status: 202 });
		return new Response(null, { status: 401 });
	});
	const kv = new MemoryKvStore();
	const federation = new FederationImpl({
		kv,
		contextLoader: mockDocumentLoader
	});
	const context = federation.createContext(new URL("https://example.com/"));
	await t.step("success", async () => {
		const activity = new Create({
			id: new URL("https://example.com/activity/1"),
			actor: new URL("https://example.com/person")
		});
		const inboxes = { "https://example.com/inbox": {
			actorIds: ["https://example.com/recipient"],
			sharedInbox: false
		} };
		await federation.sendActivity([{
			privateKey: rsaPrivateKey2,
			keyId: rsaPublicKey2.id
		}], inboxes, activity, { context });
		assertEquals(verified, ["http"]);
		assertInstanceOf(request, Request);
		assertEquals(request?.method, "POST");
		assertEquals(request?.url, "https://example.com/inbox");
		assertEquals(request?.headers.get("Content-Type"), "application/activity+json");
		verified = null;
		await federation.sendActivity([{
			privateKey: rsaPrivateKey3,
			keyId: rsaPublicKey3.id
		}], inboxes, activity.clone({ actor: new URL("https://example.com/person2") }), { context });
		assertEquals(verified, ["ld", "http"]);
		assertInstanceOf(request, Request);
		assertEquals(request?.method, "POST");
		assertEquals(request?.url, "https://example.com/inbox");
		assertEquals(request?.headers.get("Content-Type"), "application/activity+json");
		verified = null;
		await federation.sendActivity([{
			privateKey: ed25519PrivateKey,
			keyId: ed25519Multikey.id
		}], inboxes, activity.clone({ actor: new URL("https://example.com/person2") }), { context });
		assertEquals(verified, ["proof"]);
		assertInstanceOf(request, Request);
		assertEquals(request?.method, "POST");
		assertEquals(request?.url, "https://example.com/inbox");
		assertEquals(request?.headers.get("Content-Type"), "application/activity+json");
		verified = null;
		await federation.sendActivity([{
			privateKey: rsaPrivateKey3,
			keyId: rsaPublicKey3.id
		}, {
			privateKey: ed25519PrivateKey,
			keyId: ed25519Multikey.id
		}], inboxes, activity.clone({ actor: new URL("https://example.com/person2") }), { context });
		assertEquals(verified, [
			"ld",
			"proof",
			"http"
		]);
		assertInstanceOf(request, Request);
		assertEquals(request?.method, "POST");
		assertEquals(request?.url, "https://example.com/inbox");
		assertEquals(request?.headers.get("Content-Type"), "application/activity+json");
	});
	esm_default.hardReset();
});
test("FederationImpl.processQueuedTask()", async (t) => {
	await t.step("with MessageQueue having nativeRetrial", async () => {
		const kv = new MemoryKvStore();
		const queuedMessages = [];
		const queue = {
			nativeRetrial: true,
			enqueue(message, _options) {
				queuedMessages.push(message);
				return Promise.resolve();
			},
			listen(_handler, _options) {
				return Promise.resolve();
			}
		};
		const federation = new FederationImpl({
			kv,
			queue
		});
		federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, () => {
			throw new Error("Intended error for testing");
		});
		await assertRejects(() => federation.processQueuedTask(void 0, {
			type: "outbox",
			id: crypto.randomUUID(),
			baseUrl: "https://example.com",
			keys: [],
			activity: {
				"@context": "https://www.w3.org/ns/activitystreams",
				type: "Create",
				actor: "https://example.com/users/alice",
				object: {
					type: "Note",
					content: "test"
				}
			},
			activityType: "https://www.w3.org/ns/activitystreams#Create",
			inbox: "https://invalid-domain-that-does-not-exist.example/inbox",
			sharedInbox: false,
			started: (/* @__PURE__ */ new Date()).toISOString(),
			attempt: 0,
			headers: {},
			traceContext: {}
		}), Error);
		assertEquals(queuedMessages, []);
		await assertRejects(() => federation.processQueuedTask(void 0, {
			type: "inbox",
			id: crypto.randomUUID(),
			baseUrl: "https://example.com",
			activity: {
				"@context": "https://www.w3.org/ns/activitystreams",
				type: "Create",
				actor: "https://remote.example/users/alice",
				object: {
					type: "Note",
					content: "Hello world"
				}
			},
			started: (/* @__PURE__ */ new Date()).toISOString(),
			attempt: 0,
			identifier: null,
			traceContext: {}
		}), Error);
		assertEquals(queuedMessages, []);
	});
	await t.step("with MessageQueue having no nativeRetrial", async () => {
		const kv = new MemoryKvStore();
		let queuedMessages = [];
		const queue = {
			enqueue(message, _options) {
				queuedMessages.push(message);
				return Promise.resolve();
			},
			listen(_handler, _options) {
				return Promise.resolve();
			}
		};
		const federation = new FederationImpl({
			kv,
			queue
		});
		federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, () => {
			throw new Error("Intended error for testing");
		});
		const outboxMessage = {
			type: "outbox",
			id: crypto.randomUUID(),
			baseUrl: "https://example.com",
			keys: [],
			activity: {
				"@context": "https://www.w3.org/ns/activitystreams",
				type: "Create",
				actor: "https://example.com/users/alice",
				object: {
					type: "Note",
					content: "test"
				}
			},
			activityType: "https://www.w3.org/ns/activitystreams#Create",
			inbox: "https://invalid-domain-that-does-not-exist.example/inbox",
			sharedInbox: false,
			started: (/* @__PURE__ */ new Date()).toISOString(),
			attempt: 0,
			headers: {},
			traceContext: {}
		};
		await federation.processQueuedTask(void 0, outboxMessage);
		assertEquals(queuedMessages, [{
			...outboxMessage,
			attempt: 1
		}]);
		queuedMessages = [];
		const inboxMessage = {
			type: "inbox",
			id: crypto.randomUUID(),
			baseUrl: "https://example.com",
			activity: {
				"@context": "https://www.w3.org/ns/activitystreams",
				type: "Create",
				actor: "https://remote.example/users/alice",
				object: {
					type: "Note",
					content: "Hello world"
				}
			},
			started: (/* @__PURE__ */ new Date()).toISOString(),
			attempt: 0,
			identifier: null,
			traceContext: {}
		};
		await federation.processQueuedTask(void 0, inboxMessage);
		assertEquals(queuedMessages, [{
			...inboxMessage,
			attempt: 1
		}]);
	});
});
test("ContextImpl.lookupObject()", async (t) => {
	esm_default.spyGlobal();
	esm_default.get("begin:https://localhost/.well-known/webfinger", {
		headers: { "Content-Type": "application/jrd+json" },
		body: {
			subject: "acct:test@localhost",
			links: [{
				rel: "self",
				type: "application/activity+json",
				href: "https://localhost/actor"
			}]
		}
	});
	esm_default.get("https://localhost/actor", {
		headers: { "Content-Type": "application/activity+json" },
		body: {
			"@context": "https://www.w3.org/ns/activitystreams",
			"type": "Person",
			"id": "https://localhost/actor",
			"preferredUsername": "test"
		}
	});
	await t.step("allowPrivateAddress: true", async () => {
		const federation = createFederation({
			kv: new MemoryKvStore(),
			allowPrivateAddress: true
		});
		const ctx = federation.createContext(new URL("https://example.com/"));
		const result = await ctx.lookupObject("@test@localhost");
		assertInstanceOf(result, Person);
		assertEquals(result.id, new URL("https://localhost/actor"));
		assertEquals(result.preferredUsername, "test");
	});
	await t.step("allowPrivateAddress: false", async () => {
		const federation = createFederation({
			kv: new MemoryKvStore(),
			allowPrivateAddress: false
		});
		const ctx = federation.createContext(new URL("https://example.com/"));
		const result = await ctx.lookupObject("@test@localhost");
		assertEquals(result, null);
	});
	esm_default.hardReset();
});
test("ContextImpl.sendActivity()", async (t) => {
	esm_default.spyGlobal();
	let verified = null;
	let request = null;
	let collectionSyncHeader = null;
	esm_default.post("https://example.com/inbox", async (cl) => {
		verified = [];
		request = cl.request.clone();
		collectionSyncHeader = cl.request.headers.get("Collection-Synchronization");
		const options = {
			async documentLoader(url) {
				const response = await federation.fetch(new Request(url, { headers: { "accept": "application/ld+json" } }), { contextData: void 0 });
				if (response.ok) return {
					contextUrl: null,
					document: await response.json(),
					documentUrl: response.url
				};
				return await mockDocumentLoader(url);
			},
			contextLoader: mockDocumentLoader,
			keyCache: {
				async get(keyId) {
					const ctx = federation.createContext(new URL("https://example.com/"), void 0);
					const keys = await ctx.getActorKeyPairs("1");
					for (const key$1 of keys) if (key$1.keyId.href === keyId.href) if (key$1.publicKey.algorithm.name === "Ed25519") return key$1.multikey;
					else return key$1.cryptographicKey;
					return void 0;
				},
				async set(_keyId, _key) {}
			}
		};
		let json = await cl.request.json();
		if (await verifyJsonLd(json, options)) verified.push("ld");
		json = detachSignature(json);
		let activity = await verifyObject(Activity, json, options);
		if (activity == null) activity = await Activity.fromJsonLd(json, options);
		else verified.push("proof");
		const key = await verifyRequest(request, options);
		if (key != null && await doesActorOwnKey(activity, key, options)) verified.push("http");
		if (verified.length > 0) return new Response(null, { status: 202 });
		return new Response(null, { status: 401 });
	});
	const kv = new MemoryKvStore();
	const federation = new FederationImpl({
		kv,
		contextLoader: mockDocumentLoader
	});
	federation.setActorDispatcher("/{identifier}", async (ctx, identifier) => {
		if (identifier !== "1") return null;
		const keys = await ctx.getActorKeyPairs(identifier);
		return new Person({
			id: ctx.getActorUri(identifier),
			preferredUsername: "john",
			publicKey: keys[0].cryptographicKey,
			assertionMethods: keys.map((k) => k.multikey)
		});
	}).setKeyPairsDispatcher((_ctx, identifier) => {
		if (identifier !== "1") return [];
		return [{
			privateKey: rsaPrivateKey2,
			publicKey: rsaPublicKey2.publicKey
		}, {
			privateKey: ed25519PrivateKey,
			publicKey: ed25519PublicKey.publicKey
		}];
	}).mapHandle((_ctx, username) => username === "john" ? "1" : null);
	federation.setFollowersDispatcher("/users/{identifier}/followers", () => ({ items: [{
		id: new URL("https://example.com/recipient"),
		inboxId: new URL("https://example.com/inbox")
	}] }));
	await t.step("success", async () => {
		const activity = new Create({ actor: new URL("https://example.com/person") });
		const ctx = new ContextImpl({
			data: void 0,
			federation,
			url: new URL("https://example.com/"),
			documentLoader: fetchDocumentLoader,
			contextLoader: fetchDocumentLoader
		});
		await ctx.sendActivity([{
			privateKey: rsaPrivateKey2,
			keyId: rsaPublicKey2.id
		}], {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}, activity);
		assertEquals(verified, ["http"]);
		assertInstanceOf(request, Request);
		assertEquals(request?.method, "POST");
		assertEquals(request?.url, "https://example.com/inbox");
		assertEquals(request?.headers.get("Content-Type"), "application/activity+json");
		verified = null;
		await ctx.sendActivity([{
			privateKey: rsaPrivateKey3,
			keyId: rsaPublicKey3.id
		}], {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}, activity.clone({ actor: new URL("https://example.com/person2") }));
		assertEquals(verified, ["ld", "http"]);
		assertInstanceOf(request, Request);
		assertEquals(request?.method, "POST");
		assertEquals(request?.url, "https://example.com/inbox");
		assertEquals(request?.headers.get("Content-Type"), "application/activity+json");
		verified = null;
		await ctx.sendActivity({ identifier: "1" }, {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}, activity.clone({ actor: ctx.getActorUri("1") }));
		assertEquals(verified, [
			"ld",
			"proof",
			"http"
		]);
		assertInstanceOf(request, Request);
		assertEquals(request?.method, "POST");
		assertEquals(request?.url, "https://example.com/inbox");
		assertEquals(request?.headers.get("Content-Type"), "application/activity+json");
		verified = null;
		await ctx.sendActivity({ username: "john" }, {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}, activity.clone({ actor: ctx.getActorUri("1") }));
		assertEquals(verified, [
			"ld",
			"proof",
			"http"
		]);
		assertInstanceOf(request, Request);
		assertEquals(request?.method, "POST");
		assertEquals(request?.url, "https://example.com/inbox");
		assertEquals(request?.headers.get("Content-Type"), "application/activity+json");
		await assertRejects(() => ctx.sendActivity({ identifier: "not-found" }, {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}, activity.clone({ actor: ctx.getActorUri("1") })));
		await assertRejects(() => ctx.sendActivity({ username: "not-found" }, {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}, activity.clone({ actor: ctx.getActorUri("1") })));
	});
	const queue = {
		messages: [],
		enqueue(message) {
			this.messages.push(message);
			return Promise.resolve();
		},
		async listen() {},
		clear() {
			while (this.messages.length > 0) this.messages.shift();
		}
	};
	const federation2 = new FederationImpl({
		kv,
		contextLoader: mockDocumentLoader,
		queue
	});
	federation2.setActorDispatcher("/{identifier}", async (ctx, identifier) => {
		if (identifier !== "john") return null;
		const keys = await ctx.getActorKeyPairs(identifier);
		return new Person({
			id: ctx.getActorUri(identifier),
			preferredUsername: "john",
			publicKey: keys[0].cryptographicKey,
			assertionMethods: keys.map((k) => k.multikey)
		});
	}).setKeyPairsDispatcher((_ctx, identifier) => {
		if (identifier !== "john") return [];
		return [{
			privateKey: rsaPrivateKey2,
			publicKey: rsaPublicKey2.publicKey
		}, {
			privateKey: ed25519PrivateKey,
			publicKey: ed25519PublicKey.publicKey
		}];
	});
	const ctx2 = new ContextImpl({
		data: void 0,
		federation: federation2,
		url: new URL("https://example.com/"),
		documentLoader: fetchDocumentLoader,
		contextLoader: fetchDocumentLoader
	});
	await t.step("fanout: \"force\"", async () => {
		const activity = new Create({
			id: new URL("https://example.com/activity/1"),
			actor: new URL("https://example.com/person")
		});
		await ctx2.sendActivity({ username: "john" }, {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}, activity, { fanout: "force" });
		assertEquals(queue.messages, [{
			id: queue.messages[0].id,
			type: "fanout",
			activity: await activity.toJsonLd({
				format: "compact",
				contextLoader: fetchDocumentLoader
			}),
			activityId: "https://example.com/activity/1",
			activityType: "https://www.w3.org/ns/activitystreams#Create",
			baseUrl: "https://example.com",
			collectionSync: void 0,
			inboxes: { "https://example.com/inbox": {
				actorIds: ["https://example.com/recipient"],
				sharedInbox: false
			} },
			keys: queue.messages[0].type === "fanout" ? queue.messages[0].keys : [],
			traceContext: {}
		}]);
	});
	queue.clear();
	await t.step("fanout: \"skip\"", async () => {
		const activity = new Create({
			id: new URL("https://example.com/activity/1"),
			actor: new URL("https://example.com/person")
		});
		await ctx2.sendActivity({ username: "john" }, {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}, activity, { fanout: "skip" });
		assertEquals(queue.messages, [{
			...queue.messages[0],
			type: "outbox"
		}]);
	});
	queue.clear();
	await t.step("fanout: \"auto\"", async () => {
		const activity = new Create({
			id: new URL("https://example.com/activity/1"),
			actor: new URL("https://example.com/person")
		});
		await ctx2.sendActivity({ username: "john" }, {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}, activity, { fanout: "auto" });
		assertEquals(queue.messages, [{
			...queue.messages[0],
			type: "outbox"
		}]);
		queue.clear();
		await ctx2.sendActivity({ username: "john" }, [
			{
				id: new URL("https://example.com/recipient"),
				inboxId: new URL("https://example.com/inbox")
			},
			{
				id: new URL("https://example2.com/recipient"),
				inboxId: new URL("https://example2.com/inbox")
			},
			{
				id: new URL("https://example3.com/recipient"),
				inboxId: new URL("https://example3.com/inbox")
			},
			{
				id: new URL("https://example4.com/recipient"),
				inboxId: new URL("https://example4.com/inbox")
			},
			{
				id: new URL("https://example5.com/recipient"),
				inboxId: new URL("https://example5.com/inbox")
			}
		], activity, { fanout: "auto" });
		assertEquals(queue.messages, [{
			...queue.messages[0],
			type: "fanout"
		}]);
	});
	collectionSyncHeader = null;
	await t.step("followers collection without syncCollection", async () => {
		const ctx = new ContextImpl({
			data: void 0,
			federation,
			url: new URL("https://example.com/"),
			documentLoader: fetchDocumentLoader,
			contextLoader: fetchDocumentLoader
		});
		const activity = new Create({
			id: new URL("https://example.com/activity/1"),
			actor: ctx.getActorUri("1"),
			to: ctx.getFollowersUri("1")
		});
		await ctx.sendActivity({ identifier: "1" }, "followers", activity);
		assertEquals(collectionSyncHeader, null);
	});
	collectionSyncHeader = null;
	await t.step("followers collection with syncCollection", async () => {
		const ctx = new ContextImpl({
			data: void 0,
			federation,
			url: new URL("https://example.com/"),
			documentLoader: fetchDocumentLoader,
			contextLoader: fetchDocumentLoader
		});
		const activity = new Create({
			id: new URL("https://example.com/activity/2"),
			actor: ctx.getActorUri("1"),
			to: ctx.getFollowersUri("1")
		});
		await ctx.sendActivity({ identifier: "1" }, "followers", activity, {
			syncCollection: true,
			preferSharedInbox: true
		});
		assertNotEquals(collectionSyncHeader, null);
	});
	esm_default.hardReset();
});
test({
	name: "ContextImpl.routeActivity()",
	permissions: {
		env: true,
		read: true
	},
	async fn() {
		const federation = new FederationImpl({ kv: new MemoryKvStore() });
		const activities = [];
		federation.setInboxListeners("/u/{identifier}/i", "/i").on(Offer, (ctx$1, offer) => {
			activities.push([ctx$1.recipient, offer]);
		});
		const ctx = new ContextImpl({
			url: new URL("https://example.com/"),
			federation,
			data: void 0,
			documentLoader: mockDocumentLoader,
			contextLoader: fetchDocumentLoader
		});
		assertFalse(await ctx.routeActivity(null, new Offer({ actor: new URL("https://example.com/person") })));
		assertEquals(activities, []);
		const signedOffer = await signObject(new Offer({ actor: new URL("https://example.com/person2") }), ed25519PrivateKey, ed25519Multikey.id);
		assert(await ctx.routeActivity(null, signedOffer));
		assertEquals(activities, [[null, signedOffer]]);
		const signedInvite = await signObject(new Invite({ actor: new URL("https://example.com/person2") }), ed25519PrivateKey, ed25519Multikey.id);
		assert(await ctx.routeActivity("id", signedInvite));
		assertEquals(activities, [[null, signedOffer], ["id", signedInvite]]);
		assertFalse(await ctx.routeActivity(null, new Create({
			id: new URL("https://example.com/not-found"),
			actor: new URL("https://example.com/person")
		})));
		assertEquals(activities, [[null, signedOffer], ["id", signedInvite]]);
		assertFalse(await ctx.routeActivity(null, new Create({
			id: new URL("https://example.com/person"),
			actor: new URL("https://example.com/person")
		})));
		assertEquals(activities, [[null, signedOffer], ["id", signedInvite]]);
		assertFalse(await ctx.routeActivity(null, new Announce({
			id: new URL("https://example.com/announce#diffrent-id"),
			actor: new URL("https://example.com/person")
		})));
		assertEquals(activities, [[null, signedOffer], ["id", signedInvite]]);
		assertFalse(await ctx.routeActivity(null, new Announce({
			id: new URL("https://example.com/announce"),
			actor: new URL("https://example.com/person")
		})));
		assertEquals(activities, [[null, signedOffer], ["id", signedInvite]]);
		assertFalse(await ctx.routeActivity(null, new Create({
			id: new URL("https://example.com/cross-origin-actor"),
			actor: new URL("https://cross-origin.com/actor")
		})));
		assertEquals(activities, [[null, signedOffer], ["id", signedInvite]]);
		assert(await ctx.routeActivity(null, new Create({
			id: new URL("https://example.com/create"),
			actor: new URL("https://example.com/person")
		})));
		assertEquals(activities, [[null, signedOffer], ["id", signedInvite]]);
		assert(await ctx.routeActivity(null, new Invite({
			id: new URL("https://example.com/invite"),
			actor: new URL("https://example.com/person")
		})));
		assertEquals(activities, [
			[null, signedOffer],
			["id", signedInvite],
			[null, new Invite({
				id: new URL("https://example.com/invite"),
				actor: new URL("https://example.com/person"),
				object: new URL("https://example.com/object")
			})]
		]);
	}
});
test("ContextImpl.getCollectionUri()", () => {
	const federation = new FederationImpl({ kv: new MemoryKvStore() });
	const base = "https://example.com";
	const ctx = new ContextImpl({
		url: new URL(base),
		federation,
		data: void 0,
		documentLoader: mockDocumentLoader,
		contextLoader: fetchDocumentLoader
	});
	const values = { id: "123" };
	const dispatcher = (_ctx, _values) => ({ items: [] });
	let url;
	const strName = "registered";
	federation.setCollectionDispatcher(strName, Object$1, "/string-route/{id}", dispatcher);
	url = ctx.getCollectionUri(strName, values);
	assertEquals(url.href, `${base}/string-route/123`);
	const unnamedSymName = Symbol(strName);
	federation.setCollectionDispatcher(unnamedSymName, Object$1, "/symbol-route/{id}", dispatcher);
	url = ctx.getCollectionUri(unnamedSymName, values);
	assertEquals(url.href, `${base}/symbol-route/123`);
	const namedSymName = Symbol.for(strName);
	federation.setCollectionDispatcher(namedSymName, Object$1, "/named-symbol-route/{id}", dispatcher);
	url = ctx.getCollectionUri(namedSymName, values);
	assertEquals(url.href, `${base}/named-symbol-route/123`);
	const notReg = "not-registered";
	assertThrows(() => ctx.getCollectionUri(notReg, values));
	assertThrows(() => ctx.getCollectionUri(Symbol(notReg), values));
	assertThrows(() => ctx.getCollectionUri(Symbol.for(notReg), values));
});
test("InboxContextImpl.forwardActivity()", async (t) => {
	esm_default.spyGlobal();
	let verified = null;
	let request = null;
	esm_default.post("https://example.com/inbox", async (cl) => {
		verified = [];
		request = cl.request.clone();
		const options = {
			documentLoader: mockDocumentLoader,
			contextLoader: mockDocumentLoader
		};
		let json = await cl.request.json();
		if (await verifyJsonLd(json, options)) verified.push("ld");
		json = detachSignature(json);
		let activity = await verifyObject(Activity, json, options);
		if (activity == null) activity = await Activity.fromJsonLd(json, options);
		else verified.push("proof");
		const key = await verifyRequest(request, options);
		if (key != null && await doesActorOwnKey(activity, key, options)) verified.push("http");
		if (verified.length > 0) return new Response(null, { status: 202 });
		return new Response(null, { status: 401 });
	});
	const kv = new MemoryKvStore();
	const federation = new FederationImpl({
		kv,
		contextLoader: mockDocumentLoader
	});
	await t.step("skip", async () => {
		const activity = {
			"@context": "https://www.w3.org/ns/activitystreams",
			"type": "Create",
			"id": "https://example.com/activity",
			"actor": "https://example.com/person2"
		};
		const ctx = new InboxContextImpl(null, activity, "https://example.com/activity", "https://www.w3.org/ns/activitystreams#Create", {
			data: void 0,
			federation,
			url: new URL("https://example.com/"),
			documentLoader: fetchDocumentLoader,
			contextLoader: fetchDocumentLoader
		});
		await ctx.forwardActivity([{
			privateKey: rsaPrivateKey2,
			keyId: rsaPublicKey2.id
		}], {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}, { skipIfUnsigned: true });
		assertEquals(verified, null);
	});
	await t.step("unsigned", async () => {
		const activity = {
			"@context": "https://www.w3.org/ns/activitystreams",
			"type": "Create",
			"id": "https://example.com/activity",
			"actor": "https://example.com/person2"
		};
		const ctx = new InboxContextImpl(null, activity, "https://example.com/activity", "https://www.w3.org/ns/activitystreams#Create", {
			data: void 0,
			federation,
			url: new URL("https://example.com/"),
			documentLoader: fetchDocumentLoader,
			contextLoader: fetchDocumentLoader
		});
		await assertRejects(() => ctx.forwardActivity([{
			privateKey: rsaPrivateKey2,
			keyId: rsaPublicKey2.id
		}], {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}));
		assertEquals(verified, []);
	});
	await t.step("Object Integrity Proofs", async () => {
		const activity = await signObject(new Create({
			id: new URL("https://example.com/activity"),
			actor: new URL("https://example.com/person2")
		}), ed25519PrivateKey, ed25519Multikey.id, {
			contextLoader: mockDocumentLoader,
			documentLoader: mockDocumentLoader
		});
		const ctx = new InboxContextImpl(null, await activity.toJsonLd({ contextLoader: mockDocumentLoader }), activity.id?.href, getTypeId(activity).href, {
			data: void 0,
			federation,
			url: new URL("https://example.com/"),
			documentLoader: fetchDocumentLoader,
			contextLoader: fetchDocumentLoader
		});
		await ctx.forwardActivity([{
			privateKey: rsaPrivateKey2,
			keyId: rsaPublicKey2.id
		}], {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}, { skipIfUnsigned: true });
		assertEquals(verified, ["proof"]);
	});
	await t.step("LD Signatures", async () => {
		const activity = await signJsonLd({
			"@context": "https://www.w3.org/ns/activitystreams",
			"type": "Create",
			"id": "https://example.com/activity",
			"actor": "https://example.com/person2"
		}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader });
		const ctx = new InboxContextImpl(null, activity, "https://example.com/activity", "https://www.w3.org/ns/activitystreams#Create", {
			data: void 0,
			federation,
			url: new URL("https://example.com/"),
			documentLoader: fetchDocumentLoader,
			contextLoader: fetchDocumentLoader
		});
		await ctx.forwardActivity([{
			privateKey: rsaPrivateKey2,
			keyId: rsaPublicKey2.id
		}], {
			id: new URL("https://example.com/recipient"),
			inboxId: new URL("https://example.com/inbox")
		}, { skipIfUnsigned: true });
		assertEquals(verified, ["ld"]);
	});
	esm_default.hardReset();
});
test("KvSpecDeterminer", async (t) => {
	await t.step("should use default spec when not found in KV", async () => {
		const kv = new MemoryKvStore();
		const prefix = ["test", "spec"];
		const determiner = new KvSpecDeterminer(kv, prefix);
		const spec = await determiner.determineSpec("example.com");
		assertEquals(spec, "rfc9421");
	});
	await t.step("should use custom default spec", async () => {
		const kv = new MemoryKvStore();
		const prefix = ["test", "spec"];
		const determiner = new KvSpecDeterminer(kv, prefix, "draft-cavage-http-signatures-12");
		const spec = await determiner.determineSpec("example.com");
		assertEquals(spec, "draft-cavage-http-signatures-12");
	});
	await t.step("should remember and retrieve spec from KV", async () => {
		const kv = new MemoryKvStore();
		const prefix = ["test", "spec"];
		const determiner = new KvSpecDeterminer(kv, prefix);
		await determiner.rememberSpec("example.com", "draft-cavage-http-signatures-12");
		const spec = await determiner.determineSpec("example.com");
		assertEquals(spec, "draft-cavage-http-signatures-12");
		const defaultSpec = await determiner.determineSpec("other.com");
		assertEquals(defaultSpec, "rfc9421");
	});
	await t.step("should override remembered spec", async () => {
		const kv = new MemoryKvStore();
		const prefix = ["test", "spec"];
		const determiner = new KvSpecDeterminer(kv, prefix);
		await determiner.rememberSpec("example.com", "draft-cavage-http-signatures-12");
		let spec = await determiner.determineSpec("example.com");
		assertEquals(spec, "draft-cavage-http-signatures-12");
		await determiner.rememberSpec("example.com", "rfc9421");
		spec = await determiner.determineSpec("example.com");
		assertEquals(spec, "rfc9421");
	});
});

//#endregion