@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
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-Dmm3t92W.cjs");
|
|
3
3
|
const require_transformers = require("./transformers-BalCrSsr.cjs");
|
|
4
|
-
const require_http = require("./http-
|
|
4
|
+
const require_http = require("./http-DGk8XI38.cjs");
|
|
5
5
|
const require_types = require("./types-Be2zbyeG.cjs");
|
|
6
|
-
const require_owner = require("./owner-
|
|
7
|
-
const require_kv_cache = require("./kv-cache-
|
|
6
|
+
const require_owner = require("./owner-DCIH7Iq0.cjs");
|
|
7
|
+
const require_kv_cache = require("./kv-cache-DvqkKNIj.cjs");
|
|
8
8
|
let _logtape_logtape = require("@logtape/logtape");
|
|
9
9
|
let _fedify_uri_template = require("@fedify/uri-template");
|
|
10
10
|
let _fedify_vocab = require("@fedify/vocab");
|
|
@@ -735,13 +735,13 @@ function createFederationBuilder() {
|
|
|
735
735
|
}
|
|
736
736
|
//#endregion
|
|
737
737
|
//#region src/federation/circuit-breaker.ts
|
|
738
|
-
const Temporal$
|
|
738
|
+
const Temporal$11 = require_http.Temporal;
|
|
739
739
|
const LEGACY_SWEEP_MARKER = ["__fedify_meta", "circuit_breaker_state_ttl_sweep_v2"];
|
|
740
740
|
const LEGACY_SWEEP_DELETING_MARKER = { __fedifyDeletingCircuitBreakerLegacyState: true };
|
|
741
741
|
const CIRCUIT_BREAKER_STATE_VERSION = 1;
|
|
742
|
-
const LEGACY_SWEEP_LOCK_TTL = Temporal$
|
|
743
|
-
const LEGACY_SWEEP_RENEW_INTERVAL = Temporal$
|
|
744
|
-
const LEGACY_SWEEP_RETRY_WINDOW = Temporal$
|
|
742
|
+
const LEGACY_SWEEP_LOCK_TTL = Temporal$11.Duration.from({ minutes: 5 });
|
|
743
|
+
const LEGACY_SWEEP_RENEW_INTERVAL = Temporal$11.Duration.from({ minutes: 1 });
|
|
744
|
+
const LEGACY_SWEEP_RETRY_WINDOW = Temporal$11.Duration.from({ hours: 168 });
|
|
745
745
|
const LEGACY_SWEEP_WAIT_INTERVAL = 10;
|
|
746
746
|
const LEGACY_SWEEP_CAS_ATTEMPTS = 3;
|
|
747
747
|
/**
|
|
@@ -760,7 +760,7 @@ var CircuitBreaker = class {
|
|
|
760
760
|
this.#kv = options.kv;
|
|
761
761
|
this.#prefix = options.prefix;
|
|
762
762
|
this.#options = normalizeCircuitBreakerOptions(options.options ?? {});
|
|
763
|
-
this.#now = options.now ?? (() => Temporal$
|
|
763
|
+
this.#now = options.now ?? (() => Temporal$11.Now.instant());
|
|
764
764
|
this.#stateChangeObserver = options.stateChangeObserver;
|
|
765
765
|
}
|
|
766
766
|
get options() {
|
|
@@ -774,7 +774,7 @@ var CircuitBreaker = class {
|
|
|
774
774
|
this.#sweepLegacyStates();
|
|
775
775
|
const heldSince = parseHeldSince(message.circuitHeldSince);
|
|
776
776
|
const now = this.#now();
|
|
777
|
-
if (heldSince != null && Temporal$
|
|
777
|
+
if (heldSince != null && Temporal$11.Instant.compare(heldSince.add(this.#options.heldActivityTtl), now) <= 0) return {
|
|
778
778
|
type: "drop",
|
|
779
779
|
heldSince
|
|
780
780
|
};
|
|
@@ -786,12 +786,12 @@ var CircuitBreaker = class {
|
|
|
786
786
|
probe: false
|
|
787
787
|
};
|
|
788
788
|
if (oldState.state === "half-open") {
|
|
789
|
-
const halfOpened = oldState.halfOpened == null ? void 0 : Temporal$
|
|
789
|
+
const halfOpened = oldState.halfOpened == null ? void 0 : Temporal$11.Instant.from(oldState.halfOpened);
|
|
790
790
|
if (halfOpened != null) {
|
|
791
791
|
const staleAt = halfOpened.add(this.#options.recoveryDelay);
|
|
792
|
-
if (Temporal$
|
|
792
|
+
if (Temporal$11.Instant.compare(now, staleAt) < 0) {
|
|
793
793
|
const releaseAt = now.add(this.#options.releaseInterval);
|
|
794
|
-
const retryAt = Temporal$
|
|
794
|
+
const retryAt = Temporal$11.Instant.compare(releaseAt, staleAt) < 0 ? releaseAt : staleAt;
|
|
795
795
|
const cappedRetryAt = this.#capHeldRetryAt(now, heldSince, retryAt);
|
|
796
796
|
return {
|
|
797
797
|
type: "hold",
|
|
@@ -813,8 +813,8 @@ var CircuitBreaker = class {
|
|
|
813
813
|
lastConflictingState = "half-open";
|
|
814
814
|
continue;
|
|
815
815
|
}
|
|
816
|
-
const probeAt = (oldState.opened == null ? now : Temporal$
|
|
817
|
-
if (Temporal$
|
|
816
|
+
const probeAt = (oldState.opened == null ? now : Temporal$11.Instant.from(oldState.opened)).add(this.#options.recoveryDelay);
|
|
817
|
+
if (Temporal$11.Instant.compare(now, probeAt) < 0) {
|
|
818
818
|
const retryAt = this.#capHeldRetryAt(now, heldSince, probeAt);
|
|
819
819
|
return {
|
|
820
820
|
type: "hold",
|
|
@@ -879,7 +879,7 @@ var CircuitBreaker = class {
|
|
|
879
879
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
880
880
|
const oldState = await this.#get(remoteHost);
|
|
881
881
|
if (oldState?.state === "open") return void 0;
|
|
882
|
-
const oldFailures = oldState?.failures.map(Temporal$
|
|
882
|
+
const oldFailures = oldState?.failures.map(Temporal$11.Instant.from) ?? [];
|
|
883
883
|
const failures = this.#options.pruneFailures([...oldFailures, now], now);
|
|
884
884
|
let newState;
|
|
885
885
|
let transition;
|
|
@@ -963,7 +963,7 @@ var CircuitBreaker = class {
|
|
|
963
963
|
for await (const { key, value } of this.#kv.list(this.#prefix)) {
|
|
964
964
|
if (key.length !== this.#prefix.length + 1) continue;
|
|
965
965
|
await this.#migrateLegacyState(key, value);
|
|
966
|
-
if (Temporal$
|
|
966
|
+
if (Temporal$11.Instant.compare(this.#now(), leaseRenewedAt.add(LEGACY_SWEEP_RENEW_INTERVAL)) >= 0) {
|
|
967
967
|
const renewed = await this.#renewLegacySweep(markerKey, marker);
|
|
968
968
|
if (renewed == null) return;
|
|
969
969
|
marker = renewed;
|
|
@@ -987,7 +987,7 @@ var CircuitBreaker = class {
|
|
|
987
987
|
}
|
|
988
988
|
#finishLegacySweepMarker(marker) {
|
|
989
989
|
const retryUntil = getLegacySweepRetryUntil(marker);
|
|
990
|
-
if (retryUntil != null && Temporal$
|
|
990
|
+
if (retryUntil != null && Temporal$11.Instant.compare(this.#now(), Temporal$11.Instant.from(retryUntil)) < 0) return {
|
|
991
991
|
state: "done",
|
|
992
992
|
retryUntil
|
|
993
993
|
};
|
|
@@ -1030,7 +1030,7 @@ var CircuitBreaker = class {
|
|
|
1030
1030
|
#isLegacySweepDone() {
|
|
1031
1031
|
if (this.#legacySweepDone === "final") return true;
|
|
1032
1032
|
if (this.#legacySweepDone == null) return false;
|
|
1033
|
-
if (Temporal$
|
|
1033
|
+
if (Temporal$11.Instant.compare(this.#now(), this.#legacySweepDone) < 0) return true;
|
|
1034
1034
|
this.#legacySweepDone = void 0;
|
|
1035
1035
|
return false;
|
|
1036
1036
|
}
|
|
@@ -1040,7 +1040,7 @@ var CircuitBreaker = class {
|
|
|
1040
1040
|
}
|
|
1041
1041
|
#capHeldRetryAt(now, heldSince, retryAt) {
|
|
1042
1042
|
const expiresAt = (heldSince ?? now).add(this.#options.heldActivityTtl);
|
|
1043
|
-
return Temporal$
|
|
1043
|
+
return Temporal$11.Instant.compare(expiresAt, retryAt) < 0 ? expiresAt : retryAt;
|
|
1044
1044
|
}
|
|
1045
1045
|
async #get(remoteHost) {
|
|
1046
1046
|
return parseStoredCircuitBreakerKvState(await this.#kv.get(this.#key(remoteHost)));
|
|
@@ -1110,7 +1110,7 @@ function normalizeCircuitBreakerOptions(options) {
|
|
|
1110
1110
|
assertPositiveDuration(releaseInterval, "releaseInterval");
|
|
1111
1111
|
if (configuredStateTtl != null) {
|
|
1112
1112
|
assertPositiveDuration(configuredStateTtl, "stateTtl");
|
|
1113
|
-
if (Temporal$
|
|
1113
|
+
if (Temporal$11.Duration.compare(configuredStateTtl, recoveryDelay) < 0) throw new RangeError("stateTtl must be at least recoveryDelay.");
|
|
1114
1114
|
}
|
|
1115
1115
|
let failure;
|
|
1116
1116
|
let pruneFailures;
|
|
@@ -1122,13 +1122,13 @@ function normalizeCircuitBreakerOptions(options) {
|
|
|
1122
1122
|
assertPositiveDuration(failureWindow, "failureWindow");
|
|
1123
1123
|
pruneFailures = (timestamps, now) => {
|
|
1124
1124
|
const earliest = now.subtract(failureWindow);
|
|
1125
|
-
return timestamps.filter((timestamp) => Temporal$
|
|
1125
|
+
return timestamps.filter((timestamp) => Temporal$11.Instant.compare(timestamp, earliest) >= 0).slice(-failureThreshold);
|
|
1126
1126
|
};
|
|
1127
1127
|
failure = (timestamps) => {
|
|
1128
1128
|
if (timestamps.length < failureThreshold) return false;
|
|
1129
1129
|
const first = timestamps[timestamps.length - failureThreshold];
|
|
1130
1130
|
const last = timestamps[timestamps.length - 1];
|
|
1131
|
-
return Temporal$
|
|
1131
|
+
return Temporal$11.Duration.compare(first.until(last), failureWindow) <= 0;
|
|
1132
1132
|
};
|
|
1133
1133
|
stateTtl = configuredStateTtl ?? maxDuration(recoveryDelay.add(failureWindow), recoveryDelay.add(heldActivityTtl));
|
|
1134
1134
|
} else {
|
|
@@ -1148,7 +1148,7 @@ function normalizeCircuitBreakerOptions(options) {
|
|
|
1148
1148
|
};
|
|
1149
1149
|
}
|
|
1150
1150
|
function maxDuration(duration, ...durations) {
|
|
1151
|
-
return durations.reduce((max, candidate) => Temporal$
|
|
1151
|
+
return durations.reduce((max, candidate) => Temporal$11.Duration.compare(candidate, max) > 0 ? candidate : max, duration);
|
|
1152
1152
|
}
|
|
1153
1153
|
function isLegacySweepDone(value, now) {
|
|
1154
1154
|
return getLegacySweepDoneUntil(value, now) != null;
|
|
@@ -1157,8 +1157,8 @@ function getLegacySweepDoneUntil(value, now) {
|
|
|
1157
1157
|
if (typeof value !== "object" || value == null || !("state" in value)) return;
|
|
1158
1158
|
if (value.state === "final") return "final";
|
|
1159
1159
|
if (value.state === "done" && "retryUntil" in value && typeof value.retryUntil === "string") try {
|
|
1160
|
-
const retryUntil = Temporal$
|
|
1161
|
-
return Temporal$
|
|
1160
|
+
const retryUntil = Temporal$11.Instant.from(value.retryUntil);
|
|
1161
|
+
return Temporal$11.Instant.compare(now, retryUntil) < 0 ? retryUntil : void 0;
|
|
1162
1162
|
} catch {
|
|
1163
1163
|
return;
|
|
1164
1164
|
}
|
|
@@ -1166,7 +1166,7 @@ function getLegacySweepDoneUntil(value, now) {
|
|
|
1166
1166
|
function isLegacySweepRetryDue(value, now) {
|
|
1167
1167
|
if (typeof value !== "object" || value == null || !("state" in value) || value.state !== "done" || !("retryUntil" in value) || typeof value.retryUntil !== "string") return false;
|
|
1168
1168
|
try {
|
|
1169
|
-
return Temporal$
|
|
1169
|
+
return Temporal$11.Instant.compare(now, Temporal$11.Instant.from(value.retryUntil)) >= 0;
|
|
1170
1170
|
} catch {
|
|
1171
1171
|
return false;
|
|
1172
1172
|
}
|
|
@@ -1177,7 +1177,7 @@ function isLegacySweepActive(value, now) {
|
|
|
1177
1177
|
function isLegacySweepInProgress(value, now) {
|
|
1178
1178
|
if (typeof value !== "object" || value == null || !("state" in value) || value.state !== "sweeping" || !("started" in value) || typeof value.started !== "string") return false;
|
|
1179
1179
|
try {
|
|
1180
|
-
return Temporal$
|
|
1180
|
+
return Temporal$11.Instant.compare(now, Temporal$11.Instant.from(value.started).add(LEGACY_SWEEP_LOCK_TTL)) < 0;
|
|
1181
1181
|
} catch {
|
|
1182
1182
|
return false;
|
|
1183
1183
|
}
|
|
@@ -1185,7 +1185,7 @@ function isLegacySweepInProgress(value, now) {
|
|
|
1185
1185
|
function getLegacySweepRetryUntil(value) {
|
|
1186
1186
|
if (typeof value !== "object" || value == null || !("retryUntil" in value) || typeof value.retryUntil !== "string") return;
|
|
1187
1187
|
try {
|
|
1188
|
-
Temporal$
|
|
1188
|
+
Temporal$11.Instant.from(value.retryUntil);
|
|
1189
1189
|
return value.retryUntil;
|
|
1190
1190
|
} catch {
|
|
1191
1191
|
return;
|
|
@@ -1209,19 +1209,19 @@ function delay(milliseconds) {
|
|
|
1209
1209
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
1210
1210
|
}
|
|
1211
1211
|
function toInstantDuration(duration) {
|
|
1212
|
-
const parsed = Temporal$
|
|
1213
|
-
return Temporal$
|
|
1212
|
+
const parsed = Temporal$11.Duration.from(duration);
|
|
1213
|
+
return Temporal$11.Duration.from({ milliseconds: Math.trunc(parsed.total({
|
|
1214
1214
|
unit: "millisecond",
|
|
1215
|
-
relativeTo: Temporal$
|
|
1215
|
+
relativeTo: Temporal$11.PlainDateTime.from("2026-01-01T00:00:00")
|
|
1216
1216
|
})) });
|
|
1217
1217
|
}
|
|
1218
1218
|
function assertPositiveDuration(duration, name) {
|
|
1219
|
-
if (Temporal$
|
|
1219
|
+
if (Temporal$11.Duration.compare(duration, { seconds: 0 }) <= 0) throw new RangeError(`${name} must be a positive duration.`);
|
|
1220
1220
|
}
|
|
1221
1221
|
function parseHeldSince(value) {
|
|
1222
1222
|
if (value == null) return void 0;
|
|
1223
1223
|
try {
|
|
1224
|
-
return Temporal$
|
|
1224
|
+
return Temporal$11.Instant.from(value);
|
|
1225
1225
|
} catch (error) {
|
|
1226
1226
|
(0, _logtape_logtape.getLogger)([
|
|
1227
1227
|
"fedify",
|
|
@@ -1248,7 +1248,7 @@ function parseStoredCircuitBreakerKvState(value) {
|
|
|
1248
1248
|
const isInstantString = (v) => {
|
|
1249
1249
|
if (typeof v !== "string") return false;
|
|
1250
1250
|
try {
|
|
1251
|
-
Temporal$
|
|
1251
|
+
Temporal$11.Instant.from(v);
|
|
1252
1252
|
return true;
|
|
1253
1253
|
} catch {
|
|
1254
1254
|
return false;
|
|
@@ -1849,7 +1849,7 @@ async function verifyCompoundPortableObjectProofs(json, limits, options = {}) {
|
|
|
1849
1849
|
}
|
|
1850
1850
|
//#endregion
|
|
1851
1851
|
//#region src/federation/inbox.ts
|
|
1852
|
-
const Temporal$
|
|
1852
|
+
const Temporal$10 = require_http.Temporal;
|
|
1853
1853
|
async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
|
|
1854
1854
|
const logger = (0, _logtape_logtape.getLogger)([
|
|
1855
1855
|
"fedify",
|
|
@@ -1996,7 +1996,7 @@ async function routeActivity({ context: ctx, json, originalJson, normalizedActiv
|
|
|
1996
1996
|
return "error";
|
|
1997
1997
|
}
|
|
1998
1998
|
require_http.recordInboxActivity(meterProvider, "processed", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
1999
|
-
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal$
|
|
1999
|
+
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal$10.Duration.from({ days: 1 }) });
|
|
2000
2000
|
logger.info("Activity {activityId} has been processed.", {
|
|
2001
2001
|
activityId: activity.id?.href,
|
|
2002
2002
|
activity: json,
|
|
@@ -2008,7 +2008,7 @@ async function routeActivity({ context: ctx, json, originalJson, normalizedActiv
|
|
|
2008
2008
|
}
|
|
2009
2009
|
//#endregion
|
|
2010
2010
|
//#region src/federation/keycache.ts
|
|
2011
|
-
const Temporal$
|
|
2011
|
+
const Temporal$9 = require_http.Temporal;
|
|
2012
2012
|
const KEY_CACHE_GENERATION = "2";
|
|
2013
2013
|
var KvKeyCache = class {
|
|
2014
2014
|
kv;
|
|
@@ -2021,8 +2021,8 @@ var KvKeyCache = class {
|
|
|
2021
2021
|
this.kv = kv;
|
|
2022
2022
|
this.prefix = prefix;
|
|
2023
2023
|
this.options = options;
|
|
2024
|
-
this.unavailableKeyTtl = options.unavailableKeyTtl ?? Temporal$
|
|
2025
|
-
this.keyTtl = options.keyTtl ?? Temporal$
|
|
2024
|
+
this.unavailableKeyTtl = options.unavailableKeyTtl ?? Temporal$9.Duration.from({ minutes: 10 });
|
|
2025
|
+
this.keyTtl = options.keyTtl ?? Temporal$9.Duration.from({ days: 30 });
|
|
2026
2026
|
this.nullKeys = /* @__PURE__ */ new Map();
|
|
2027
2027
|
}
|
|
2028
2028
|
#getFetchErrorKey(keyId) {
|
|
@@ -2042,13 +2042,13 @@ var KvKeyCache = class {
|
|
|
2042
2042
|
async get(keyId) {
|
|
2043
2043
|
const negativeExpiration = this.nullKeys.get(keyId.href);
|
|
2044
2044
|
if (negativeExpiration != null) {
|
|
2045
|
-
if (Temporal$
|
|
2045
|
+
if (Temporal$9.Now.instant().until(negativeExpiration).sign >= 0) return null;
|
|
2046
2046
|
this.nullKeys.delete(keyId.href);
|
|
2047
2047
|
}
|
|
2048
2048
|
const serialized = await this.kv.get(this.#entryKey(keyId));
|
|
2049
2049
|
if (serialized === void 0) return void 0;
|
|
2050
2050
|
if (serialized === null) {
|
|
2051
|
-
this.nullKeys.set(keyId.href, Temporal$
|
|
2051
|
+
this.nullKeys.set(keyId.href, Temporal$9.Now.instant().add(this.unavailableKeyTtl));
|
|
2052
2052
|
return null;
|
|
2053
2053
|
}
|
|
2054
2054
|
try {
|
|
@@ -2064,7 +2064,7 @@ var KvKeyCache = class {
|
|
|
2064
2064
|
}
|
|
2065
2065
|
async set(keyId, key) {
|
|
2066
2066
|
if (key == null) {
|
|
2067
|
-
this.nullKeys.set(keyId.href, Temporal$
|
|
2067
|
+
this.nullKeys.set(keyId.href, Temporal$9.Now.instant().add(this.unavailableKeyTtl));
|
|
2068
2068
|
await this.kv.set(this.#entryKey(keyId), null, { ttl: this.unavailableKeyTtl });
|
|
2069
2069
|
return;
|
|
2070
2070
|
}
|
|
@@ -2320,7 +2320,7 @@ function parseHashlinkGatewayRequest(url) {
|
|
|
2320
2320
|
}
|
|
2321
2321
|
//#endregion
|
|
2322
2322
|
//#region src/federation/temporal.ts
|
|
2323
|
-
const Temporal$
|
|
2323
|
+
const Temporal$8 = require_http.Temporal;
|
|
2324
2324
|
function isPlainObject$1(value) {
|
|
2325
2325
|
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
2326
2326
|
}
|
|
@@ -2330,7 +2330,7 @@ function normalizeDateTimeLiteral(value) {
|
|
|
2330
2330
|
function isMalformedDateTimeLiteral(value) {
|
|
2331
2331
|
if (typeof value !== "string") return false;
|
|
2332
2332
|
try {
|
|
2333
|
-
Temporal$
|
|
2333
|
+
Temporal$8.Instant.from(normalizeDateTimeLiteral(value));
|
|
2334
2334
|
return false;
|
|
2335
2335
|
} catch {
|
|
2336
2336
|
return true;
|
|
@@ -2339,7 +2339,7 @@ function isMalformedDateTimeLiteral(value) {
|
|
|
2339
2339
|
function isMalformedDurationLiteral(value) {
|
|
2340
2340
|
if (typeof value !== "string") return false;
|
|
2341
2341
|
try {
|
|
2342
|
-
Temporal$
|
|
2342
|
+
Temporal$8.Duration.from(value);
|
|
2343
2343
|
return false;
|
|
2344
2344
|
} catch {
|
|
2345
2345
|
return true;
|
|
@@ -2410,7 +2410,7 @@ async function hasMalformedKnownTemporalLiteral(value, contextLoader) {
|
|
|
2410
2410
|
}
|
|
2411
2411
|
//#endregion
|
|
2412
2412
|
//#region src/federation/handler.ts
|
|
2413
|
-
const Temporal$
|
|
2413
|
+
const Temporal$7 = require_http.Temporal;
|
|
2414
2414
|
const rawInboxContextFactorySymbol = Symbol("fedify.rawInboxContextFactory");
|
|
2415
2415
|
const INBOX_COMPOUND_PROOF_LIMITS = {
|
|
2416
2416
|
maxDepth: 64,
|
|
@@ -2638,13 +2638,13 @@ function recordCollectionMetrics(meterProvider, base, result, options = {}) {
|
|
|
2638
2638
|
* @param parameters The parameters for handling the collection.
|
|
2639
2639
|
* @returns A promise that resolves to an HTTP response.
|
|
2640
2640
|
*/
|
|
2641
|
-
async function handleCollection(request, { name: name$
|
|
2642
|
-
const spanName = name$
|
|
2641
|
+
async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
|
|
2642
|
+
const spanName = name$2.trim().replace(/\s+/g, "_");
|
|
2643
2643
|
tracerProvider = tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
|
|
2644
2644
|
const tracer = tracerProvider.getTracer(require_http.name, require_http.version);
|
|
2645
2645
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
2646
2646
|
const metricBase = {
|
|
2647
|
-
kind: getCollectionMetricKind(name$
|
|
2647
|
+
kind: getCollectionMetricKind(name$2),
|
|
2648
2648
|
page: cursor != null,
|
|
2649
2649
|
dispatcher: "built_in"
|
|
2650
2650
|
};
|
|
@@ -2685,7 +2685,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
2685
2685
|
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
2686
2686
|
return await onNotFound(request);
|
|
2687
2687
|
}
|
|
2688
|
-
const items = filterCollectionItems(page.items, name$
|
|
2688
|
+
const items = filterCollectionItems(page.items, name$2, filterPredicate);
|
|
2689
2689
|
itemCount = items.length;
|
|
2690
2690
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
2691
2691
|
return items;
|
|
@@ -2725,7 +2725,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
2725
2725
|
} else {
|
|
2726
2726
|
const uri = new URL(baseUri);
|
|
2727
2727
|
uri.searchParams.set("cursor", cursor);
|
|
2728
|
-
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$
|
|
2728
|
+
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
|
|
2729
2729
|
kind: _opentelemetry_api.SpanKind.SERVER,
|
|
2730
2730
|
attributes: {
|
|
2731
2731
|
"activitypub.collection.id": uri.href,
|
|
@@ -2741,7 +2741,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
2741
2741
|
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
2742
2742
|
return await onNotFound(request);
|
|
2743
2743
|
}
|
|
2744
|
-
const items = filterCollectionItems(page.items, name$
|
|
2744
|
+
const items = filterCollectionItems(page.items, name$2, filterPredicate);
|
|
2745
2745
|
itemCount = items.length;
|
|
2746
2746
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
2747
2747
|
return {
|
|
@@ -3247,7 +3247,7 @@ async function handleInbox(request, options) {
|
|
|
3247
3247
|
* @returns A promise that resolves to an HTTP response.
|
|
3248
3248
|
*/
|
|
3249
3249
|
async function handleInboxInternal(request, parameters, span) {
|
|
3250
|
-
const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, publicKeyTtl, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, inboxChallengePolicy, meterProvider, tracerProvider } = parameters;
|
|
3250
|
+
const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, publicKeyTtl, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, inboxChallengePolicy, meterProvider, tracerProvider, portableInbox } = parameters;
|
|
3251
3251
|
const logger = (0, _logtape_logtape.getLogger)([
|
|
3252
3252
|
"fedify",
|
|
3253
3253
|
"federation",
|
|
@@ -3260,7 +3260,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
3260
3260
|
message: "Actor dispatcher is not set."
|
|
3261
3261
|
});
|
|
3262
3262
|
return await onNotFound(request);
|
|
3263
|
-
} else if (recipient != null) {
|
|
3263
|
+
} else if (recipient != null && portableInbox == null) {
|
|
3264
3264
|
const actor = await actorDispatcher(ctx, recipient);
|
|
3265
3265
|
if (actor == null || actor instanceof _fedify_vocab.Tombstone) {
|
|
3266
3266
|
logger.error("Actor {recipient} not found.", { recipient });
|
|
@@ -3722,6 +3722,19 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
3722
3722
|
tracerProvider,
|
|
3723
3723
|
idempotencyStrategy: parameters.idempotencyStrategy
|
|
3724
3724
|
});
|
|
3725
|
+
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.", {
|
|
3726
|
+
activityId: activity.id.href,
|
|
3727
|
+
inbox: portableInbox.recipient.canonicalInboxId
|
|
3728
|
+
});
|
|
3729
|
+
else try {
|
|
3730
|
+
await portableInbox.forward(json, activity.id, (0, _fedify_vocab.getTypeId)(activity).href);
|
|
3731
|
+
} catch (error) {
|
|
3732
|
+
logger.error("Failed to forward activity {activityId} to the other gateways of the portable inbox {inbox}:\n{error}", {
|
|
3733
|
+
activityId: activity.id.href,
|
|
3734
|
+
inbox: portableInbox.recipient.canonicalInboxId,
|
|
3735
|
+
error
|
|
3736
|
+
});
|
|
3737
|
+
}
|
|
3725
3738
|
if (routeResult === "alreadyProcessed") return new Response(`Activity <${activity.id}> has already been processed.`, {
|
|
3726
3739
|
status: 202,
|
|
3727
3740
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
@@ -3747,6 +3760,12 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
3747
3760
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
3748
3761
|
});
|
|
3749
3762
|
}
|
|
3763
|
+
const PORTABLE_INBOX_FORWARDABLE_RESULTS = /* @__PURE__ */ new Set([
|
|
3764
|
+
"success",
|
|
3765
|
+
"enqueued",
|
|
3766
|
+
"alreadyProcessed",
|
|
3767
|
+
"unsupportedActivity"
|
|
3768
|
+
]);
|
|
3750
3769
|
/**
|
|
3751
3770
|
* Handles a custom collection request.
|
|
3752
3771
|
* @template TItem The type of items in the collection.
|
|
@@ -3869,8 +3888,8 @@ var CustomCollectionHandler = class {
|
|
|
3869
3888
|
* @param CollectionPage The CollectionPage constructor.
|
|
3870
3889
|
* @param filterPredicate Optional filter predicate for items.
|
|
3871
3890
|
*/
|
|
3872
|
-
constructor(name$
|
|
3873
|
-
this.name = name$
|
|
3891
|
+
constructor(name$1, values, context, callbacks, tracerProvider = _opentelemetry_api.trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate) {
|
|
3892
|
+
this.name = name$1;
|
|
3874
3893
|
this.values = values;
|
|
3875
3894
|
this.context = context;
|
|
3876
3895
|
this.callbacks = callbacks;
|
|
@@ -4340,7 +4359,7 @@ async function buildAcceptSignatureHeader(policy, kv, noncePrefix) {
|
|
|
4340
4359
|
}
|
|
4341
4360
|
async function setKey(kv, key, policy) {
|
|
4342
4361
|
const seconds = policy.nonceTtlSeconds ?? 300;
|
|
4343
|
-
const ttl = Temporal$
|
|
4362
|
+
const ttl = Temporal$7.Duration.from({ seconds });
|
|
4344
4363
|
await kv.set(key, true, { ttl });
|
|
4345
4364
|
}
|
|
4346
4365
|
const DEF_COMPONENTS = [
|
|
@@ -4365,7 +4384,7 @@ function isPortableGatewayKey(key) {
|
|
|
4365
4384
|
}
|
|
4366
4385
|
//#endregion
|
|
4367
4386
|
//#region src/federation/kv.ts
|
|
4368
|
-
const Temporal$
|
|
4387
|
+
const Temporal$6 = require_http.Temporal;
|
|
4369
4388
|
/**
|
|
4370
4389
|
* A key–value store that stores values in memory.
|
|
4371
4390
|
* Do not use this in production as it does not persist values.
|
|
@@ -4385,7 +4404,7 @@ var MemoryKvStore = class {
|
|
|
4385
4404
|
const entry = this.#values[encodedKey];
|
|
4386
4405
|
if (entry == null) return Promise.resolve(void 0);
|
|
4387
4406
|
const [value, expiration] = entry;
|
|
4388
|
-
if (expiration != null && Temporal$
|
|
4407
|
+
if (expiration != null && Temporal$6.Now.instant().until(expiration).sign < 0) {
|
|
4389
4408
|
delete this.#values[encodedKey];
|
|
4390
4409
|
return Promise.resolve(void 0);
|
|
4391
4410
|
}
|
|
@@ -4396,7 +4415,7 @@ var MemoryKvStore = class {
|
|
|
4396
4415
|
*/
|
|
4397
4416
|
set(key, value, options) {
|
|
4398
4417
|
const encodedKey = this.#encodeKey(key);
|
|
4399
|
-
const expiration = options?.ttl == null ? null : Temporal$
|
|
4418
|
+
const expiration = options?.ttl == null ? null : Temporal$6.Now.instant().add(options.ttl.round({ largestUnit: "hour" }));
|
|
4400
4419
|
this.#values[encodedKey] = [value, expiration];
|
|
4401
4420
|
return Promise.resolve();
|
|
4402
4421
|
}
|
|
@@ -4418,13 +4437,13 @@ var MemoryKvStore = class {
|
|
|
4418
4437
|
if (entry == null) currentValue = void 0;
|
|
4419
4438
|
else {
|
|
4420
4439
|
const [value, expiration] = entry;
|
|
4421
|
-
if (expiration != null && Temporal$
|
|
4440
|
+
if (expiration != null && Temporal$6.Now.instant().until(expiration).sign < 0) {
|
|
4422
4441
|
delete this.#values[encodedKey];
|
|
4423
4442
|
currentValue = void 0;
|
|
4424
4443
|
} else currentValue = value;
|
|
4425
4444
|
}
|
|
4426
4445
|
if (!(0, es_toolkit.isEqual)(currentValue, expectedValue)) return Promise.resolve(false);
|
|
4427
|
-
const expiration = options?.ttl == null ? null : Temporal$
|
|
4446
|
+
const expiration = options?.ttl == null ? null : Temporal$6.Now.instant().add(options.ttl.round({ largestUnit: "hour" }));
|
|
4428
4447
|
this.#values[encodedKey] = [newValue, expiration];
|
|
4429
4448
|
return Promise.resolve(true);
|
|
4430
4449
|
}
|
|
@@ -4432,7 +4451,7 @@ var MemoryKvStore = class {
|
|
|
4432
4451
|
* {@inheritDoc KvStore.list}
|
|
4433
4452
|
*/
|
|
4434
4453
|
async *list(prefix) {
|
|
4435
|
-
const now = Temporal$
|
|
4454
|
+
const now = Temporal$6.Now.instant();
|
|
4436
4455
|
for (const [encodedKey, entry] of Object.entries(this.#values)) {
|
|
4437
4456
|
const key = JSON.parse(encodedKey);
|
|
4438
4457
|
if (prefix != null) {
|
|
@@ -4626,7 +4645,11 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
|
|
|
4626
4645
|
rsaKey = key;
|
|
4627
4646
|
break;
|
|
4628
4647
|
}
|
|
4629
|
-
if (rsaKey == null) logger.
|
|
4648
|
+
if (rsaKey == null && keys.length < 1) logger.debug("Sending the activity {activityId} to {inbox} without a signature.", {
|
|
4649
|
+
activityId,
|
|
4650
|
+
inbox: inbox.href
|
|
4651
|
+
});
|
|
4652
|
+
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.", {
|
|
4630
4653
|
inbox: inbox.href,
|
|
4631
4654
|
keys: keys.map((pair) => ({
|
|
4632
4655
|
keyId: pair.keyId.href,
|
|
@@ -4968,6 +4991,252 @@ function serializeDataPointType(dataPointType) {
|
|
|
4968
4991
|
}
|
|
4969
4992
|
}
|
|
4970
4993
|
//#endregion
|
|
4994
|
+
//#region src/federation/portable-inbox.ts
|
|
4995
|
+
const Temporal$5 = require_http.Temporal;
|
|
4996
|
+
const MAX_TIMEOUT_MS = 2 ** 31 - 1;
|
|
4997
|
+
/**
|
|
4998
|
+
* Fills in the defaults of {@link PortableInboxForwardingOptions} and
|
|
4999
|
+
* validates them.
|
|
5000
|
+
* @param options The options.
|
|
5001
|
+
* @returns The resolved options.
|
|
5002
|
+
* @throws {RangeError} If `maxTargets` is not a non-negative integer, if `ttl`
|
|
5003
|
+
* is not positive, if `deadline` is negative or longer
|
|
5004
|
+
* than 2,147,483,647 milliseconds (about 24.8 days), or
|
|
5005
|
+
* if either duration has calendar units, i.e., weeks,
|
|
5006
|
+
* months, or years, whose length depends on the date.
|
|
5007
|
+
*/
|
|
5008
|
+
function resolvePortableInboxForwardingOptions(options = {}) {
|
|
5009
|
+
const maxTargets = options.maxTargets ?? 10;
|
|
5010
|
+
if (!Number.isInteger(maxTargets) || maxTargets < 0) throw new RangeError("portableInboxForwarding.maxTargets must be a non-negative integer.");
|
|
5011
|
+
const ttl = Temporal$5.Duration.from(options.ttl ?? { days: 30 });
|
|
5012
|
+
if (ttl.total("millisecond") <= 0) throw new RangeError("portableInboxForwarding.ttl must be positive.");
|
|
5013
|
+
const deadline = Temporal$5.Duration.from(options.deadline ?? { seconds: 10 });
|
|
5014
|
+
const deadlineMs = deadline.total("millisecond");
|
|
5015
|
+
if (deadlineMs < 0 || deadlineMs > MAX_TIMEOUT_MS) throw new RangeError(`portableInboxForwarding.deadline must be between 0 and ${MAX_TIMEOUT_MS} milliseconds.`);
|
|
5016
|
+
return {
|
|
5017
|
+
maxTargets,
|
|
5018
|
+
ttl,
|
|
5019
|
+
deadline
|
|
5020
|
+
};
|
|
5021
|
+
}
|
|
5022
|
+
/**
|
|
5023
|
+
* Decides whether this server accepts a delivery to a portable inbox on behalf
|
|
5024
|
+
* of the given actor, which the actor dispatcher returned for the identifier
|
|
5025
|
+
* in the requested inbox path.
|
|
5026
|
+
*
|
|
5027
|
+
* The actor document comes from the application, so it is trusted as is and
|
|
5028
|
+
* its proof is not verified here.
|
|
5029
|
+
* @param actor The actor the actor dispatcher returned.
|
|
5030
|
+
* @param options The requested DID, the canonical form of the requested inbox
|
|
5031
|
+
* ID, and the origin of this server.
|
|
5032
|
+
* @returns The resolution.
|
|
5033
|
+
*/
|
|
5034
|
+
function resolvePortableInboxRecipient(actor, { authority, canonicalInboxId, localOrigin }) {
|
|
5035
|
+
if (actor == null) return {
|
|
5036
|
+
status: "rejected",
|
|
5037
|
+
reason: "notFound"
|
|
5038
|
+
};
|
|
5039
|
+
const id = actor.id;
|
|
5040
|
+
if (id == null || id.protocol !== "ap:" && id.protocol !== "ap+ef61:") return {
|
|
5041
|
+
status: "rejected",
|
|
5042
|
+
reason: "notPortable"
|
|
5043
|
+
};
|
|
5044
|
+
if (!isSameAuthority(id, authority)) return {
|
|
5045
|
+
status: "rejected",
|
|
5046
|
+
reason: "authorityMismatch"
|
|
5047
|
+
};
|
|
5048
|
+
const inboxId = actor.inboxId;
|
|
5049
|
+
if (inboxId == null || canonicalize(inboxId) !== canonicalInboxId) return {
|
|
5050
|
+
status: "rejected",
|
|
5051
|
+
reason: "inboxMismatch"
|
|
5052
|
+
};
|
|
5053
|
+
const gateways = actor.gateways;
|
|
5054
|
+
if (!gateways.some((g) => (0, _fedify_vocab_runtime.isGatewayUrl)(g) && g.origin === localOrigin)) return {
|
|
5055
|
+
status: "rejected",
|
|
5056
|
+
reason: "notGateway"
|
|
5057
|
+
};
|
|
5058
|
+
return {
|
|
5059
|
+
status: "accepted",
|
|
5060
|
+
recipient: {
|
|
5061
|
+
inboxId,
|
|
5062
|
+
canonicalInboxId,
|
|
5063
|
+
gateways
|
|
5064
|
+
}
|
|
5065
|
+
};
|
|
5066
|
+
}
|
|
5067
|
+
function isSameAuthority(id, authority) {
|
|
5068
|
+
try {
|
|
5069
|
+
return (0, _fedify_vocab_runtime.getFe34Origin)(id) === authority;
|
|
5070
|
+
} catch (error) {
|
|
5071
|
+
if (error instanceof TypeError) return false;
|
|
5072
|
+
throw error;
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
function canonicalize(iri) {
|
|
5076
|
+
try {
|
|
5077
|
+
return (0, _fedify_vocab_runtime.canonicalizePortableUri)(typeof iri === "string" ? iri : (0, _fedify_vocab_runtime.formatIri)(iri));
|
|
5078
|
+
} catch (error) {
|
|
5079
|
+
if (error instanceof TypeError) return null;
|
|
5080
|
+
throw error;
|
|
5081
|
+
}
|
|
5082
|
+
}
|
|
5083
|
+
/**
|
|
5084
|
+
* Forwards an activity received in a portable inbox to the inboxes of the same
|
|
5085
|
+
* portable actor on its other gateways, as FEP-ef61 recommends.
|
|
5086
|
+
*
|
|
5087
|
+
* FEP-ef61 forbids forwarding an activity from an inbox more than once, so
|
|
5088
|
+
* each target gateway is claimed with an atomic compare-and-swap before the
|
|
5089
|
+
* activity is handed off, and a claim is never released, even if the hand-off
|
|
5090
|
+
* fails, since a failed hand-off does not prove that nothing was delivered.
|
|
5091
|
+
* Forwarding is therefore best effort. Without {@link KvStore.cas}, nothing is
|
|
5092
|
+
* forwarded.
|
|
5093
|
+
*
|
|
5094
|
+
* Forwarded requests are not signed with HTTP Signatures; the receiving
|
|
5095
|
+
* gateways authenticate the activity by its own proof.
|
|
5096
|
+
* @param parameters The parameters.
|
|
5097
|
+
* @returns The target inbox URLs that the activity was handed off to.
|
|
5098
|
+
*/
|
|
5099
|
+
async function forwardPortableInboxActivity(parameters) {
|
|
5100
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
5101
|
+
"fedify",
|
|
5102
|
+
"federation",
|
|
5103
|
+
"inbox"
|
|
5104
|
+
]);
|
|
5105
|
+
const { recipient, activityId, excludedOrigins, kv, kvPrefix, outboxQueue } = parameters;
|
|
5106
|
+
const { maxTargets, ttl } = parameters.options ?? resolvePortableInboxForwardingOptions();
|
|
5107
|
+
if (kv.cas == null || maxTargets < 1) return [];
|
|
5108
|
+
const canonicalActivityId = canonicalize(activityId) ?? activityId.href;
|
|
5109
|
+
const targets = getForwardingTargets(recipient, excludedOrigins);
|
|
5110
|
+
if (targets.length > maxTargets) {
|
|
5111
|
+
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.", {
|
|
5112
|
+
inbox: recipient.canonicalInboxId,
|
|
5113
|
+
activityId: activityId.href,
|
|
5114
|
+
max: maxTargets
|
|
5115
|
+
});
|
|
5116
|
+
targets.length = maxTargets;
|
|
5117
|
+
}
|
|
5118
|
+
const claimed = [];
|
|
5119
|
+
for (const { gateway, inbox } of targets) {
|
|
5120
|
+
const key = [
|
|
5121
|
+
...kvPrefix,
|
|
5122
|
+
recipient.canonicalInboxId,
|
|
5123
|
+
canonicalActivityId,
|
|
5124
|
+
gateway
|
|
5125
|
+
];
|
|
5126
|
+
if (await kv.cas(key, void 0, true, { ttl })) claimed.push(inbox);
|
|
5127
|
+
}
|
|
5128
|
+
if (claimed.length < 1) return [];
|
|
5129
|
+
logger.debug("Forwarding activity {activityId} from the portable inbox {inbox} to other gateways:\n{targets}", {
|
|
5130
|
+
activityId: activityId.href,
|
|
5131
|
+
inbox: recipient.canonicalInboxId,
|
|
5132
|
+
targets: claimed.map((t) => t.href)
|
|
5133
|
+
});
|
|
5134
|
+
if (outboxQueue == null) await forwardImmediately(parameters, claimed);
|
|
5135
|
+
else await enqueueForwarding(parameters, outboxQueue, claimed);
|
|
5136
|
+
return claimed;
|
|
5137
|
+
}
|
|
5138
|
+
function getForwardingTargets(recipient, excludedOrigins) {
|
|
5139
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
5140
|
+
"fedify",
|
|
5141
|
+
"federation",
|
|
5142
|
+
"inbox"
|
|
5143
|
+
]);
|
|
5144
|
+
const seen = new Set(excludedOrigins);
|
|
5145
|
+
const targets = [];
|
|
5146
|
+
for (const gateway of recipient.gateways) {
|
|
5147
|
+
if (!(0, _fedify_vocab_runtime.isGatewayUrl)(gateway) || seen.has(gateway.origin)) continue;
|
|
5148
|
+
seen.add(gateway.origin);
|
|
5149
|
+
let inbox;
|
|
5150
|
+
try {
|
|
5151
|
+
inbox = (0, _fedify_vocab_runtime.toCompatibleEf61Id)(recipient.inboxId, gateway.origin);
|
|
5152
|
+
} catch (error) {
|
|
5153
|
+
if (!(error instanceof TypeError)) throw error;
|
|
5154
|
+
logger.warn("Cannot forward to the gateway {gateway}, as the portable inbox {inbox} cannot be represented as its compatible identifier.", {
|
|
5155
|
+
gateway: gateway.origin,
|
|
5156
|
+
inbox: recipient.canonicalInboxId
|
|
5157
|
+
});
|
|
5158
|
+
continue;
|
|
5159
|
+
}
|
|
5160
|
+
targets.push({
|
|
5161
|
+
gateway: gateway.origin,
|
|
5162
|
+
inbox
|
|
5163
|
+
});
|
|
5164
|
+
}
|
|
5165
|
+
return targets;
|
|
5166
|
+
}
|
|
5167
|
+
async function forwardImmediately({ activity, activityId, activityType, allowPrivateAddress, options, meterProvider, tracerProvider }, inboxes) {
|
|
5168
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
5169
|
+
"fedify",
|
|
5170
|
+
"federation",
|
|
5171
|
+
"inbox"
|
|
5172
|
+
]);
|
|
5173
|
+
const deadline = (options ?? resolvePortableInboxForwardingOptions()).deadline.total("millisecond");
|
|
5174
|
+
const sends = inboxes.map((inbox) => sendActivity({
|
|
5175
|
+
activity,
|
|
5176
|
+
activityId: activityId.href,
|
|
5177
|
+
activityType,
|
|
5178
|
+
keys: [],
|
|
5179
|
+
inbox,
|
|
5180
|
+
allowPrivateAddress,
|
|
5181
|
+
meterProvider,
|
|
5182
|
+
tracerProvider
|
|
5183
|
+
}).catch((error) => {
|
|
5184
|
+
logger.error("Failed to forward activity {activityId} to {inbox}; it will not be forwarded to the inbox again:\n{error}", {
|
|
5185
|
+
activityId: activityId.href,
|
|
5186
|
+
inbox: inbox.href,
|
|
5187
|
+
error
|
|
5188
|
+
});
|
|
5189
|
+
}));
|
|
5190
|
+
let timer;
|
|
5191
|
+
const timedOut = await Promise.race([Promise.all(sends).then(() => false), new Promise((resolve) => {
|
|
5192
|
+
timer = setTimeout(() => resolve(true), deadline);
|
|
5193
|
+
})]);
|
|
5194
|
+
clearTimeout(timer);
|
|
5195
|
+
if (timedOut) logger.warn("Forwarding activity {activityId} to other gateways did not finish in {deadline} ms; responding to the delivery without waiting for it.", {
|
|
5196
|
+
activityId: activityId.href,
|
|
5197
|
+
deadline
|
|
5198
|
+
});
|
|
5199
|
+
}
|
|
5200
|
+
async function enqueueForwarding({ activity, activityId, activityType, baseUrl, startQueue, meterProvider }, outboxQueue, inboxes) {
|
|
5201
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
5202
|
+
"fedify",
|
|
5203
|
+
"federation",
|
|
5204
|
+
"inbox"
|
|
5205
|
+
]);
|
|
5206
|
+
startQueue?.();
|
|
5207
|
+
const started = (/* @__PURE__ */ new Date()).toISOString();
|
|
5208
|
+
const traceContext = {};
|
|
5209
|
+
_opentelemetry_api.propagation.inject(_opentelemetry_api.context.active(), traceContext);
|
|
5210
|
+
await Promise.all(inboxes.map(async (inbox) => {
|
|
5211
|
+
const message = {
|
|
5212
|
+
type: "outbox",
|
|
5213
|
+
id: crypto.randomUUID(),
|
|
5214
|
+
baseUrl,
|
|
5215
|
+
keys: [],
|
|
5216
|
+
activity,
|
|
5217
|
+
activityId: activityId.href,
|
|
5218
|
+
activityType,
|
|
5219
|
+
inbox: inbox.href,
|
|
5220
|
+
sharedInbox: false,
|
|
5221
|
+
started,
|
|
5222
|
+
attempt: 0,
|
|
5223
|
+
headers: {},
|
|
5224
|
+
traceContext
|
|
5225
|
+
};
|
|
5226
|
+
try {
|
|
5227
|
+
await outboxQueue.enqueue(message);
|
|
5228
|
+
} catch (error) {
|
|
5229
|
+
logger.error("Failed to enqueue activity {activityId} to forward to {inbox}; it will not be forwarded to the inbox again:\n{error}", {
|
|
5230
|
+
activityId: activityId.href,
|
|
5231
|
+
inbox: inbox.href,
|
|
5232
|
+
error
|
|
5233
|
+
});
|
|
5234
|
+
return;
|
|
5235
|
+
}
|
|
5236
|
+
require_http.recordOutboxEnqueue(meterProvider, outboxQueue, message);
|
|
5237
|
+
}));
|
|
5238
|
+
}
|
|
5239
|
+
//#endregion
|
|
4971
5240
|
//#region src/federation/outgoing-proof.ts
|
|
4972
5241
|
function isPortableUrl(url) {
|
|
4973
5242
|
return url.protocol === "ap:" || url.protocol === "ap+ef61:";
|
|
@@ -5870,6 +6139,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5870
6139
|
kvPrefixes;
|
|
5871
6140
|
publicKeyTtl;
|
|
5872
6141
|
httpMessageSignaturesSpecTtl;
|
|
6142
|
+
portableInboxForwarding;
|
|
5873
6143
|
inboxQueue;
|
|
5874
6144
|
outboxQueue;
|
|
5875
6145
|
fanoutQueue;
|
|
@@ -5902,6 +6172,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5902
6172
|
benchmarkMetricReader;
|
|
5903
6173
|
benchmarkTriggerOptions;
|
|
5904
6174
|
#mediaUploaderNoAuthWarned = false;
|
|
6175
|
+
#portableInboxForwardingWarned = false;
|
|
5905
6176
|
#queueDepthGaugeSourceId = `fedify-${(++nextQueueDepthGaugeSourceId).toString(36)}`;
|
|
5906
6177
|
#queueDepthGaugeEntries = [];
|
|
5907
6178
|
#queueDepthGaugeMeterProvider;
|
|
@@ -5933,10 +6204,12 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5933
6204
|
acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"],
|
|
5934
6205
|
circuitBreaker: ["_fedify", "circuit"],
|
|
5935
6206
|
taskDeduplication: ["_fedify", "taskDeduplication"],
|
|
6207
|
+
portableInboxForwarding: ["_fedify", "portableInboxForwarding"],
|
|
5936
6208
|
...options.kvPrefixes ?? {}
|
|
5937
6209
|
};
|
|
5938
6210
|
this.publicKeyTtl = Temporal$2.Duration.from(options.publicKeyTtl ?? { days: 30 });
|
|
5939
6211
|
this.httpMessageSignaturesSpecTtl = Temporal$2.Duration.from(options.httpMessageSignaturesSpecTtl ?? { days: 90 });
|
|
6212
|
+
this.portableInboxForwarding = resolvePortableInboxForwardingOptions(options.portableInboxForwarding);
|
|
5940
6213
|
if (options.queue == null) {
|
|
5941
6214
|
this.inboxQueue = void 0;
|
|
5942
6215
|
this.outboxQueue = void 0;
|
|
@@ -6606,8 +6879,8 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
6606
6879
|
]);
|
|
6607
6880
|
const baseUrl = new URL(message.baseUrl);
|
|
6608
6881
|
let context = this.#createContext(baseUrl, ctxData);
|
|
6609
|
-
if (message.identifier != null) context = this.#createContext(baseUrl, ctxData, { documentLoader: await context.getDocumentLoader({ identifier: message.identifier }) });
|
|
6610
|
-
else if (this.sharedInboxKeyDispatcher != null) {
|
|
6882
|
+
if (message.identifier != null && this.actorCallbacks?.keyPairsDispatcher != null) context = this.#createContext(baseUrl, ctxData, { documentLoader: await context.getDocumentLoader({ identifier: message.identifier }) });
|
|
6883
|
+
else if (message.identifier == null && this.sharedInboxKeyDispatcher != null) {
|
|
6611
6884
|
const identity = await this.sharedInboxKeyDispatcher(context);
|
|
6612
6885
|
if (identity != null) context = this.#createContext(baseUrl, ctxData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
|
|
6613
6886
|
}
|
|
@@ -7200,6 +7473,15 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
7200
7473
|
span,
|
|
7201
7474
|
metricState
|
|
7202
7475
|
});
|
|
7476
|
+
if (request.method === "POST") {
|
|
7477
|
+
const portable = parsePortableGatewayRequest(url);
|
|
7478
|
+
if (portable != null) return await this.#fetchPortableInbox(request, portable, {
|
|
7479
|
+
onNotFound,
|
|
7480
|
+
contextData,
|
|
7481
|
+
span,
|
|
7482
|
+
metricState
|
|
7483
|
+
});
|
|
7484
|
+
}
|
|
7203
7485
|
const portable = request.method === "GET" || request.method === "HEAD" ? parsePortableGatewayRequest(url) : null;
|
|
7204
7486
|
if (portable != null) return await discardBodyForHead(request, await this.#fetchPortableObject(request, portable, {
|
|
7205
7487
|
onNotFound,
|
|
@@ -7351,35 +7633,17 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
7351
7633
|
onNotFound
|
|
7352
7634
|
});
|
|
7353
7635
|
context = this.#createContext(request, contextData, { documentLoader: await context.getDocumentLoader({ identifier: route.values.identifier }) });
|
|
7354
|
-
case "sharedInbox":
|
|
7636
|
+
case "sharedInbox":
|
|
7355
7637
|
if (routeName !== "inbox" && this.sharedInboxKeyDispatcher != null) {
|
|
7356
7638
|
const identity = await this.sharedInboxKeyDispatcher(context);
|
|
7357
7639
|
if (identity != null) context = this.#createContext(request, contextData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
|
|
7358
7640
|
}
|
|
7359
|
-
|
|
7360
|
-
const inboxContextFactory = context.toInboxContext.bind(context);
|
|
7361
|
-
inboxContextFactory[rawInboxContextFactorySymbol] = context.toInboxContext.bind(context);
|
|
7362
|
-
return await handleInbox(request, {
|
|
7641
|
+
return await this.#handleInbox(request, {
|
|
7363
7642
|
recipient: route.values.identifier ?? null,
|
|
7364
7643
|
context,
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
kvPrefixes: this.kvPrefixes,
|
|
7368
|
-
publicKeyTtl: this.publicKeyTtl,
|
|
7369
|
-
queue: this.inboxQueue,
|
|
7370
|
-
actorDispatcher: this.actorCallbacks?.dispatcher,
|
|
7371
|
-
inboxListeners: this.inboxListeners,
|
|
7372
|
-
inboxErrorHandler: this.inboxErrorHandler,
|
|
7373
|
-
unverifiedActivityHandler: this.unverifiedActivityHandler,
|
|
7374
|
-
onNotFound,
|
|
7375
|
-
signatureTimeWindow: this.signatureTimeWindow,
|
|
7376
|
-
skipSignatureVerification: this.skipSignatureVerification,
|
|
7377
|
-
inboxChallengePolicy: this.inboxChallengePolicy,
|
|
7378
|
-
meterProvider: this.meterProvider,
|
|
7379
|
-
tracerProvider: this.tracerProvider,
|
|
7380
|
-
idempotencyStrategy: this.idempotencyStrategy
|
|
7644
|
+
contextData,
|
|
7645
|
+
onNotFound
|
|
7381
7646
|
});
|
|
7382
|
-
}
|
|
7383
7647
|
case "following": return await handleCollection(request, {
|
|
7384
7648
|
name: "following",
|
|
7385
7649
|
identifier: route.values.identifier,
|
|
@@ -7532,6 +7796,113 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
7532
7796
|
onNotFound
|
|
7533
7797
|
});
|
|
7534
7798
|
}
|
|
7799
|
+
async #handleInbox(request, { recipient, context, contextData, onNotFound, portableInbox }) {
|
|
7800
|
+
if (!this.manuallyStartQueue) this._startQueueInternal(contextData);
|
|
7801
|
+
const inboxContextFactory = context.toInboxContext.bind(context);
|
|
7802
|
+
inboxContextFactory[rawInboxContextFactorySymbol] = context.toInboxContext.bind(context);
|
|
7803
|
+
return await handleInbox(request, {
|
|
7804
|
+
recipient,
|
|
7805
|
+
context,
|
|
7806
|
+
inboxContextFactory,
|
|
7807
|
+
kv: this.kv,
|
|
7808
|
+
kvPrefixes: this.kvPrefixes,
|
|
7809
|
+
publicKeyTtl: this.publicKeyTtl,
|
|
7810
|
+
queue: this.inboxQueue,
|
|
7811
|
+
actorDispatcher: this.actorCallbacks?.dispatcher,
|
|
7812
|
+
inboxListeners: this.inboxListeners,
|
|
7813
|
+
inboxErrorHandler: this.inboxErrorHandler,
|
|
7814
|
+
unverifiedActivityHandler: this.unverifiedActivityHandler,
|
|
7815
|
+
onNotFound,
|
|
7816
|
+
signatureTimeWindow: this.signatureTimeWindow,
|
|
7817
|
+
skipSignatureVerification: this.skipSignatureVerification,
|
|
7818
|
+
inboxChallengePolicy: this.inboxChallengePolicy,
|
|
7819
|
+
meterProvider: this.meterProvider,
|
|
7820
|
+
tracerProvider: this.tracerProvider,
|
|
7821
|
+
idempotencyStrategy: this.idempotencyStrategy,
|
|
7822
|
+
portableInbox
|
|
7823
|
+
});
|
|
7824
|
+
}
|
|
7825
|
+
async #fetchPortableInbox(request, portable, { onNotFound, contextData, span, metricState }) {
|
|
7826
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
7827
|
+
"fedify",
|
|
7828
|
+
"federation",
|
|
7829
|
+
"inbox"
|
|
7830
|
+
]);
|
|
7831
|
+
if (portable.type === "malformed") {
|
|
7832
|
+
metricState.endpoint = "not_found";
|
|
7833
|
+
logger.debug("Malformed FEP-ef61 gateway inbox request {url}: {error}", {
|
|
7834
|
+
url: request.url,
|
|
7835
|
+
error: portable.error
|
|
7836
|
+
});
|
|
7837
|
+
return new Response("Malformed portable inbox ID.", {
|
|
7838
|
+
status: 400,
|
|
7839
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
7840
|
+
});
|
|
7841
|
+
}
|
|
7842
|
+
const route = this.router.route(portable.path);
|
|
7843
|
+
if (route == null || route.name !== "inbox") {
|
|
7844
|
+
metricState.endpoint = "not_found";
|
|
7845
|
+
return await onNotFound(request);
|
|
7846
|
+
}
|
|
7847
|
+
metricState.routeTemplate = PORTABLE_GATEWAY_ROUTE_PREFIX + route.template;
|
|
7848
|
+
metricState.endpoint = "inbox";
|
|
7849
|
+
span.updateName(`${request.method} ${metricState.routeTemplate}`);
|
|
7850
|
+
const identifier = route.values.identifier;
|
|
7851
|
+
let context = this.#createContext(request, contextData);
|
|
7852
|
+
const actor = this.actorCallbacks?.dispatcher == null ? null : await this.actorCallbacks.dispatcher(context, identifier);
|
|
7853
|
+
const localOrigin = context.canonicalOrigin;
|
|
7854
|
+
const resolution = resolvePortableInboxRecipient(actor == null || actor instanceof _fedify_vocab.Tombstone ? null : actor, {
|
|
7855
|
+
authority: portable.portableRequest.authority,
|
|
7856
|
+
canonicalInboxId: portable.canonicalId,
|
|
7857
|
+
localOrigin
|
|
7858
|
+
});
|
|
7859
|
+
if (resolution.status === "rejected") {
|
|
7860
|
+
logger.debug("Not accepting a delivery to the portable inbox {inbox} on behalf of the actor {identifier}: {reason}.", {
|
|
7861
|
+
inbox: portable.canonicalId,
|
|
7862
|
+
identifier,
|
|
7863
|
+
reason: resolution.reason
|
|
7864
|
+
});
|
|
7865
|
+
return await onNotFound(request);
|
|
7866
|
+
}
|
|
7867
|
+
const { recipient } = resolution;
|
|
7868
|
+
if (this.actorCallbacks?.keyPairsDispatcher != null) context = this.#createContext(request, contextData, { documentLoader: await context.getDocumentLoader({ identifier }) });
|
|
7869
|
+
const excludedOrigins = [localOrigin, new URL(request.url).origin];
|
|
7870
|
+
return await this.#handleInbox(request, {
|
|
7871
|
+
recipient: identifier,
|
|
7872
|
+
context,
|
|
7873
|
+
contextData,
|
|
7874
|
+
onNotFound,
|
|
7875
|
+
portableInbox: {
|
|
7876
|
+
recipient,
|
|
7877
|
+
forward: async (activity, activityId, activityType) => {
|
|
7878
|
+
if (this.portableInboxForwarding.maxTargets < 1) return;
|
|
7879
|
+
if (this.kv.cas == null) {
|
|
7880
|
+
if (!this.#portableInboxForwardingWarned) {
|
|
7881
|
+
this.#portableInboxForwardingWarned = true;
|
|
7882
|
+
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.");
|
|
7883
|
+
}
|
|
7884
|
+
return;
|
|
7885
|
+
}
|
|
7886
|
+
await forwardPortableInboxActivity({
|
|
7887
|
+
recipient,
|
|
7888
|
+
activity,
|
|
7889
|
+
activityId,
|
|
7890
|
+
activityType,
|
|
7891
|
+
excludedOrigins,
|
|
7892
|
+
baseUrl: context.origin,
|
|
7893
|
+
kv: this.kv,
|
|
7894
|
+
kvPrefix: this.kvPrefixes.portableInboxForwarding,
|
|
7895
|
+
outboxQueue: this.outboxQueue,
|
|
7896
|
+
startQueue: this.manuallyStartQueue ? void 0 : () => this._startQueueInternal(contextData),
|
|
7897
|
+
allowPrivateAddress: this.allowPrivateAddress,
|
|
7898
|
+
options: this.portableInboxForwarding,
|
|
7899
|
+
meterProvider: this.meterProvider,
|
|
7900
|
+
tracerProvider: this.tracerProvider
|
|
7901
|
+
});
|
|
7902
|
+
}
|
|
7903
|
+
}
|
|
7904
|
+
});
|
|
7905
|
+
}
|
|
7535
7906
|
async #fetchHashlinkMedia(request, media, { onNotFound, contextData, span, metricState }) {
|
|
7536
7907
|
metricState.endpoint = "hashlink_media";
|
|
7537
7908
|
metricState.routeTemplate = HASHLINK_MEDIA_ROUTE_TEMPLATE;
|
|
@@ -7762,6 +8133,12 @@ var ContextImpl = class ContextImpl {
|
|
|
7762
8133
|
if (path == null) throw new _fedify_uri_template.RouterError("No inbox path registered.");
|
|
7763
8134
|
return new URL(path, this.canonicalOrigin);
|
|
7764
8135
|
}
|
|
8136
|
+
getPortableInboxUri(identifier, authority) {
|
|
8137
|
+
const path = this.federation.router.build("inbox", { identifier });
|
|
8138
|
+
if (path == null) throw new _fedify_uri_template.RouterError("No inbox path registered.");
|
|
8139
|
+
if (authority == null) throw new TypeError("The authority of a portable ID is required outside an FEP-ef61 gateway request.");
|
|
8140
|
+
return buildPortableUri(authority, path);
|
|
8141
|
+
}
|
|
7765
8142
|
getFollowingUri(identifier) {
|
|
7766
8143
|
const path = this.federation.router.build("following", { identifier });
|
|
7767
8144
|
if (path == null) throw new _fedify_uri_template.RouterError("No following collection path registered.");
|
|
@@ -8364,6 +8741,9 @@ var RequestContextImpl = class RequestContextImpl extends ContextImpl {
|
|
|
8364
8741
|
getPortableObjectUri(cls, values, authority) {
|
|
8365
8742
|
return super.getPortableObjectUri(cls, values, authority ?? this.portableRequest?.authority);
|
|
8366
8743
|
}
|
|
8744
|
+
getPortableInboxUri(identifier, authority) {
|
|
8745
|
+
return super.getPortableInboxUri(identifier, authority ?? this.portableRequest?.authority);
|
|
8746
|
+
}
|
|
8367
8747
|
async getActor(identifier, options) {
|
|
8368
8748
|
if (this.federation.actorCallbacks == null || this.federation.actorCallbacks.dispatcher == null) throw new Error("No actor dispatcher registered.");
|
|
8369
8749
|
if (this.#invokedFromActorDispatcher != null) (0, _logtape_logtape.getLogger)([
|