@fedify/fedify 0.7.0-dev.132 → 0.7.0-dev.134
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @fedify/fedify might be problematic. Click here for more details.
- package/CHANGES.md +16 -1
- package/esm/federation/handler.js +23 -0
- package/esm/federation/middleware.js +85 -3
- package/esm/vocab/vocab.js +258 -0
- package/package.json +1 -1
- package/types/federation/callback.d.ts +27 -0
- package/types/federation/callback.d.ts.map +1 -1
- package/types/federation/context.d.ts +26 -1
- package/types/federation/context.d.ts.map +1 -1
- package/types/federation/handler.d.ts +11 -1
- package/types/federation/handler.d.ts.map +1 -1
- package/types/federation/middleware.d.ts +121 -7
- package/types/federation/middleware.d.ts.map +1 -1
- package/types/testing/context.d.ts.map +1 -1
- package/types/vocab/vocab.d.ts +172 -0
- package/types/vocab/vocab.d.ts.map +1 -1
    
        package/CHANGES.md
    CHANGED
    
    | @@ -26,7 +26,7 @@ To be released. | |
| 26 26 | 
             
                option now responds with `Vary: Accept, Signature` header.
         | 
| 27 27 |  | 
| 28 28 | 
             
             -  Added log messages using the [LogTape] library.  Currently the below
         | 
| 29 | 
            -
                categories are used:
         | 
| 29 | 
            +
                logger categories are used:
         | 
| 30 30 |  | 
| 31 31 | 
             
                 -  `["fedify"]`
         | 
| 32 32 | 
             
                 -  `["fedify", "federation"]`
         | 
| @@ -35,9 +35,24 @@ To be released. | |
| 35 35 |  | 
| 36 36 | 
             
             -  Added `RequestContext.getActor()` method.
         | 
| 37 37 |  | 
| 38 | 
            +
             -  Activity Vocabulary classes now have `typeId` static property.
         | 
| 39 | 
            +
             | 
| 40 | 
            +
             -  Dispatcher setters and inbox listener setters in `Federation` now take
         | 
| 41 | 
            +
                a path as <code>`${string}{handle}${string}`</code> instead of `string`
         | 
| 42 | 
            +
                so that it is more type-safe.
         | 
| 43 | 
            +
             | 
| 44 | 
            +
             -  Added generalized object dispatchers.  [[#33]]
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                 -  Added `Federation.setObjectDispatcher()` method.
         | 
| 47 | 
            +
                 -  Added `ObjectDispatcher` type.
         | 
| 48 | 
            +
                 -  Added `ObjectAuthorizePredicate` type.
         | 
| 49 | 
            +
                 -  Added `Context.getObjectUri()` method.
         | 
| 50 | 
            +
                 -  Added `RequestContext.getObject()` method.
         | 
| 51 | 
            +
             | 
| 38 52 | 
             
            [public addressing]: https://www.w3.org/TR/activitypub/#public-addressing
         | 
| 39 53 | 
             
            [authorized fetch]: https://swicg.github.io/activitypub-http-signature/#authorized-fetch
         | 
| 40 54 | 
             
            [LogTape]: https://github.com/dahlia/logtape
         | 
| 55 | 
            +
            [#33]: https://github.com/dahlia/fedify/issues/33
         | 
| 41 56 |  | 
| 42 57 |  | 
| 43 58 | 
             
            Version 0.6.1
         | 
| @@ -38,6 +38,29 @@ export async function handleActor(request, { handle, context, actorDispatcher, a | |
| 38 38 | 
             
                    },
         | 
| 39 39 | 
             
                });
         | 
| 40 40 | 
             
            }
         | 
| 41 | 
            +
            export async function handleObject(request, { values, context, objectDispatcher, authorizePredicate, onNotFound, onNotAcceptable, onUnauthorized, }) {
         | 
| 42 | 
            +
                if (objectDispatcher == null)
         | 
| 43 | 
            +
                    return await onNotFound(request);
         | 
| 44 | 
            +
                const object = await objectDispatcher(context, values);
         | 
| 45 | 
            +
                if (object == null)
         | 
| 46 | 
            +
                    return await onNotFound(request);
         | 
| 47 | 
            +
                if (!acceptsJsonLd(request))
         | 
| 48 | 
            +
                    return await onNotAcceptable(request);
         | 
| 49 | 
            +
                if (authorizePredicate != null) {
         | 
| 50 | 
            +
                    const key = await context.getSignedKey();
         | 
| 51 | 
            +
                    const keyOwner = await context.getSignedKeyOwner();
         | 
| 52 | 
            +
                    if (!await authorizePredicate(context, values, key, keyOwner)) {
         | 
| 53 | 
            +
                        return await onUnauthorized(request);
         | 
| 54 | 
            +
                    }
         | 
| 55 | 
            +
                }
         | 
| 56 | 
            +
                const jsonLd = await object.toJsonLd(context);
         | 
| 57 | 
            +
                return new Response(JSON.stringify(jsonLd), {
         | 
| 58 | 
            +
                    headers: {
         | 
| 59 | 
            +
                        "Content-Type": "application/activity+json",
         | 
| 60 | 
            +
                        Vary: "Accept",
         | 
| 61 | 
            +
                    },
         | 
| 62 | 
            +
                });
         | 
| 63 | 
            +
            }
         | 
| 41 64 | 
             
            export async function handleCollection(request, { handle, context, collectionCallbacks, onUnauthorized, onNotFound, onNotAcceptable, }) {
         | 
| 42 65 | 
             
                if (collectionCallbacks == null)
         | 
| 43 66 | 
             
                    return await onNotFound(request);
         | 
| @@ -7,7 +7,7 @@ import { handleNodeInfo, handleNodeInfoJrd } from "../nodeinfo/handler.js"; | |
| 7 7 | 
             
            import { fetchDocumentLoader, getAuthenticatedDocumentLoader, kvCache, } from "../runtime/docloader.js";
         | 
| 8 8 | 
             
            import { Activity, CryptographicKey } from "../vocab/mod.js";
         | 
| 9 9 | 
             
            import { handleWebFinger } from "../webfinger/handler.js";
         | 
| 10 | 
            -
            import { handleActor, handleCollection, handleInbox, } from "./handler.js";
         | 
| 10 | 
            +
            import { handleActor, handleCollection, handleInbox, handleObject, } from "./handler.js";
         | 
| 11 11 | 
             
            import { Router, RouterError } from "./router.js";
         | 
| 12 12 | 
             
            import { extractInboxes, sendActivity } from "./send.js";
         | 
| 13 13 | 
             
            /**
         | 
| @@ -25,6 +25,7 @@ export class Federation { | |
| 25 25 | 
             
                #router;
         | 
| 26 26 | 
             
                #nodeInfoDispatcher;
         | 
| 27 27 | 
             
                #actorCallbacks;
         | 
| 28 | 
            +
                #objectCallbacks;
         | 
| 28 29 | 
             
                #outboxCallbacks;
         | 
| 29 30 | 
             
                #followingCallbacks;
         | 
| 30 31 | 
             
                #followersCallbacks;
         | 
| @@ -54,6 +55,7 @@ export class Federation { | |
| 54 55 | 
             
                    this.#router.add("/.well-known/webfinger", "webfinger");
         | 
| 55 56 | 
             
                    this.#router.add("/.well-known/nodeinfo", "nodeInfoJrd");
         | 
| 56 57 | 
             
                    this.#inboxListeners = new Map();
         | 
| 58 | 
            +
                    this.#objectCallbacks = {};
         | 
| 57 59 | 
             
                    this.#documentLoader = documentLoader ?? kvCache({
         | 
| 58 60 | 
             
                        loader: fetchDocumentLoader,
         | 
| 59 61 | 
             
                        kv: kv,
         | 
| @@ -179,6 +181,22 @@ export class Federation { | |
| 179 181 | 
             
                            }
         | 
| 180 182 | 
             
                            return new URL(path, url);
         | 
| 181 183 | 
             
                        },
         | 
| 184 | 
            +
                        getObjectUri: (cls, values) => {
         | 
| 185 | 
            +
                            const callbacks = this.#objectCallbacks[cls.typeId.href];
         | 
| 186 | 
            +
                            if (callbacks == null) {
         | 
| 187 | 
            +
                                throw new RouterError("No object dispatcher registered.");
         | 
| 188 | 
            +
                            }
         | 
| 189 | 
            +
                            for (const param of callbacks.parameters) {
         | 
| 190 | 
            +
                                if (!(param in values)) {
         | 
| 191 | 
            +
                                    throw new TypeError(`Missing parameter: ${param}`);
         | 
| 192 | 
            +
                                }
         | 
| 193 | 
            +
                            }
         | 
| 194 | 
            +
                            const path = this.#router.build(`object:${cls.typeId.href}`, values);
         | 
| 195 | 
            +
                            if (path == null) {
         | 
| 196 | 
            +
                                throw new RouterError("No object dispatcher registered.");
         | 
| 197 | 
            +
                            }
         | 
| 198 | 
            +
                            return new URL(path, url);
         | 
| 199 | 
            +
                        },
         | 
| 182 200 | 
             
                        getOutboxUri: (handle) => {
         | 
| 183 201 | 
             
                            const path = this.#router.build("outbox", { handle });
         | 
| 184 202 | 
             
                            if (path == null) {
         | 
| @@ -255,7 +273,7 @@ export class Federation { | |
| 255 273 | 
             
                                this.#actorCallbacks.dispatcher == null) {
         | 
| 256 274 | 
             
                                throw new Error("No actor dispatcher registered.");
         | 
| 257 275 | 
             
                            }
         | 
| 258 | 
            -
                            return this.#actorCallbacks.dispatcher({
         | 
| 276 | 
            +
                            return await this.#actorCallbacks.dispatcher({
         | 
| 259 277 | 
             
                                ...reqCtx,
         | 
| 260 278 | 
             
                                getActor(handle2) {
         | 
| 261 279 | 
             
                                    getLogger(["fedify", "federation"]).warn("RequestContext.getActor({getActorHandle}) is invoked from " +
         | 
| @@ -265,6 +283,32 @@ export class Federation { | |
| 265 283 | 
             
                                },
         | 
| 266 284 | 
             
                            }, handle, await context.getActorKey(handle));
         | 
| 267 285 | 
             
                        },
         | 
| 286 | 
            +
                        getObject: async (cls, values) => {
         | 
| 287 | 
            +
                            const callbacks = this.#objectCallbacks[cls.typeId.href];
         | 
| 288 | 
            +
                            if (callbacks == null) {
         | 
| 289 | 
            +
                                throw new Error("No object dispatcher registered.");
         | 
| 290 | 
            +
                            }
         | 
| 291 | 
            +
                            for (const param of callbacks.parameters) {
         | 
| 292 | 
            +
                                if (!(param in values)) {
         | 
| 293 | 
            +
                                    throw new TypeError(`Missing parameter: ${param}`);
         | 
| 294 | 
            +
                                }
         | 
| 295 | 
            +
                            }
         | 
| 296 | 
            +
                            return await callbacks.dispatcher({
         | 
| 297 | 
            +
                                ...reqCtx,
         | 
| 298 | 
            +
                                getObject(cls2, values2) {
         | 
| 299 | 
            +
                                    getLogger(["fedify", "federation"]).warn("RequestContext.getObject({getObjectClass}, " +
         | 
| 300 | 
            +
                                        "{getObjectValues}) is invoked from the object dispatcher " +
         | 
| 301 | 
            +
                                        "({actorDispatcherClass}, {actorDispatcherValues}); " +
         | 
| 302 | 
            +
                                        "this may cause an infinite loop.", {
         | 
| 303 | 
            +
                                        getObjectClass: cls2.name,
         | 
| 304 | 
            +
                                        getObjectValues: values2,
         | 
| 305 | 
            +
                                        actorDispatcherClass: cls.name,
         | 
| 306 | 
            +
                                        actorDispatcherValues: values,
         | 
| 307 | 
            +
                                    });
         | 
| 308 | 
            +
                                    return reqCtx.getObject(cls2, values2);
         | 
| 309 | 
            +
                                },
         | 
| 310 | 
            +
                            }, values);
         | 
| 311 | 
            +
                        },
         | 
| 268 312 | 
             
                        async getSignedKey() {
         | 
| 269 313 | 
             
                            if (signedKey !== undefined)
         | 
| 270 314 | 
             
                                return signedKey;
         | 
| @@ -348,6 +392,30 @@ export class Federation { | |
| 348 392 | 
             
                    };
         | 
| 349 393 | 
             
                    return setters;
         | 
| 350 394 | 
             
                }
         | 
| 395 | 
            +
                setObjectDispatcher(
         | 
| 396 | 
            +
                // deno-lint-ignore no-explicit-any
         | 
| 397 | 
            +
                cls, path, dispatcher) {
         | 
| 398 | 
            +
                    const routeName = `object:${cls.typeId.href}`;
         | 
| 399 | 
            +
                    if (this.#router.has(routeName)) {
         | 
| 400 | 
            +
                        throw new RouterError(`Object dispatcher for ${cls.name} already set.`);
         | 
| 401 | 
            +
                    }
         | 
| 402 | 
            +
                    const variables = this.#router.add(path, routeName);
         | 
| 403 | 
            +
                    if (variables.size < 1) {
         | 
| 404 | 
            +
                        throw new RouterError("Path for object dispatcher must have at least one variable.");
         | 
| 405 | 
            +
                    }
         | 
| 406 | 
            +
                    const callbacks = {
         | 
| 407 | 
            +
                        dispatcher,
         | 
| 408 | 
            +
                        parameters: variables,
         | 
| 409 | 
            +
                    };
         | 
| 410 | 
            +
                    this.#objectCallbacks[cls.typeId.href] = callbacks;
         | 
| 411 | 
            +
                    const setters = {
         | 
| 412 | 
            +
                        authorize(predicate) {
         | 
| 413 | 
            +
                            callbacks.authorizePredicate = predicate;
         | 
| 414 | 
            +
                            return setters;
         | 
| 415 | 
            +
                        },
         | 
| 416 | 
            +
                    };
         | 
| 417 | 
            +
                    return setters;
         | 
| 418 | 
            +
                }
         | 
| 351 419 | 
             
                /**
         | 
| 352 420 | 
             
                 * Registers an outbox dispatcher.
         | 
| 353 421 | 
             
                 *
         | 
| @@ -632,6 +700,7 @@ export class Federation { | |
| 632 700 | 
             
                 * @deprecated Use {@link Federation.fetch} instead.
         | 
| 633 701 | 
             
                 */
         | 
| 634 702 | 
             
                handle(request, options) {
         | 
| 703 | 
            +
                    getLogger(["fedify", "federation"]).warn("Federation.handle() is deprecated.  Use Federation.fetch() instead.");
         | 
| 635 704 | 
             
                    return this.fetch(request, options);
         | 
| 636 705 | 
             
                }
         | 
| 637 706 | 
             
                /**
         | 
| @@ -657,7 +726,7 @@ export class Federation { | |
| 657 726 | 
             
                        return response instanceof Promise ? await response : response;
         | 
| 658 727 | 
             
                    }
         | 
| 659 728 | 
             
                    let context = this.createContext(request, contextData);
         | 
| 660 | 
            -
                    switch (route.name) {
         | 
| 729 | 
            +
                    switch (route.name.replace(/:.*$/, "")) {
         | 
| 661 730 | 
             
                        case "webfinger":
         | 
| 662 731 | 
             
                            return await handleWebFinger(request, {
         | 
| 663 732 | 
             
                                context,
         | 
| @@ -681,6 +750,19 @@ export class Federation { | |
| 681 750 | 
             
                                onNotFound,
         | 
| 682 751 | 
             
                                onNotAcceptable,
         | 
| 683 752 | 
             
                            });
         | 
| 753 | 
            +
                        case "object": {
         | 
| 754 | 
            +
                            const typeId = route.name.replace(/^object:/, "");
         | 
| 755 | 
            +
                            const callbacks = this.#objectCallbacks[typeId];
         | 
| 756 | 
            +
                            return await handleObject(request, {
         | 
| 757 | 
            +
                                values: route.values,
         | 
| 758 | 
            +
                                context,
         | 
| 759 | 
            +
                                objectDispatcher: callbacks?.dispatcher,
         | 
| 760 | 
            +
                                authorizePredicate: callbacks?.authorizePredicate,
         | 
| 761 | 
            +
                                onUnauthorized,
         | 
| 762 | 
            +
                                onNotFound,
         | 
| 763 | 
            +
                                onNotAcceptable,
         | 
| 764 | 
            +
                            });
         | 
| 765 | 
            +
                        }
         | 
| 684 766 | 
             
                        case "outbox":
         | 
| 685 767 | 
             
                            return await handleCollection(request, {
         | 
| 686 768 | 
             
                                handle: route.values.handle,
         |