@fedify/fedify 2.5.0-dev.2370 → 2.5.0-dev.2420
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.
- package/dist/{builder-DzGfERAV.mjs → builder-Cwc3Eu59.mjs} +2 -2
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-hnZ7MNwN.mjs → compound-proof-Dtwrhs1I.mjs} +2 -2
- package/dist/{context-C2-UDo2c.d.ts → context-2wLkQrjb.d.ts} +11 -0
- package/dist/{context-BjD_CTxh.d.cts → context-DPocjp1l.d.cts} +11 -0
- package/dist/{deno-CkkDV5gw.mjs → deno-D4J5fszd.mjs} +1 -1
- package/dist/{docloader-D6eQ0vA8.mjs → docloader-ChK1DQkj.mjs} +4 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +3 -3
- package/dist/federation/handler-compound-proof.test.mjs +5 -5
- package/dist/federation/handler.test.mjs +258 -6
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox-report.test.mjs +30 -5
- package/dist/federation/inbox.test.mjs +43 -0
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +172 -10
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +2 -2
- package/dist/federation/mod.d.ts +2 -2
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +4 -5
- package/dist/federation/portable-actor.test.mjs +3 -3
- package/dist/federation/portable-chain.test.mjs +3 -3
- package/dist/federation/portable-collection.test.mjs +4 -4
- package/dist/federation/portable-delivery.test.mjs +2 -2
- package/dist/federation/portable-gateway-keys.test.mjs +4 -4
- package/dist/federation/portable-inbox.test.mjs +4 -4
- package/dist/federation/portable-parse-uri.test.mjs +1 -1
- package/dist/federation/portable-route-activity.test.mjs +2 -2
- package/dist/federation/portable.test.mjs +3 -3
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{federation-BkIRrGXG.cjs → federation-DxQI9dSY.cjs} +125 -20
- package/dist/{http-DxljNiCC.mjs → http--kHQomKT.mjs} +4 -4
- package/dist/{http-C2gfnrKF.js → http-C8rIQ1pK.js} +110 -12
- package/dist/{http-CjpGhvxc.cjs → http-SBTDzPkh.cjs} +109 -11
- package/dist/inbox-BGD7K-Ra.mjs +188 -0
- package/dist/{kv-cache-DDwF4zee.mjs → kv-cache-B8LMKVe8.mjs} +1 -1
- package/dist/{kv-cache-nsVJF31j.js → kv-cache-DSUypZWS.js} +3 -1
- package/dist/{kv-cache-BVtotkZw.cjs → kv-cache-Dp_dDR1f.cjs} +3 -1
- package/dist/{key-8sZWp4lR.mjs → ld-BeBdBDAY.mjs} +760 -16
- package/dist/{metrics-DiQRjv6y.mjs → metrics-Dtb81JxK.mjs} +1 -1
- package/dist/{middleware-C_EdDn9c.js → middleware-DNjv5rBL.js} +125 -20
- package/dist/{middleware-BBnHLNpJ.mjs → middleware-DhI-75xh.mjs} +1 -1
- package/dist/{middleware-ClqRVObS.mjs → middleware-rQqAHzIo.mjs} +107 -180
- package/dist/{mod-BBPd4M_0.d.cts → mod-DKGoPoQC.d.cts} +1 -1
- package/dist/{mod-CUYBGKof.d.ts → mod-Dm-7RdhR.d.ts} +1 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +2 -2
- package/dist/mod.d.ts +2 -2
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-IiJfae6n.mjs → owner-u_-HQrgE.mjs} +3 -3
- package/dist/{portable-collection-BeS_vx1G.cjs → portable-collection-B-x4XwxP.cjs} +1 -1
- package/dist/{portable-collection-CUDbGQ5_.mjs → portable-collection-BKkTJQTd.mjs} +2 -2
- package/dist/{portable-collection-BtyE_2Rv.js → portable-collection-DReoGwZQ.js} +1 -1
- package/dist/{portable-key-ENr3Sc97.mjs → portable-key-Bkm6sXJB.mjs} +1 -1
- package/dist/{proof-CGtiU4Lz.mjs → proof-DOV4ZiAo.mjs} +3 -4
- package/dist/{send-CRYlnzg6.mjs → send-C_Fffu0I.mjs} +3 -3
- package/dist/sig/compound-proof-discovery.test.mjs +1 -1
- package/dist/sig/compound-proof-producer.test.mjs +1 -1
- package/dist/sig/compound-proof-verification.test.mjs +2 -2
- package/dist/sig/compound-proof.test.mjs +2 -2
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +208 -3
- package/dist/sig/ld.test.mjs +1 -2
- package/dist/sig/mitra-receive-interop.test.mjs +4 -4
- package/dist/sig/mitra-send-interop.test.mjs +3 -3
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +1 -1
- package/dist/sig/portable-interop.test.mjs +3 -3
- package/dist/sig/portable-key.test.mjs +5 -6
- package/dist/sig/proof.test.mjs +3 -3
- package/dist/sig/tootik-request-interop.test.mjs +4 -4
- package/dist/sig/tootik-send-interop.test.mjs +3 -3
- package/dist/sig/verification.test.mjs +3 -3
- package/dist/{temporal-CDbdX_g_.mjs → temporal-BRgOZRRr.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +11 -0
- package/dist/utils/docloader.test.mjs +112 -37
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +7 -7
- package/dist/ld-DXaIENwZ.mjs +0 -658
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Temporal } from "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { t as getAuthenticationOrigin } from "./portable-key-id-Ca30-hOa.mjs";
|
|
5
|
+
import { n as version, t as name } from "./deno-D4J5fszd.mjs";
|
|
6
|
+
import { g as recordInboxActivity, n as getDurationMs, r as getFederationMetrics } from "./metrics-Dtb81JxK.mjs";
|
|
7
|
+
import { getLogger } from "@logtape/logtape";
|
|
8
|
+
import { getTypeId } from "@fedify/vocab";
|
|
9
|
+
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
10
|
+
//#region src/federation/inbox.ts
|
|
11
|
+
/**
|
|
12
|
+
* Checks whether an activity's ID and all of its actor IDs have the same
|
|
13
|
+
* [FEP-fe34] origin. Activities without an ID have no origin to authenticate.
|
|
14
|
+
*
|
|
15
|
+
* Origins are compared by `getAuthenticationOrigin()`, so a portable activity
|
|
16
|
+
* and its portable actors, including compatible identifiers on any gateway,
|
|
17
|
+
* are compared by their DIDs. An ID that has no supported origin, such as an
|
|
18
|
+
* opaque URN, never matches.
|
|
19
|
+
*
|
|
20
|
+
* The caller is responsible for rejecting a missing primary actor ID before
|
|
21
|
+
* relying on this check.
|
|
22
|
+
*
|
|
23
|
+
* [FEP-fe34]: https://w3id.org/fep/fe34
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
function actorsHaveSameOrigin(activity) {
|
|
27
|
+
if (activity.id == null) return true;
|
|
28
|
+
const origin = getAuthenticationOrigin(activity.id);
|
|
29
|
+
return origin != null && activity.actorIds.every((actor) => getAuthenticationOrigin(actor) === origin);
|
|
30
|
+
}
|
|
31
|
+
async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
|
|
32
|
+
const logger = getLogger([
|
|
33
|
+
"fedify",
|
|
34
|
+
"federation",
|
|
35
|
+
"inbox"
|
|
36
|
+
]);
|
|
37
|
+
if (activity.actorId == null) {
|
|
38
|
+
logger.error("Missing actor.", { activity: json });
|
|
39
|
+
span.setStatus({
|
|
40
|
+
code: SpanStatusCode.ERROR,
|
|
41
|
+
message: "Missing actor."
|
|
42
|
+
});
|
|
43
|
+
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
44
|
+
return "missingActor";
|
|
45
|
+
}
|
|
46
|
+
let cacheKey = null;
|
|
47
|
+
if (activity.id != null) {
|
|
48
|
+
const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, getTypeId(activity).href);
|
|
49
|
+
const strategy = idempotencyStrategy ?? "per-inbox";
|
|
50
|
+
let keyString;
|
|
51
|
+
if (typeof strategy === "function") keyString = await strategy(inboxContext, activity);
|
|
52
|
+
else switch (strategy) {
|
|
53
|
+
case "global":
|
|
54
|
+
keyString = activity.id.href;
|
|
55
|
+
break;
|
|
56
|
+
case "per-origin":
|
|
57
|
+
keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
58
|
+
break;
|
|
59
|
+
case "per-inbox":
|
|
60
|
+
keyString = `${ctx.origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
|
|
61
|
+
break;
|
|
62
|
+
default: keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
63
|
+
}
|
|
64
|
+
if (keyString != null) cacheKey = [...kvPrefixes.activityIdempotence, keyString];
|
|
65
|
+
}
|
|
66
|
+
if (cacheKey != null) {
|
|
67
|
+
if (await kv.get(cacheKey) === true) {
|
|
68
|
+
logger.debug("Activity {activityId} has already been processed.", {
|
|
69
|
+
activityId: activity.id?.href,
|
|
70
|
+
activity: json,
|
|
71
|
+
recipient
|
|
72
|
+
});
|
|
73
|
+
span.setStatus({
|
|
74
|
+
code: SpanStatusCode.UNSET,
|
|
75
|
+
message: `Activity ${activity.id?.href} has already been processed.`
|
|
76
|
+
});
|
|
77
|
+
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
78
|
+
return "alreadyProcessed";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
span.setAttribute("activitypub.actor.id", activity.actorId.href);
|
|
82
|
+
if (queue != null) {
|
|
83
|
+
const carrier = {};
|
|
84
|
+
propagation.inject(context.active(), carrier);
|
|
85
|
+
try {
|
|
86
|
+
await queue.enqueue({
|
|
87
|
+
type: "inbox",
|
|
88
|
+
id: crypto.randomUUID(),
|
|
89
|
+
baseUrl: ctx.origin,
|
|
90
|
+
activity: originalJson ?? json,
|
|
91
|
+
...normalizedActivity == null ? {} : { normalizedActivity },
|
|
92
|
+
...ldSignatureVerified == null ? {} : { ldSignatureVerified },
|
|
93
|
+
identifier: recipient,
|
|
94
|
+
attempt: 0,
|
|
95
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
96
|
+
traceContext: carrier
|
|
97
|
+
});
|
|
98
|
+
} catch (error) {
|
|
99
|
+
logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
|
|
100
|
+
error,
|
|
101
|
+
activityId: activity.id?.href,
|
|
102
|
+
activity: json,
|
|
103
|
+
recipient
|
|
104
|
+
});
|
|
105
|
+
span.setStatus({
|
|
106
|
+
code: SpanStatusCode.ERROR,
|
|
107
|
+
message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
|
|
108
|
+
});
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
getFederationMetrics(meterProvider).recordQueueTaskEnqueued({
|
|
112
|
+
role: "inbox",
|
|
113
|
+
queue,
|
|
114
|
+
activityType: getTypeId(activity).href
|
|
115
|
+
}, 0);
|
|
116
|
+
recordInboxActivity(meterProvider, "queued", getTypeId(activity).href);
|
|
117
|
+
logger.info("Activity {activityId} is enqueued.", {
|
|
118
|
+
activityId: activity.id?.href,
|
|
119
|
+
activity: json,
|
|
120
|
+
recipient
|
|
121
|
+
});
|
|
122
|
+
return "enqueued";
|
|
123
|
+
}
|
|
124
|
+
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
125
|
+
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
|
|
126
|
+
const dispatched = inboxListeners?.dispatchWithClass(activity);
|
|
127
|
+
if (dispatched == null) {
|
|
128
|
+
logger.error("Unsupported activity type:\n{activity}", {
|
|
129
|
+
activity: json,
|
|
130
|
+
recipient
|
|
131
|
+
});
|
|
132
|
+
span.setStatus({
|
|
133
|
+
code: SpanStatusCode.UNSET,
|
|
134
|
+
message: `Unsupported activity type: ${getTypeId(activity).href}`
|
|
135
|
+
});
|
|
136
|
+
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
137
|
+
span.end();
|
|
138
|
+
return "unsupportedActivity";
|
|
139
|
+
}
|
|
140
|
+
const { class: cls, listener } = dispatched;
|
|
141
|
+
span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
|
142
|
+
try {
|
|
143
|
+
const activityType = getTypeId(activity).href;
|
|
144
|
+
const started = performance.now();
|
|
145
|
+
try {
|
|
146
|
+
const contextFactory = listenerInboxContextFactory ?? inboxContextFactory;
|
|
147
|
+
await listener(contextFactory(recipient, contextFactory === inboxContextFactory ? json : originalJson ?? json, activity.id?.href, activityType), activity);
|
|
148
|
+
} finally {
|
|
149
|
+
getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
try {
|
|
153
|
+
await inboxErrorHandler?.(ctx, error);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
156
|
+
error,
|
|
157
|
+
activityId: activity.id?.href,
|
|
158
|
+
activity: json,
|
|
159
|
+
recipient
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
|
|
163
|
+
error,
|
|
164
|
+
activityId: activity.id?.href,
|
|
165
|
+
activity: json,
|
|
166
|
+
recipient
|
|
167
|
+
});
|
|
168
|
+
span.setStatus({
|
|
169
|
+
code: SpanStatusCode.ERROR,
|
|
170
|
+
message: String(error)
|
|
171
|
+
});
|
|
172
|
+
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
173
|
+
span.end();
|
|
174
|
+
return "error";
|
|
175
|
+
}
|
|
176
|
+
recordInboxActivity(meterProvider, "processed", getTypeId(activity).href);
|
|
177
|
+
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
|
|
178
|
+
logger.info("Activity {activityId} has been processed.", {
|
|
179
|
+
activityId: activity.id?.href,
|
|
180
|
+
activity: json,
|
|
181
|
+
recipient
|
|
182
|
+
});
|
|
183
|
+
span.end();
|
|
184
|
+
return "success";
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
188
|
+
export { routeActivity as n, actorsHaveSameOrigin as t };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { p as recordDocumentCache } from "./metrics-
|
|
4
|
+
import { p as recordDocumentCache } from "./metrics-Dtb81JxK.mjs";
|
|
5
5
|
import { getLogger } from "@logtape/logtape";
|
|
6
6
|
import { preloadedContexts } from "@fedify/vocab-runtime";
|
|
7
7
|
//#region src/utils/kv-cache.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { ct as recordDocumentCache, q as validateCryptoKey, t as doubleKnock } from "./http-
|
|
3
|
+
import { ct as recordDocumentCache, q as validateCryptoKey, t as doubleKnock } from "./http-C8rIQ1pK.js";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
5
|
import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, resolveDocumentLoaderTimeout, validatePublicUrl, withDocumentLoaderTimeout } from "@fedify/vocab-runtime";
|
|
6
6
|
import { curry } from "es-toolkit";
|
|
@@ -62,6 +62,8 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
|
|
|
62
62
|
}, options);
|
|
63
63
|
}
|
|
64
64
|
async function validateUrl(url, options) {
|
|
65
|
+
const protocol = new URL(url).protocol;
|
|
66
|
+
if (protocol !== "http:" && protocol !== "https:") throw new UrlError(`Unsupported protocol: ${protocol}`);
|
|
65
67
|
if (!allowPrivateAddress) {
|
|
66
68
|
try {
|
|
67
69
|
await validatePublicUrl(url);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
|
-
const require_http = require("./http-
|
|
2
|
+
const require_http = require("./http-SBTDzPkh.cjs");
|
|
3
3
|
let _logtape_logtape = require("@logtape/logtape");
|
|
4
4
|
let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
|
|
5
5
|
let es_toolkit = require("es-toolkit");
|
|
@@ -61,6 +61,8 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
|
|
|
61
61
|
}, options);
|
|
62
62
|
}
|
|
63
63
|
async function validateUrl(url, options) {
|
|
64
|
+
const protocol = new URL(url).protocol;
|
|
65
|
+
if (protocol !== "http:" && protocol !== "https:") throw new _fedify_vocab_runtime.UrlError(`Unsupported protocol: ${protocol}`);
|
|
64
66
|
if (!allowPrivateAddress) {
|
|
65
67
|
try {
|
|
66
68
|
await (0, _fedify_vocab_runtime.validatePublicUrl)(url);
|