@fedify/fedify 0.7.0-dev.131 → 0.7.0-dev.133
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 +18 -1
- package/esm/federation/handler.js +23 -0
- package/esm/federation/middleware.js +98 -2
- 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 +34 -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,16 +26,33 @@ 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"]`
|
33
33
|
- `["fedify", "federation", "inbox"]`
|
34
34
|
- `["fedify", "federation", "outbox"]`
|
35
35
|
|
36
|
+
- Added `RequestContext.getActor()` method.
|
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
|
+
|
36
52
|
[public addressing]: https://www.w3.org/TR/activitypub/#public-addressing
|
37
53
|
[authorized fetch]: https://swicg.github.io/activitypub-http-signature/#authorized-fetch
|
38
54
|
[LogTape]: https://github.com/dahlia/logtape
|
55
|
+
[#33]: https://github.com/dahlia/fedify/issues/33
|
39
56
|
|
40
57
|
|
41
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) {
|
@@ -250,6 +268,47 @@ export class Federation {
|
|
250
268
|
...context,
|
251
269
|
request,
|
252
270
|
url,
|
271
|
+
getActor: async (handle) => {
|
272
|
+
if (this.#actorCallbacks == null ||
|
273
|
+
this.#actorCallbacks.dispatcher == null) {
|
274
|
+
throw new Error("No actor dispatcher registered.");
|
275
|
+
}
|
276
|
+
return await this.#actorCallbacks.dispatcher({
|
277
|
+
...reqCtx,
|
278
|
+
getActor(handle2) {
|
279
|
+
getLogger(["fedify", "federation"]).warn("RequestContext.getActor({getActorHandle}) is invoked from " +
|
280
|
+
"the actor dispatcher ({actorDispatcherHandle}); " +
|
281
|
+
"this may cause an infinite loop.", { getActorHandle: handle2, actorDispatcherHandle: handle });
|
282
|
+
return reqCtx.getActor(handle2);
|
283
|
+
},
|
284
|
+
}, handle, await context.getActorKey(handle));
|
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
|
+
},
|
253
312
|
async getSignedKey() {
|
254
313
|
if (signedKey !== undefined)
|
255
314
|
return signedKey;
|
@@ -333,6 +392,30 @@ export class Federation {
|
|
333
392
|
};
|
334
393
|
return setters;
|
335
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
|
+
}
|
336
419
|
/**
|
337
420
|
* Registers an outbox dispatcher.
|
338
421
|
*
|
@@ -642,7 +725,7 @@ export class Federation {
|
|
642
725
|
return response instanceof Promise ? await response : response;
|
643
726
|
}
|
644
727
|
let context = this.createContext(request, contextData);
|
645
|
-
switch (route.name) {
|
728
|
+
switch (route.name.replace(/:.*$/, "")) {
|
646
729
|
case "webfinger":
|
647
730
|
return await handleWebFinger(request, {
|
648
731
|
context,
|
@@ -666,6 +749,19 @@ export class Federation {
|
|
666
749
|
onNotFound,
|
667
750
|
onNotAcceptable,
|
668
751
|
});
|
752
|
+
case "object": {
|
753
|
+
const typeId = route.name.replace(/^object:/, "");
|
754
|
+
const callbacks = this.#objectCallbacks[typeId];
|
755
|
+
return await handleObject(request, {
|
756
|
+
values: route.values,
|
757
|
+
context,
|
758
|
+
objectDispatcher: callbacks?.dispatcher,
|
759
|
+
authorizePredicate: callbacks?.authorizePredicate,
|
760
|
+
onUnauthorized,
|
761
|
+
onNotFound,
|
762
|
+
onNotAcceptable,
|
763
|
+
});
|
764
|
+
}
|
669
765
|
case "outbox":
|
670
766
|
return await handleCollection(request, {
|
671
767
|
handle: route.values.handle,
|