Loading CHANGES.md +2 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ To be released. to `AuthenticatedDocumentLoaderFactory` type. - `GetAuthenticatedDocumentLoaderOptions` interface became to extend `DocumentLoaderFactoryOptions` interface. - Added a type parameter `TContextData` to `CreateFederationOptions` interface. - Introduced `ActivityTransformer`s for adjusting outgoing activities before sending them so that some ActivityPub implementations with quirks Loading src/compat/transformers.test.ts +9 −2 Original line number Diff line number Diff line import { assertEquals } from "@std/assert/assert-equals"; import { assertNotEquals } from "@std/assert/assert-not-equals"; import { MemoryKvStore } from "../federation/kv.ts"; import { FederationImpl } from "../federation/middleware.ts"; import { test } from "../testing/mod.ts"; import { Follow, Person } from "../vocab/vocab.ts"; import { actorDehydrator, autoIdAssigner } from "./transformers.ts"; const federation = new FederationImpl<void>({ kv: new MemoryKvStore(), }); const context = federation.createContext(new URL("http://example.com/")); test("autoIdAssigner", async () => { const activity = new Follow({ actor: new URL("http://example.com/actors/1"), Loading @@ -13,7 +20,7 @@ test("autoIdAssigner", async () => { preferredUsername: "bob", }), }); const result = autoIdAssigner(activity); const result = autoIdAssigner(activity, context); assertNotEquals(result.id, null); assertEquals( await result.toJsonLd(), Loading Loading @@ -43,7 +50,7 @@ test("actorDehydrator()", async () => { preferredUsername: "bob", }), }); const result = actorDehydrator(activity); const result = actorDehydrator(activity, context); assertEquals( await result.toJsonLd(), await new Follow({ Loading src/compat/transformers.ts +24 −7 Original line number Diff line number Diff line import { getLogger } from "@logtape/logtape"; import type { Context } from "../federation/context.ts"; import type { Activity } from "../vocab/vocab.ts"; import type { ActivityTransformer } from "./types.ts"; Loading @@ -14,11 +15,16 @@ const logger = getLogger(["fedify", "compat", "transformers"]); * ``` * urn:uuid:12345678-1234-5678-1234-567812345678 * ``` * @typeParam TContextData The type of the context data. * @param activity The activity to assign an ID to. * @param context The context of the activity. * @return The activity with an ID assigned. * @since 1.4.0 */ export function autoIdAssigner(activity: Activity): Activity { export function autoIdAssigner<TContextData>( activity: Activity, _context: Context<TContextData>, ): Activity { if (activity.id != null) return activity; const id = new URL(`urn:uuid:${crypto.randomUUID()}`); logger.warn( Loading Loading @@ -69,11 +75,16 @@ export function autoIdAssigner(activity: Activity): Activity { * * As some ActivityPub implementations like Threads fail to deal with inlined * actor objects, this transformer can be used to work around this issue. * @typeParam TContextData The type of the context data. * @param activity The activity to dehydrate the actor property of. * @param context The context of the activity. * @returns The dehydrated activity. * @since 1.4.0 */ export function actorDehydrator(activity: Activity): Activity { export function actorDehydrator<TContextData>( activity: Activity, _context: Context<TContextData>, ): Activity { if (activity.actorIds.length < 1) return activity; return activity.clone({ actors: activity.actorIds, Loading @@ -81,11 +92,17 @@ export function actorDehydrator(activity: Activity): Activity { } /** * The default activity transformers that are applied to all outgoing * Gets the default activity transformers that are applied to all outgoing * activities. * @typeParam TContextData The type of the context data. * @returns The default activity transformers. * @since 1.4.0 */ export const defaultActivityTransformers: readonly ActivityTransformer[] = [ export function getDefaultActivityTransformers< TContextData, >(): readonly ActivityTransformer<TContextData>[] { return [ autoIdAssigner, actorDehydrator, ]; } src/compat/types.ts +5 −1 Original line number Diff line number Diff line import type { Context } from "../federation/context.ts"; import type { Activity } from "../vocab/vocab.ts"; /** * A function that transforms an activity object. * @since 1.4.0 */ export type ActivityTransformer = (activity: Activity) => Activity; export type ActivityTransformer<TContextData> = ( activity: Activity, context: Context<TContextData>, ) => Activity; src/federation/middleware.test.ts +5 −4 Original line number Diff line number Diff line Loading @@ -980,6 +980,7 @@ test("FederationImpl.sendActivity()", async (t) => { kv, contextLoader: mockDocumentLoader, }); const context = federation.createContext(new URL("https://example.com/")); await t.step("success", async () => { const activity = new Create({ Loading @@ -993,7 +994,7 @@ test("FederationImpl.sendActivity()", async (t) => { [{ privateKey: rsaPrivateKey2, keyId: rsaPublicKey2.id! }], recipient, activity, { contextData: undefined, origin: "https://example.com" }, { context }, ); assertEquals(verified, ["http"]); assertInstanceOf(request, Request); Loading @@ -1011,7 +1012,7 @@ test("FederationImpl.sendActivity()", async (t) => { activity.clone({ actor: new URL("https://example.com/person2"), }), { contextData: undefined, origin: "https://example.com" }, { context }, ); assertEquals(verified, ["ld", "http"]); assertInstanceOf(request, Request); Loading @@ -1031,7 +1032,7 @@ test("FederationImpl.sendActivity()", async (t) => { activity.clone({ actor: new URL("https://example.com/person2"), }), { contextData: undefined, origin: "https://example.com" }, { context }, ); assertEquals(verified, ["proof"]); assertInstanceOf(request, Request); Loading @@ -1052,7 +1053,7 @@ test("FederationImpl.sendActivity()", async (t) => { activity.clone({ actor: new URL("https://example.com/person2"), }), { contextData: undefined, origin: "https://example.com" }, { context }, ); assertEquals(verified, ["ld", "proof", "http"]); assertInstanceOf(request, Request); Loading Loading
CHANGES.md +2 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ To be released. to `AuthenticatedDocumentLoaderFactory` type. - `GetAuthenticatedDocumentLoaderOptions` interface became to extend `DocumentLoaderFactoryOptions` interface. - Added a type parameter `TContextData` to `CreateFederationOptions` interface. - Introduced `ActivityTransformer`s for adjusting outgoing activities before sending them so that some ActivityPub implementations with quirks Loading
src/compat/transformers.test.ts +9 −2 Original line number Diff line number Diff line import { assertEquals } from "@std/assert/assert-equals"; import { assertNotEquals } from "@std/assert/assert-not-equals"; import { MemoryKvStore } from "../federation/kv.ts"; import { FederationImpl } from "../federation/middleware.ts"; import { test } from "../testing/mod.ts"; import { Follow, Person } from "../vocab/vocab.ts"; import { actorDehydrator, autoIdAssigner } from "./transformers.ts"; const federation = new FederationImpl<void>({ kv: new MemoryKvStore(), }); const context = federation.createContext(new URL("http://example.com/")); test("autoIdAssigner", async () => { const activity = new Follow({ actor: new URL("http://example.com/actors/1"), Loading @@ -13,7 +20,7 @@ test("autoIdAssigner", async () => { preferredUsername: "bob", }), }); const result = autoIdAssigner(activity); const result = autoIdAssigner(activity, context); assertNotEquals(result.id, null); assertEquals( await result.toJsonLd(), Loading Loading @@ -43,7 +50,7 @@ test("actorDehydrator()", async () => { preferredUsername: "bob", }), }); const result = actorDehydrator(activity); const result = actorDehydrator(activity, context); assertEquals( await result.toJsonLd(), await new Follow({ Loading
src/compat/transformers.ts +24 −7 Original line number Diff line number Diff line import { getLogger } from "@logtape/logtape"; import type { Context } from "../federation/context.ts"; import type { Activity } from "../vocab/vocab.ts"; import type { ActivityTransformer } from "./types.ts"; Loading @@ -14,11 +15,16 @@ const logger = getLogger(["fedify", "compat", "transformers"]); * ``` * urn:uuid:12345678-1234-5678-1234-567812345678 * ``` * @typeParam TContextData The type of the context data. * @param activity The activity to assign an ID to. * @param context The context of the activity. * @return The activity with an ID assigned. * @since 1.4.0 */ export function autoIdAssigner(activity: Activity): Activity { export function autoIdAssigner<TContextData>( activity: Activity, _context: Context<TContextData>, ): Activity { if (activity.id != null) return activity; const id = new URL(`urn:uuid:${crypto.randomUUID()}`); logger.warn( Loading Loading @@ -69,11 +75,16 @@ export function autoIdAssigner(activity: Activity): Activity { * * As some ActivityPub implementations like Threads fail to deal with inlined * actor objects, this transformer can be used to work around this issue. * @typeParam TContextData The type of the context data. * @param activity The activity to dehydrate the actor property of. * @param context The context of the activity. * @returns The dehydrated activity. * @since 1.4.0 */ export function actorDehydrator(activity: Activity): Activity { export function actorDehydrator<TContextData>( activity: Activity, _context: Context<TContextData>, ): Activity { if (activity.actorIds.length < 1) return activity; return activity.clone({ actors: activity.actorIds, Loading @@ -81,11 +92,17 @@ export function actorDehydrator(activity: Activity): Activity { } /** * The default activity transformers that are applied to all outgoing * Gets the default activity transformers that are applied to all outgoing * activities. * @typeParam TContextData The type of the context data. * @returns The default activity transformers. * @since 1.4.0 */ export const defaultActivityTransformers: readonly ActivityTransformer[] = [ export function getDefaultActivityTransformers< TContextData, >(): readonly ActivityTransformer<TContextData>[] { return [ autoIdAssigner, actorDehydrator, ]; }
src/compat/types.ts +5 −1 Original line number Diff line number Diff line import type { Context } from "../federation/context.ts"; import type { Activity } from "../vocab/vocab.ts"; /** * A function that transforms an activity object. * @since 1.4.0 */ export type ActivityTransformer = (activity: Activity) => Activity; export type ActivityTransformer<TContextData> = ( activity: Activity, context: Context<TContextData>, ) => Activity;
src/federation/middleware.test.ts +5 −4 Original line number Diff line number Diff line Loading @@ -980,6 +980,7 @@ test("FederationImpl.sendActivity()", async (t) => { kv, contextLoader: mockDocumentLoader, }); const context = federation.createContext(new URL("https://example.com/")); await t.step("success", async () => { const activity = new Create({ Loading @@ -993,7 +994,7 @@ test("FederationImpl.sendActivity()", async (t) => { [{ privateKey: rsaPrivateKey2, keyId: rsaPublicKey2.id! }], recipient, activity, { contextData: undefined, origin: "https://example.com" }, { context }, ); assertEquals(verified, ["http"]); assertInstanceOf(request, Request); Loading @@ -1011,7 +1012,7 @@ test("FederationImpl.sendActivity()", async (t) => { activity.clone({ actor: new URL("https://example.com/person2"), }), { contextData: undefined, origin: "https://example.com" }, { context }, ); assertEquals(verified, ["ld", "http"]); assertInstanceOf(request, Request); Loading @@ -1031,7 +1032,7 @@ test("FederationImpl.sendActivity()", async (t) => { activity.clone({ actor: new URL("https://example.com/person2"), }), { contextData: undefined, origin: "https://example.com" }, { context }, ); assertEquals(verified, ["proof"]); assertInstanceOf(request, Request); Loading @@ -1052,7 +1053,7 @@ test("FederationImpl.sendActivity()", async (t) => { activity.clone({ actor: new URL("https://example.com/person2"), }), { contextData: undefined, origin: "https://example.com" }, { context }, ); assertEquals(verified, ["ld", "proof", "http"]); assertInstanceOf(request, Request); Loading