Loading CHANGES.md +2 −1 Original line number Diff line number Diff line Loading @@ -41,7 +41,8 @@ To be released. - The type of `ActorKeyPairsDispatcher<TContextData>`'s first parameter became `Context` (was `TContextData`). - Deprecated `Federation.sendActivity()` method. - Deprecated `Federation.sendActivity()` method. Use `Context.sendActivity()` method instead. - The last parameter of `Federation.sendActivity()` method is no longer optional. Also, it now takes the required `contextData` option. Loading docs/manual/federation.md +1 −0 Original line number Diff line number Diff line Loading @@ -180,6 +180,7 @@ the retry policy by providing a custom function that satisfies the `RetryPolicy` type. Or you can adjust the parameters of the built-in `createExponentialBackoffRetryPolicy()` function. How the `Federation` object recognizes the domain name ------------------------------------------------------ Loading federation/handler.ts +4 −6 Original line number Diff line number Diff line Loading @@ -20,11 +20,11 @@ import type { CollectionCursor, CollectionDispatcher, InboxErrorHandler, InboxListener, ObjectAuthorizePredicate, ObjectDispatcher, } from "./callback.ts"; import type { RequestContext } from "./context.ts"; import type { InboxListenerSet } from "./inbox.ts"; import type { KvKey, KvStore } from "./kv.ts"; import type { MessageQueue } from "./mq.ts"; import type { InboxMessage } from "./queue.ts"; Loading Loading @@ -316,9 +316,7 @@ export interface InboxHandlerParameters<TContextData> { kvPrefix: KvKey; queue?: MessageQueue; actorDispatcher?: ActorDispatcher<TContextData>; inboxListenerDispatcher: ( activity: Activity, ) => InboxListener<TContextData, Activity> | null; inboxListeners?: InboxListenerSet<TContextData>; inboxErrorHandler?: InboxErrorHandler<TContextData>; onNotFound(request: Request): Response | Promise<Response>; signatureTimeWindow: Temporal.DurationLike; Loading @@ -333,7 +331,7 @@ export async function handleInbox<TContextData>( kvPrefix, queue, actorDispatcher, inboxListenerDispatcher, inboxListeners, inboxErrorHandler, onNotFound, signatureTimeWindow, Loading Loading @@ -463,7 +461,7 @@ export async function handleInbox<TContextData>( headers: { "Content-Type": "text/plain; charset=utf-8" }, }); } const listener = inboxListenerDispatcher(activity); const listener = inboxListeners?.dispatch(activity); if (listener == null) { logger.error( "Unsupported activity type:\n{activity}", Loading federation/inbox.test.ts 0 → 100644 +50 −0 Original line number Diff line number Diff line import { assertEquals } from "@std/assert/assert-equals"; import { assertThrows } from "@std/assert/assert-throws"; import { test } from "../testing/mod.ts"; import { Activity, Create, Invite, Offer, Update } from "../vocab/vocab.ts"; import { InboxListenerSet } from "./inbox.ts"; test("InboxListenerSet", () => { const listeners = new InboxListenerSet<void>(); const activity = new Activity({}); const offer = new Offer({}); const invite = new Invite({}); const create = new Create({}); const update = new Update({}); assertEquals(listeners.dispatch(activity), null); assertEquals(listeners.dispatch(offer), null); assertEquals(listeners.dispatch(invite), null); assertEquals(listeners.dispatch(create), null); assertEquals(listeners.dispatch(update), null); const listenOffer = () => {}; listeners.add(Offer, listenOffer); assertEquals(listeners.dispatch(activity), null); assertEquals(listeners.dispatch(offer), listenOffer); assertEquals(listeners.dispatch(invite), listenOffer); assertEquals(listeners.dispatch(create), null); assertEquals(listeners.dispatch(update), null); const listenCreate = () => {}; listeners.add(Create, listenCreate); assertEquals(listeners.dispatch(activity), null); assertEquals(listeners.dispatch(offer), listenOffer); assertEquals(listeners.dispatch(invite), listenOffer); assertEquals(listeners.dispatch(create), listenCreate); assertEquals(listeners.dispatch(update), null); const listenActivity = () => {}; listeners.add(Activity, listenActivity); assertEquals(listeners.dispatch(activity), listenActivity); assertEquals(listeners.dispatch(offer), listenOffer); assertEquals(listeners.dispatch(invite), listenOffer); assertEquals(listeners.dispatch(create), listenCreate); assertEquals(listeners.dispatch(update), listenActivity); assertThrows( () => listeners.add(Activity, listenActivity), TypeError, "Listener already set for this type.", ); }); federation/inbox.ts 0 → 100644 +47 −0 Original line number Diff line number Diff line import { Activity } from "../vocab/vocab.ts"; import type { InboxListener } from "./callback.ts"; export class InboxListenerSet<TContextData> { #listeners: Map< new (...args: unknown[]) => Activity, InboxListener<TContextData, Activity> >; constructor() { this.#listeners = new Map(); } add<TActivity extends Activity>( // deno-lint-ignore no-explicit-any type: new (...args: any[]) => TActivity, listener: InboxListener<TContextData, TActivity>, ): void { if (this.#listeners.has(type)) { throw new TypeError("Listener already set for this type."); } this.#listeners.set( type, listener as InboxListener<TContextData, Activity>, ); } dispatch<TActivity extends Activity>( activity: TActivity, ): InboxListener<TContextData, TActivity> | null { // deno-lint-ignore no-explicit-any let cls: new (...args: any[]) => Activity = activity // deno-lint-ignore no-explicit-any .constructor as unknown as new (...args: any[]) => Activity; const inboxListeners = this.#listeners; if (inboxListeners == null) { return null; } while (true) { if (inboxListeners.has(cls)) break; if (cls === Activity) return null; cls = globalThis.Object.getPrototypeOf(cls); } const listener = inboxListeners.get(cls)!; return listener; } } Loading
CHANGES.md +2 −1 Original line number Diff line number Diff line Loading @@ -41,7 +41,8 @@ To be released. - The type of `ActorKeyPairsDispatcher<TContextData>`'s first parameter became `Context` (was `TContextData`). - Deprecated `Federation.sendActivity()` method. - Deprecated `Federation.sendActivity()` method. Use `Context.sendActivity()` method instead. - The last parameter of `Federation.sendActivity()` method is no longer optional. Also, it now takes the required `contextData` option. Loading
docs/manual/federation.md +1 −0 Original line number Diff line number Diff line Loading @@ -180,6 +180,7 @@ the retry policy by providing a custom function that satisfies the `RetryPolicy` type. Or you can adjust the parameters of the built-in `createExponentialBackoffRetryPolicy()` function. How the `Federation` object recognizes the domain name ------------------------------------------------------ Loading
federation/handler.ts +4 −6 Original line number Diff line number Diff line Loading @@ -20,11 +20,11 @@ import type { CollectionCursor, CollectionDispatcher, InboxErrorHandler, InboxListener, ObjectAuthorizePredicate, ObjectDispatcher, } from "./callback.ts"; import type { RequestContext } from "./context.ts"; import type { InboxListenerSet } from "./inbox.ts"; import type { KvKey, KvStore } from "./kv.ts"; import type { MessageQueue } from "./mq.ts"; import type { InboxMessage } from "./queue.ts"; Loading Loading @@ -316,9 +316,7 @@ export interface InboxHandlerParameters<TContextData> { kvPrefix: KvKey; queue?: MessageQueue; actorDispatcher?: ActorDispatcher<TContextData>; inboxListenerDispatcher: ( activity: Activity, ) => InboxListener<TContextData, Activity> | null; inboxListeners?: InboxListenerSet<TContextData>; inboxErrorHandler?: InboxErrorHandler<TContextData>; onNotFound(request: Request): Response | Promise<Response>; signatureTimeWindow: Temporal.DurationLike; Loading @@ -333,7 +331,7 @@ export async function handleInbox<TContextData>( kvPrefix, queue, actorDispatcher, inboxListenerDispatcher, inboxListeners, inboxErrorHandler, onNotFound, signatureTimeWindow, Loading Loading @@ -463,7 +461,7 @@ export async function handleInbox<TContextData>( headers: { "Content-Type": "text/plain; charset=utf-8" }, }); } const listener = inboxListenerDispatcher(activity); const listener = inboxListeners?.dispatch(activity); if (listener == null) { logger.error( "Unsupported activity type:\n{activity}", Loading
federation/inbox.test.ts 0 → 100644 +50 −0 Original line number Diff line number Diff line import { assertEquals } from "@std/assert/assert-equals"; import { assertThrows } from "@std/assert/assert-throws"; import { test } from "../testing/mod.ts"; import { Activity, Create, Invite, Offer, Update } from "../vocab/vocab.ts"; import { InboxListenerSet } from "./inbox.ts"; test("InboxListenerSet", () => { const listeners = new InboxListenerSet<void>(); const activity = new Activity({}); const offer = new Offer({}); const invite = new Invite({}); const create = new Create({}); const update = new Update({}); assertEquals(listeners.dispatch(activity), null); assertEquals(listeners.dispatch(offer), null); assertEquals(listeners.dispatch(invite), null); assertEquals(listeners.dispatch(create), null); assertEquals(listeners.dispatch(update), null); const listenOffer = () => {}; listeners.add(Offer, listenOffer); assertEquals(listeners.dispatch(activity), null); assertEquals(listeners.dispatch(offer), listenOffer); assertEquals(listeners.dispatch(invite), listenOffer); assertEquals(listeners.dispatch(create), null); assertEquals(listeners.dispatch(update), null); const listenCreate = () => {}; listeners.add(Create, listenCreate); assertEquals(listeners.dispatch(activity), null); assertEquals(listeners.dispatch(offer), listenOffer); assertEquals(listeners.dispatch(invite), listenOffer); assertEquals(listeners.dispatch(create), listenCreate); assertEquals(listeners.dispatch(update), null); const listenActivity = () => {}; listeners.add(Activity, listenActivity); assertEquals(listeners.dispatch(activity), listenActivity); assertEquals(listeners.dispatch(offer), listenOffer); assertEquals(listeners.dispatch(invite), listenOffer); assertEquals(listeners.dispatch(create), listenCreate); assertEquals(listeners.dispatch(update), listenActivity); assertThrows( () => listeners.add(Activity, listenActivity), TypeError, "Listener already set for this type.", ); });
federation/inbox.ts 0 → 100644 +47 −0 Original line number Diff line number Diff line import { Activity } from "../vocab/vocab.ts"; import type { InboxListener } from "./callback.ts"; export class InboxListenerSet<TContextData> { #listeners: Map< new (...args: unknown[]) => Activity, InboxListener<TContextData, Activity> >; constructor() { this.#listeners = new Map(); } add<TActivity extends Activity>( // deno-lint-ignore no-explicit-any type: new (...args: any[]) => TActivity, listener: InboxListener<TContextData, TActivity>, ): void { if (this.#listeners.has(type)) { throw new TypeError("Listener already set for this type."); } this.#listeners.set( type, listener as InboxListener<TContextData, Activity>, ); } dispatch<TActivity extends Activity>( activity: TActivity, ): InboxListener<TContextData, TActivity> | null { // deno-lint-ignore no-explicit-any let cls: new (...args: any[]) => Activity = activity // deno-lint-ignore no-explicit-any .constructor as unknown as new (...args: any[]) => Activity; const inboxListeners = this.#listeners; if (inboxListeners == null) { return null; } while (true) { if (inboxListeners.has(cls)) break; if (cls === Activity) return null; cls = globalThis.Object.getPrototypeOf(cls); } const listener = inboxListeners.get(cls)!; return listener; } }