UNPKG

@fedify/fedify

Version:

An ActivityPub server framework

452 lines • 23.1 kB
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