Unverified Commit 42bcb98c authored by Hong Minhee's avatar Hong Minhee
Browse files

Make sendActivity("followers") available 4 Context

parent 04c33cab
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -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]]

@@ -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
+29 −5
Original line number Diff line number Diff line
@@ -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,
@@ -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>;
+16 −30
Original line number Diff line number Diff line
@@ -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>;
}

/**
@@ -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>;
}

/**
+15 −5
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import type {
  CollectionDispatcher,
  ObjectDispatcher,
} from "./callback.ts";
import type { RequestContext } from "./context.ts";
import {
  acceptsJsonLd,
  handleActor,
@@ -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,
@@ -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;
+25 −9
Original line number Diff line number Diff line
@@ -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";
@@ -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.
@@ -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.
@@ -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>;
@@ -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,
>(
@@ -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