@fedify/fedify 2.4.0-dev.2071 → 2.4.0-dev.2073
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-DVQ0yhKJ.mjs → builder-CVHpPAjK.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-KmXDfWFp.mjs → compound-proof-B5Skp-vE.mjs} +1 -1
- package/dist/{context-DsB9kF6q.d.ts → context-BSywYspi.d.ts} +109 -1
- package/dist/{context-DijrgHDe.d.cts → context-CZjZYXFC.d.cts} +109 -1
- package/dist/{context-DG5QeQ8W.mjs → context-DlZhxdFT.mjs} +2 -1
- package/dist/{deno-B-NtYZVW.mjs → deno-LYTbBhYV.mjs} +1 -1
- package/dist/{docloader-C6yOVoaF.mjs → docloader-C0SWQylk.mjs} +2 -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 +4 -4
- package/dist/federation/handler.test.mjs +6 -6
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +8 -8
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +5 -5
- package/dist/federation/portable-gateway-keys.test.mjs +4 -4
- package/dist/federation/portable-inbox.test.d.mts +2 -0
- package/dist/federation/portable-inbox.test.mjs +546 -0
- package/dist/federation/portable.test.mjs +3 -3
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/tasks/enqueue.test.mjs +2 -2
- package/dist/federation/tasks/tasks.test.mjs +3 -3
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +2 -2
- package/dist/{federation-CUiHc7dP.cjs → federation-B1aeTKKN.cjs} +468 -88
- package/dist/{http-DKWi8RjI.mjs → http-C7qHlv-b.mjs} +4 -4
- package/dist/{http-CuLWIJdf.cjs → http-DGk8XI38.cjs} +1 -1
- package/dist/{http-BoWBwC0g.js → http-vfTpzGEq.js} +1 -1
- package/dist/{key-lfcx_NaE.mjs → key-BYXnTGro.mjs} +2 -2
- package/dist/{kv-cache-BdnmnAVc.js → kv-cache-DCJuwdnB.js} +1 -1
- package/dist/{kv-cache-d8EjO29d.mjs → kv-cache-DEH5jyND.mjs} +1 -1
- package/dist/{kv-cache-DE8kSNQU.cjs → kv-cache-DvqkKNIj.cjs} +1 -1
- package/dist/{ld-jyjdrWjG.mjs → ld-DT3e2hOv.mjs} +3 -3
- package/dist/{metrics-B5Rei-HC.mjs → metrics-DvlvYpq_.mjs} +1 -1
- package/dist/{middleware-CdVnCDXD.mjs → middleware-BJcez_E3.mjs} +1 -1
- package/dist/{middleware-CAObohey.js → middleware-CCbyUF4c.js} +417 -38
- package/dist/{middleware-CPx4ZWwt.mjs → middleware-CpyCNOW_.mjs} +423 -48
- package/dist/{mod-DMkflQdF.d.ts → mod-B2h2KY0q.d.ts} +1 -1
- package/dist/{mod-DTAaBwiY.d.cts → mod-JzkaEXsi.d.cts} +1 -1
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +4 -4
- package/dist/mod.d.ts +4 -4
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/handler.test.mjs +2 -2
- package/dist/{owner-ByqNQVaa.mjs → owner-CDsVeZI7.mjs} +3 -3
- package/dist/{owner-BXufwAzm.cjs → owner-DCIH7Iq0.cjs} +1 -1
- package/dist/{owner-1TFakcTF.js → owner-DfGz0cXm.js} +1 -1
- package/dist/{portable-key-Cw_ow5vn.mjs → portable-key-DCyKGCQd.mjs} +2 -2
- package/dist/{proof-B6gwdhN5.mjs → proof-CxsVG6vM.mjs} +4 -4
- package/dist/{send-DsK7xevO.mjs → send-BZJ7NsVV.mjs} +8 -4
- 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 +1 -1
- package/dist/sig/ld.test.mjs +2 -2
- package/dist/sig/mod.cjs +3 -3
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +2 -2
- package/dist/sig/portable-key.test.mjs +3 -3
- package/dist/sig/proof.test.mjs +3 -3
- package/dist/{sig-TTn9wYrE.cjs → sig-C6TnADze.cjs} +2 -2
- package/dist/{sig-BR_Z6mpY.js → sig-Cs9_ecdh.js} +2 -2
- package/dist/{temporal-Bw5jtrBY.mjs → temporal-CLzSfIOV.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +108 -0
- package/dist/testing/mod.mjs +2 -2
- package/dist/{testing-By1YYsIK.mjs → testing-CO_cEvQc.mjs} +1 -1
- package/dist/utils/docloader.test.mjs +2 -2
- 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 +6 -6
|
@@ -2,10 +2,10 @@ import { Temporal } from "temporal-polyfill";
|
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
import { t as __exportAll } from "./rolldown-runtime-BC-Eo5Mj.js";
|
|
4
4
|
import { r as getDefaultActivityTransformers } from "./transformers-DAtXl_Kc.js";
|
|
5
|
-
import { $ as recordCollectionPageItems, C as hasSignature, D as signJsonLd, E as isInvalidUrlTypeError, H as hasPortableActor, J as getRemoteHost, K as getDurationMs, L as importJwk, M as exportJwk, O as verifyCompactJsonLd, Q as recordCollectionDispatchDuration, R as validateCryptoKey, S as getNormalizationContextLoader, T as isClearlyMalformedContextReference, U as isCompatibleKeyId, V as getGatewayKeyBase, W as isPortableUri, X as isAbortError, Y as instrumentDocumentLoader, Z as recordCircuitBreakerStateChange, _ as assertSafeJsonLd, a as verifyRequestDetailed, at as recordOutboxActivity, c as hasProofLike, ct as registerQueueDepthGauge, d as verifyObject, et as recordCollectionRequest, f as verifyPortableObjectProof, g as InvalidContextReferenceError, ht as version, i as verifyRequest, it as recordInboxActivity, j as wrapContextLoaderForJsonLd, l as signObject, lt as normalizeOutgoingActivityJsonLd, mt as name, n as parseRfc9421SignatureInput, ot as recordOutboxEnqueue, p as verifyPortableObjectProofPolicy, q as getFederationMetrics, rt as recordFanoutRecipients, st as recordWebFingerHandle, t as doubleKnock, tt as recordCollectionTotalItems, u as verifyMapLocalProof, ut as formatAcceptSignature, w as hasSignatureLike, x as detachSignature, y as compactJsonLd } from "./http-
|
|
5
|
+
import { $ as recordCollectionPageItems, C as hasSignature, D as signJsonLd, E as isInvalidUrlTypeError, H as hasPortableActor, J as getRemoteHost, K as getDurationMs, L as importJwk, M as exportJwk, O as verifyCompactJsonLd, Q as recordCollectionDispatchDuration, R as validateCryptoKey, S as getNormalizationContextLoader, T as isClearlyMalformedContextReference, U as isCompatibleKeyId, V as getGatewayKeyBase, W as isPortableUri, X as isAbortError, Y as instrumentDocumentLoader, Z as recordCircuitBreakerStateChange, _ as assertSafeJsonLd, a as verifyRequestDetailed, at as recordOutboxActivity, c as hasProofLike, ct as registerQueueDepthGauge, d as verifyObject, et as recordCollectionRequest, f as verifyPortableObjectProof, g as InvalidContextReferenceError, ht as version, i as verifyRequest, it as recordInboxActivity, j as wrapContextLoaderForJsonLd, l as signObject, lt as normalizeOutgoingActivityJsonLd, mt as name, n as parseRfc9421SignatureInput, ot as recordOutboxEnqueue, p as verifyPortableObjectProofPolicy, q as getFederationMetrics, rt as recordFanoutRecipients, st as recordWebFingerHandle, t as doubleKnock, tt as recordCollectionTotalItems, u as verifyMapLocalProof, ut as formatAcceptSignature, w as hasSignatureLike, x as detachSignature, y as compactJsonLd } from "./http-vfTpzGEq.js";
|
|
6
6
|
import { a as MAX_BODY_SIZE, i as BodyTooLargeError, n as getNodeInfo, o as readBoundedText, t as nodeInfoToJson } from "./types-C_cNlNVK.js";
|
|
7
|
-
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-
|
|
8
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-
|
|
7
|
+
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-DfGz0cXm.js";
|
|
8
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-DCJuwdnB.js";
|
|
9
9
|
import { getLogger, withContext } from "@logtape/logtape";
|
|
10
10
|
import { Router, RouterError, assertPath } from "@fedify/uri-template";
|
|
11
11
|
import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
@@ -2633,13 +2633,13 @@ function recordCollectionMetrics(meterProvider, base, result, options = {}) {
|
|
|
2633
2633
|
* @param parameters The parameters for handling the collection.
|
|
2634
2634
|
* @returns A promise that resolves to an HTTP response.
|
|
2635
2635
|
*/
|
|
2636
|
-
async function handleCollection(request, { name: name$
|
|
2637
|
-
const spanName = name$
|
|
2636
|
+
async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
|
|
2637
|
+
const spanName = name$2.trim().replace(/\s+/g, "_");
|
|
2638
2638
|
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
2639
2639
|
const tracer = tracerProvider.getTracer(name, version);
|
|
2640
2640
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
2641
2641
|
const metricBase = {
|
|
2642
|
-
kind: getCollectionMetricKind(name$
|
|
2642
|
+
kind: getCollectionMetricKind(name$2),
|
|
2643
2643
|
page: cursor != null,
|
|
2644
2644
|
dispatcher: "built_in"
|
|
2645
2645
|
};
|
|
@@ -2680,7 +2680,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
2680
2680
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
2681
2681
|
return await onNotFound(request);
|
|
2682
2682
|
}
|
|
2683
|
-
const items = filterCollectionItems(page.items, name$
|
|
2683
|
+
const items = filterCollectionItems(page.items, name$2, filterPredicate);
|
|
2684
2684
|
itemCount = items.length;
|
|
2685
2685
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
2686
2686
|
return items;
|
|
@@ -2720,7 +2720,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
2720
2720
|
} else {
|
|
2721
2721
|
const uri = new URL(baseUri);
|
|
2722
2722
|
uri.searchParams.set("cursor", cursor);
|
|
2723
|
-
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$
|
|
2723
|
+
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
|
|
2724
2724
|
kind: SpanKind.SERVER,
|
|
2725
2725
|
attributes: {
|
|
2726
2726
|
"activitypub.collection.id": uri.href,
|
|
@@ -2736,7 +2736,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
2736
2736
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
2737
2737
|
return await onNotFound(request);
|
|
2738
2738
|
}
|
|
2739
|
-
const items = filterCollectionItems(page.items, name$
|
|
2739
|
+
const items = filterCollectionItems(page.items, name$2, filterPredicate);
|
|
2740
2740
|
itemCount = items.length;
|
|
2741
2741
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
2742
2742
|
return {
|
|
@@ -3242,7 +3242,7 @@ async function handleInbox(request, options) {
|
|
|
3242
3242
|
* @returns A promise that resolves to an HTTP response.
|
|
3243
3243
|
*/
|
|
3244
3244
|
async function handleInboxInternal(request, parameters, span) {
|
|
3245
|
-
const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, publicKeyTtl, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, inboxChallengePolicy, meterProvider, tracerProvider } = parameters;
|
|
3245
|
+
const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, publicKeyTtl, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, inboxChallengePolicy, meterProvider, tracerProvider, portableInbox } = parameters;
|
|
3246
3246
|
const logger = getLogger([
|
|
3247
3247
|
"fedify",
|
|
3248
3248
|
"federation",
|
|
@@ -3255,7 +3255,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
3255
3255
|
message: "Actor dispatcher is not set."
|
|
3256
3256
|
});
|
|
3257
3257
|
return await onNotFound(request);
|
|
3258
|
-
} else if (recipient != null) {
|
|
3258
|
+
} else if (recipient != null && portableInbox == null) {
|
|
3259
3259
|
const actor = await actorDispatcher(ctx, recipient);
|
|
3260
3260
|
if (actor == null || actor instanceof Tombstone) {
|
|
3261
3261
|
logger.error("Actor {recipient} not found.", { recipient });
|
|
@@ -3717,6 +3717,19 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
3717
3717
|
tracerProvider,
|
|
3718
3718
|
idempotencyStrategy: parameters.idempotencyStrategy
|
|
3719
3719
|
});
|
|
3720
|
+
if (portableInbox != null && activity.id != null && PORTABLE_INBOX_FORWARDABLE_RESULTS.has(routeResult)) if (skipSignatureVerification || !(ldSigVerified || proofVerified)) logger.debug("Not forwarding activity {activityId} to the other gateways of the portable inbox {inbox}, as it is not authenticated by its own proof.", {
|
|
3721
|
+
activityId: activity.id.href,
|
|
3722
|
+
inbox: portableInbox.recipient.canonicalInboxId
|
|
3723
|
+
});
|
|
3724
|
+
else try {
|
|
3725
|
+
await portableInbox.forward(json, activity.id, getTypeId(activity).href);
|
|
3726
|
+
} catch (error) {
|
|
3727
|
+
logger.error("Failed to forward activity {activityId} to the other gateways of the portable inbox {inbox}:\n{error}", {
|
|
3728
|
+
activityId: activity.id.href,
|
|
3729
|
+
inbox: portableInbox.recipient.canonicalInboxId,
|
|
3730
|
+
error
|
|
3731
|
+
});
|
|
3732
|
+
}
|
|
3720
3733
|
if (routeResult === "alreadyProcessed") return new Response(`Activity <${activity.id}> has already been processed.`, {
|
|
3721
3734
|
status: 202,
|
|
3722
3735
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
@@ -3742,6 +3755,12 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
3742
3755
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
3743
3756
|
});
|
|
3744
3757
|
}
|
|
3758
|
+
const PORTABLE_INBOX_FORWARDABLE_RESULTS = /* @__PURE__ */ new Set([
|
|
3759
|
+
"success",
|
|
3760
|
+
"enqueued",
|
|
3761
|
+
"alreadyProcessed",
|
|
3762
|
+
"unsupportedActivity"
|
|
3763
|
+
]);
|
|
3745
3764
|
/**
|
|
3746
3765
|
* Handles a custom collection request.
|
|
3747
3766
|
* @template TItem The type of items in the collection.
|
|
@@ -3864,8 +3883,8 @@ var CustomCollectionHandler = class {
|
|
|
3864
3883
|
* @param CollectionPage The CollectionPage constructor.
|
|
3865
3884
|
* @param filterPredicate Optional filter predicate for items.
|
|
3866
3885
|
*/
|
|
3867
|
-
constructor(name$
|
|
3868
|
-
this.name = name$
|
|
3886
|
+
constructor(name$1, values, context, callbacks, tracerProvider = trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate) {
|
|
3887
|
+
this.name = name$1;
|
|
3869
3888
|
this.values = values;
|
|
3870
3889
|
this.context = context;
|
|
3871
3890
|
this.callbacks = callbacks;
|
|
@@ -4533,7 +4552,11 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
|
|
|
4533
4552
|
rsaKey = key;
|
|
4534
4553
|
break;
|
|
4535
4554
|
}
|
|
4536
|
-
if (rsaKey == null) logger.
|
|
4555
|
+
if (rsaKey == null && keys.length < 1) logger.debug("Sending the activity {activityId} to {inbox} without a signature.", {
|
|
4556
|
+
activityId,
|
|
4557
|
+
inbox: inbox.href
|
|
4558
|
+
});
|
|
4559
|
+
else if (rsaKey == null) logger.warn("No supported key found to sign the request to {inbox}. The request will be sent without a signature. In order to sign the request, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
|
|
4537
4560
|
inbox: inbox.href,
|
|
4538
4561
|
keys: keys.map((pair) => ({
|
|
4539
4562
|
keyId: pair.keyId.href,
|
|
@@ -4875,6 +4898,251 @@ function serializeDataPointType(dataPointType) {
|
|
|
4875
4898
|
}
|
|
4876
4899
|
}
|
|
4877
4900
|
//#endregion
|
|
4901
|
+
//#region src/federation/portable-inbox.ts
|
|
4902
|
+
const MAX_TIMEOUT_MS = 2 ** 31 - 1;
|
|
4903
|
+
/**
|
|
4904
|
+
* Fills in the defaults of {@link PortableInboxForwardingOptions} and
|
|
4905
|
+
* validates them.
|
|
4906
|
+
* @param options The options.
|
|
4907
|
+
* @returns The resolved options.
|
|
4908
|
+
* @throws {RangeError} If `maxTargets` is not a non-negative integer, if `ttl`
|
|
4909
|
+
* is not positive, if `deadline` is negative or longer
|
|
4910
|
+
* than 2,147,483,647 milliseconds (about 24.8 days), or
|
|
4911
|
+
* if either duration has calendar units, i.e., weeks,
|
|
4912
|
+
* months, or years, whose length depends on the date.
|
|
4913
|
+
*/
|
|
4914
|
+
function resolvePortableInboxForwardingOptions(options = {}) {
|
|
4915
|
+
const maxTargets = options.maxTargets ?? 10;
|
|
4916
|
+
if (!Number.isInteger(maxTargets) || maxTargets < 0) throw new RangeError("portableInboxForwarding.maxTargets must be a non-negative integer.");
|
|
4917
|
+
const ttl = Temporal.Duration.from(options.ttl ?? { days: 30 });
|
|
4918
|
+
if (ttl.total("millisecond") <= 0) throw new RangeError("portableInboxForwarding.ttl must be positive.");
|
|
4919
|
+
const deadline = Temporal.Duration.from(options.deadline ?? { seconds: 10 });
|
|
4920
|
+
const deadlineMs = deadline.total("millisecond");
|
|
4921
|
+
if (deadlineMs < 0 || deadlineMs > MAX_TIMEOUT_MS) throw new RangeError(`portableInboxForwarding.deadline must be between 0 and ${MAX_TIMEOUT_MS} milliseconds.`);
|
|
4922
|
+
return {
|
|
4923
|
+
maxTargets,
|
|
4924
|
+
ttl,
|
|
4925
|
+
deadline
|
|
4926
|
+
};
|
|
4927
|
+
}
|
|
4928
|
+
/**
|
|
4929
|
+
* Decides whether this server accepts a delivery to a portable inbox on behalf
|
|
4930
|
+
* of the given actor, which the actor dispatcher returned for the identifier
|
|
4931
|
+
* in the requested inbox path.
|
|
4932
|
+
*
|
|
4933
|
+
* The actor document comes from the application, so it is trusted as is and
|
|
4934
|
+
* its proof is not verified here.
|
|
4935
|
+
* @param actor The actor the actor dispatcher returned.
|
|
4936
|
+
* @param options The requested DID, the canonical form of the requested inbox
|
|
4937
|
+
* ID, and the origin of this server.
|
|
4938
|
+
* @returns The resolution.
|
|
4939
|
+
*/
|
|
4940
|
+
function resolvePortableInboxRecipient(actor, { authority, canonicalInboxId, localOrigin }) {
|
|
4941
|
+
if (actor == null) return {
|
|
4942
|
+
status: "rejected",
|
|
4943
|
+
reason: "notFound"
|
|
4944
|
+
};
|
|
4945
|
+
const id = actor.id;
|
|
4946
|
+
if (id == null || id.protocol !== "ap:" && id.protocol !== "ap+ef61:") return {
|
|
4947
|
+
status: "rejected",
|
|
4948
|
+
reason: "notPortable"
|
|
4949
|
+
};
|
|
4950
|
+
if (!isSameAuthority(id, authority)) return {
|
|
4951
|
+
status: "rejected",
|
|
4952
|
+
reason: "authorityMismatch"
|
|
4953
|
+
};
|
|
4954
|
+
const inboxId = actor.inboxId;
|
|
4955
|
+
if (inboxId == null || canonicalize(inboxId) !== canonicalInboxId) return {
|
|
4956
|
+
status: "rejected",
|
|
4957
|
+
reason: "inboxMismatch"
|
|
4958
|
+
};
|
|
4959
|
+
const gateways = actor.gateways;
|
|
4960
|
+
if (!gateways.some((g) => isGatewayUrl(g) && g.origin === localOrigin)) return {
|
|
4961
|
+
status: "rejected",
|
|
4962
|
+
reason: "notGateway"
|
|
4963
|
+
};
|
|
4964
|
+
return {
|
|
4965
|
+
status: "accepted",
|
|
4966
|
+
recipient: {
|
|
4967
|
+
inboxId,
|
|
4968
|
+
canonicalInboxId,
|
|
4969
|
+
gateways
|
|
4970
|
+
}
|
|
4971
|
+
};
|
|
4972
|
+
}
|
|
4973
|
+
function isSameAuthority(id, authority) {
|
|
4974
|
+
try {
|
|
4975
|
+
return getFe34Origin(id) === authority;
|
|
4976
|
+
} catch (error) {
|
|
4977
|
+
if (error instanceof TypeError) return false;
|
|
4978
|
+
throw error;
|
|
4979
|
+
}
|
|
4980
|
+
}
|
|
4981
|
+
function canonicalize(iri) {
|
|
4982
|
+
try {
|
|
4983
|
+
return canonicalizePortableUri(typeof iri === "string" ? iri : formatIri(iri));
|
|
4984
|
+
} catch (error) {
|
|
4985
|
+
if (error instanceof TypeError) return null;
|
|
4986
|
+
throw error;
|
|
4987
|
+
}
|
|
4988
|
+
}
|
|
4989
|
+
/**
|
|
4990
|
+
* Forwards an activity received in a portable inbox to the inboxes of the same
|
|
4991
|
+
* portable actor on its other gateways, as FEP-ef61 recommends.
|
|
4992
|
+
*
|
|
4993
|
+
* FEP-ef61 forbids forwarding an activity from an inbox more than once, so
|
|
4994
|
+
* each target gateway is claimed with an atomic compare-and-swap before the
|
|
4995
|
+
* activity is handed off, and a claim is never released, even if the hand-off
|
|
4996
|
+
* fails, since a failed hand-off does not prove that nothing was delivered.
|
|
4997
|
+
* Forwarding is therefore best effort. Without {@link KvStore.cas}, nothing is
|
|
4998
|
+
* forwarded.
|
|
4999
|
+
*
|
|
5000
|
+
* Forwarded requests are not signed with HTTP Signatures; the receiving
|
|
5001
|
+
* gateways authenticate the activity by its own proof.
|
|
5002
|
+
* @param parameters The parameters.
|
|
5003
|
+
* @returns The target inbox URLs that the activity was handed off to.
|
|
5004
|
+
*/
|
|
5005
|
+
async function forwardPortableInboxActivity(parameters) {
|
|
5006
|
+
const logger = getLogger([
|
|
5007
|
+
"fedify",
|
|
5008
|
+
"federation",
|
|
5009
|
+
"inbox"
|
|
5010
|
+
]);
|
|
5011
|
+
const { recipient, activityId, excludedOrigins, kv, kvPrefix, outboxQueue } = parameters;
|
|
5012
|
+
const { maxTargets, ttl } = parameters.options ?? resolvePortableInboxForwardingOptions();
|
|
5013
|
+
if (kv.cas == null || maxTargets < 1) return [];
|
|
5014
|
+
const canonicalActivityId = canonicalize(activityId) ?? activityId.href;
|
|
5015
|
+
const targets = getForwardingTargets(recipient, excludedOrigins);
|
|
5016
|
+
if (targets.length > maxTargets) {
|
|
5017
|
+
logger.warn("The portable actor that owns the inbox {inbox} has more than {max} other gateways; the activity {activityId} is forwarded only to the first {max} of them.", {
|
|
5018
|
+
inbox: recipient.canonicalInboxId,
|
|
5019
|
+
activityId: activityId.href,
|
|
5020
|
+
max: maxTargets
|
|
5021
|
+
});
|
|
5022
|
+
targets.length = maxTargets;
|
|
5023
|
+
}
|
|
5024
|
+
const claimed = [];
|
|
5025
|
+
for (const { gateway, inbox } of targets) {
|
|
5026
|
+
const key = [
|
|
5027
|
+
...kvPrefix,
|
|
5028
|
+
recipient.canonicalInboxId,
|
|
5029
|
+
canonicalActivityId,
|
|
5030
|
+
gateway
|
|
5031
|
+
];
|
|
5032
|
+
if (await kv.cas(key, void 0, true, { ttl })) claimed.push(inbox);
|
|
5033
|
+
}
|
|
5034
|
+
if (claimed.length < 1) return [];
|
|
5035
|
+
logger.debug("Forwarding activity {activityId} from the portable inbox {inbox} to other gateways:\n{targets}", {
|
|
5036
|
+
activityId: activityId.href,
|
|
5037
|
+
inbox: recipient.canonicalInboxId,
|
|
5038
|
+
targets: claimed.map((t) => t.href)
|
|
5039
|
+
});
|
|
5040
|
+
if (outboxQueue == null) await forwardImmediately(parameters, claimed);
|
|
5041
|
+
else await enqueueForwarding(parameters, outboxQueue, claimed);
|
|
5042
|
+
return claimed;
|
|
5043
|
+
}
|
|
5044
|
+
function getForwardingTargets(recipient, excludedOrigins) {
|
|
5045
|
+
const logger = getLogger([
|
|
5046
|
+
"fedify",
|
|
5047
|
+
"federation",
|
|
5048
|
+
"inbox"
|
|
5049
|
+
]);
|
|
5050
|
+
const seen = new Set(excludedOrigins);
|
|
5051
|
+
const targets = [];
|
|
5052
|
+
for (const gateway of recipient.gateways) {
|
|
5053
|
+
if (!isGatewayUrl(gateway) || seen.has(gateway.origin)) continue;
|
|
5054
|
+
seen.add(gateway.origin);
|
|
5055
|
+
let inbox;
|
|
5056
|
+
try {
|
|
5057
|
+
inbox = toCompatibleEf61Id(recipient.inboxId, gateway.origin);
|
|
5058
|
+
} catch (error) {
|
|
5059
|
+
if (!(error instanceof TypeError)) throw error;
|
|
5060
|
+
logger.warn("Cannot forward to the gateway {gateway}, as the portable inbox {inbox} cannot be represented as its compatible identifier.", {
|
|
5061
|
+
gateway: gateway.origin,
|
|
5062
|
+
inbox: recipient.canonicalInboxId
|
|
5063
|
+
});
|
|
5064
|
+
continue;
|
|
5065
|
+
}
|
|
5066
|
+
targets.push({
|
|
5067
|
+
gateway: gateway.origin,
|
|
5068
|
+
inbox
|
|
5069
|
+
});
|
|
5070
|
+
}
|
|
5071
|
+
return targets;
|
|
5072
|
+
}
|
|
5073
|
+
async function forwardImmediately({ activity, activityId, activityType, allowPrivateAddress, options, meterProvider, tracerProvider }, inboxes) {
|
|
5074
|
+
const logger = getLogger([
|
|
5075
|
+
"fedify",
|
|
5076
|
+
"federation",
|
|
5077
|
+
"inbox"
|
|
5078
|
+
]);
|
|
5079
|
+
const deadline = (options ?? resolvePortableInboxForwardingOptions()).deadline.total("millisecond");
|
|
5080
|
+
const sends = inboxes.map((inbox) => sendActivity({
|
|
5081
|
+
activity,
|
|
5082
|
+
activityId: activityId.href,
|
|
5083
|
+
activityType,
|
|
5084
|
+
keys: [],
|
|
5085
|
+
inbox,
|
|
5086
|
+
allowPrivateAddress,
|
|
5087
|
+
meterProvider,
|
|
5088
|
+
tracerProvider
|
|
5089
|
+
}).catch((error) => {
|
|
5090
|
+
logger.error("Failed to forward activity {activityId} to {inbox}; it will not be forwarded to the inbox again:\n{error}", {
|
|
5091
|
+
activityId: activityId.href,
|
|
5092
|
+
inbox: inbox.href,
|
|
5093
|
+
error
|
|
5094
|
+
});
|
|
5095
|
+
}));
|
|
5096
|
+
let timer;
|
|
5097
|
+
const timedOut = await Promise.race([Promise.all(sends).then(() => false), new Promise((resolve) => {
|
|
5098
|
+
timer = setTimeout(() => resolve(true), deadline);
|
|
5099
|
+
})]);
|
|
5100
|
+
clearTimeout(timer);
|
|
5101
|
+
if (timedOut) logger.warn("Forwarding activity {activityId} to other gateways did not finish in {deadline} ms; responding to the delivery without waiting for it.", {
|
|
5102
|
+
activityId: activityId.href,
|
|
5103
|
+
deadline
|
|
5104
|
+
});
|
|
5105
|
+
}
|
|
5106
|
+
async function enqueueForwarding({ activity, activityId, activityType, baseUrl, startQueue, meterProvider }, outboxQueue, inboxes) {
|
|
5107
|
+
const logger = getLogger([
|
|
5108
|
+
"fedify",
|
|
5109
|
+
"federation",
|
|
5110
|
+
"inbox"
|
|
5111
|
+
]);
|
|
5112
|
+
startQueue?.();
|
|
5113
|
+
const started = (/* @__PURE__ */ new Date()).toISOString();
|
|
5114
|
+
const traceContext = {};
|
|
5115
|
+
propagation.inject(context.active(), traceContext);
|
|
5116
|
+
await Promise.all(inboxes.map(async (inbox) => {
|
|
5117
|
+
const message = {
|
|
5118
|
+
type: "outbox",
|
|
5119
|
+
id: crypto.randomUUID(),
|
|
5120
|
+
baseUrl,
|
|
5121
|
+
keys: [],
|
|
5122
|
+
activity,
|
|
5123
|
+
activityId: activityId.href,
|
|
5124
|
+
activityType,
|
|
5125
|
+
inbox: inbox.href,
|
|
5126
|
+
sharedInbox: false,
|
|
5127
|
+
started,
|
|
5128
|
+
attempt: 0,
|
|
5129
|
+
headers: {},
|
|
5130
|
+
traceContext
|
|
5131
|
+
};
|
|
5132
|
+
try {
|
|
5133
|
+
await outboxQueue.enqueue(message);
|
|
5134
|
+
} catch (error) {
|
|
5135
|
+
logger.error("Failed to enqueue activity {activityId} to forward to {inbox}; it will not be forwarded to the inbox again:\n{error}", {
|
|
5136
|
+
activityId: activityId.href,
|
|
5137
|
+
inbox: inbox.href,
|
|
5138
|
+
error
|
|
5139
|
+
});
|
|
5140
|
+
return;
|
|
5141
|
+
}
|
|
5142
|
+
recordOutboxEnqueue(meterProvider, outboxQueue, message);
|
|
5143
|
+
}));
|
|
5144
|
+
}
|
|
5145
|
+
//#endregion
|
|
4878
5146
|
//#region src/federation/outgoing-proof.ts
|
|
4879
5147
|
function isPortableUrl(url) {
|
|
4880
5148
|
return url.protocol === "ap:" || url.protocol === "ap+ef61:";
|
|
@@ -5774,6 +6042,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5774
6042
|
kvPrefixes;
|
|
5775
6043
|
publicKeyTtl;
|
|
5776
6044
|
httpMessageSignaturesSpecTtl;
|
|
6045
|
+
portableInboxForwarding;
|
|
5777
6046
|
inboxQueue;
|
|
5778
6047
|
outboxQueue;
|
|
5779
6048
|
fanoutQueue;
|
|
@@ -5806,6 +6075,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5806
6075
|
benchmarkMetricReader;
|
|
5807
6076
|
benchmarkTriggerOptions;
|
|
5808
6077
|
#mediaUploaderNoAuthWarned = false;
|
|
6078
|
+
#portableInboxForwardingWarned = false;
|
|
5809
6079
|
#queueDepthGaugeSourceId = `fedify-${(++nextQueueDepthGaugeSourceId).toString(36)}`;
|
|
5810
6080
|
#queueDepthGaugeEntries = [];
|
|
5811
6081
|
#queueDepthGaugeMeterProvider;
|
|
@@ -5837,10 +6107,12 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5837
6107
|
acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"],
|
|
5838
6108
|
circuitBreaker: ["_fedify", "circuit"],
|
|
5839
6109
|
taskDeduplication: ["_fedify", "taskDeduplication"],
|
|
6110
|
+
portableInboxForwarding: ["_fedify", "portableInboxForwarding"],
|
|
5840
6111
|
...options.kvPrefixes ?? {}
|
|
5841
6112
|
};
|
|
5842
6113
|
this.publicKeyTtl = Temporal.Duration.from(options.publicKeyTtl ?? { days: 30 });
|
|
5843
6114
|
this.httpMessageSignaturesSpecTtl = Temporal.Duration.from(options.httpMessageSignaturesSpecTtl ?? { days: 90 });
|
|
6115
|
+
this.portableInboxForwarding = resolvePortableInboxForwardingOptions(options.portableInboxForwarding);
|
|
5844
6116
|
if (options.queue == null) {
|
|
5845
6117
|
this.inboxQueue = void 0;
|
|
5846
6118
|
this.outboxQueue = void 0;
|
|
@@ -6510,8 +6782,8 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
6510
6782
|
]);
|
|
6511
6783
|
const baseUrl = new URL(message.baseUrl);
|
|
6512
6784
|
let context = this.#createContext(baseUrl, ctxData);
|
|
6513
|
-
if (message.identifier != null) context = this.#createContext(baseUrl, ctxData, { documentLoader: await context.getDocumentLoader({ identifier: message.identifier }) });
|
|
6514
|
-
else if (this.sharedInboxKeyDispatcher != null) {
|
|
6785
|
+
if (message.identifier != null && this.actorCallbacks?.keyPairsDispatcher != null) context = this.#createContext(baseUrl, ctxData, { documentLoader: await context.getDocumentLoader({ identifier: message.identifier }) });
|
|
6786
|
+
else if (message.identifier == null && this.sharedInboxKeyDispatcher != null) {
|
|
6515
6787
|
const identity = await this.sharedInboxKeyDispatcher(context);
|
|
6516
6788
|
if (identity != null) context = this.#createContext(baseUrl, ctxData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
|
|
6517
6789
|
}
|
|
@@ -7104,6 +7376,15 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
7104
7376
|
span,
|
|
7105
7377
|
metricState
|
|
7106
7378
|
});
|
|
7379
|
+
if (request.method === "POST") {
|
|
7380
|
+
const portable = parsePortableGatewayRequest(url);
|
|
7381
|
+
if (portable != null) return await this.#fetchPortableInbox(request, portable, {
|
|
7382
|
+
onNotFound,
|
|
7383
|
+
contextData,
|
|
7384
|
+
span,
|
|
7385
|
+
metricState
|
|
7386
|
+
});
|
|
7387
|
+
}
|
|
7107
7388
|
const portable = request.method === "GET" || request.method === "HEAD" ? parsePortableGatewayRequest(url) : null;
|
|
7108
7389
|
if (portable != null) return await discardBodyForHead(request, await this.#fetchPortableObject(request, portable, {
|
|
7109
7390
|
onNotFound,
|
|
@@ -7255,35 +7536,17 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
7255
7536
|
onNotFound
|
|
7256
7537
|
});
|
|
7257
7538
|
context = this.#createContext(request, contextData, { documentLoader: await context.getDocumentLoader({ identifier: route.values.identifier }) });
|
|
7258
|
-
case "sharedInbox":
|
|
7539
|
+
case "sharedInbox":
|
|
7259
7540
|
if (routeName !== "inbox" && this.sharedInboxKeyDispatcher != null) {
|
|
7260
7541
|
const identity = await this.sharedInboxKeyDispatcher(context);
|
|
7261
7542
|
if (identity != null) context = this.#createContext(request, contextData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
|
|
7262
7543
|
}
|
|
7263
|
-
|
|
7264
|
-
const inboxContextFactory = context.toInboxContext.bind(context);
|
|
7265
|
-
inboxContextFactory[rawInboxContextFactorySymbol] = context.toInboxContext.bind(context);
|
|
7266
|
-
return await handleInbox(request, {
|
|
7544
|
+
return await this.#handleInbox(request, {
|
|
7267
7545
|
recipient: route.values.identifier ?? null,
|
|
7268
7546
|
context,
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
kvPrefixes: this.kvPrefixes,
|
|
7272
|
-
publicKeyTtl: this.publicKeyTtl,
|
|
7273
|
-
queue: this.inboxQueue,
|
|
7274
|
-
actorDispatcher: this.actorCallbacks?.dispatcher,
|
|
7275
|
-
inboxListeners: this.inboxListeners,
|
|
7276
|
-
inboxErrorHandler: this.inboxErrorHandler,
|
|
7277
|
-
unverifiedActivityHandler: this.unverifiedActivityHandler,
|
|
7278
|
-
onNotFound,
|
|
7279
|
-
signatureTimeWindow: this.signatureTimeWindow,
|
|
7280
|
-
skipSignatureVerification: this.skipSignatureVerification,
|
|
7281
|
-
inboxChallengePolicy: this.inboxChallengePolicy,
|
|
7282
|
-
meterProvider: this.meterProvider,
|
|
7283
|
-
tracerProvider: this.tracerProvider,
|
|
7284
|
-
idempotencyStrategy: this.idempotencyStrategy
|
|
7547
|
+
contextData,
|
|
7548
|
+
onNotFound
|
|
7285
7549
|
});
|
|
7286
|
-
}
|
|
7287
7550
|
case "following": return await handleCollection(request, {
|
|
7288
7551
|
name: "following",
|
|
7289
7552
|
identifier: route.values.identifier,
|
|
@@ -7436,6 +7699,113 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
7436
7699
|
onNotFound
|
|
7437
7700
|
});
|
|
7438
7701
|
}
|
|
7702
|
+
async #handleInbox(request, { recipient, context, contextData, onNotFound, portableInbox }) {
|
|
7703
|
+
if (!this.manuallyStartQueue) this._startQueueInternal(contextData);
|
|
7704
|
+
const inboxContextFactory = context.toInboxContext.bind(context);
|
|
7705
|
+
inboxContextFactory[rawInboxContextFactorySymbol] = context.toInboxContext.bind(context);
|
|
7706
|
+
return await handleInbox(request, {
|
|
7707
|
+
recipient,
|
|
7708
|
+
context,
|
|
7709
|
+
inboxContextFactory,
|
|
7710
|
+
kv: this.kv,
|
|
7711
|
+
kvPrefixes: this.kvPrefixes,
|
|
7712
|
+
publicKeyTtl: this.publicKeyTtl,
|
|
7713
|
+
queue: this.inboxQueue,
|
|
7714
|
+
actorDispatcher: this.actorCallbacks?.dispatcher,
|
|
7715
|
+
inboxListeners: this.inboxListeners,
|
|
7716
|
+
inboxErrorHandler: this.inboxErrorHandler,
|
|
7717
|
+
unverifiedActivityHandler: this.unverifiedActivityHandler,
|
|
7718
|
+
onNotFound,
|
|
7719
|
+
signatureTimeWindow: this.signatureTimeWindow,
|
|
7720
|
+
skipSignatureVerification: this.skipSignatureVerification,
|
|
7721
|
+
inboxChallengePolicy: this.inboxChallengePolicy,
|
|
7722
|
+
meterProvider: this.meterProvider,
|
|
7723
|
+
tracerProvider: this.tracerProvider,
|
|
7724
|
+
idempotencyStrategy: this.idempotencyStrategy,
|
|
7725
|
+
portableInbox
|
|
7726
|
+
});
|
|
7727
|
+
}
|
|
7728
|
+
async #fetchPortableInbox(request, portable, { onNotFound, contextData, span, metricState }) {
|
|
7729
|
+
const logger = getLogger([
|
|
7730
|
+
"fedify",
|
|
7731
|
+
"federation",
|
|
7732
|
+
"inbox"
|
|
7733
|
+
]);
|
|
7734
|
+
if (portable.type === "malformed") {
|
|
7735
|
+
metricState.endpoint = "not_found";
|
|
7736
|
+
logger.debug("Malformed FEP-ef61 gateway inbox request {url}: {error}", {
|
|
7737
|
+
url: request.url,
|
|
7738
|
+
error: portable.error
|
|
7739
|
+
});
|
|
7740
|
+
return new Response("Malformed portable inbox ID.", {
|
|
7741
|
+
status: 400,
|
|
7742
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
7743
|
+
});
|
|
7744
|
+
}
|
|
7745
|
+
const route = this.router.route(portable.path);
|
|
7746
|
+
if (route == null || route.name !== "inbox") {
|
|
7747
|
+
metricState.endpoint = "not_found";
|
|
7748
|
+
return await onNotFound(request);
|
|
7749
|
+
}
|
|
7750
|
+
metricState.routeTemplate = PORTABLE_GATEWAY_ROUTE_PREFIX + route.template;
|
|
7751
|
+
metricState.endpoint = "inbox";
|
|
7752
|
+
span.updateName(`${request.method} ${metricState.routeTemplate}`);
|
|
7753
|
+
const identifier = route.values.identifier;
|
|
7754
|
+
let context = this.#createContext(request, contextData);
|
|
7755
|
+
const actor = this.actorCallbacks?.dispatcher == null ? null : await this.actorCallbacks.dispatcher(context, identifier);
|
|
7756
|
+
const localOrigin = context.canonicalOrigin;
|
|
7757
|
+
const resolution = resolvePortableInboxRecipient(actor == null || actor instanceof Tombstone ? null : actor, {
|
|
7758
|
+
authority: portable.portableRequest.authority,
|
|
7759
|
+
canonicalInboxId: portable.canonicalId,
|
|
7760
|
+
localOrigin
|
|
7761
|
+
});
|
|
7762
|
+
if (resolution.status === "rejected") {
|
|
7763
|
+
logger.debug("Not accepting a delivery to the portable inbox {inbox} on behalf of the actor {identifier}: {reason}.", {
|
|
7764
|
+
inbox: portable.canonicalId,
|
|
7765
|
+
identifier,
|
|
7766
|
+
reason: resolution.reason
|
|
7767
|
+
});
|
|
7768
|
+
return await onNotFound(request);
|
|
7769
|
+
}
|
|
7770
|
+
const { recipient } = resolution;
|
|
7771
|
+
if (this.actorCallbacks?.keyPairsDispatcher != null) context = this.#createContext(request, contextData, { documentLoader: await context.getDocumentLoader({ identifier }) });
|
|
7772
|
+
const excludedOrigins = [localOrigin, new URL(request.url).origin];
|
|
7773
|
+
return await this.#handleInbox(request, {
|
|
7774
|
+
recipient: identifier,
|
|
7775
|
+
context,
|
|
7776
|
+
contextData,
|
|
7777
|
+
onNotFound,
|
|
7778
|
+
portableInbox: {
|
|
7779
|
+
recipient,
|
|
7780
|
+
forward: async (activity, activityId, activityType) => {
|
|
7781
|
+
if (this.portableInboxForwarding.maxTargets < 1) return;
|
|
7782
|
+
if (this.kv.cas == null) {
|
|
7783
|
+
if (!this.#portableInboxForwardingWarned) {
|
|
7784
|
+
this.#portableInboxForwardingWarned = true;
|
|
7785
|
+
logger.warn("Activities delivered to FEP-ef61 portable inboxes are not forwarded to the other gateways, as the key–value store does not support compare-and-swap (KvStore.cas()), which is needed to forward each activity at most once.");
|
|
7786
|
+
}
|
|
7787
|
+
return;
|
|
7788
|
+
}
|
|
7789
|
+
await forwardPortableInboxActivity({
|
|
7790
|
+
recipient,
|
|
7791
|
+
activity,
|
|
7792
|
+
activityId,
|
|
7793
|
+
activityType,
|
|
7794
|
+
excludedOrigins,
|
|
7795
|
+
baseUrl: context.origin,
|
|
7796
|
+
kv: this.kv,
|
|
7797
|
+
kvPrefix: this.kvPrefixes.portableInboxForwarding,
|
|
7798
|
+
outboxQueue: this.outboxQueue,
|
|
7799
|
+
startQueue: this.manuallyStartQueue ? void 0 : () => this._startQueueInternal(contextData),
|
|
7800
|
+
allowPrivateAddress: this.allowPrivateAddress,
|
|
7801
|
+
options: this.portableInboxForwarding,
|
|
7802
|
+
meterProvider: this.meterProvider,
|
|
7803
|
+
tracerProvider: this.tracerProvider
|
|
7804
|
+
});
|
|
7805
|
+
}
|
|
7806
|
+
}
|
|
7807
|
+
});
|
|
7808
|
+
}
|
|
7439
7809
|
async #fetchHashlinkMedia(request, media, { onNotFound, contextData, span, metricState }) {
|
|
7440
7810
|
metricState.endpoint = "hashlink_media";
|
|
7441
7811
|
metricState.routeTemplate = HASHLINK_MEDIA_ROUTE_TEMPLATE;
|
|
@@ -7666,6 +8036,12 @@ var ContextImpl = class ContextImpl {
|
|
|
7666
8036
|
if (path == null) throw new RouterError("No inbox path registered.");
|
|
7667
8037
|
return new URL(path, this.canonicalOrigin);
|
|
7668
8038
|
}
|
|
8039
|
+
getPortableInboxUri(identifier, authority) {
|
|
8040
|
+
const path = this.federation.router.build("inbox", { identifier });
|
|
8041
|
+
if (path == null) throw new RouterError("No inbox path registered.");
|
|
8042
|
+
if (authority == null) throw new TypeError("The authority of a portable ID is required outside an FEP-ef61 gateway request.");
|
|
8043
|
+
return buildPortableUri(authority, path);
|
|
8044
|
+
}
|
|
7669
8045
|
getFollowingUri(identifier) {
|
|
7670
8046
|
const path = this.federation.router.build("following", { identifier });
|
|
7671
8047
|
if (path == null) throw new RouterError("No following collection path registered.");
|
|
@@ -8268,6 +8644,9 @@ var RequestContextImpl = class RequestContextImpl extends ContextImpl {
|
|
|
8268
8644
|
getPortableObjectUri(cls, values, authority) {
|
|
8269
8645
|
return super.getPortableObjectUri(cls, values, authority ?? this.portableRequest?.authority);
|
|
8270
8646
|
}
|
|
8647
|
+
getPortableInboxUri(identifier, authority) {
|
|
8648
|
+
return super.getPortableInboxUri(identifier, authority ?? this.portableRequest?.authority);
|
|
8649
|
+
}
|
|
8271
8650
|
async getActor(identifier, options) {
|
|
8272
8651
|
if (this.federation.actorCallbacks == null || this.federation.actorCallbacks.dispatcher == null) throw new Error("No actor dispatcher registered.");
|
|
8273
8652
|
if (this.#invokedFromActorDispatcher != null) getLogger([
|