@fedify/fedify 0.7.0-dev.131 → 0.7.0-dev.133
Sign up to get free protection for your applications and to get access to all the features.
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,
|