Loading CHANGES.md +1 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ To be released. - Removed `expand` option of `Object.toJsonLd()` method, which was deprecated in version 0.14.0. Use `format: "expand"` option instead. - Added `Context.lookupObject()` method. - Renamed the short option `-c` for `--compact` of `fedify lookup` command to `-C` to avoid conflict with the short option `-c` for `--cache-dir`. - Added `-r`/`--raw` option to `fedify lookup` command to output the raw JSON Loading docs/manual/context.md +85 −4 Original line number Diff line number Diff line Loading @@ -21,11 +21,13 @@ callbacks. The key features of the `Context` object are as follows: - Carrying [`TContextData`](./federation.md#tcontextdata) - Building the object URIs (e.g., actor URIs, shared inbox URI) - Dispatching Activity Vocabulary objects - [Building the object URIs](#building-the-object-uris) (e.g., actor URIs, shared inbox URI) - [Dispatching Activity Vocabulary objects](#dispatching-objects) - Getting the current HTTP request - Enqueuing an outgoing activity - Getting a `DocumentLoader` - [Enqueuing an outgoing activity](#enqueuing-an-outgoing-activity) - [Getting a `DocumentLoader`](#getting-a-documentloader) - [Looking up remote objects](#looking-up-remote-objects) Where to get a `Context` object Loading Loading @@ -268,3 +270,82 @@ type, but they are used for different purposes: Sometimes a document loader needs to be authenticated to load a remote document which requires authorization, but a context loader mostly needs to be highly cached and doesn't require authorization. Looking up remote objects ------------------------- *This API is available since Fedify 0.15.0.* > [!TIP] > In most cases, you don't need to look up remote objects explicitly. > Instead, you can use the dereferencing accessors to fetch the remote objects > implicitly. > > For example, you can get the `object` from an `Activity` object directly: > > ~~~~ typescript > const object = await activity.getObject(); > ~~~~ > > … instead of: > > ~~~~ typescript > const object = activity.objectId == null > ? null > : await ctx.lookupObject(activity.objectId); > ~~~~ Suppose your app has a search box that allows the user to look up a fediverse user by the handle or a post by the URI. In such cases, you need to look up the object from a remote server that your app haven't interacted with yet. The `Context.lookupObject()` method plays a role in such cases. The following shows an example of looking up an actor object from the handle: ~~~~ typescript const actor = await ctx.lookupObject("@hongminhee@todon.eu"); ~~~~ In the above example, the `~Context.lookupObject()` method queries the remote server's WebFinger endpoint to get the actor's URI from the handle, and then fetches the actor object from the URI. > [!TIP] > The `~Context.lookupObject()` method accepts a fediverse handle without > prefix `@` as well: > > ~~~~ typescript > const actor = await ctx.lookupObject("hongminhee@todon.eu"); > ~~~~ > > Also an `acct:` URI: > > ~~~~ typescript > const actor = await ctx.lookupObject("acct:hongminhee@todon.eu"); > ~~~~ The `~Context.lookupObject()` method is not limited to the actor object. It can look up any object in the Activity Vocabulary. For example the following shows an example of looking up a `Note` object from the URI: ~~~~ typescript const note = await ctx.lookupObject( "https://todon.eu/@hongminhee/112060633798771581" ); ~~~~ > [!NOTE] > Some objects require authentication to look up, such as a `Note` object with > a visibility of followers-only. In such cases, you need to use > the `Context.getDocumentLoader()` method to get an authenticated > `DocumentLoader` object. The `~Context.lookupObject()` method takes the > `documentLoader` option to specify the method to fetch the remote object: > > ~~~~ typescript > const documentLoader = await ctx.getDocumentLoader({ handle: "john" }); > const note = await ctx.lookupObject("...", { documentLoader }); > ~~~~ > > See the [*Getting an authenticated > `DocumentLoader`*](#getting-an-authenticated-documentloader) > section for details. docs/manual/vocab.md +2 −55 Original line number Diff line number Diff line Loading @@ -205,61 +205,8 @@ the constructor. Looking up remote objects ------------------------- *This API is available since Fedify 0.2.0.* Suppose your app has a search box that allows the user to look up a fediverse user by the handle or a post by the URI. In such cases, you need to look up the object from a remote server that your app haven't interacted with yet. The `lookupObject()` function plays a role in such cases. The following shows an example of looking up an actor object from the handle: ~~~~ typescript import { lookupObject } from "@fedify/fedify"; const actor = await lookupObject("@hongminhee@todon.eu"); ~~~~ In the above example, the `lookupObject()` function queries the remote server's WebFinger endpoint to get the actor's URI from the handle, and then fetches the actor object from the URI. > [!TIP] > The `lookupObject()` function accepts a fediverse handle without prefix `@` > as well: > > ~~~~ typescript > const actor = await lookupObject("hongminhee@todon.eu"); > ~~~~ > > Also an `acct:` URI: > > ~~~~ typescript > const actor = await lookupObject("acct:hongminhee@todon.eu"); > ~~~~ The `lookupObject()` function is not limited to the actor object. It can look up any object in the Activity Vocabulary. For example, the following shows an example of looking up a `Note` object from the URI: ~~~~ typescript const note = await lookupObject("https://todon.eu/@hongminhee/112060633798771581"); ~~~~ > [!NOTE] > Some objects require authentication to look up, such as a `Note` object with > a visibility of followers-only. In such cases, you need to use > the `Context.getDocumentLoader()` method to get an authenticated > `DocumentLoader` object. The `lookupObject()` function takes the > `documentLoader` option to specify the method to fetch the remote object: > > ~~~~ typescript > const documentLoader = await ctx.getDocumentLoader({ handle: "john" }); > const note = await lookupObject("...", { documentLoader }); > ~~~~ > > See the [*Getting an authenticated > `DocumentLoader`*](./context.md#getting-an-authenticated-documentloader) > section for details. See the [*Looking up remote objects* section](./context.md#looking-up-remote-objects) in the *Context* docs. JSON-LD Loading src/federation/context.ts +43 −0 Original line number Diff line number Diff line import type { DocumentLoader } from "../runtime/docloader.ts"; import type { Actor, Recipient } from "../vocab/actor.ts"; import type { LookupObjectOptions } from "../vocab/lookup.ts"; import type { Activity, CryptographicKey, Loading Loading @@ -189,6 +190,48 @@ export interface Context<TContextData> { identity: { keyId: URL; privateKey: 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>; /** * Sends an activity to recipients' inboxes. * @param sender The sender's handle or the sender's key pair(s). Loading src/federation/middleware.test.ts +19 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ import { } from "../testing/keys.ts"; import { test } from "../testing/mod.ts"; import { lookupObject } from "../vocab/lookup.ts"; import { Create, Multikey, Note, Person } from "../vocab/vocab.ts"; import { Create, Multikey, Note, Object, Person } from "../vocab/vocab.ts"; import type { Context } from "./context.ts"; import { MemoryKvStore } from "./kv.ts"; import { createFederation } from "./middleware.ts"; Loading Loading @@ -183,6 +183,7 @@ test("Federation.createContext()", async (t) => { documentUrl: "https://example.com/object", document: true, }); assertEquals(await ctx.lookupObject("https://example.com/object"), null); assertRejects( () => ctx.sendActivity({ handle: "handle" }, [], new Create({})), TypeError, Loading @@ -196,6 +197,23 @@ test("Federation.createContext()", async (t) => { }), ); const federation2 = createFederation<number>({ kv, documentLoader: mockDocumentLoader, contextLoader: mockDocumentLoader, }); const ctx2 = federation2.createContext( new URL("https://example.com/"), 123, ); assertEquals( await ctx2.lookupObject("https://example.com/object"), new Object({ id: new URL("https://example.com/object"), name: "Fetched object", }), ); federation.setObjectDispatcher( Note, "/users/{handle}/notes/{id}", Loading Loading
CHANGES.md +1 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ To be released. - Removed `expand` option of `Object.toJsonLd()` method, which was deprecated in version 0.14.0. Use `format: "expand"` option instead. - Added `Context.lookupObject()` method. - Renamed the short option `-c` for `--compact` of `fedify lookup` command to `-C` to avoid conflict with the short option `-c` for `--cache-dir`. - Added `-r`/`--raw` option to `fedify lookup` command to output the raw JSON Loading
docs/manual/context.md +85 −4 Original line number Diff line number Diff line Loading @@ -21,11 +21,13 @@ callbacks. The key features of the `Context` object are as follows: - Carrying [`TContextData`](./federation.md#tcontextdata) - Building the object URIs (e.g., actor URIs, shared inbox URI) - Dispatching Activity Vocabulary objects - [Building the object URIs](#building-the-object-uris) (e.g., actor URIs, shared inbox URI) - [Dispatching Activity Vocabulary objects](#dispatching-objects) - Getting the current HTTP request - Enqueuing an outgoing activity - Getting a `DocumentLoader` - [Enqueuing an outgoing activity](#enqueuing-an-outgoing-activity) - [Getting a `DocumentLoader`](#getting-a-documentloader) - [Looking up remote objects](#looking-up-remote-objects) Where to get a `Context` object Loading Loading @@ -268,3 +270,82 @@ type, but they are used for different purposes: Sometimes a document loader needs to be authenticated to load a remote document which requires authorization, but a context loader mostly needs to be highly cached and doesn't require authorization. Looking up remote objects ------------------------- *This API is available since Fedify 0.15.0.* > [!TIP] > In most cases, you don't need to look up remote objects explicitly. > Instead, you can use the dereferencing accessors to fetch the remote objects > implicitly. > > For example, you can get the `object` from an `Activity` object directly: > > ~~~~ typescript > const object = await activity.getObject(); > ~~~~ > > … instead of: > > ~~~~ typescript > const object = activity.objectId == null > ? null > : await ctx.lookupObject(activity.objectId); > ~~~~ Suppose your app has a search box that allows the user to look up a fediverse user by the handle or a post by the URI. In such cases, you need to look up the object from a remote server that your app haven't interacted with yet. The `Context.lookupObject()` method plays a role in such cases. The following shows an example of looking up an actor object from the handle: ~~~~ typescript const actor = await ctx.lookupObject("@hongminhee@todon.eu"); ~~~~ In the above example, the `~Context.lookupObject()` method queries the remote server's WebFinger endpoint to get the actor's URI from the handle, and then fetches the actor object from the URI. > [!TIP] > The `~Context.lookupObject()` method accepts a fediverse handle without > prefix `@` as well: > > ~~~~ typescript > const actor = await ctx.lookupObject("hongminhee@todon.eu"); > ~~~~ > > Also an `acct:` URI: > > ~~~~ typescript > const actor = await ctx.lookupObject("acct:hongminhee@todon.eu"); > ~~~~ The `~Context.lookupObject()` method is not limited to the actor object. It can look up any object in the Activity Vocabulary. For example the following shows an example of looking up a `Note` object from the URI: ~~~~ typescript const note = await ctx.lookupObject( "https://todon.eu/@hongminhee/112060633798771581" ); ~~~~ > [!NOTE] > Some objects require authentication to look up, such as a `Note` object with > a visibility of followers-only. In such cases, you need to use > the `Context.getDocumentLoader()` method to get an authenticated > `DocumentLoader` object. The `~Context.lookupObject()` method takes the > `documentLoader` option to specify the method to fetch the remote object: > > ~~~~ typescript > const documentLoader = await ctx.getDocumentLoader({ handle: "john" }); > const note = await ctx.lookupObject("...", { documentLoader }); > ~~~~ > > See the [*Getting an authenticated > `DocumentLoader`*](#getting-an-authenticated-documentloader) > section for details.
docs/manual/vocab.md +2 −55 Original line number Diff line number Diff line Loading @@ -205,61 +205,8 @@ the constructor. Looking up remote objects ------------------------- *This API is available since Fedify 0.2.0.* Suppose your app has a search box that allows the user to look up a fediverse user by the handle or a post by the URI. In such cases, you need to look up the object from a remote server that your app haven't interacted with yet. The `lookupObject()` function plays a role in such cases. The following shows an example of looking up an actor object from the handle: ~~~~ typescript import { lookupObject } from "@fedify/fedify"; const actor = await lookupObject("@hongminhee@todon.eu"); ~~~~ In the above example, the `lookupObject()` function queries the remote server's WebFinger endpoint to get the actor's URI from the handle, and then fetches the actor object from the URI. > [!TIP] > The `lookupObject()` function accepts a fediverse handle without prefix `@` > as well: > > ~~~~ typescript > const actor = await lookupObject("hongminhee@todon.eu"); > ~~~~ > > Also an `acct:` URI: > > ~~~~ typescript > const actor = await lookupObject("acct:hongminhee@todon.eu"); > ~~~~ The `lookupObject()` function is not limited to the actor object. It can look up any object in the Activity Vocabulary. For example, the following shows an example of looking up a `Note` object from the URI: ~~~~ typescript const note = await lookupObject("https://todon.eu/@hongminhee/112060633798771581"); ~~~~ > [!NOTE] > Some objects require authentication to look up, such as a `Note` object with > a visibility of followers-only. In such cases, you need to use > the `Context.getDocumentLoader()` method to get an authenticated > `DocumentLoader` object. The `lookupObject()` function takes the > `documentLoader` option to specify the method to fetch the remote object: > > ~~~~ typescript > const documentLoader = await ctx.getDocumentLoader({ handle: "john" }); > const note = await lookupObject("...", { documentLoader }); > ~~~~ > > See the [*Getting an authenticated > `DocumentLoader`*](./context.md#getting-an-authenticated-documentloader) > section for details. See the [*Looking up remote objects* section](./context.md#looking-up-remote-objects) in the *Context* docs. JSON-LD Loading
src/federation/context.ts +43 −0 Original line number Diff line number Diff line import type { DocumentLoader } from "../runtime/docloader.ts"; import type { Actor, Recipient } from "../vocab/actor.ts"; import type { LookupObjectOptions } from "../vocab/lookup.ts"; import type { Activity, CryptographicKey, Loading Loading @@ -189,6 +190,48 @@ export interface Context<TContextData> { identity: { keyId: URL; privateKey: 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>; /** * Sends an activity to recipients' inboxes. * @param sender The sender's handle or the sender's key pair(s). Loading
src/federation/middleware.test.ts +19 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ import { } from "../testing/keys.ts"; import { test } from "../testing/mod.ts"; import { lookupObject } from "../vocab/lookup.ts"; import { Create, Multikey, Note, Person } from "../vocab/vocab.ts"; import { Create, Multikey, Note, Object, Person } from "../vocab/vocab.ts"; import type { Context } from "./context.ts"; import { MemoryKvStore } from "./kv.ts"; import { createFederation } from "./middleware.ts"; Loading Loading @@ -183,6 +183,7 @@ test("Federation.createContext()", async (t) => { documentUrl: "https://example.com/object", document: true, }); assertEquals(await ctx.lookupObject("https://example.com/object"), null); assertRejects( () => ctx.sendActivity({ handle: "handle" }, [], new Create({})), TypeError, Loading @@ -196,6 +197,23 @@ test("Federation.createContext()", async (t) => { }), ); const federation2 = createFederation<number>({ kv, documentLoader: mockDocumentLoader, contextLoader: mockDocumentLoader, }); const ctx2 = federation2.createContext( new URL("https://example.com/"), 123, ); assertEquals( await ctx2.lookupObject("https://example.com/object"), new Object({ id: new URL("https://example.com/object"), name: "Fetched object", }), ); federation.setObjectDispatcher( Note, "/users/{handle}/notes/{id}", Loading