@fedify/fedify 2.3.14 → 2.3.15
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-C6yXExXl.mjs → builder-B6vSXYxn.mjs} +2 -2
- package/dist/{client-CAM_bQXx.d.cts → client-BIBzqEIp.d.cts} +0 -1
- package/dist/{client-1He2YuBF.mjs → client-DpUNYKla.mjs} +113 -64
- package/dist/{client-CSddvgWN.d.ts → client-jE7oGvRh.d.ts} +0 -1
- 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/{context-MOrCC7yl.d.cts → context-CE_Tj_Oc.d.cts} +28 -2
- package/dist/{context-BWbdE_MZ.d.ts → context-H_5tZXg-.d.ts} +28 -2
- package/dist/{deno-y3L4Oa94.mjs → deno-BCuL3mid.mjs} +1 -1
- package/dist/docloader-D2dr1_6B.mjs +125 -0
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +5 -5
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox.test.mjs +1 -1
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +455 -29
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +2 -2
- package/dist/federation/mod.d.ts +2 -2
- package/dist/federation/mod.js +1 -1
- package/dist/federation/send.test.mjs +498 -88
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{http-DNM9KyIO.js → http-BkjxY3U0.js} +172 -103
- package/dist/{http-BCSf1KIY.mjs → http-CSDcThvz.mjs} +196 -98
- package/dist/{http-DQmNpfym.d.ts → http-CZPYFK3b.d.cts} +1 -2
- package/dist/{http-DiWQB8Mr.d.cts → http-DmP0sr2l.d.ts} +1 -2
- package/dist/{http-D77Wjrcg.cjs → http-gOkAiIe1.cjs} +173 -104
- package/dist/{inbox-NObX26c9.mjs → inbox-BHxez4d5.mjs} +2 -2
- package/dist/{key-APURZ0ky.mjs → key-D8IMQ72M.mjs} +2 -2
- package/dist/{kv-cache-Bg4utAuv.cjs → kv-cache-Alm5UmJa.cjs} +59 -17
- package/dist/{kv-cache-C69dzgQi.mjs → kv-cache-Blr9nDDp.mjs} +1 -1
- package/dist/{kv-cache-LBCwHNLu.js → kv-cache-CkXgU-ak.js} +60 -18
- package/dist/{ld-BoLqbDaL.mjs → ld-CVxxE1Pp.mjs} +3 -3
- package/dist/{metrics-DBSl8wEr.mjs → metrics-SYIZHFf_.mjs} +1 -1
- package/dist/{middleware-BQUQ0pPl.cjs → middleware-4yPyyoF5.cjs} +237 -99
- package/dist/{middleware-CZ8tI1qL.mjs → middleware-CfskJGfV.mjs} +81 -41
- package/dist/{middleware-DAqo2eOY.mjs → middleware-D3JWfiCu.mjs} +1 -1
- package/dist/{middleware-CRd1JmuL.js → middleware-DhN7c65B.js} +238 -100
- package/dist/{mod-LmD03uRA.d.ts → mod-CDFFESfL.d.ts} +1 -1
- package/dist/{mod-Bid79LFU.d.cts → mod-C_gFmeNq.d.cts} +1 -1
- package/dist/{mod-Co1M3Ux0.d.cts → mod-DDVkmZ_h.d.cts} +2 -8
- package/dist/{mod-B6PwkUMU.d.cts → mod-DDjtbkZO.d.cts} +1 -1
- package/dist/{mod-Br8yJNcE.d.ts → mod-DMjhnFbg.d.ts} +2 -8
- package/dist/{mod-TLEvgT1i.d.ts → mod-HQXFvOHs.d.ts} +1 -1
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +6 -6
- package/dist/mod.d.ts +6 -6
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/client.test.mjs +337 -19
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.d.cts +1 -1
- package/dist/nodeinfo/mod.d.ts +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/otel/exporter.test.mjs +292 -30
- package/dist/otel/mod.cjs +36 -30
- package/dist/otel/mod.d.cts +13 -0
- package/dist/otel/mod.d.ts +13 -0
- package/dist/otel/mod.js +36 -30
- package/dist/{owner-DjKvngeb.mjs → owner-BluLVPf0.mjs} +2 -2
- package/dist/{proof-CWTnYhjS.cjs → proof-CR-HwI0l.cjs} +1 -1
- package/dist/{proof-D2oFOdNA.mjs → proof-Co6nQBVR.mjs} +3 -3
- package/dist/{proof-s9sy44kx.js → proof-Dg0d9QBo.js} +1 -1
- package/dist/{send-xDK5UnL-.mjs → send-D_DnqOFA.mjs} +171 -73
- package/dist/sig/http.test.mjs +528 -2
- package/dist/sig/key.test.mjs +2 -2
- package/dist/sig/ld.test.mjs +2 -2
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/proof.test.mjs +1 -1
- package/dist/{temporal-C6icc5n9.mjs → temporal-B8FUNkXB.mjs} +1 -1
- package/dist/testing/mod.d.mts +26 -0
- package/dist/{types-miAYfiGO.js → types-B1_pRFE_.js} +112 -63
- package/dist/{types-DRPjmHVV.cjs → types-DOvyymM4.cjs} +111 -62
- package/dist/utils/docloader.test.mjs +318 -34
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +8 -8
- package/dist/docloader-yuyzZC9a.mjs +0 -83
|
@@ -9,15 +9,15 @@ import { t as assertRejects } from "../assert_rejects-D1HYAik3.mjs";
|
|
|
9
9
|
import { n as assertInstanceOf, t as assert } from "../assert-CEO06XCw.mjs";
|
|
10
10
|
import { t as assertNotEquals } from "../assert_not_equals-DkVK8oqV.mjs";
|
|
11
11
|
import { t as assertStrictEquals } from "../assert_strict_equals-XEgZAlrj.mjs";
|
|
12
|
-
import { g as recordInboxActivity } from "../metrics-
|
|
13
|
-
import {
|
|
12
|
+
import { g as recordInboxActivity } from "../metrics-SYIZHFf_.mjs";
|
|
13
|
+
import { c as signRequest, u as verifyRequest } from "../http-CSDcThvz.mjs";
|
|
14
14
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
|
|
15
|
-
import {
|
|
15
|
+
import { n as getAuthenticatedDocumentLoader } from "../docloader-D2dr1_6B.mjs";
|
|
16
16
|
import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
|
|
17
|
-
import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-
|
|
18
|
-
import { t as doesActorOwnKey } from "../owner-
|
|
19
|
-
import { i as verifyObject, r as signObject } from "../proof-
|
|
20
|
-
import { g as handleBenchmarkTrigger, i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-
|
|
17
|
+
import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-CVxxE1Pp.mjs";
|
|
18
|
+
import { t as doesActorOwnKey } from "../owner-BluLVPf0.mjs";
|
|
19
|
+
import { i as verifyObject, r as signObject } from "../proof-Co6nQBVR.mjs";
|
|
20
|
+
import { g as handleBenchmarkTrigger, i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-CfskJGfV.mjs";
|
|
21
21
|
import { t as CircuitBreaker } from "../circuit-breaker-D1fxthDI.mjs";
|
|
22
22
|
import { t as markCircuitBreakerLegacySweepDone } from "../circuit-breaker-test-utils-CAdwjgMi.mjs";
|
|
23
23
|
import { configure, reset } from "@logtape/logtape";
|
|
@@ -27,9 +27,9 @@ import { Create, Offer, Person, getTypeId, lookupObject } from "@fedify/vocab";
|
|
|
27
27
|
import { SpanStatusCode, metrics } from "@opentelemetry/api";
|
|
28
28
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
29
29
|
import { FetchError, UrlError, getDocumentLoader } from "@fedify/vocab-runtime";
|
|
30
|
-
import dns from "node:dns/promises";
|
|
31
30
|
import serialize from "json-canon";
|
|
32
31
|
import { DataPointType, MeterProvider as MeterProvider$1, MetricReader } from "@opentelemetry/sdk-metrics";
|
|
32
|
+
import dns from "node:dns/promises";
|
|
33
33
|
//#region ../fixture/src/fixtures/example.com/create.json
|
|
34
34
|
var id$2 = "https://example.com/create";
|
|
35
35
|
var create_default = {
|
|
@@ -94,7 +94,7 @@ var person2_default = {
|
|
|
94
94
|
};
|
|
95
95
|
//#endregion
|
|
96
96
|
//#region src/federation/middleware.test.ts
|
|
97
|
-
const documentLoader = getDocumentLoader();
|
|
97
|
+
const documentLoader = getDocumentLoader({ allowPrivateAddress: true });
|
|
98
98
|
let logtapeLock = Promise.resolve();
|
|
99
99
|
async function withLogtapeLock(fn) {
|
|
100
100
|
const run = logtapeLock.then(fn, fn);
|
|
@@ -649,7 +649,8 @@ test({
|
|
|
649
649
|
const federation = createFederation({
|
|
650
650
|
kv,
|
|
651
651
|
documentLoaderFactory: () => rejectingLoader,
|
|
652
|
-
contextLoaderFactory: () => mockDocumentLoader
|
|
652
|
+
contextLoaderFactory: () => mockDocumentLoader,
|
|
653
|
+
authenticatedDocumentLoaderFactory: (identity) => getAuthenticatedDocumentLoader(identity, { allowPrivateAddress: true })
|
|
653
654
|
});
|
|
654
655
|
let ctx = federation.createContext(new URL("https://example.com:1234/"), 123);
|
|
655
656
|
assertEquals(ctx.data, 123);
|
|
@@ -760,7 +761,7 @@ test({
|
|
|
760
761
|
documentUrl: "https://example.com/auth-check",
|
|
761
762
|
document: true
|
|
762
763
|
});
|
|
763
|
-
assertEquals(await ctx.lookupObject("https://example.com/object"), null);
|
|
764
|
+
assertEquals(await withDnsLookup(() => Promise.reject(/* @__PURE__ */ new Error("Resolver unavailable")), () => ctx.lookupObject("https://example.com/object")), null);
|
|
764
765
|
await assertRejects(() => ctx.sendActivity({ identifier: "handle" }, [], new vocab.Create({})), TypeError, "The activity to send must have at least one actor property.");
|
|
765
766
|
await ctx.sendActivity({ identifier: "handle" }, [], new vocab.Create({ actor: new URL("https://example.com/users/handle") }));
|
|
766
767
|
esm_default.get("https://example.com/object", () => new Response(JSON.stringify({
|
|
@@ -1009,7 +1010,7 @@ test({
|
|
|
1009
1010
|
assertStrictEquals(clone.contextLoader, ctx.contextLoader);
|
|
1010
1011
|
assertStrictEquals(clone.federation, ctx.federation);
|
|
1011
1012
|
});
|
|
1012
|
-
esm_default.get("
|
|
1013
|
+
esm_default.get("path:/.well-known/nodeinfo", (cl) => {
|
|
1013
1014
|
const headers = cl.options.headers ?? {};
|
|
1014
1015
|
assertEquals(new Headers(headers).get("User-Agent"), "CustomUserAgent/1.2.3");
|
|
1015
1016
|
return new Response(JSON.stringify({ links: [{
|
|
@@ -1017,7 +1018,7 @@ test({
|
|
|
1017
1018
|
href: "https://example.com/nodeinfo/2.1"
|
|
1018
1019
|
}] }));
|
|
1019
1020
|
});
|
|
1020
|
-
esm_default.get("
|
|
1021
|
+
esm_default.get("path:/nodeinfo/2.1", (cl) => {
|
|
1021
1022
|
const headers = cl.options.headers ?? {};
|
|
1022
1023
|
assertEquals(new Headers(headers).get("User-Agent"), "CustomUserAgent/1.2.3");
|
|
1023
1024
|
return new Response(JSON.stringify({
|
|
@@ -1038,7 +1039,11 @@ test({
|
|
|
1038
1039
|
kv,
|
|
1039
1040
|
userAgent: "CustomUserAgent/1.2.3"
|
|
1040
1041
|
}).createContext(new URL("https://example.com/"), 123);
|
|
1041
|
-
|
|
1042
|
+
const lookupExample = () => Promise.resolve([{
|
|
1043
|
+
address: "93.184.215.14",
|
|
1044
|
+
family: 4
|
|
1045
|
+
}]);
|
|
1046
|
+
assertEquals(await withDnsLookup(lookupExample, () => ctx.lookupNodeInfo("https://example.com/")), {
|
|
1042
1047
|
software: {
|
|
1043
1048
|
name: "foo",
|
|
1044
1049
|
version: "1.2.3"
|
|
@@ -1050,7 +1055,7 @@ test({
|
|
|
1050
1055
|
localComments: 456
|
|
1051
1056
|
}
|
|
1052
1057
|
});
|
|
1053
|
-
assertEquals(await ctx.lookupNodeInfo("https://example.com/", { parse: "none" }), {
|
|
1058
|
+
assertEquals(await withDnsLookup(lookupExample, () => ctx.lookupNodeInfo("https://example.com/", { parse: "none" })), {
|
|
1054
1059
|
software: {
|
|
1055
1060
|
name: "foo",
|
|
1056
1061
|
version: "1.2.3"
|
|
@@ -1169,6 +1174,408 @@ test({
|
|
|
1169
1174
|
esm_default.hardReset();
|
|
1170
1175
|
}
|
|
1171
1176
|
});
|
|
1177
|
+
test("Federation.fetch() accepts unencoded path colons", async () => {
|
|
1178
|
+
const identifier = "https://pod.example/alice";
|
|
1179
|
+
const federation = createFederation({
|
|
1180
|
+
kv: new MemoryKvStore(),
|
|
1181
|
+
contextLoaderFactory: () => mockDocumentLoader
|
|
1182
|
+
});
|
|
1183
|
+
const dispatched = [];
|
|
1184
|
+
federation.setActorDispatcher("/users/{identifier}", (ctx, id) => {
|
|
1185
|
+
dispatched.push(id);
|
|
1186
|
+
assertEquals(ctx.url.pathname, "/users/https:%2F%2Fpod.example%2Falice");
|
|
1187
|
+
assertEquals(ctx.url.search, "?page=1");
|
|
1188
|
+
assertEquals(ctx.request.url, ctx.url.href);
|
|
1189
|
+
return new vocab.Person({ id: ctx.getActorUri(id) });
|
|
1190
|
+
});
|
|
1191
|
+
const request = new Request("https://local.example/users/https:%2F%2Fpod.example%2Falice?page=1", { headers: { Accept: "application/activity+json" } });
|
|
1192
|
+
const response = await federation.fetch(request, { contextData: void 0 });
|
|
1193
|
+
assertEquals(response.status, 200);
|
|
1194
|
+
assertEquals(dispatched, [identifier]);
|
|
1195
|
+
const canonical = "https://local.example/users/https%3A%2F%2Fpod.example%2Falice";
|
|
1196
|
+
const body = await response.json();
|
|
1197
|
+
assert(body != null && typeof body === "object" && "id" in body);
|
|
1198
|
+
assertEquals(body.id, canonical);
|
|
1199
|
+
const ctx = federation.createContext(request, void 0);
|
|
1200
|
+
assertEquals(ctx.getActorUri(identifier).href, canonical);
|
|
1201
|
+
assertEquals(ctx.parseUri(new URL(canonical)), {
|
|
1202
|
+
type: "actor",
|
|
1203
|
+
identifier
|
|
1204
|
+
});
|
|
1205
|
+
assertEquals(ctx.parseUri(new URL(request.url)), null);
|
|
1206
|
+
assertEquals(ctx.parseUri(new URL(canonical.replace("local.example", "other.example"))), null);
|
|
1207
|
+
});
|
|
1208
|
+
test("Federation.fetch() verifies signatures over unencoded path colons", async (t) => {
|
|
1209
|
+
const identifier = "https://pod.example/alice";
|
|
1210
|
+
const canonical = "https://local.example/users/https%3A%2F%2Fpod.example%2Falice/inbox";
|
|
1211
|
+
const encoded = canonical + "?delivery=1";
|
|
1212
|
+
const normalized = encoded.replace("%3A", ":");
|
|
1213
|
+
for (const spec of ["draft-cavage-http-signatures-12", "rfc9421"]) for (const target of [
|
|
1214
|
+
"encoded",
|
|
1215
|
+
"normalized",
|
|
1216
|
+
"changed after signing"
|
|
1217
|
+
]) await t.step(`${spec}: ${target}`, async () => {
|
|
1218
|
+
const recipients = [];
|
|
1219
|
+
const federation = createFederation({
|
|
1220
|
+
kv: new MemoryKvStore(),
|
|
1221
|
+
documentLoaderFactory: () => mockDocumentLoader,
|
|
1222
|
+
contextLoaderFactory: () => mockDocumentLoader
|
|
1223
|
+
});
|
|
1224
|
+
federation.setActorDispatcher("/users/{identifier}", (ctx, id) => new vocab.Person({ id: ctx.getActorUri(id) })).setKeyPairsDispatcher(() => []);
|
|
1225
|
+
federation.setInboxListeners("/users/{identifier}/inbox").on(vocab.Create, (ctx) => {
|
|
1226
|
+
recipients.push(ctx.recipient);
|
|
1227
|
+
});
|
|
1228
|
+
const signed = await signRequest(new Request(target === "normalized" ? normalized : encoded, {
|
|
1229
|
+
method: "POST",
|
|
1230
|
+
headers: { "Content-Type": "application/activity+json" },
|
|
1231
|
+
body: JSON.stringify(create_default)
|
|
1232
|
+
}), rsaPrivateKey2, rsaPublicKey2.id, { spec });
|
|
1233
|
+
const request = target === "changed after signing" ? new Request(normalized, signed) : signed;
|
|
1234
|
+
const response = await federation.fetch(request, { contextData: void 0 });
|
|
1235
|
+
assertEquals(request.url, target === "encoded" ? encoded : normalized);
|
|
1236
|
+
const valid = target !== "changed after signing";
|
|
1237
|
+
assertEquals(response.status, valid ? 202 : 401);
|
|
1238
|
+
assertEquals(recipients, valid ? [identifier] : []);
|
|
1239
|
+
assertEquals(federation.createContext(new URL(canonical), void 0).getInboxUri(identifier).href, canonical);
|
|
1240
|
+
});
|
|
1241
|
+
});
|
|
1242
|
+
test("RequestContext signature checks", async (t) => {
|
|
1243
|
+
const [tracerProvider, exporter] = createTestTracerProvider();
|
|
1244
|
+
const keyId = rsaPublicKey3.id.href;
|
|
1245
|
+
const ownerId = rsaPublicKey3.ownerId.href;
|
|
1246
|
+
const request = await signRequest(new Request("https://example.com/"), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1247
|
+
function setup(options = {}) {
|
|
1248
|
+
const loads = {
|
|
1249
|
+
key: 0,
|
|
1250
|
+
owner: 0
|
|
1251
|
+
};
|
|
1252
|
+
const loader = async (url, loaderOptions) => {
|
|
1253
|
+
if (url === keyId) loads.key++;
|
|
1254
|
+
else if (url === ownerId) loads.owner++;
|
|
1255
|
+
await options.gate;
|
|
1256
|
+
return await (options.load ?? mockDocumentLoader)(url, loaderOptions);
|
|
1257
|
+
};
|
|
1258
|
+
const federation = createFederation({
|
|
1259
|
+
kv: new MemoryKvStore(),
|
|
1260
|
+
documentLoaderFactory: () => loader,
|
|
1261
|
+
contextLoaderFactory: () => mockDocumentLoader,
|
|
1262
|
+
tracerProvider: options.tracerProvider ?? tracerProvider
|
|
1263
|
+
});
|
|
1264
|
+
return {
|
|
1265
|
+
ctx: federation.createContext(options.request ?? new Request(request), void 0),
|
|
1266
|
+
loads,
|
|
1267
|
+
loader,
|
|
1268
|
+
federation
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
try {
|
|
1272
|
+
for (const mode of [
|
|
1273
|
+
"keys",
|
|
1274
|
+
"owners",
|
|
1275
|
+
"warm owners",
|
|
1276
|
+
"mixed"
|
|
1277
|
+
]) await t.step(`coalesces ${mode}`, async () => {
|
|
1278
|
+
exporter.clear();
|
|
1279
|
+
let release;
|
|
1280
|
+
const { ctx, loads, loader } = setup({ gate: new Promise((resolve) => release = resolve) });
|
|
1281
|
+
if (mode === "warm owners") {
|
|
1282
|
+
release();
|
|
1283
|
+
assert(await ctx.getSignedKey());
|
|
1284
|
+
}
|
|
1285
|
+
const results = Promise.all(Array.from({ length: 8 }, (_, i) => {
|
|
1286
|
+
const options = i % 2 === 0 ? {} : {
|
|
1287
|
+
documentLoader: loader,
|
|
1288
|
+
contextLoader: ctx.contextLoader,
|
|
1289
|
+
tracerProvider
|
|
1290
|
+
};
|
|
1291
|
+
return mode === "keys" || mode === "mixed" && i % 2 === 0 ? ctx.getSignedKey(options) : ctx.getSignedKeyOwner(options);
|
|
1292
|
+
}));
|
|
1293
|
+
release();
|
|
1294
|
+
for (const result of await results) assert(result != null);
|
|
1295
|
+
assertEquals(loads, {
|
|
1296
|
+
key: 1,
|
|
1297
|
+
owner: mode === "keys" ? 0 : 1
|
|
1298
|
+
});
|
|
1299
|
+
assertEquals(exporter.getSpans("http_signatures.verify").length, 1);
|
|
1300
|
+
assert(await ctx.getSignedKey());
|
|
1301
|
+
if (mode !== "keys") assert(await ctx.getSignedKeyOwner());
|
|
1302
|
+
assertEquals(loads, {
|
|
1303
|
+
key: 1,
|
|
1304
|
+
owner: mode === "keys" ? 0 : 1
|
|
1305
|
+
});
|
|
1306
|
+
});
|
|
1307
|
+
await t.step("memoizes null keys", async () => {
|
|
1308
|
+
exporter.clear();
|
|
1309
|
+
const { ctx, loads } = setup({ load: () => Promise.reject(/* @__PURE__ */ new Error("unavailable")) });
|
|
1310
|
+
for (let i = 0; i < 3; i++) {
|
|
1311
|
+
assertEquals(await ctx.getSignedKey(), null);
|
|
1312
|
+
assertEquals(await ctx.getSignedKeyOwner(), null);
|
|
1313
|
+
}
|
|
1314
|
+
assertEquals(loads, {
|
|
1315
|
+
key: 1,
|
|
1316
|
+
owner: 0
|
|
1317
|
+
});
|
|
1318
|
+
assertEquals(exporter.getSpans("http_signatures.verify").length, 1);
|
|
1319
|
+
assert(await ctx.getSignedKey({ documentLoader: mockDocumentLoader }));
|
|
1320
|
+
assert(await ctx.getSignedKeyOwner({ documentLoader: mockDocumentLoader }));
|
|
1321
|
+
});
|
|
1322
|
+
await t.step("coalesces key-owner FetchError results", async () => {
|
|
1323
|
+
const { ctx, loads } = setup({ load: async (url, options) => {
|
|
1324
|
+
if (url === ownerId) throw new FetchError(new URL(url), "HTTP 401: Unauthorized");
|
|
1325
|
+
return await mockDocumentLoader(url, options);
|
|
1326
|
+
} });
|
|
1327
|
+
assert(await ctx.getSignedKey());
|
|
1328
|
+
assertEquals(await Promise.all(Array.from({ length: 8 }, () => ctx.getSignedKeyOwner())), Array(8).fill(null));
|
|
1329
|
+
assertEquals(await ctx.getSignedKeyOwner(), null);
|
|
1330
|
+
assertEquals(loads, {
|
|
1331
|
+
key: 1,
|
|
1332
|
+
owner: 1
|
|
1333
|
+
});
|
|
1334
|
+
assert(await ctx.getSignedKeyOwner({ documentLoader: mockDocumentLoader }));
|
|
1335
|
+
});
|
|
1336
|
+
await t.step("memoizes unsigned requests", async () => {
|
|
1337
|
+
exporter.clear();
|
|
1338
|
+
const { ctx, loads } = setup({ request: new Request("https://example.com/") });
|
|
1339
|
+
for (let i = 0; i < 3; i++) {
|
|
1340
|
+
assertEquals(await ctx.getSignedKey(), null);
|
|
1341
|
+
assertEquals(await ctx.getSignedKeyOwner(), null);
|
|
1342
|
+
}
|
|
1343
|
+
assertEquals(loads, {
|
|
1344
|
+
key: 0,
|
|
1345
|
+
owner: 0
|
|
1346
|
+
});
|
|
1347
|
+
assertEquals(exporter.getSpans("http_signatures.verify").length, 1);
|
|
1348
|
+
});
|
|
1349
|
+
await t.step("memoizes null owners without trusting a key's owner claim", async () => {
|
|
1350
|
+
const { ctx, loads } = setup({ load: async (url, options) => url === ownerId ? {
|
|
1351
|
+
documentUrl: url,
|
|
1352
|
+
contextUrl: null,
|
|
1353
|
+
document: {
|
|
1354
|
+
...person2_default,
|
|
1355
|
+
publicKey: []
|
|
1356
|
+
}
|
|
1357
|
+
} : await mockDocumentLoader(url, options) });
|
|
1358
|
+
assert(await ctx.getSignedKey());
|
|
1359
|
+
assertEquals(await Promise.all(Array.from({ length: 8 }, () => ctx.getSignedKeyOwner())), Array(8).fill(null));
|
|
1360
|
+
for (let i = 0; i < 3; i++) assertEquals(await ctx.getSignedKeyOwner(), null);
|
|
1361
|
+
assertEquals(loads, {
|
|
1362
|
+
key: 1,
|
|
1363
|
+
owner: 1
|
|
1364
|
+
});
|
|
1365
|
+
assert(await ctx.getSignedKeyOwner({ documentLoader: mockDocumentLoader }));
|
|
1366
|
+
assert(await ctx.getSignedKeyOwner());
|
|
1367
|
+
});
|
|
1368
|
+
await t.step("isolates concurrent document loaders before success", async () => {
|
|
1369
|
+
const { ctx, loads } = setup();
|
|
1370
|
+
let otherLoads = 0;
|
|
1371
|
+
const otherLoader = async (url, options) => {
|
|
1372
|
+
otherLoads++;
|
|
1373
|
+
if (url === ownerId) return {
|
|
1374
|
+
documentUrl: url,
|
|
1375
|
+
contextUrl: null,
|
|
1376
|
+
document: {
|
|
1377
|
+
...person2_default,
|
|
1378
|
+
name: "Other view"
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
return await mockDocumentLoader(url, options);
|
|
1382
|
+
};
|
|
1383
|
+
const [first, second] = await Promise.all([ctx.getSignedKeyOwner(), ctx.getSignedKeyOwner({ documentLoader: otherLoader })]);
|
|
1384
|
+
assertEquals(first?.name, "Jane Doe");
|
|
1385
|
+
assertEquals(second?.name, "Other view");
|
|
1386
|
+
const cached = await ctx.getSignedKeyOwner();
|
|
1387
|
+
assert(cached === first || cached === second);
|
|
1388
|
+
assertStrictEquals(await ctx.getSignedKeyOwner({ documentLoader: otherLoader }), cached);
|
|
1389
|
+
assertEquals(loads, {
|
|
1390
|
+
key: 1,
|
|
1391
|
+
owner: 1
|
|
1392
|
+
});
|
|
1393
|
+
assertEquals(otherLoads, 2);
|
|
1394
|
+
});
|
|
1395
|
+
await t.step("preserves in-flight null while another loader succeeds", async () => {
|
|
1396
|
+
exporter.clear();
|
|
1397
|
+
let release;
|
|
1398
|
+
const gate = new Promise((resolve) => release = resolve);
|
|
1399
|
+
const { ctx, loads } = setup({ load: async () => {
|
|
1400
|
+
await gate;
|
|
1401
|
+
throw new TypeError("fetch failed");
|
|
1402
|
+
} });
|
|
1403
|
+
const failed = Promise.all([ctx.getSignedKey(), ctx.getSignedKeyOwner()]);
|
|
1404
|
+
const owner = await ctx.getSignedKeyOwner({ documentLoader: mockDocumentLoader });
|
|
1405
|
+
assert(owner);
|
|
1406
|
+
release();
|
|
1407
|
+
assertEquals(await failed, [null, null]);
|
|
1408
|
+
assertStrictEquals(await ctx.getSignedKeyOwner(), owner);
|
|
1409
|
+
assert(await ctx.getSignedKey());
|
|
1410
|
+
assertEquals(loads, {
|
|
1411
|
+
key: 1,
|
|
1412
|
+
owner: 0
|
|
1413
|
+
});
|
|
1414
|
+
assertEquals(exporter.getSpans("http_signatures.verify").length, 2);
|
|
1415
|
+
});
|
|
1416
|
+
await t.step("isolates context loaders", async () => {
|
|
1417
|
+
exporter.clear();
|
|
1418
|
+
const { ctx, loads } = setup();
|
|
1419
|
+
const otherContextLoader = (url, options) => mockDocumentLoader(url, options);
|
|
1420
|
+
await Promise.all([ctx.getSignedKeyOwner(), ctx.getSignedKeyOwner({ contextLoader: otherContextLoader })]);
|
|
1421
|
+
assertEquals(loads, {
|
|
1422
|
+
key: 2,
|
|
1423
|
+
owner: 2
|
|
1424
|
+
});
|
|
1425
|
+
assertEquals(exporter.getSpans("http_signatures.verify").length, 2);
|
|
1426
|
+
});
|
|
1427
|
+
await t.step("shares work across tracer providers", async () => {
|
|
1428
|
+
exporter.clear();
|
|
1429
|
+
const [otherProvider, otherExporter] = createTestTracerProvider();
|
|
1430
|
+
try {
|
|
1431
|
+
const { ctx, loads } = setup();
|
|
1432
|
+
await Promise.all([
|
|
1433
|
+
ctx.getSignedKeyOwner(),
|
|
1434
|
+
ctx.getSignedKeyOwner({ tracerProvider: otherProvider }),
|
|
1435
|
+
ctx.getSignedKey(),
|
|
1436
|
+
ctx.getSignedKey({ tracerProvider: otherProvider })
|
|
1437
|
+
]);
|
|
1438
|
+
assert(await ctx.getSignedKeyOwner({ tracerProvider: otherProvider }));
|
|
1439
|
+
assertEquals(loads, {
|
|
1440
|
+
key: 1,
|
|
1441
|
+
owner: 1
|
|
1442
|
+
});
|
|
1443
|
+
assertEquals(exporter.getSpans("http_signatures.verify").length, 1);
|
|
1444
|
+
assertEquals(otherExporter.getSpans("http_signatures.verify").length, 0);
|
|
1445
|
+
} finally {
|
|
1446
|
+
await otherProvider.shutdown();
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
await t.step("reuses success with a fresh authorization loader", async () => {
|
|
1450
|
+
exporter.clear();
|
|
1451
|
+
const { ctx, loads } = setup();
|
|
1452
|
+
let freshLoads = 0;
|
|
1453
|
+
const freshLoader = (url, options) => {
|
|
1454
|
+
freshLoads++;
|
|
1455
|
+
return mockDocumentLoader(url, options);
|
|
1456
|
+
};
|
|
1457
|
+
const key = await ctx.getSignedKey({ documentLoader: freshLoader });
|
|
1458
|
+
assert(key);
|
|
1459
|
+
assertStrictEquals(await ctx.getSignedKey(), key);
|
|
1460
|
+
const owner = await ctx.getSignedKeyOwner();
|
|
1461
|
+
assert(owner);
|
|
1462
|
+
assertStrictEquals(await ctx.getSignedKeyOwner({ documentLoader: freshLoader }), owner);
|
|
1463
|
+
assertEquals(loads, {
|
|
1464
|
+
key: 0,
|
|
1465
|
+
owner: 1
|
|
1466
|
+
});
|
|
1467
|
+
assertEquals(freshLoads, 1);
|
|
1468
|
+
assertEquals(exporter.getSpans("http_signatures.verify").length, 1);
|
|
1469
|
+
});
|
|
1470
|
+
await t.step("retains a transient owner lookup failure as null", async () => {
|
|
1471
|
+
let fail = true;
|
|
1472
|
+
const { ctx, loads } = setup({ load: async (url, options) => {
|
|
1473
|
+
if (url === ownerId && fail) throw new TypeError("fetch failed");
|
|
1474
|
+
return await mockDocumentLoader(url, options);
|
|
1475
|
+
} });
|
|
1476
|
+
assert(await ctx.getSignedKey());
|
|
1477
|
+
assertEquals(await ctx.getSignedKeyOwner(), null);
|
|
1478
|
+
fail = false;
|
|
1479
|
+
assertEquals(await ctx.getSignedKeyOwner(), null);
|
|
1480
|
+
assertEquals(loads, {
|
|
1481
|
+
key: 1,
|
|
1482
|
+
owner: 1
|
|
1483
|
+
});
|
|
1484
|
+
assert(await ctx.getSignedKeyOwner({ documentLoader: mockDocumentLoader }));
|
|
1485
|
+
});
|
|
1486
|
+
await t.step("rechecks an owner after another loader verifies the key", async () => {
|
|
1487
|
+
let fail = true;
|
|
1488
|
+
const { ctx, loads } = setup({ load: async (url, options) => {
|
|
1489
|
+
if (url === keyId && fail) throw new TypeError("fetch failed");
|
|
1490
|
+
return await mockDocumentLoader(url, options);
|
|
1491
|
+
} });
|
|
1492
|
+
assertEquals(await ctx.getSignedKeyOwner(), null);
|
|
1493
|
+
fail = false;
|
|
1494
|
+
assertEquals(await ctx.getSignedKeyOwner(), null);
|
|
1495
|
+
assertEquals(loads, {
|
|
1496
|
+
key: 1,
|
|
1497
|
+
owner: 0
|
|
1498
|
+
});
|
|
1499
|
+
assert(await ctx.getSignedKey({ documentLoader: mockDocumentLoader }));
|
|
1500
|
+
assertEquals((await ctx.getSignedKeyOwner())?.id?.href, ownerId);
|
|
1501
|
+
assertEquals(loads, {
|
|
1502
|
+
key: 1,
|
|
1503
|
+
owner: 1
|
|
1504
|
+
});
|
|
1505
|
+
});
|
|
1506
|
+
await t.step("retries unexpected key rejections from a rejection handler", async () => {
|
|
1507
|
+
exporter.clear();
|
|
1508
|
+
const failure = /* @__PURE__ */ new Error("transient context loader failure");
|
|
1509
|
+
let rejectContext = true;
|
|
1510
|
+
const contextLoader = async (url, options) => {
|
|
1511
|
+
if (rejectContext) throw failure;
|
|
1512
|
+
return await mockDocumentLoader(url, options);
|
|
1513
|
+
};
|
|
1514
|
+
const { ctx, loads } = setup();
|
|
1515
|
+
const options = { contextLoader };
|
|
1516
|
+
const failedKeys = Promise.allSettled([
|
|
1517
|
+
ctx.getSignedKey(options),
|
|
1518
|
+
ctx.getSignedKeyOwner(options),
|
|
1519
|
+
ctx.getSignedKey(options)
|
|
1520
|
+
]);
|
|
1521
|
+
const recovery = ctx.getSignedKey(options).catch(() => {
|
|
1522
|
+
rejectContext = false;
|
|
1523
|
+
return ctx.getSignedKey(options);
|
|
1524
|
+
});
|
|
1525
|
+
for (const result of await failedKeys) assertEquals(result.status, "rejected");
|
|
1526
|
+
assert(await recovery);
|
|
1527
|
+
assertEquals(loads, {
|
|
1528
|
+
key: 2,
|
|
1529
|
+
owner: 0
|
|
1530
|
+
});
|
|
1531
|
+
assertEquals(exporter.getSpans("http_signatures.verify").length, 2);
|
|
1532
|
+
});
|
|
1533
|
+
await t.step("retries owner rejections while retaining the verified key", async () => {
|
|
1534
|
+
exporter.clear();
|
|
1535
|
+
let rejectContext = false;
|
|
1536
|
+
const contextLoader = async (url, options) => {
|
|
1537
|
+
if (rejectContext) throw new Error("transient owner context failure");
|
|
1538
|
+
return await mockDocumentLoader(url, options);
|
|
1539
|
+
};
|
|
1540
|
+
const { ctx, loads } = setup();
|
|
1541
|
+
const options = { contextLoader };
|
|
1542
|
+
assert(await ctx.getSignedKey(options));
|
|
1543
|
+
assertEquals(loads, {
|
|
1544
|
+
key: 1,
|
|
1545
|
+
owner: 0
|
|
1546
|
+
});
|
|
1547
|
+
rejectContext = true;
|
|
1548
|
+
const failures = await Promise.allSettled([ctx.getSignedKeyOwner(options), ctx.getSignedKeyOwner(options)]);
|
|
1549
|
+
for (const result of failures) assertEquals(result.status, "rejected");
|
|
1550
|
+
assertEquals(loads, {
|
|
1551
|
+
key: 1,
|
|
1552
|
+
owner: 1
|
|
1553
|
+
});
|
|
1554
|
+
rejectContext = false;
|
|
1555
|
+
assert(await ctx.getSignedKeyOwner(options));
|
|
1556
|
+
assertEquals(loads, {
|
|
1557
|
+
key: 1,
|
|
1558
|
+
owner: 2
|
|
1559
|
+
});
|
|
1560
|
+
assertEquals(exporter.getSpans("http_signatures.verify").length, 1);
|
|
1561
|
+
});
|
|
1562
|
+
await t.step("keeps clones and separate requests independent", async () => {
|
|
1563
|
+
exporter.clear();
|
|
1564
|
+
const { ctx, loads, federation } = setup();
|
|
1565
|
+
assert(await ctx.getSignedKeyOwner());
|
|
1566
|
+
const clone = ctx.clone(void 0);
|
|
1567
|
+
const separate = federation.createContext(new Request(request), void 0);
|
|
1568
|
+
await Promise.all([clone.getSignedKeyOwner(), separate.getSignedKeyOwner()]);
|
|
1569
|
+
assertEquals(loads, {
|
|
1570
|
+
key: 3,
|
|
1571
|
+
owner: 3
|
|
1572
|
+
});
|
|
1573
|
+
assertEquals(exporter.getSpans("http_signatures.verify").length, 3);
|
|
1574
|
+
});
|
|
1575
|
+
} finally {
|
|
1576
|
+
await tracerProvider.shutdown();
|
|
1577
|
+
}
|
|
1578
|
+
});
|
|
1172
1579
|
test("Federation.fetch() [rfc9421] multiple POST signatures", async (t) => {
|
|
1173
1580
|
for (const validSecond of [true, false]) await t.step(validSecond ? "valid second signature" : "both signatures invalid", async () => {
|
|
1174
1581
|
const inbox = [];
|
|
@@ -1212,7 +1619,7 @@ test("Federation.fetch()", async (t) => {
|
|
|
1212
1619
|
kv,
|
|
1213
1620
|
documentLoaderFactory: () => mockDocumentLoader,
|
|
1214
1621
|
authenticatedDocumentLoaderFactory(identity) {
|
|
1215
|
-
const docLoader = getAuthenticatedDocumentLoader(identity);
|
|
1622
|
+
const docLoader = getAuthenticatedDocumentLoader(identity, { allowPrivateAddress: true });
|
|
1216
1623
|
return (url) => {
|
|
1217
1624
|
if (new URL(url).host === "example.com") return docLoader(url);
|
|
1218
1625
|
return mockDocumentLoader(url);
|
|
@@ -1663,7 +2070,7 @@ test("Federation.setInboxListeners()", async (t) => {
|
|
|
1663
2070
|
kv,
|
|
1664
2071
|
documentLoaderFactory: () => mockDocumentLoader,
|
|
1665
2072
|
authenticatedDocumentLoaderFactory(identity) {
|
|
1666
|
-
const docLoader = getAuthenticatedDocumentLoader(identity);
|
|
2073
|
+
const docLoader = getAuthenticatedDocumentLoader(identity, { allowPrivateAddress: true });
|
|
1667
2074
|
return (url) => {
|
|
1668
2075
|
const urlObj = new URL(url);
|
|
1669
2076
|
authenticatedRequests.push([url, identity.keyId.href]);
|
|
@@ -1986,7 +2393,7 @@ test("Federation.setInboxListeners()", async (t) => {
|
|
|
1986
2393
|
kv,
|
|
1987
2394
|
documentLoaderFactory: () => mockDocumentLoader,
|
|
1988
2395
|
authenticatedDocumentLoaderFactory(identity) {
|
|
1989
|
-
const docLoader = getAuthenticatedDocumentLoader(identity);
|
|
2396
|
+
const docLoader = getAuthenticatedDocumentLoader(identity, { allowPrivateAddress: true });
|
|
1990
2397
|
return (url) => {
|
|
1991
2398
|
if (new URL(url).host === "example.com") return docLoader(url);
|
|
1992
2399
|
return mockDocumentLoader(url);
|
|
@@ -2199,10 +2606,11 @@ test("Federation.setOutboxListeners()", async (t) => {
|
|
|
2199
2606
|
sinks: ["buffer"]
|
|
2200
2607
|
}]
|
|
2201
2608
|
});
|
|
2202
|
-
const federation =
|
|
2609
|
+
const federation = new FederationImpl({
|
|
2203
2610
|
kv,
|
|
2204
2611
|
documentLoaderFactory: () => mockDocumentLoader
|
|
2205
2612
|
});
|
|
2613
|
+
federation.allowPrivateAddress = true;
|
|
2206
2614
|
federation.setActorDispatcher("/users/{identifier}", (_ctx, identifier) => identifier === "john" ? new vocab.Person({}) : null).setKeyPairsDispatcher(() => [{
|
|
2207
2615
|
privateKey: rsaPrivateKey2,
|
|
2208
2616
|
publicKey: rsaPublicKey2.publicKey
|
|
@@ -2305,10 +2713,11 @@ test("Federation.setOutboxListeners()", async (t) => {
|
|
|
2305
2713
|
sinks: ["buffer"]
|
|
2306
2714
|
}]
|
|
2307
2715
|
});
|
|
2308
|
-
const federation =
|
|
2716
|
+
const federation = new FederationImpl({
|
|
2309
2717
|
kv,
|
|
2310
2718
|
documentLoaderFactory: () => mockDocumentLoader
|
|
2311
2719
|
});
|
|
2720
|
+
federation.allowPrivateAddress = true;
|
|
2312
2721
|
federation.setActorDispatcher("/{identifier}", (_ctx, identifier) => identifier === "person2" ? new vocab.Person({}) : null).setKeyPairsDispatcher(() => [{
|
|
2313
2722
|
privateKey: rsaPrivateKey2,
|
|
2314
2723
|
publicKey: rsaPublicKey2.publicKey
|
|
@@ -2540,7 +2949,7 @@ test("FederationImpl.sendActivity()", async (t) => {
|
|
|
2540
2949
|
});
|
|
2541
2950
|
const federation = new FederationImpl({
|
|
2542
2951
|
kv: new MemoryKvStore(),
|
|
2543
|
-
|
|
2952
|
+
allowPrivateAddress: true
|
|
2544
2953
|
});
|
|
2545
2954
|
const context = federation.createContext(new URL("https://example.com/"));
|
|
2546
2955
|
await t.step("success", async () => {
|
|
@@ -6128,7 +6537,7 @@ test("ContextImpl.sendActivity()", async (t) => {
|
|
|
6128
6537
|
const kv = new MemoryKvStore();
|
|
6129
6538
|
const federation = new FederationImpl({
|
|
6130
6539
|
kv,
|
|
6131
|
-
|
|
6540
|
+
allowPrivateAddress: true
|
|
6132
6541
|
});
|
|
6133
6542
|
federation.setActorDispatcher("/{identifier}", async (ctx, identifier) => {
|
|
6134
6543
|
if (identifier !== "1") return null;
|
|
@@ -6251,11 +6660,13 @@ test("ContextImpl.sendActivity()", async (t) => {
|
|
|
6251
6660
|
});
|
|
6252
6661
|
await t.step("records recipient span attributes correctly", async () => {
|
|
6253
6662
|
const [tracerProvider, exporter] = createTestTracerProvider();
|
|
6254
|
-
const
|
|
6663
|
+
const federation3 = new FederationImpl({
|
|
6255
6664
|
kv,
|
|
6256
6665
|
contextLoaderFactory: () => mockDocumentLoader,
|
|
6257
6666
|
tracerProvider
|
|
6258
|
-
})
|
|
6667
|
+
});
|
|
6668
|
+
federation3.allowPrivateAddress = true;
|
|
6669
|
+
const ctx = federation3.createContext(new URL("https://example.com/"), void 0);
|
|
6259
6670
|
const activity = new vocab.Create({
|
|
6260
6671
|
id: new URL("https://example.com/activity/telemetry"),
|
|
6261
6672
|
actor: new URL("https://example.com/person"),
|
|
@@ -6289,7 +6700,7 @@ test("ContextImpl.sendActivity()", async (t) => {
|
|
|
6289
6700
|
};
|
|
6290
6701
|
const federation2 = new FederationImpl({
|
|
6291
6702
|
kv,
|
|
6292
|
-
|
|
6703
|
+
allowPrivateAddress: true,
|
|
6293
6704
|
queue
|
|
6294
6705
|
});
|
|
6295
6706
|
federation2.setActorDispatcher("/{identifier}", async (ctx, identifier) => {
|
|
@@ -6460,7 +6871,7 @@ test("ContextImpl.sendActivity()", async (t) => {
|
|
|
6460
6871
|
const asyncEnqueued = [];
|
|
6461
6872
|
const fed = new FederationImpl({
|
|
6462
6873
|
kv,
|
|
6463
|
-
|
|
6874
|
+
allowPrivateAddress: true,
|
|
6464
6875
|
queue: {
|
|
6465
6876
|
enqueue(message) {
|
|
6466
6877
|
return new Promise((resolve) => {
|
|
@@ -6687,10 +7098,10 @@ test({
|
|
|
6687
7098
|
const signedInvite = await signObject(new vocab.Invite({ actor: new URL("https://example.com/person2") }), ed25519PrivateKey, ed25519Multikey.id);
|
|
6688
7099
|
assert(await ctx.routeActivity("id", signedInvite));
|
|
6689
7100
|
assertEquals(activities, [[null, signedOffer], ["id", signedInvite]]);
|
|
6690
|
-
assertFalse(await ctx.routeActivity(null, new vocab.Create({
|
|
7101
|
+
assertFalse(await withDnsLookup(() => Promise.reject(/* @__PURE__ */ new Error("Resolver unavailable")), () => ctx.routeActivity(null, new vocab.Create({
|
|
6691
7102
|
id: new URL("https://example.com/not-found"),
|
|
6692
7103
|
actor: new URL("https://example.com/person")
|
|
6693
|
-
})));
|
|
7104
|
+
}))));
|
|
6694
7105
|
assertEquals(activities, [[null, signedOffer], ["id", signedInvite]]);
|
|
6695
7106
|
assertFalse(await ctx.routeActivity(null, new vocab.Create({
|
|
6696
7107
|
id: new URL("https://example.com/person"),
|
|
@@ -7196,7 +7607,7 @@ test("InboxContextImpl.forwardActivity()", async (t) => {
|
|
|
7196
7607
|
const kv = new MemoryKvStore();
|
|
7197
7608
|
const federation = new FederationImpl({
|
|
7198
7609
|
kv,
|
|
7199
|
-
|
|
7610
|
+
allowPrivateAddress: true
|
|
7200
7611
|
});
|
|
7201
7612
|
await t.step("skip", async () => {
|
|
7202
7613
|
await new InboxContextImpl(null, {
|
|
@@ -7319,6 +7730,7 @@ test("InboxContextImpl.forwardActivity()", async (t) => {
|
|
|
7319
7730
|
contextLoaderFactory: () => mockDocumentLoader,
|
|
7320
7731
|
tracerProvider
|
|
7321
7732
|
});
|
|
7733
|
+
federationWithTracing.allowPrivateAddress = true;
|
|
7322
7734
|
await new InboxContextImpl(null, await signJsonLd({
|
|
7323
7735
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
7324
7736
|
"type": "Create",
|
|
@@ -7511,5 +7923,19 @@ test("Federation.fetch() bounds inbox bodies before dispatch", async () => {
|
|
|
7511
7923
|
}), { contextData: void 0 })).status, 202);
|
|
7512
7924
|
assert(dispatched);
|
|
7513
7925
|
});
|
|
7926
|
+
/**
|
|
7927
|
+
* Runs the given function while `node:dns/promises`' `lookup()`, which the
|
|
7928
|
+
* default public fetch resolves hostnames with, is replaced, so that no test
|
|
7929
|
+
* depends on the real DNS.
|
|
7930
|
+
*/
|
|
7931
|
+
async function withDnsLookup(lookup, fn) {
|
|
7932
|
+
const originalLookup = dns.lookup;
|
|
7933
|
+
dns.lookup = lookup;
|
|
7934
|
+
try {
|
|
7935
|
+
return await fn();
|
|
7936
|
+
} finally {
|
|
7937
|
+
dns.lookup = originalLookup;
|
|
7938
|
+
}
|
|
7939
|
+
}
|
|
7514
7940
|
//#endregion
|
|
7515
7941
|
export {};
|
package/dist/federation/mod.cjs
CHANGED
|
@@ -2,7 +2,7 @@ const { Temporal } = require("@js-temporal/polyfill");
|
|
|
2
2
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
4
|
require("../chunk-DDcVe30Y.cjs");
|
|
5
|
-
const require_middleware = require("../middleware-
|
|
5
|
+
const require_middleware = require("../middleware-4yPyyoF5.cjs");
|
|
6
6
|
let _logtape_logtape = require("@logtape/logtape");
|
|
7
7
|
let _fedify_uri_template = require("@fedify/uri-template");
|
|
8
8
|
let es_toolkit = require("es-toolkit");
|