Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ To be released. Note that 1.6.0 was skipped due to a mistake in the versioning.
- Added `Context.lookupWebFinger()` method to make WebFinger lookups
accessible from the context. [[#227]]

- Added `Context.federation` property to access the `Federation`
object from the context. [[#235]]

- Introduced `FederationBuilder` for creating a federation instance with
a builder pattern.

Expand Down Expand Up @@ -39,6 +42,7 @@ To be released. Note that 1.6.0 was skipped due to a mistake in the versioning.
[RFC 9421]: https://www.rfc-editor.org/rfc/rfc9421
[#208]: https://github.com/fedify-dev/fedify/issues/208
[#227]: https://github.com/fedify-dev/fedify/issues/227
[#235]: https://github.com/fedify-dev/fedify/pull/235


Version 1.5.3
Expand Down
23 changes: 23 additions & 0 deletions docs/manual/federation.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,29 @@ export const federation = await builder.build({
});
~~~~

If you want to access the `Federation` object inside dispatchers or listeners
before the `FederationBuilder` instantiates it, you can use
the `Context.federation` property. The `Context.federation` property refers
to the `Federation` object that is to be instantiated by the `FederationBuilder`
and is available in the `Context` object passed to the dispatchers and
listeners. For example, you can access the `Federation` object like this:

~~~~ typescript twoslash
import { type FederationBuilder, Person } from "@fedify/fedify";
const builder = null as unknown as FederationBuilder<void>;
// ---cut-before---
builder.setActorDispatcher(
"/users/{handle}",
async (ctx, handle) => {
const federation = ctx.federation; // Access the `Federation` object
// Omitted for brevity
// ---cut-start---
return new Person({});
// ---cut-end---
}
);
~~~~


The `~Federation.fetch()` API
-----------------------------
Expand Down
7 changes: 7 additions & 0 deletions fedify/federation/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
} from "../vocab/vocab.ts";
import type { ResourceDescriptor } from "../webfinger/jrd.ts";
import type { LookupWebFingerOptions } from "../webfinger/lookup.ts";
import type { Federation } from "./federation.ts";
import type { SenderKeyPair } from "./send.ts";

/**
Expand Down Expand Up @@ -77,6 +78,12 @@ export interface Context<TContextData> {
*/
readonly contextLoader: DocumentLoader;

/**
* The federation object that this context belongs to.
* @since 1.6.0
*/
readonly federation: Federation<TContextData>;

/**
* Builds the URI of the NodeInfo document.
* @returns The NodeInfo URI.
Expand Down
11 changes: 11 additions & 0 deletions fedify/federation/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
respondWithObjectIfAcceptable,
} from "./handler.ts";
import { MemoryKvStore } from "./kv.ts";
import { createFederation } from "./middleware.ts";

test("acceptsJsonLd()", () => {
assert(acceptsJsonLd(
Expand Down Expand Up @@ -68,7 +69,9 @@ test("acceptsJsonLd()", () => {
});

test("handleActor()", async () => {
const federation = createFederation<void>({ kv: new MemoryKvStore() });
let context = createRequestContext<void>({
federation,
data: undefined,
url: new URL("https://example.com/"),
getActorUri(identifier) {
Expand Down Expand Up @@ -332,7 +335,9 @@ test("handleActor()", async () => {
});

test("handleObject()", async () => {
const federation = createFederation<void>({ kv: new MemoryKvStore() });
let context = createRequestContext<void>({
federation,
data: undefined,
url: new URL("https://example.com/"),
getObjectUri(_cls, values) {
Expand Down Expand Up @@ -591,7 +596,9 @@ test("handleObject()", async () => {
});

test("handleCollection()", async () => {
const federation = createFederation<void>({ kv: new MemoryKvStore() });
let context = createRequestContext<void>({
federation,
data: undefined,
url: new URL("https://example.com/"),
getActorUri(identifier) {
Expand Down Expand Up @@ -1142,7 +1149,9 @@ test("handleInbox()", async () => {
method: "POST",
body: JSON.stringify(await activity.toJsonLd()),
});
const federation = createFederation<void>({ kv: new MemoryKvStore() });
const unsignedContext = createRequestContext({
federation,
request: unsignedRequest,
url: new URL(unsignedRequest.url),
data: undefined,
Expand Down Expand Up @@ -1221,6 +1230,7 @@ test("handleInbox()", async () => {
rsaPublicKey3.id!,
);
const signedContext = createRequestContext({
federation,
request: signedRequest,
url: new URL(signedRequest.url),
data: undefined,
Expand Down Expand Up @@ -1291,6 +1301,7 @@ test("handleInbox()", async () => {
rsaPublicKey3.id!,
);
const signedInvalidContext = createRequestContext({
federation,
request: signedInvalidRequest,
url: new URL(signedInvalidRequest.url),
data: undefined,
Expand Down
1 change: 1 addition & 0 deletions fedify/federation/middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ test("Federation.createContext()", async (t) => {
assertEquals(ctx.hostname, "example.com");
assertStrictEquals(ctx.documentLoader, documentLoader);
assertStrictEquals(ctx.contextLoader, mockDocumentLoader);
assertStrictEquals(ctx.federation, federation);
assertThrows(() => ctx.getNodeInfoUri(), RouterError);
assertThrows(() => ctx.getActorUri("handle"), RouterError);
assertThrows(
Expand Down
6 changes: 6 additions & 0 deletions fedify/nodeinfo/handler.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { assertEquals } from "@std/assert";
import type { NodeInfoDispatcher } from "../federation/callback.ts";
import { MemoryKvStore } from "../federation/kv.ts";
import { createFederation } from "../federation/middleware.ts";
import { createRequestContext } from "../testing/context.ts";
import { test } from "../testing/mod.ts";
import { handleNodeInfo, handleNodeInfoJrd } from "./handler.ts";
import { parseSemVer } from "./semver.ts";

test("handleNodeInfo()", async () => {
const request = new Request("https://example.com/nodeinfo/2.1");
const federation = createFederation<void>({ kv: new MemoryKvStore() });
const context = createRequestContext<void>({
federation,
data: undefined,
request,
url: new URL(request.url),
Expand Down Expand Up @@ -59,7 +63,9 @@ test("handleNodeInfo()", async () => {

test("handleNodeInfoJrd()", async () => {
const request = new Request("https://example.com/.well-known/nodeinfo");
const federation = createFederation<void>({ kv: new MemoryKvStore() });
let context = createRequestContext<void>({
federation,
data: undefined,
request,
url: new URL(request.url),
Expand Down
11 changes: 10 additions & 1 deletion fedify/testing/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
InboxContext,
RequestContext,
} from "../federation/context.ts";
import type { Federation } from "../federation/federation.ts";
import { RouterError } from "../federation/router.ts";
import {
lookupObject as globalLookupObject,
Expand All @@ -14,6 +15,7 @@ import { mockDocumentLoader } from "./docloader.ts";

export function createContext<TContextData>(
{
federation,
url,
canonicalOrigin,
data,
Expand All @@ -39,13 +41,18 @@ export function createContext<TContextData>(
lookupWebFinger,
sendActivity,
routeActivity,
}: Partial<Context<TContextData>> & { url?: URL; data: TContextData },
}: Partial<Context<TContextData>> & {
url?: URL;
data: TContextData;
federation: Federation<TContextData>;
},
): Context<TContextData> {
function throwRouteError(): URL {
throw new RouterError("Not implemented");
}
url ??= new URL("http://example.com/");
return {
federation,
data,
origin: url.origin,
canonicalOrigin: canonicalOrigin ?? url.origin,
Expand Down Expand Up @@ -106,6 +113,7 @@ export function createRequestContext<TContextData>(
args: Partial<RequestContext<TContextData>> & {
url: URL;
data: TContextData;
federation: Federation<TContextData>;
},
): RequestContext<TContextData> {
return {
Expand All @@ -127,6 +135,7 @@ export function createInboxContext<TContextData>(
url?: URL;
data: TContextData;
recipient?: string | null;
federation: Federation<TContextData>;
},
): InboxContext<TContextData> {
return {
Expand Down
4 changes: 4 additions & 0 deletions fedify/webfinger/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import type {
ActorHandleMapper,
} from "../federation/callback.ts";
import type { RequestContext } from "../federation/context.ts";
import { MemoryKvStore } from "../federation/kv.ts";
import { createFederation } from "../federation/middleware.ts";
import { createRequestContext } from "../testing/context.ts";
import { test } from "../testing/mod.ts";
import type { Actor } from "../vocab/actor.ts";
Expand All @@ -15,7 +17,9 @@ test("handleWebFinger()", async (t) => {
const url = new URL("https://example.com/.well-known/webfinger");

function createContext(url: URL): RequestContext<void> {
const federation = createFederation<void>({ kv: new MemoryKvStore() });
const context = createRequestContext<void>({
federation,
url,
data: undefined,
getActorUri(identifier) {
Expand Down
Loading