Loading CHANGES.md +18 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,23 @@ Version 0.14.0 To be released. - Removed the limitation that the `sendActivity({ handle: string }, "followers", Activity)` overload is only available for `RequestContext` but not for `Context`. Now it is available for both. [[#115]] - Added `Context.sendActivity({ handle: string }, "followers", Activity)` overload. - Added type parameter `TContext` to `CollectionsDispatcher` type's first parameter to distinguish between `RequestContext` and `Context`. - The first parameter of `CollectionDispatcher` type became `TContext` (was `RequestContext`). - Added type parameter `TContext` to `CollectionsCursor` type's first parameter to distinguish between `RequestContext` and `Context`. - The first parameter of `CollectionCursor` type became `TContext` (was `RequestContext`). - Added type parameter `TContext` to `CollectionsCallbackSetters` type's first parameter to distinguish between `RequestContext` and `Context`. - Added `source` property to `Object` class in Activity Vocabulary API. [[#114]] Loading @@ -34,6 +51,7 @@ To be released. Deno. [#114]: https://github.com/dahlia/fedify/issues/114 [#115]: https://github.com/dahlia/fedify/issues/115 Version 0.13.0 Loading src/federation/callback.ts +29 −5 Original line number Diff line number Diff line Loading @@ -61,10 +61,24 @@ export type ObjectDispatcher< /** * A callback that dispatches a collection. * * @typeParam TContextData The context data to pass to the {@link Context}. * @typeParam TItem The type of items in the collection. * @typeParam TContext The type of the context. {@link Context} or * {@link RequestContext}. * @typeParam TContextData The context data to pass to the `TContext`. * @typeParam TFilter The type of the filter, if any. * @param context The context. * @param handle The handle of the collection owner. * @param cursor The cursor to start the collection from, or `null` to dispatch * the entire collection without pagination. * @param filter The filter to apply to the collection, if any. */ export type CollectionDispatcher<TItem, TContextData, TFilter> = ( context: RequestContext<TContextData>, export type CollectionDispatcher< TItem, TContext extends Context<TContextData>, TContextData, TFilter, > = ( context: TContext, handle: string, cursor: string | null, filter?: TFilter, Loading @@ -84,10 +98,20 @@ export type CollectionCounter<TContextData, TFilter> = ( /** * A callback that returns a cursor for a collection. * * @typeParam TContext The type of the context. {@link Context} or * {@link RequestContext}. * @typeParam TContextData The context data to pass to the {@link Context}. * @typeParam TFilter The type of the filter, if any. * @param context The context. * @param handle The handle of the collection owner. * @param filter The filter to apply to the collection, if any. */ export type CollectionCursor<TContextData, TFilter> = ( context: RequestContext<TContextData>, export type CollectionCursor< TContext extends Context<TContextData>, TContextData, TFilter, > = ( context: TContext, handle: string, filter?: TFilter, ) => string | null | Promise<string | null>; Loading src/federation/context.ts +16 −30 Original line number Diff line number Diff line Loading @@ -202,6 +202,22 @@ export interface Context<TContextData> { activity: Activity, options?: SendActivityOptions, ): Promise<void>; /** * Sends an activity to the outboxes of the sender's followers. * @param sender The sender's handle. * @param recipients In this case, it must be `"followers"`. * @param activity The activity to send. * @param options Options for sending the activity. * @throws {Error} If no followers collection is registered. * @since 0.14.0 */ sendActivity( sender: { handle: string }, recipients: "followers", activity: Activity, options?: SendActivityOptions, ): Promise<void>; } /** Loading Loading @@ -271,36 +287,6 @@ export interface RequestContext<TContextData> extends Context<TContextData> { * @since 0.7.0 */ getSignedKeyOwner(): Promise<Actor | null>; /** * Sends an activity to recipients' inboxes. * @param sender The sender's handle or the sender's key pair(s). * @param recipients The recipients of the activity. * @param activity The activity to send. * @param options Options for sending the activity. */ sendActivity( sender: SenderKeyPair | SenderKeyPair[] | { handle: string }, recipients: Recipient | Recipient[], activity: Activity, options?: SendActivityOptions, ): Promise<void>; /** * Sends an activity to the outboxes of the sender's followers. * @param sender The sender's handle. * @param recipients In this case, it must be `"followers"`. * @param activity The activity to send. * @param options Options for sending the activity. * @throws {Error} If no followers collection is registered. * @since 0.8.0 */ sendActivity( sender: { handle: string }, recipients: "followers", activity: Activity, options?: SendActivityOptions, ): Promise<void>; } /** Loading src/federation/handler.test.ts +15 −5 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import type { CollectionDispatcher, ObjectDispatcher, } from "./callback.ts"; import type { RequestContext } from "./context.ts"; import { acceptsJsonLd, handleActor, Loading Loading @@ -552,7 +553,12 @@ test("handleCollection()", async () => { return new URL(`https://example.com/users/${handle}`); }, }); const dispatcher: CollectionDispatcher<Activity, void, void> = ( const dispatcher: CollectionDispatcher< Activity, RequestContext<void>, void, void > = ( _ctx, handle, cursor, Loading @@ -575,10 +581,14 @@ test("handleCollection()", async () => { }; const counter: CollectionCounter<void, void> = (_ctx, handle) => handle === "someone" ? 3 : null; const firstCursor: CollectionCursor<void, void> = (_ctx, handle) => handle === "someone" ? "0" : null; const lastCursor: CollectionCursor<void, void> = (_ctx, handle) => handle === "someone" ? "2" : null; const firstCursor: CollectionCursor<RequestContext<void>, void, void> = ( _ctx, handle, ) => handle === "someone" ? "0" : null; const lastCursor: CollectionCursor<RequestContext<void>, void, void> = ( _ctx, handle, ) => handle === "someone" ? "2" : null; let onNotFoundCalled: Request | null = null; const onNotFound = (request: Request) => { onNotFoundCalled = request; Loading src/federation/handler.ts +25 −9 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ import type { ObjectAuthorizePredicate, ObjectDispatcher, } from "./callback.ts"; import type { RequestContext } from "./context.ts"; import type { Context, RequestContext } from "./context.ts"; import type { InboxListenerSet } from "./inbox.ts"; import type { KvKey, KvStore } from "./kv.ts"; import type { MessageQueue } from "./mq.ts"; Loading Loading @@ -136,11 +136,16 @@ export async function handleObject<TContextData>( /** * Callbacks for handling a collection. */ export interface CollectionCallbacks<TItem, TContextData, TFilter> { export interface CollectionCallbacks< TItem, TContext extends Context<TContextData>, TContextData, TFilter, > { /** * A callback that dispatches a collection. */ dispatcher: CollectionDispatcher<TItem, TContextData, TFilter>; dispatcher: CollectionDispatcher<TItem, TContext, TContextData, TFilter>; /** * A callback that counts the number of items in a collection. Loading @@ -150,12 +155,12 @@ export interface CollectionCallbacks<TItem, TContextData, TFilter> { /** * A callback that returns the first cursor for a collection. */ firstCursor?: CollectionCursor<TContextData, TFilter>; firstCursor?: CollectionCursor<TContext, TContextData, TFilter>; /** * A callback that returns the last cursor for a collection. */ lastCursor?: CollectionCursor<TContextData, TFilter>; lastCursor?: CollectionCursor<TContext, TContextData, TFilter>; /** * A callback that determines if a request is authorized to access the collection. Loading @@ -163,13 +168,23 @@ export interface CollectionCallbacks<TItem, TContextData, TFilter> { authorizePredicate?: AuthorizePredicate<TContextData>; } export interface CollectionHandlerParameters<TItem, TContextData, TFilter> { export interface CollectionHandlerParameters< TItem, TContext extends RequestContext<TContextData>, TContextData, TFilter, > { name: string; handle: string; filter?: TFilter; filterPredicate?: (item: TItem) => boolean; context: RequestContext<TContextData>; collectionCallbacks?: CollectionCallbacks<TItem, TContextData, TFilter>; context: TContext; collectionCallbacks?: CollectionCallbacks< TItem, TContext, TContextData, TFilter >; onUnauthorized(request: Request): Response | Promise<Response>; onNotFound(request: Request): Response | Promise<Response>; onNotAcceptable(request: Request): Response | Promise<Response>; Loading @@ -177,6 +192,7 @@ export interface CollectionHandlerParameters<TItem, TContextData, TFilter> { export async function handleCollection< TItem extends URL | Object | Link | Recipient, TContext extends RequestContext<TContextData>, TContextData, TFilter, >( Loading @@ -191,7 +207,7 @@ export async function handleCollection< onUnauthorized, onNotFound, onNotAcceptable, }: CollectionHandlerParameters<TItem, TContextData, TFilter>, }: CollectionHandlerParameters<TItem, TContext, TContextData, TFilter>, ): Promise<Response> { if (collectionCallbacks == null) return await onNotFound(request); const url = new URL(request.url); Loading Loading
CHANGES.md +18 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,23 @@ Version 0.14.0 To be released. - Removed the limitation that the `sendActivity({ handle: string }, "followers", Activity)` overload is only available for `RequestContext` but not for `Context`. Now it is available for both. [[#115]] - Added `Context.sendActivity({ handle: string }, "followers", Activity)` overload. - Added type parameter `TContext` to `CollectionsDispatcher` type's first parameter to distinguish between `RequestContext` and `Context`. - The first parameter of `CollectionDispatcher` type became `TContext` (was `RequestContext`). - Added type parameter `TContext` to `CollectionsCursor` type's first parameter to distinguish between `RequestContext` and `Context`. - The first parameter of `CollectionCursor` type became `TContext` (was `RequestContext`). - Added type parameter `TContext` to `CollectionsCallbackSetters` type's first parameter to distinguish between `RequestContext` and `Context`. - Added `source` property to `Object` class in Activity Vocabulary API. [[#114]] Loading @@ -34,6 +51,7 @@ To be released. Deno. [#114]: https://github.com/dahlia/fedify/issues/114 [#115]: https://github.com/dahlia/fedify/issues/115 Version 0.13.0 Loading
src/federation/callback.ts +29 −5 Original line number Diff line number Diff line Loading @@ -61,10 +61,24 @@ export type ObjectDispatcher< /** * A callback that dispatches a collection. * * @typeParam TContextData The context data to pass to the {@link Context}. * @typeParam TItem The type of items in the collection. * @typeParam TContext The type of the context. {@link Context} or * {@link RequestContext}. * @typeParam TContextData The context data to pass to the `TContext`. * @typeParam TFilter The type of the filter, if any. * @param context The context. * @param handle The handle of the collection owner. * @param cursor The cursor to start the collection from, or `null` to dispatch * the entire collection without pagination. * @param filter The filter to apply to the collection, if any. */ export type CollectionDispatcher<TItem, TContextData, TFilter> = ( context: RequestContext<TContextData>, export type CollectionDispatcher< TItem, TContext extends Context<TContextData>, TContextData, TFilter, > = ( context: TContext, handle: string, cursor: string | null, filter?: TFilter, Loading @@ -84,10 +98,20 @@ export type CollectionCounter<TContextData, TFilter> = ( /** * A callback that returns a cursor for a collection. * * @typeParam TContext The type of the context. {@link Context} or * {@link RequestContext}. * @typeParam TContextData The context data to pass to the {@link Context}. * @typeParam TFilter The type of the filter, if any. * @param context The context. * @param handle The handle of the collection owner. * @param filter The filter to apply to the collection, if any. */ export type CollectionCursor<TContextData, TFilter> = ( context: RequestContext<TContextData>, export type CollectionCursor< TContext extends Context<TContextData>, TContextData, TFilter, > = ( context: TContext, handle: string, filter?: TFilter, ) => string | null | Promise<string | null>; Loading
src/federation/context.ts +16 −30 Original line number Diff line number Diff line Loading @@ -202,6 +202,22 @@ export interface Context<TContextData> { activity: Activity, options?: SendActivityOptions, ): Promise<void>; /** * Sends an activity to the outboxes of the sender's followers. * @param sender The sender's handle. * @param recipients In this case, it must be `"followers"`. * @param activity The activity to send. * @param options Options for sending the activity. * @throws {Error} If no followers collection is registered. * @since 0.14.0 */ sendActivity( sender: { handle: string }, recipients: "followers", activity: Activity, options?: SendActivityOptions, ): Promise<void>; } /** Loading Loading @@ -271,36 +287,6 @@ export interface RequestContext<TContextData> extends Context<TContextData> { * @since 0.7.0 */ getSignedKeyOwner(): Promise<Actor | null>; /** * Sends an activity to recipients' inboxes. * @param sender The sender's handle or the sender's key pair(s). * @param recipients The recipients of the activity. * @param activity The activity to send. * @param options Options for sending the activity. */ sendActivity( sender: SenderKeyPair | SenderKeyPair[] | { handle: string }, recipients: Recipient | Recipient[], activity: Activity, options?: SendActivityOptions, ): Promise<void>; /** * Sends an activity to the outboxes of the sender's followers. * @param sender The sender's handle. * @param recipients In this case, it must be `"followers"`. * @param activity The activity to send. * @param options Options for sending the activity. * @throws {Error} If no followers collection is registered. * @since 0.8.0 */ sendActivity( sender: { handle: string }, recipients: "followers", activity: Activity, options?: SendActivityOptions, ): Promise<void>; } /** Loading
src/federation/handler.test.ts +15 −5 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import type { CollectionDispatcher, ObjectDispatcher, } from "./callback.ts"; import type { RequestContext } from "./context.ts"; import { acceptsJsonLd, handleActor, Loading Loading @@ -552,7 +553,12 @@ test("handleCollection()", async () => { return new URL(`https://example.com/users/${handle}`); }, }); const dispatcher: CollectionDispatcher<Activity, void, void> = ( const dispatcher: CollectionDispatcher< Activity, RequestContext<void>, void, void > = ( _ctx, handle, cursor, Loading @@ -575,10 +581,14 @@ test("handleCollection()", async () => { }; const counter: CollectionCounter<void, void> = (_ctx, handle) => handle === "someone" ? 3 : null; const firstCursor: CollectionCursor<void, void> = (_ctx, handle) => handle === "someone" ? "0" : null; const lastCursor: CollectionCursor<void, void> = (_ctx, handle) => handle === "someone" ? "2" : null; const firstCursor: CollectionCursor<RequestContext<void>, void, void> = ( _ctx, handle, ) => handle === "someone" ? "0" : null; const lastCursor: CollectionCursor<RequestContext<void>, void, void> = ( _ctx, handle, ) => handle === "someone" ? "2" : null; let onNotFoundCalled: Request | null = null; const onNotFound = (request: Request) => { onNotFoundCalled = request; Loading
src/federation/handler.ts +25 −9 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ import type { ObjectAuthorizePredicate, ObjectDispatcher, } from "./callback.ts"; import type { RequestContext } from "./context.ts"; import type { Context, RequestContext } from "./context.ts"; import type { InboxListenerSet } from "./inbox.ts"; import type { KvKey, KvStore } from "./kv.ts"; import type { MessageQueue } from "./mq.ts"; Loading Loading @@ -136,11 +136,16 @@ export async function handleObject<TContextData>( /** * Callbacks for handling a collection. */ export interface CollectionCallbacks<TItem, TContextData, TFilter> { export interface CollectionCallbacks< TItem, TContext extends Context<TContextData>, TContextData, TFilter, > { /** * A callback that dispatches a collection. */ dispatcher: CollectionDispatcher<TItem, TContextData, TFilter>; dispatcher: CollectionDispatcher<TItem, TContext, TContextData, TFilter>; /** * A callback that counts the number of items in a collection. Loading @@ -150,12 +155,12 @@ export interface CollectionCallbacks<TItem, TContextData, TFilter> { /** * A callback that returns the first cursor for a collection. */ firstCursor?: CollectionCursor<TContextData, TFilter>; firstCursor?: CollectionCursor<TContext, TContextData, TFilter>; /** * A callback that returns the last cursor for a collection. */ lastCursor?: CollectionCursor<TContextData, TFilter>; lastCursor?: CollectionCursor<TContext, TContextData, TFilter>; /** * A callback that determines if a request is authorized to access the collection. Loading @@ -163,13 +168,23 @@ export interface CollectionCallbacks<TItem, TContextData, TFilter> { authorizePredicate?: AuthorizePredicate<TContextData>; } export interface CollectionHandlerParameters<TItem, TContextData, TFilter> { export interface CollectionHandlerParameters< TItem, TContext extends RequestContext<TContextData>, TContextData, TFilter, > { name: string; handle: string; filter?: TFilter; filterPredicate?: (item: TItem) => boolean; context: RequestContext<TContextData>; collectionCallbacks?: CollectionCallbacks<TItem, TContextData, TFilter>; context: TContext; collectionCallbacks?: CollectionCallbacks< TItem, TContext, TContextData, TFilter >; onUnauthorized(request: Request): Response | Promise<Response>; onNotFound(request: Request): Response | Promise<Response>; onNotAcceptable(request: Request): Response | Promise<Response>; Loading @@ -177,6 +192,7 @@ export interface CollectionHandlerParameters<TItem, TContextData, TFilter> { export async function handleCollection< TItem extends URL | Object | Link | Recipient, TContext extends RequestContext<TContextData>, TContextData, TFilter, >( Loading @@ -191,7 +207,7 @@ export async function handleCollection< onUnauthorized, onNotFound, onNotAcceptable, }: CollectionHandlerParameters<TItem, TContextData, TFilter>, }: CollectionHandlerParameters<TItem, TContext, TContextData, TFilter>, ): Promise<Response> { if (collectionCallbacks == null) return await onNotFound(request); const url = new URL(request.url); Loading