Loading CHANGES.md +3 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ To be released. Note that 1.6.0 was skipped due to a mistake in the versioning. - Added `Context.federation` property to access the `Federation` object from the context. [[#235]] - Added `Context.clone()` method. [[#237]] - Introduced `FederationBuilder` for creating a federation instance with a builder pattern. Loading Loading @@ -43,6 +45,7 @@ To be released. Note that 1.6.0 was skipped due to a mistake in the versioning. [#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 [#237]: https://github.com/fedify-dev/fedify/pull/237 Version 1.5.3 Loading docs/manual/context.md +18 −0 Original line number Diff line number Diff line Loading @@ -482,3 +482,21 @@ if (isActor(actor)) { } } ~~~~ Replacing the context data -------------------------- *This API is available since Fedify 1.6.0.* You can replace the context data by calling the `Context.clone()` method. This is useful when you want to create a new context based on the existing one but with different data. The following shows an example of replacing the context data: ~~~~ typescript twoslash import { type Context } from "@fedify/fedify"; const ctx = null as unknown as Context<{ foo: string; bar: number }>; // ---cut-before--- const newCtx = ctx.clone({ ...ctx.data, foo: "new value" }); ~~~~ fedify/federation/context.ts +30 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,16 @@ export interface Context<TContextData> { */ readonly federation: Federation<TContextData>; /** * Creates a new context with the same properties as this one, * but with the given data. * @param data The new data to associate with the context. * @returns A new context with the same properties as this one, * but with the given data. * @since 1.6.0 */ clone(data: TContextData): Context<TContextData>; /** * Builds the URI of the NodeInfo document. * @returns The NodeInfo URI. Loading Loading @@ -429,6 +439,16 @@ export interface RequestContext<TContextData> extends Context<TContextData> { */ readonly url: URL; /** * Creates a new context with the same properties as this one, * but with the given data. * @param data The new data to associate with the context. * @returns A new context with the same properties as this one, * but with the given data. * @since 1.6.0 */ clone(data: TContextData): RequestContext<TContextData>; /** * Gets an {@link Actor} object for the given identifier. * @param identifier The actor's identifier. Loading Loading @@ -539,6 +559,16 @@ export interface InboxContext<TContextData> extends Context<TContextData> { */ recipient: string | null; /** * Creates a new context with the same properties as this one, * but with the given data. * @param data The new data to associate with the context. * @returns A new context with the same properties as this one, * but with the given data. * @since 1.6.0 */ clone(data: TContextData): InboxContext<TContextData>; /** * Forwards a received activity to the recipients' inboxes. The forwarded * activity will be signed in HTTP Signatures by the forwarder, but its Loading fedify/federation/handler.test.ts +25 −9 Original line number Diff line number Diff line Loading @@ -1180,7 +1180,7 @@ test("handleInbox()", async () => { recipient: null, context: unsignedContext, inboxContextFactory(_activity) { return createInboxContext(unsignedContext); return createInboxContext({ ...unsignedContext, clone: undefined }); }, ...inboxOptions, actorDispatcher: undefined, Loading @@ -1193,7 +1193,11 @@ test("handleInbox()", async () => { recipient: "nobody", context: unsignedContext, inboxContextFactory(_activity) { return createInboxContext({ ...unsignedContext, recipient: "nobody" }); return createInboxContext({ ...unsignedContext, clone: undefined, recipient: "nobody", }); }, ...inboxOptions, }); Loading @@ -1205,7 +1209,7 @@ test("handleInbox()", async () => { recipient: null, context: unsignedContext, inboxContextFactory(_activity) { return createInboxContext(unsignedContext); return createInboxContext({ ...unsignedContext, clone: undefined }); }, ...inboxOptions, }); Loading @@ -1216,7 +1220,11 @@ test("handleInbox()", async () => { recipient: "someone", context: unsignedContext, inboxContextFactory(_activity) { return createInboxContext({ ...unsignedContext, recipient: "someone" }); return createInboxContext({ ...unsignedContext, clone: undefined, recipient: "someone", }); }, ...inboxOptions, }); Loading @@ -1240,7 +1248,7 @@ test("handleInbox()", async () => { recipient: null, context: signedContext, inboxContextFactory(_activity) { return createInboxContext(unsignedContext); return createInboxContext({ ...unsignedContext, clone: undefined }); }, ...inboxOptions, }); Loading @@ -1251,7 +1259,11 @@ test("handleInbox()", async () => { recipient: "someone", context: signedContext, inboxContextFactory(_activity) { return createInboxContext({ ...unsignedContext, recipient: "someone" }); return createInboxContext({ ...unsignedContext, clone: undefined, recipient: "someone", }); }, ...inboxOptions, }); Loading @@ -1262,7 +1274,7 @@ test("handleInbox()", async () => { recipient: null, context: unsignedContext, inboxContextFactory(_activity) { return createInboxContext(unsignedContext); return createInboxContext({ ...unsignedContext, clone: undefined }); }, ...inboxOptions, skipSignatureVerification: true, Loading @@ -1274,7 +1286,11 @@ test("handleInbox()", async () => { recipient: "someone", context: unsignedContext, inboxContextFactory(_activity) { return createInboxContext({ ...unsignedContext, recipient: "someone" }); return createInboxContext({ ...unsignedContext, clone: undefined, recipient: "someone", }); }, ...inboxOptions, skipSignatureVerification: true, Loading Loading @@ -1311,7 +1327,7 @@ test("handleInbox()", async () => { recipient: null, context: signedContext, inboxContextFactory(_activity) { return createInboxContext(signedInvalidContext); return createInboxContext({ ...signedInvalidContext, clone: undefined }); }, ...inboxOptions, }); Loading fedify/federation/middleware.test.ts +34 −0 Original line number Diff line number Diff line Loading @@ -726,6 +726,22 @@ test("Federation.createContext()", async (t) => { ); }); await t.step("Context.clone()", () => { const federation = createFederation<number>({ 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); }); mf.mock("GET@/.well-known/nodeinfo", (req) => { assertEquals(new URL(req.url).host, "example.com"); assertEquals(req.headers.get("User-Agent"), "CustomUserAgent/1.2.3"); Loading Loading @@ -875,6 +891,24 @@ test("Federation.createContext()", async (t) => { ); }); await t.step("RequestContext.clone()", () => { const federation = createFederation<number>({ kv, }); const req = new Request("https://example.com/"); 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); }); mf.uninstall(); }); Loading Loading
CHANGES.md +3 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ To be released. Note that 1.6.0 was skipped due to a mistake in the versioning. - Added `Context.federation` property to access the `Federation` object from the context. [[#235]] - Added `Context.clone()` method. [[#237]] - Introduced `FederationBuilder` for creating a federation instance with a builder pattern. Loading Loading @@ -43,6 +45,7 @@ To be released. Note that 1.6.0 was skipped due to a mistake in the versioning. [#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 [#237]: https://github.com/fedify-dev/fedify/pull/237 Version 1.5.3 Loading
docs/manual/context.md +18 −0 Original line number Diff line number Diff line Loading @@ -482,3 +482,21 @@ if (isActor(actor)) { } } ~~~~ Replacing the context data -------------------------- *This API is available since Fedify 1.6.0.* You can replace the context data by calling the `Context.clone()` method. This is useful when you want to create a new context based on the existing one but with different data. The following shows an example of replacing the context data: ~~~~ typescript twoslash import { type Context } from "@fedify/fedify"; const ctx = null as unknown as Context<{ foo: string; bar: number }>; // ---cut-before--- const newCtx = ctx.clone({ ...ctx.data, foo: "new value" }); ~~~~
fedify/federation/context.ts +30 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,16 @@ export interface Context<TContextData> { */ readonly federation: Federation<TContextData>; /** * Creates a new context with the same properties as this one, * but with the given data. * @param data The new data to associate with the context. * @returns A new context with the same properties as this one, * but with the given data. * @since 1.6.0 */ clone(data: TContextData): Context<TContextData>; /** * Builds the URI of the NodeInfo document. * @returns The NodeInfo URI. Loading Loading @@ -429,6 +439,16 @@ export interface RequestContext<TContextData> extends Context<TContextData> { */ readonly url: URL; /** * Creates a new context with the same properties as this one, * but with the given data. * @param data The new data to associate with the context. * @returns A new context with the same properties as this one, * but with the given data. * @since 1.6.0 */ clone(data: TContextData): RequestContext<TContextData>; /** * Gets an {@link Actor} object for the given identifier. * @param identifier The actor's identifier. Loading Loading @@ -539,6 +559,16 @@ export interface InboxContext<TContextData> extends Context<TContextData> { */ recipient: string | null; /** * Creates a new context with the same properties as this one, * but with the given data. * @param data The new data to associate with the context. * @returns A new context with the same properties as this one, * but with the given data. * @since 1.6.0 */ clone(data: TContextData): InboxContext<TContextData>; /** * Forwards a received activity to the recipients' inboxes. The forwarded * activity will be signed in HTTP Signatures by the forwarder, but its Loading
fedify/federation/handler.test.ts +25 −9 Original line number Diff line number Diff line Loading @@ -1180,7 +1180,7 @@ test("handleInbox()", async () => { recipient: null, context: unsignedContext, inboxContextFactory(_activity) { return createInboxContext(unsignedContext); return createInboxContext({ ...unsignedContext, clone: undefined }); }, ...inboxOptions, actorDispatcher: undefined, Loading @@ -1193,7 +1193,11 @@ test("handleInbox()", async () => { recipient: "nobody", context: unsignedContext, inboxContextFactory(_activity) { return createInboxContext({ ...unsignedContext, recipient: "nobody" }); return createInboxContext({ ...unsignedContext, clone: undefined, recipient: "nobody", }); }, ...inboxOptions, }); Loading @@ -1205,7 +1209,7 @@ test("handleInbox()", async () => { recipient: null, context: unsignedContext, inboxContextFactory(_activity) { return createInboxContext(unsignedContext); return createInboxContext({ ...unsignedContext, clone: undefined }); }, ...inboxOptions, }); Loading @@ -1216,7 +1220,11 @@ test("handleInbox()", async () => { recipient: "someone", context: unsignedContext, inboxContextFactory(_activity) { return createInboxContext({ ...unsignedContext, recipient: "someone" }); return createInboxContext({ ...unsignedContext, clone: undefined, recipient: "someone", }); }, ...inboxOptions, }); Loading @@ -1240,7 +1248,7 @@ test("handleInbox()", async () => { recipient: null, context: signedContext, inboxContextFactory(_activity) { return createInboxContext(unsignedContext); return createInboxContext({ ...unsignedContext, clone: undefined }); }, ...inboxOptions, }); Loading @@ -1251,7 +1259,11 @@ test("handleInbox()", async () => { recipient: "someone", context: signedContext, inboxContextFactory(_activity) { return createInboxContext({ ...unsignedContext, recipient: "someone" }); return createInboxContext({ ...unsignedContext, clone: undefined, recipient: "someone", }); }, ...inboxOptions, }); Loading @@ -1262,7 +1274,7 @@ test("handleInbox()", async () => { recipient: null, context: unsignedContext, inboxContextFactory(_activity) { return createInboxContext(unsignedContext); return createInboxContext({ ...unsignedContext, clone: undefined }); }, ...inboxOptions, skipSignatureVerification: true, Loading @@ -1274,7 +1286,11 @@ test("handleInbox()", async () => { recipient: "someone", context: unsignedContext, inboxContextFactory(_activity) { return createInboxContext({ ...unsignedContext, recipient: "someone" }); return createInboxContext({ ...unsignedContext, clone: undefined, recipient: "someone", }); }, ...inboxOptions, skipSignatureVerification: true, Loading Loading @@ -1311,7 +1327,7 @@ test("handleInbox()", async () => { recipient: null, context: signedContext, inboxContextFactory(_activity) { return createInboxContext(signedInvalidContext); return createInboxContext({ ...signedInvalidContext, clone: undefined }); }, ...inboxOptions, }); Loading
fedify/federation/middleware.test.ts +34 −0 Original line number Diff line number Diff line Loading @@ -726,6 +726,22 @@ test("Federation.createContext()", async (t) => { ); }); await t.step("Context.clone()", () => { const federation = createFederation<number>({ 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); }); mf.mock("GET@/.well-known/nodeinfo", (req) => { assertEquals(new URL(req.url).host, "example.com"); assertEquals(req.headers.get("User-Agent"), "CustomUserAgent/1.2.3"); Loading Loading @@ -875,6 +891,24 @@ test("Federation.createContext()", async (t) => { ); }); await t.step("RequestContext.clone()", () => { const federation = createFederation<number>({ kv, }); const req = new Request("https://example.com/"); 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); }); mf.uninstall(); }); Loading