@fedify/fedify
Version:
An ActivityPub server framework
452 lines • 23.1 kB
TypeScript
import * as dntShim from "../_dnt.shims.js";
import { type Span, type TracerProvider } from "@opentelemetry/api";
import type { ActivityTransformer } from "../compat/types.js";
import { type GetNodeInfoOptions } from "../nodeinfo/client.js";
import type { JsonValue, NodeInfo } from "../nodeinfo/types.js";
import { type AuthenticatedDocumentLoaderFactory, type DocumentLoader, type DocumentLoaderFactory, type GetUserAgentOptions } from "../runtime/docloader.js";
import type { Actor, Recipient } from "../vocab/actor.js";
import { type LookupObjectOptions, type TraverseCollectionOptions } from "../vocab/lookup.js";
import { Activity, type Collection, type Hashtag, type Like, type Link, type Object } from "../vocab/vocab.js";
import type { ActorAliasMapper, ActorDispatcher, ActorHandleMapper, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionDispatcher, InboxErrorHandler, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectDispatcher, OutboxErrorHandler, SharedInboxKeyDispatcher } from "./callback.js";
import type { ActorKeyPair, Context, ForwardActivityOptions, InboxContext, ParseUriResult, RequestContext, RouteActivityOptions, SendActivityOptionsForCollection } from "./context.js";
import type { ActorCallbackSetters, CollectionCallbackSetters, Federation, FederationFetchOptions, FederationStartQueueOptions, InboxListenerSetters, ObjectCallbackSetters } from "./federation.js";
import { type CollectionCallbacks } from "./handler.js";
import { InboxListenerSet } from "./inbox.js";
import type { KvKey, KvStore } from "./kv.js";
import type { MessageQueue } from "./mq.js";
import { type RetryPolicy } from "./retry.js";
import { Router } from "./router.js";
import { type SenderKeyPair } from "./send.js";
/**
* Options for {@link createFederation} function.
* @typeParam TContextData The type of the context data.
* @since 0.10.0
*/
export interface CreateFederationOptions<TContextData> {
/**
* The key-value store used for caching, outbox queues, and inbox idempotence.
*/
kv: KvStore;
/**
* Prefixes for namespacing keys in the Deno KV store. By default, all keys
* are prefixed with `["_fedify"]`.
*/
kvPrefixes?: Partial<FederationKvPrefixes>;
/**
* The message queue for sending and receiving activities. If not provided,
* activities will not be queued and will be processed immediately.
*
* If a `MessageQueue` is provided, both the `inbox` and `outbox` queues
* will be set to the same queue.
*
* If a `FederationQueueOptions` object is provided, you can set the queues
* separately (since Fedify 1.3.0).
*/
queue?: FederationQueueOptions | MessageQueue;
/**
* Whether to start the task queue manually or automatically.
*
* If `true`, the task queue will not start automatically and you need to
* manually start it by calling the {@link Federation.startQueue} method.
*
* If `false`, the task queue will start automatically as soon as
* the first task is enqueued.
*
* By default, the queue starts automatically.
*
* @since 0.12.0
*/
manuallyStartQueue?: boolean;
/**
* The canonical base URL of the server. This is used for constructing
* absolute URLs and fediverse handles.
* @since 1.5.0
*/
origin?: string | FederationOrigin;
/**
* A custom JSON-LD document loader factory. By default, this uses
* the built-in cache-backed loader that fetches remote documents over
* HTTP(S).
* @since 1.4.0
*/
documentLoaderFactory?: DocumentLoaderFactory;
/**
* A custom JSON-LD context loader factory. By default, this uses the same
* loader as the document loader.
* @since 1.4.0
*/
contextLoaderFactory?: DocumentLoaderFactory;
/**
* A custom JSON-LD document loader. By default, this uses the built-in
* cache-backed loader that fetches remote documents over HTTP(S).
* @deprecated Use {@link documentLoaderFactory} instead.
*/
documentLoader?: DocumentLoader;
/**
* A custom JSON-LD context loader. By default, this uses the same loader
* as the document loader.
* @deprecated Use {@link contextLoaderFactory} instead.
*/
contextLoader?: DocumentLoader;
/**
* A factory function that creates an authenticated document loader for a
* given identity. This is used for fetching documents that require
* authentication.
*/
authenticatedDocumentLoaderFactory?: AuthenticatedDocumentLoaderFactory;
/**
* Whether to allow fetching private network addresses in the document loader.
*
* If turned on, {@link CreateFederationOptions.documentLoader},
* {@link CreateFederationOptions.contextLoader}, and
* {@link CreateFederationOptions.authenticatedDocumentLoaderFactory}
* cannot be configured.
*
* Mostly useful for testing purposes. *Do not use in production.*
*
* Turned off by default.
* @since 0.15.0
*/
allowPrivateAddress?: boolean;
/**
* Options for making `User-Agent` strings for HTTP requests.
* If a string is provided, it is used as the `User-Agent` header.
* If an object is provided, it is passed to the {@link getUserAgent}
* function.
* @since 1.3.0
*/
userAgent?: GetUserAgentOptions | string;
/**
* A callback that handles errors during outbox processing. Note that this
* callback can be called multiple times for the same activity, because
* the delivery is retried according to the backoff schedule until it
* succeeds or reaches the maximum retry count.
*
* If any errors are thrown in this callback, they are ignored.
*/
onOutboxError?: OutboxErrorHandler;
/**
* The time window for verifying HTTP Signatures of incoming requests. If the
* request is older or newer than this window, it is rejected. Or if it is
* `false`, the request's timestamp is not checked at all.
*
* By default, the window is an hour.
*/
signatureTimeWindow?: dntShim.Temporal.Duration | dntShim.Temporal.DurationLike | false;
/**
* Whether to skip HTTP Signatures verification for incoming activities.
* This is useful for testing purposes, but should not be used in production.
*
* By default, this is `false` (i.e., signatures are verified).
* @since 0.13.0
*/
skipSignatureVerification?: boolean;
/**
* The retry policy for sending activities to recipients' inboxes.
* By default, this uses an exponential backoff strategy with a maximum of
* 10 attempts and a maximum delay of 12 hours.
* @since 0.12.0
*/
outboxRetryPolicy?: RetryPolicy;
/**
* The retry policy for processing incoming activities. By default, this
* uses an exponential backoff strategy with a maximum of 10 attempts and a
* maximum delay of 12 hours.
* @since 0.12.0
*/
inboxRetryPolicy?: RetryPolicy;
/**
* Activity transformers that are applied to outgoing activities. It is
* useful for adjusting outgoing activities to satisfy some ActivityPub
* implementations.
*
* By default, {@link defaultActivityTransformers} are applied.
* @since 1.4.0
*/
activityTransformers?: readonly ActivityTransformer<TContextData>[];
/**
* Whether the router should be insensitive to trailing slashes in the URL
* paths. For example, if this option is `true`, `/foo` and `/foo/` are
* treated as the same path. Turned off by default.
* @since 0.12.0
*/
trailingSlashInsensitive?: boolean;
/**
* The OpenTelemetry tracer provider for tracing operations. If not provided,
* the default global tracer provider is used.
* @since 1.3.0
*/
tracerProvider?: TracerProvider;
}
/**
* Configures the task queues for sending and receiving activities.
* @since 1.3.0
*/
export interface FederationQueueOptions {
/**
* The message queue for incoming activities. If not provided, incoming
* activities will not be queued and will be processed immediately.
*/
inbox?: MessageQueue;
/**
* The message queue for outgoing activities. If not provided, outgoing
* activities will not be queued and will be sent immediately.
*/
outbox?: MessageQueue;
/**
* The message queue for fanning out outgoing activities. If not provided,
* outgoing activities will not be fanned out in the background, but will be
* fanned out immediately, which causes slow response times on
* {@link Context.sendActivity} calls.
*/
fanout?: MessageQueue;
}
/**
* Prefixes for namespacing keys in the Deno KV store.
*/
export interface FederationKvPrefixes {
/**
* The key prefix used for storing whether activities have already been
* processed or not. `["_fedify", "activityIdempotence"]` by default.
*/
activityIdempotence: KvKey;
/**
* The key prefix used for storing remote JSON-LD documents.
* `["_fedify", "remoteDocument"]` by default.
*/
remoteDocument: KvKey;
/**
* The key prefix used for caching public keys.
* `["_fedify", "publicKey"]` by default.
* @since 0.12.0
*/
publicKey: KvKey;
}
/**
* Options for {@link CreateFederationOptions.origin} when it is not a string.
* @since 1.5.0
*/
export interface FederationOrigin {
/**
* The canonical hostname for fediverse handles (which are looked up through
* WebFinger). This is used for WebFinger lookups. It has to be a valid
* hostname, e.g., `"example.com"`.
*/
handleHost: string;
/**
* The canonical origin for web URLs. This is used for constructing absolute
* URLs. It has to start with either `"http://"` or `"https://"`, and must
* not contain a path or query string, e.g., `"https://example.com"`.
*/
webOrigin: string;
}
/**
* Create a new {@link Federation} instance.
* @param parameters Parameters for initializing the instance.
* @returns A new {@link Federation} instance.
* @since 0.10.0
*/
export declare function createFederation<TContextData>(options: CreateFederationOptions<TContextData>): Federation<TContextData>;
export declare class FederationImpl<TContextData> implements Federation<TContextData> {
#private;
kv: KvStore;
kvPrefixes: FederationKvPrefixes;
inboxQueue?: MessageQueue;
outboxQueue?: MessageQueue;
fanoutQueue?: MessageQueue;
inboxQueueStarted: boolean;
outboxQueueStarted: boolean;
fanoutQueueStarted: boolean;
manuallyStartQueue: boolean;
origin?: FederationOrigin;
router: Router;
nodeInfoDispatcher?: NodeInfoDispatcher<TContextData>;
actorCallbacks?: ActorCallbacks<TContextData>;
objectCallbacks: Record<string, ObjectCallbacks<TContextData, string>>;
objectTypeIds: Record<string, (new (...args: any[]) => Object) & {
typeId: URL;
}>;
inboxPath?: string;
inboxCallbacks?: CollectionCallbacks<Activity, RequestContext<TContextData>, TContextData, void>;
outboxCallbacks?: CollectionCallbacks<Activity, RequestContext<TContextData>, TContextData, void>;
followingCallbacks?: CollectionCallbacks<Actor | URL, RequestContext<TContextData>, TContextData, void>;
followersCallbacks?: CollectionCallbacks<Recipient, Context<TContextData>, TContextData, URL>;
likedCallbacks?: CollectionCallbacks<Like, RequestContext<TContextData>, TContextData, void>;
featuredCallbacks?: CollectionCallbacks<Object, RequestContext<TContextData>, TContextData, void>;
featuredTagsCallbacks?: CollectionCallbacks<Hashtag, RequestContext<TContextData>, TContextData, void>;
inboxListeners?: InboxListenerSet<TContextData>;
inboxErrorHandler?: InboxErrorHandler<TContextData>;
sharedInboxKeyDispatcher?: SharedInboxKeyDispatcher<TContextData>;
documentLoaderFactory: DocumentLoaderFactory;
contextLoaderFactory: DocumentLoaderFactory;
authenticatedDocumentLoaderFactory: AuthenticatedDocumentLoaderFactory;
allowPrivateAddress: boolean;
userAgent?: GetUserAgentOptions | string;
onOutboxError?: OutboxErrorHandler;
signatureTimeWindow: dntShim.Temporal.Duration | dntShim.Temporal.DurationLike | false;
skipSignatureVerification: boolean;
outboxRetryPolicy: RetryPolicy;
inboxRetryPolicy: RetryPolicy;
activityTransformers: readonly ActivityTransformer<TContextData>[];
tracerProvider: TracerProvider;
constructor(options: CreateFederationOptions<TContextData>);
_startQueueInternal(ctxData: TContextData, signal?: AbortSignal, queue?: keyof FederationQueueOptions): Promise<void>;
startQueue(contextData: TContextData, options?: FederationStartQueueOptions): Promise<void>;
createContext(baseUrl: URL, contextData: TContextData): Context<TContextData>;
createContext(request: Request, contextData: TContextData): RequestContext<TContextData>;
setNodeInfoDispatcher(path: string, dispatcher: NodeInfoDispatcher<TContextData>): void;
setActorDispatcher(path: `${string}{identifier}${string}` | `${string}{handle}${string}`, dispatcher: ActorDispatcher<TContextData>): ActorCallbackSetters<TContextData>;
setObjectDispatcher<TObject extends Object, TParam extends string>(cls: (new (...args: any[]) => TObject) & {
typeId: URL;
}, path: `${string}{${TParam}}${string}{${TParam}}${string}{${TParam}}${string}{${TParam}}${string}{${TParam}}${string}{${TParam}}${string}`, dispatcher: ObjectDispatcher<TContextData, TObject, TParam>): ObjectCallbackSetters<TContextData, TObject, TParam>;
setObjectDispatcher<TObject extends Object, TParam extends string>(cls: (new (...args: any[]) => TObject) & {
typeId: URL;
}, path: `${string}{${TParam}}${string}{${TParam}}${string}{${TParam}}${string}{${TParam}}${string}{${TParam}}${string}`, dispatcher: ObjectDispatcher<TContextData, TObject, TParam>): ObjectCallbackSetters<TContextData, TObject, TParam>;
setObjectDispatcher<TObject extends Object, TParam extends string>(cls: (new (...args: any[]) => TObject) & {
typeId: URL;
}, path: `${string}{${TParam}}${string}{${TParam}}${string}{${TParam}}${string}{${TParam}}${string}`, dispatcher: ObjectDispatcher<TContextData, TObject, TParam>): ObjectCallbackSetters<TContextData, TObject, TParam>;
setObjectDispatcher<TObject extends Object, TParam extends string>(cls: (new (...args: any[]) => TObject) & {
typeId: URL;
}, path: `${string}{${TParam}}${string}{${TParam}}${string}{${TParam}}${string}`, dispatcher: ObjectDispatcher<TContextData, TObject, TParam>): ObjectCallbackSetters<TContextData, TObject, TParam>;
setObjectDispatcher<TObject extends Object, TParam extends string>(cls: (new (...args: any[]) => TObject) & {
typeId: URL;
}, path: `${string}{${TParam}}${string}{${TParam}}${string}`, dispatcher: ObjectDispatcher<TContextData, TObject, TParam>): ObjectCallbackSetters<TContextData, TObject, TParam>;
setObjectDispatcher<TObject extends Object, TParam extends string>(cls: (new (...args: any[]) => TObject) & {
typeId: URL;
}, path: `${string}{${TParam}}${string}`, dispatcher: ObjectDispatcher<TContextData, TObject, TParam>): ObjectCallbackSetters<TContextData, TObject, TParam>;
setInboxDispatcher(path: `${string}{identifier}${string}` | `${string}{handle}${string}`, dispatcher: CollectionDispatcher<Activity, RequestContext<TContextData>, TContextData, void>): CollectionCallbackSetters<RequestContext<TContextData>, TContextData, void>;
setOutboxDispatcher(path: `${string}{identifier}${string}` | `${string}{handle}${string}`, dispatcher: CollectionDispatcher<Activity, RequestContext<TContextData>, TContextData, void>): CollectionCallbackSetters<RequestContext<TContextData>, TContextData, void>;
setFollowingDispatcher(path: `${string}{identifier}${string}` | `${string}{handle}${string}`, dispatcher: CollectionDispatcher<Actor | URL, RequestContext<TContextData>, TContextData, void>): CollectionCallbackSetters<RequestContext<TContextData>, TContextData, void>;
setFollowersDispatcher(path: `${string}{identifier}${string}` | `${string}{handle}${string}`, dispatcher: CollectionDispatcher<Recipient, Context<TContextData>, TContextData, URL>): CollectionCallbackSetters<Context<TContextData>, TContextData, URL>;
setLikedDispatcher(path: `${string}{identifier}${string}` | `${string}{handle}${string}`, dispatcher: CollectionDispatcher<Like, RequestContext<TContextData>, TContextData, void>): CollectionCallbackSetters<RequestContext<TContextData>, TContextData, void>;
setFeaturedDispatcher(path: `${string}{identifier}${string}` | `${string}{handle}${string}`, dispatcher: CollectionDispatcher<Object, RequestContext<TContextData>, TContextData, void>): CollectionCallbackSetters<RequestContext<TContextData>, TContextData, void>;
setFeaturedTagsDispatcher(path: `${string}{identifier}${string}` | `${string}{handle}${string}`, dispatcher: CollectionDispatcher<Hashtag, RequestContext<TContextData>, TContextData, void>): CollectionCallbackSetters<RequestContext<TContextData>, TContextData, void>;
setInboxListeners(inboxPath: `${string}{identifier}${string}` | `${string}{handle}${string}`, sharedInboxPath?: string): InboxListenerSetters<TContextData>;
sendActivity(keys: SenderKeyPair[], inboxes: Record<string, {
actorIds: Iterable<string>;
sharedInbox: boolean;
}>, activity: Activity, options: SendActivityInternalOptions<TContextData>): Promise<void>;
fetch(request: Request, options: FederationFetchOptions<TContextData>): Promise<Response>;
}
interface ContextOptions<TContextData> {
url: URL;
federation: FederationImpl<TContextData>;
data: TContextData;
documentLoader: DocumentLoader;
contextLoader: DocumentLoader;
invokedFromActorKeyPairsDispatcher?: {
identifier: string;
};
}
export declare class ContextImpl<TContextData> implements Context<TContextData> {
readonly url: URL;
readonly federation: FederationImpl<TContextData>;
readonly data: TContextData;
readonly documentLoader: DocumentLoader;
readonly contextLoader: DocumentLoader;
readonly invokedFromActorKeyPairsDispatcher?: {
identifier: string;
};
constructor({ url, federation, data, documentLoader, contextLoader, invokedFromActorKeyPairsDispatcher, }: ContextOptions<TContextData>);
toInboxContext(recipient: string | null, activity: unknown, activityId: string | undefined, activityType: string): InboxContextImpl<TContextData>;
get hostname(): string;
get host(): string;
get origin(): string;
get canonicalOrigin(): string;
get tracerProvider(): TracerProvider;
getNodeInfoUri(): URL;
getActorUri(identifier: string): URL;
getObjectUri<TObject extends Object>(cls: (new (...args: any[]) => TObject) & {
typeId: URL;
}, values: Record<string, string>): URL;
getOutboxUri(identifier: string): URL;
getInboxUri(): URL;
getInboxUri(identifier: string): URL;
getFollowingUri(identifier: string): URL;
getFollowersUri(identifier: string): URL;
getLikedUri(identifier: string): URL;
getFeaturedUri(identifier: string): URL;
getFeaturedTagsUri(identifier: string): URL;
parseUri(uri: URL | null): ParseUriResult | null;
getActorKeyPairs(identifier: string): Promise<ActorKeyPair[]>;
protected getKeyPairsFromIdentifier(identifier: string): Promise<(dntShim.CryptoKeyPair & {
keyId: URL;
})[]>;
protected getRsaKeyPairFromIdentifier(identifier: string): Promise<dntShim.CryptoKeyPair & {
keyId: URL;
} | null>;
getDocumentLoader(identity: {
identifier: string;
} | {
username: string;
} | {
handle: string;
}): Promise<DocumentLoader>;
getDocumentLoader(identity: SenderKeyPair): DocumentLoader;
lookupObject(identifier: string | URL, options?: LookupObjectOptions): Promise<Object | null>;
traverseCollection(collection: Collection, options?: TraverseCollectionOptions): AsyncIterable<Object | Link>;
lookupNodeInfo(url: URL | string, options?: GetNodeInfoOptions & {
parse?: "strict" | "best-effort";
}): Promise<NodeInfo | undefined>;
lookupNodeInfo(url: URL | string, options?: GetNodeInfoOptions & {
parse: "none";
}): Promise<JsonValue | undefined>;
sendActivity(sender: SenderKeyPair | SenderKeyPair[] | {
identifier: string;
} | {
username: string;
} | {
handle: string;
}, recipients: Recipient | Recipient[] | "followers", activity: Activity, options?: SendActivityOptionsForCollection): Promise<void>;
protected sendActivityInternal(sender: SenderKeyPair | SenderKeyPair[] | {
identifier: string;
} | {
username: string;
} | {
handle: string;
}, recipients: Recipient | Recipient[] | "followers", activity: Activity, options: SendActivityOptionsForCollection, span: Span): Promise<void>;
getFollowers(identifier: string): AsyncIterable<Recipient>;
routeActivity(recipient: string | null, activity: Activity, options?: RouteActivityOptions): Promise<boolean>;
protected routeActivityInternal(recipient: string | null, activity: Activity, options: RouteActivityOptions | undefined, span: Span): Promise<boolean>;
}
export declare class InboxContextImpl<TContextData> extends ContextImpl<TContextData> implements InboxContext<TContextData> {
readonly recipient: string | null;
readonly activity: unknown;
readonly activityId?: string;
readonly activityType: string;
constructor(recipient: string | null, activity: unknown, activityId: string | undefined, activityType: string, options: ContextOptions<TContextData>);
forwardActivity(forwarder: SenderKeyPair | SenderKeyPair[] | {
identifier: string;
} | {
username: string;
} | {
handle: string;
}, recipients: Recipient | Recipient[], options?: ForwardActivityOptions): Promise<void>;
forwardActivity(forwarder: {
identifier: string;
} | {
username: string;
} | {
handle: string;
}, recipients: "followers", options?: ForwardActivityOptions): Promise<void>;
private forwardActivityInternal;
}
interface ActorCallbacks<TContextData> {
dispatcher?: ActorDispatcher<TContextData>;
keyPairsDispatcher?: ActorKeyPairsDispatcher<TContextData>;
handleMapper?: ActorHandleMapper<TContextData>;
aliasMapper?: ActorAliasMapper<TContextData>;
authorizePredicate?: AuthorizePredicate<TContextData>;
}
interface ObjectCallbacks<TContextData, TParam extends string> {
dispatcher: ObjectDispatcher<TContextData, Object, string>;
parameters: Set<TParam>;
authorizePredicate?: ObjectAuthorizePredicate<TContextData, TParam>;
}
interface SendActivityInternalOptions<TContextData> {
immediate?: boolean;
collectionSync?: string;
context: Context<TContextData>;
}
export {};
//# sourceMappingURL=middleware.d.ts.map