UNPKG

@fedify/fedify

Version:

An ActivityPub server framework

713 lines • 26.3 kB
import * as dntShim from "../_dnt.shims.js"; import type { TracerProvider } from "@opentelemetry/api"; import type { GetNodeInfoOptions } from "../nodeinfo/client.js"; import type { JsonValue, NodeInfo } from "../nodeinfo/types.js"; import type { DocumentLoader } from "../runtime/docloader.js"; import type { GetKeyOwnerOptions } from "../sig/owner.js"; import type { Actor, Recipient } from "../vocab/actor.js"; import type { LookupObjectOptions, TraverseCollectionOptions } from "../vocab/lookup.js"; import type { Activity, Collection, CryptographicKey, Link, Multikey, Object } from "../vocab/vocab.js"; import type { SenderKeyPair } from "./send.js"; /** * A context. */ export interface Context<TContextData> { /** * The origin of the federated server, including the scheme (`http://` or * `https://`) and the host (e.g., `example.com:8080`). * @since 0.12.0 */ readonly origin: string; /** * The canonical origin of the federated server, including the scheme * (`http://` or `https://`) and the host (e.g., `example.com:8080`). * * When the associated {@link Federation} object does not have any explicit * canonical origin, it is the same as the {@link Context.origin}. * @since 1.5.0 */ readonly canonicalOrigin: string; /** * The host of the federated server, including the hostname * (e.g., `example.com`) and the port following a colon (e.g., `:8080`) * if it is not the default port for the scheme. * @since 0.12.0 */ readonly host: string; /** * The hostname of the federated server (e.g., `example.com`). This is * the same as the host without the port. * @since 0.12.0 */ readonly hostname: string; /** * The user-defined data associated with the context. */ readonly data: TContextData; /** * The OpenTelemetry tracer provider. * @since 1.3.0 */ readonly tracerProvider: TracerProvider; /** * The document loader for loading remote JSON-LD documents. */ readonly documentLoader: DocumentLoader; /** * The context loader for loading remote JSON-LD contexts. */ readonly contextLoader: DocumentLoader; /** * Builds the URI of the NodeInfo document. * @returns The NodeInfo URI. * @throws {RouterError} If no NodeInfo dispatcher is available. * @since 0.2.0 */ getNodeInfoUri(): URL; /** * Builds the URI of an actor with the given identifier. * @param identifier The actor's identifier. * @returns The actor's URI. * @throws {RouterError} If no actor dispatcher is available. */ getActorUri(identifier: string): URL; /** * Builds the URI of an object with the given class and values. * @param cls The class of the object. * @param values The values to pass to the object dispatcher. * @returns The object's URI. * @throws {RouteError} If no object dispatcher is available for the class. * @throws {TypeError} If values are invalid. * @since 0.7.0 */ getObjectUri<TObject extends Object>(cls: (new (...args: any[]) => TObject) & { typeId: URL; }, values: Record<string, string>): URL; /** * Builds the URI of an actor's outbox with the given identifier. * @param identifier The actor's identifier. * @returns The actor's outbox URI. * @throws {RouterError} If no outbox dispatcher is available. */ getOutboxUri(identifier: string): URL; /** * Builds the URI of the shared inbox. * @returns The shared inbox URI. * @throws {RouterError} If no inbox listener is available. */ getInboxUri(): URL; /** * Builds the URI of an actor's inbox with the given identifier. * @param identifier The actor's identifier. * @returns The actor's inbox URI. * @throws {RouterError} If no inbox listener is available. */ getInboxUri(identifier: string): URL; /** * Builds the URI of an actor's following collection with the given * identifier. * @param identifier The actor's identifier. * @returns The actor's following collection URI. * @throws {RouterError} If no following collection is available. */ getFollowingUri(identifier: string): URL; /** * Builds the URI of an actor's followers collection with the given * identifier. * @param identifier The actor's identifier. * @returns The actor's followers collection URI. * @throws {RouterError} If no followers collection is available. */ getFollowersUri(identifier: string): URL; /** * Builds the URI of an actor's liked collection with the given identifier. * @param identifier The actor's identifier. * @returns The actor's liked collection URI. * @throws {RouterError} If no liked collection is available. * @since 0.11.0 */ getLikedUri(identifier: string): URL; /** * Builds the URI of an actor's featured collection with the given identifier. * @param identifier The actor's identifier. * @returns The actor's featured collection URI. * @throws {RouterError} If no featured collection is available. * @since 0.11.0 */ getFeaturedUri(identifier: string): URL; /** * Builds the URI of an actor's featured tags collection with the given * identifier. * @param identifier The actor's identifier. * @returns The actor's featured tags collection URI. * @throws {RouterError} If no featured tags collection is available. * @since 0.11.0 */ getFeaturedTagsUri(identifier: string): URL; /** * Determines the type of the URI and extracts the associated data. * @param uri The URI to parse. * @returns The result of parsing the URI. If `null` is given or * the URI is not recognized, `null` is returned. * @since 0.9.0 */ parseUri(uri: URL | null): ParseUriResult | null; /** * Gets the key pairs for an actor. * @param identifier The actor's identifier. * @returns An async iterable of the actor's key pairs. It can be empty. * @since 0.10.0 */ getActorKeyPairs(identifier: string): Promise<ActorKeyPair[]>; /** * Gets an authenticated {@link DocumentLoader} for the given identity. * Note that an authenticated document loader intentionally does not cache * the fetched documents. * @param identity The identity to get the document loader for. * The actor's identifier or username. * @returns The authenticated document loader. * @throws {Error} If the identity is not valid. * @throws {TypeError} If the key is invalid or unsupported. * @since 0.4.0 */ getDocumentLoader(identity: { identifier: string; } | { username: string; } | { handle: string; }): Promise<DocumentLoader>; /** * Gets an authenticated {@link DocumentLoader} for the given identity. * Note that an authenticated document loader intentionally does not cache * the fetched documents. * @param identity The identity to get the document loader for. * The actor's key pair. * @returns The authenticated document loader. * @throws {TypeError} If the key is invalid or unsupported. * @since 0.4.0 */ getDocumentLoader(identity: { keyId: URL; privateKey: dntShim.CryptoKey; }): DocumentLoader; /** * Looks up an ActivityStreams object by its URI (including `acct:` URIs) * or a fediverse handle (e.g., `@user@server` or `user@server`). * * @example * ``` typescript * // Look up an actor by its fediverse handle: * await ctx.lookupObject("@hongminhee@fosstodon.org"); * // returning a `Person` object. * * // A fediverse handle can omit the leading '@': * await ctx.lookupObject("hongminhee@fosstodon.org"); * // returning a `Person` object. * * // A `acct:` URI can be used as well: * await ctx.lookupObject("acct:hongminhee@fosstodon.org"); * // returning a `Person` object. * * // Look up an object by its URI: * await ctx.lookupObject("https://todon.eu/@hongminhee/112060633798771581"); * // returning a `Note` object. * * // It can be a `URL` object as well: * await ctx.lookupObject( * new URL("https://todon.eu/@hongminhee/112060633798771581") * ); * // returning a `Note` object. * ``` * * It's almost the same as the {@link lookupObject} function, but it uses * the context's document loader and context loader by default. * * @param identifier The URI or fediverse handle to look up. * @param options Lookup options. * @returns The object, or `null` if not found. * @since 0.15.0 */ lookupObject(identifier: string | URL, options?: LookupObjectOptions): Promise<Object | null>; /** * Traverses a collection, yielding each item in the collection. * If the collection is paginated, it will fetch the next page * automatically. * * @example * ``` typescript * const collection = await ctx.lookupObject(collectionUrl); * if (collection instanceof Collection) { * for await (const item of ctx.traverseCollection(collection)) { * console.log(item.id?.href); * } * } * ``` * * It's almost the same as the {@link traverseCollection} function, but it * uses the context's document loader and context loader by default. * @param collection The collection to traverse. * @param options Options for traversing the collection. * @returns An async iterable of each item in the collection. * @since 1.1.0 */ traverseCollection(collection: Collection, options?: TraverseCollectionOptions): AsyncIterable<Object | Link>; /** * Fetches the NodeInfo document from the given URL. * @param url The base URL of the server. If `options.direct` is turned off * (default), the NodeInfo document will be fetched from * the `.well-known` location of this URL (hence the only origin * of the URL is used). If `options.direct` is turned on, * the NodeInfo document will be fetched from the given URL. * @param options Options for fetching the NodeInfo document. * @returns The NodeInfo document if it could be fetched successfully. * Otherwise, `undefined` is returned. * @since 1.4.0 */ lookupNodeInfo(url: URL | string, options?: GetNodeInfoOptions & { parse?: "strict" | "best-effort"; }): Promise<NodeInfo | undefined>; /** * Fetches the NodeInfo document from the given URL. * @param url The base URL of the server. If `options.direct` is turned off * (default), the NodeInfo document will be fetched from * the `.well-known` location of this URL (hence the only origin * of the URL is used). If `options.direct` is turned on, * the NodeInfo document will be fetched from the given URL. * @param options Options for fetching the NodeInfo document. * @returns The NodeInfo document if it could be fetched successfully. * Otherwise, `undefined` is returned. * @since 1.4.0 */ lookupNodeInfo(url: URL | string, options?: GetNodeInfoOptions & { parse: "none"; }): Promise<JsonValue | undefined>; /** * Sends an activity to recipients' inboxes. * @param sender The sender's identifier or the sender's username 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[] | { identifier: string; } | { username: string; } | { 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 identifier or the sender's username. * @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: { identifier: string; } | { username: string; } | { handle: string; }, recipients: "followers", activity: Activity, options?: SendActivityOptionsForCollection): Promise<void>; /** * Manually routes an activity to the appropriate inbox listener. * * It is useful for routing an activity that is not received from the network, * or for routing an activity that is enclosed in another activity. * * Note that the activity will be verified if it has Object Integrity Proofs * or is equivalent to the actual remote object. If the activity is not * verified, it will be rejected. * @param recipient The recipient of the activity. If it is `null`, * the activity will be routed to the shared inbox. * Otherwise, the activity will be routed to the personal * inbox of the recipient with the given identifier. * @param activity The activity to route. It must have a proof or * a dereferenceable `id` to verify the activity. * @param options Options for routing the activity. * @returns `true` if the activity is successfully verified and routed. * Otherwise, `false`. * @since 1.3.0 */ routeActivity(recipient: string | null, activity: Activity, options?: RouteActivityOptions): Promise<boolean>; } /** * A context for a request. */ export interface RequestContext<TContextData> extends Context<TContextData> { /** * The request object. */ readonly request: Request; /** * The URL of the request. */ readonly url: URL; /** * Gets an {@link Actor} object for the given identifier. * @param identifier The actor's identifier. * @returns The actor object, or `null` if the actor is not found. * @throws {Error} If no actor dispatcher is available. * @since 0.7.0 */ getActor(identifier: string): Promise<Actor | null>; /** * Gets an object of the given class with the given values. * @param cls The class to instantiate. * @param values The values to pass to the object dispatcher. * @returns The object of the given class with the given values, or `null` * if the object is not found. * @throws {Error} If no object dispatcher is available for the class. * @throws {TypeError} If values are invalid. * @since 0.7.0 */ getObject<TObject extends Object>(cls: (new (...args: any[]) => TObject) & { typeId: URL; }, values: Record<string, string>): Promise<TObject | null>; /** * Gets the public key of the sender, if any exists and it is verified. * Otherwise, `null` is returned. * * This can be used for implementing [authorized fetch] (also known as * secure mode) in ActivityPub. * * [authorized fetch]: https://swicg.github.io/activitypub-http-signature/#authorized-fetch * * @returns The public key of the sender, or `null` if the sender is not verified. * @since 0.7.0 */ getSignedKey(): Promise<CryptographicKey | null>; /** * Gets the public key of the sender, if any exists and it is verified. * Otherwise, `null` is returned. * * This can be used for implementing [authorized fetch] (also known as * secure mode) in ActivityPub. * * [authorized fetch]: https://swicg.github.io/activitypub-http-signature/#authorized-fetch * * @param options Options for getting the signed key. You usually may want to * specify the custom `documentLoader` so that making * an HTTP request to the sender's server is signed with * your [instance actor]. * @returns The public key of the sender, or `null` if the sender is not verified. * @since 1.5.0 * * [instance actor]: https://swicg.github.io/activitypub-http-signature/#instance-actor */ getSignedKey(options: GetSignedKeyOptions): Promise<CryptographicKey | null>; /** * Gets the owner of the signed key, if any exists and it is verified. * Otherwise, `null` is returned. * * This can be used for implementing [authorized fetch] (also known as * secure mode) in ActivityPub. * * [authorized fetch]: https://swicg.github.io/activitypub-http-signature/#authorized-fetch * * @returns The owner of the signed key, or `null` if the key is not verified * or the owner is not found. * @since 0.7.0 */ getSignedKeyOwner(): Promise<Actor | null>; /** * Gets the owner of the signed key, if any exists and it is verified. * Otherwise, `null` is returned. * * This can be used for implementing [authorized fetch] (also known as * secure mode) in ActivityPub. * * [authorized fetch]: https://swicg.github.io/activitypub-http-signature/#authorized-fetch * * @param options Options for getting the key owner. You usually may want to * specify the custom `documentLoader` so that making * an HTTP request to the key owner's server is signed with * your [instance actor]. * @returns The owner of the signed key, or `null` if the key is not verified * or the owner is not found. * @since 1.5.0 * * [instance actor]: https://swicg.github.io/activitypub-http-signature/#instance-actor */ getSignedKeyOwner(options: GetKeyOwnerOptions): Promise<Actor | null>; } /** * A context for inbox listeners. * @since 1.0.0 */ export interface InboxContext<TContextData> extends Context<TContextData> { /** * The identifier of the recipient of the inbox. If the inbox is a shared * inbox, it is `null`. * @since 1.2.0 */ recipient: string | null; /** * Forwards a received activity to the recipients' inboxes. The forwarded * activity will be signed in HTTP Signatures by the forwarder, but its * payload will not be modified, i.e., Linked Data Signatures and Object * Integrity Proofs will not be added. Therefore, if the activity is not * signed (i.e., it has neither Linked Data Signatures nor Object Integrity * Proofs), the recipient probably will not trust the activity. * @param forwarder The forwarder's identifier or the forwarder's username * or the forwarder's key pair(s). * @param recipients The recipients of the activity. * @param options Options for forwarding the activity. * @since 1.0.0 */ forwardActivity(forwarder: SenderKeyPair | SenderKeyPair[] | { identifier: string; } | { username: string; } | { handle: string; }, recipients: Recipient | Recipient[], options?: ForwardActivityOptions): Promise<void>; /** * Forwards a received activity to the recipients' inboxes. The forwarded * activity will be signed in HTTP Signatures by the forwarder, but its * payload will not be modified, i.e., Linked Data Signatures and Object * Integrity Proofs will not be added. Therefore, if the activity is not * signed (i.e., it has neither Linked Data Signatures nor Object Integrity * Proofs), the recipient probably will not trust the activity. * @param forwarder The forwarder's identifier or the forwarder's username. * @param recipients In this case, it must be `"followers"`. * @param options Options for forwarding the activity. * @since 1.0.0 */ forwardActivity(forwarder: { identifier: string; } | { username: string; } | { handle: string; }, recipients: "followers", options?: ForwardActivityOptions): Promise<void>; } /** * A result of parsing an URI. */ export type ParseUriResult = /** * The case of an actor URI. */ { readonly type: "actor"; readonly identifier: string; readonly handle: string; } /** * The case of an object URI. */ | { readonly type: "object"; readonly class: (new (...args: any[]) => Object) & { typeId: URL; }; readonly typeId: URL; readonly values: Record<string, string>; } /** * The case of an shared inbox URI. */ | { readonly type: "inbox"; readonly identifier: undefined; readonly handle: undefined; } /** * The case of an personal inbox URI. */ | { readonly type: "inbox"; readonly identifier: string; readonly handle: string; } /** * The case of an outbox collection URI. */ | { readonly type: "outbox"; readonly identifier: string; readonly handle: string; } /** * The case of a following collection URI. */ | { readonly type: "following"; readonly identifier: string; readonly handle: string; } /** * The case of a followers collection URI. */ | { readonly type: "followers"; readonly identifier: string; readonly handle: string; } /** * The case of a liked collection URI. * @since 0.11.0 */ | { readonly type: "liked"; readonly identifier: string; readonly handle: string; } /** * The case of a featured collection URI. * @since 0.11.0 */ | { readonly type: "featured"; readonly identifier: string; readonly handle: string; } /** * The case of a featured tags collection URI. * @since 0.11.0 */ | { readonly type: "featuredTags"; readonly identifier: string; readonly handle: string; }; /** * Options for {@link Context.sendActivity} method. */ export interface SendActivityOptions { /** * Whether to prefer the shared inbox for the recipients. */ preferSharedInbox?: boolean; /** * Whether to send the activity immediately, without enqueuing it. * If `true`, the activity will be sent immediately and the retrial * policy will not be applied. * * @since 0.3.0 */ immediate?: boolean; /** * Determines how activities are queued when sent to multiple recipients. * * - "auto" (default): Automatically chooses optimal strategy based on * recipient count. * - "skip": Always enqueues individual messages per recipient, * bypassing the fanout queue. Use when payload needs to vary per recipient. * - "force": Always uses fanout queue regardless of recipient count. * Useful for testing or special cases. * * This option is ignored when `immediate: true` is specified, as immediate * delivery bypasses all queuing mechanisms. * * @default `"auto"` * @since 1.5.0 */ fanout?: "auto" | "skip" | "force"; /** * The base URIs to exclude from the recipients' inboxes. It is useful * for excluding the recipients having the same shared inbox with the sender. * * Note that the only `origin` parts of the `URL`s are compared. * * @since 0.9.0 */ excludeBaseUris?: URL[]; } /** * Options for {@link Context.sendActivity} method when sending to a collection. * @since 1.5.0 */ export interface SendActivityOptionsForCollection extends SendActivityOptions { /** * Whether to synchronize the collection using `Collection-Synchronization` * header ([FEP-8fcf]). * * [FEP-8fcf]: https://w3id.org/fep/8fcf */ syncCollection?: boolean; } /** * Options for {@link InboxContext.forwardActivity} method. * @since 1.0.0 */ export type ForwardActivityOptions = Omit<SendActivityOptions, "fanout"> & { /** * Whether to skip forwarding the activity if it is not signed, i.e., it has * neither Linked Data Signatures nor Object Integrity Proofs. * * If the activity is not signed, the recipient probably will not trust the * activity. Therefore, it is recommended to skip forwarding the activity * if it is not signed. */ skipIfUnsigned: boolean; }; /** * Options for {@link Context.routeActivity} method. * @since 1.3.0 */ export interface RouteActivityOptions { /** * Whether to skip enqueuing the activity and invoke the listener immediately. * If no inbox queue is available, this option is ignored and the activity * will be always invoked immediately. * @default false */ immediate?: boolean; /** * The document loader for loading remote JSON-LD documents. */ documentLoader?: DocumentLoader; /** * The context loader for loading remote JSON-LD contexts. */ contextLoader?: DocumentLoader; /** * The OpenTelemetry tracer provider. If omitted, the global tracer provider * is used. */ tracerProvider?: TracerProvider; } /** * Options for {@link Context.getSignedKey} method. * @since 1.5.0 */ export interface GetSignedKeyOptions { /** * The document loader for loading remote JSON-LD documents. */ documentLoader?: DocumentLoader; /** * The context loader for loading remote JSON-LD contexts. */ contextLoader?: DocumentLoader; /** * The OpenTelemetry tracer provider. If omitted, the global tracer provider * is used. */ tracerProvider?: TracerProvider; } /** * A pair of a public key and a private key in various formats. * @since 0.10.0 */ export interface ActorKeyPair extends dntShim.CryptoKeyPair { /** * The URI of the public key, which is used for verifying HTTP Signatures. */ keyId: URL; /** * A {@link CryptographicKey} instance of the public key. */ cryptographicKey: CryptographicKey; /** * A {@link Multikey} instance of the public key. */ multikey: Multikey; } //# sourceMappingURL=context.d.ts.map