@fedify/fedify 2.4.0-dev.2077 → 2.4.0-dev.2093
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-i3jxVe9m.mjs → builder-Ld4Y9ymR.mjs} +34 -3
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-Cayy-ECj.mjs → compound-proof-CTx_lewQ.mjs} +191 -13
- package/dist/{deno-Djkzd-G_.mjs → deno-8t8k_6Yv.mjs} +1 -1
- package/dist/{docloader-dA0RswUV.mjs → docloader-DJKcsAiF.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 +3 -3
- package/dist/federation/handler.test.mjs +5 -5
- 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 +7 -7
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +254 -5
- package/dist/federation/portable-gateway-keys.test.mjs +72 -11
- package/dist/federation/portable-inbox.test.mjs +513 -6
- package/dist/federation/portable.test.mjs +182 -4
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +89 -1
- package/dist/{federation-CFd0Jhhv.cjs → federation-aSHO04ob.cjs} +472 -74
- package/dist/{http-CCJ97Jql.mjs → http-B2ftY0Ha.mjs} +4 -4
- package/dist/{http-Dvy_IwRa.cjs → http-DSaSAN7X.cjs} +1609 -1457
- package/dist/{http-aqh3yFdM.js → http-DyT7DP1Y.js} +1518 -1390
- package/dist/{key-IxTTC8Yf.mjs → key-Dwm4VswT.mjs} +39 -99
- package/dist/{kv-cache-BP69NV0P.cjs → kv-cache-BSJAlP4C.cjs} +1 -1
- package/dist/{kv-cache-Bwwbh-xR.mjs → kv-cache-D0yYukqk.mjs} +1 -1
- package/dist/{kv-cache-Y6SDRSX6.js → kv-cache-DmroAz3M.js} +1 -1
- package/dist/{ld-l0B9nREb.mjs → ld-BYZOoDzZ.mjs} +3 -3
- package/dist/{metrics-D1p6hv4z.mjs → metrics-B7YwKTFr.mjs} +1 -1
- package/dist/{middleware-BDjCUHMS.mjs → middleware-BNeqKSsL.mjs} +1 -1
- package/dist/{middleware-SSfryVzY.js → middleware-Blq0882d.js} +480 -82
- package/dist/{middleware-BMgzqBtr.mjs → middleware-E_0IH72m.mjs} +278 -80
- package/dist/{mod-HDSvS-ww.d.cts → mod-C8LieBzF.d.cts} +16 -3
- package/dist/{mod-C9WSpZWm.d.ts → mod-Coacostr.d.ts} +16 -3
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +1 -1
- package/dist/mod.d.ts +1 -1
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-DGbYmXqU.mjs → owner-Cgx2Dc7I.mjs} +5 -4
- package/dist/{owner-jH1z_YfD.js → owner-DAGSC5gX.js} +2 -2
- package/dist/{owner-BpOxL5PG.cjs → owner-szIZl4qI.cjs} +2 -2
- package/dist/{portable-key-CCwJGSsU.mjs → portable-key-Cn1WJSN1.mjs} +60 -41
- package/dist/portable-key-id-Bva5iT3W.mjs +136 -0
- package/dist/{proof-CVL7JnZd.mjs → proof-DVXC6nYz.mjs} +59 -18
- package/dist/{send-Ejy1jOz9.mjs → send-D-hOvYKy.mjs} +3 -3
- package/dist/sig/compound-proof-discovery.test.mjs +1 -1
- package/dist/sig/compound-proof-producer.test.mjs +1 -1
- package/dist/sig/compound-proof-verification.test.mjs +160 -3
- 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.d.cts +1 -1
- package/dist/sig/mod.d.ts +1 -1
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +69 -5
- package/dist/sig/portable-key.test.mjs +176 -10
- package/dist/sig/proof.test.mjs +107 -4
- package/dist/{sig-Dpy1B1Ad.js → sig-B5Ell7pO.js} +19 -3
- package/dist/{sig-xoqy3ATq.cjs → sig-CXOKN4Br.cjs} +19 -3
- package/dist/{temporal-DNJ1cNpr.mjs → temporal-CqpKGEPk.mjs} +1 -1
- package/dist/testing/kv.test.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 +7 -7
|
@@ -7,13 +7,15 @@ import { t as assertInstanceOf } from "../assert_instance_of-mrXWWNY-.mjs";
|
|
|
7
7
|
import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
|
|
8
8
|
import "../std__assert-DvMDRPes.mjs";
|
|
9
9
|
import { t as assertStrictEquals } from "../assert_strict_equals-CYvBmkpC.mjs";
|
|
10
|
-
import {
|
|
11
|
-
import { s as
|
|
10
|
+
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
11
|
+
import { i as signObject, s as verifyPortableObjectProof } from "../proof-DVXC6nYz.mjs";
|
|
12
|
+
import { s as signRequest } from "../http-B2ftY0Ha.mjs";
|
|
12
13
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
13
|
-
import { o as createFederation,
|
|
14
|
+
import { o as createFederation, y as PORTABLE_OBJECT_CONTENT_TYPE } from "../middleware-E_0IH72m.mjs";
|
|
15
|
+
import { configure, reset } from "@logtape/logtape";
|
|
14
16
|
import { Collection, Create, Note, Object as Object$1, Person, Tombstone, lookupObject } from "@fedify/vocab";
|
|
15
17
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
16
|
-
import { exportDidKey, formatIri, parseIri } from "@fedify/vocab-runtime";
|
|
18
|
+
import { exportDidKey, formatIri, parseIri, toCompatibleEf61Id } from "@fedify/vocab-runtime";
|
|
17
19
|
//#region src/federation/portable.test.ts
|
|
18
20
|
const did = await exportDidKey(ed25519PublicKey.publicKey);
|
|
19
21
|
const keyId = new URL(`${did}#${did.substring(8)}`);
|
|
@@ -276,6 +278,73 @@ test("Federation.fetch() authorizes portable object requests", async (t) => {
|
|
|
276
278
|
assertEquals(dispatched, 1);
|
|
277
279
|
});
|
|
278
280
|
});
|
|
281
|
+
test("Federation.fetch() serves portable objects with compatible IDs", async (t) => {
|
|
282
|
+
const federation = createTestFederation();
|
|
283
|
+
const compatible = (path, authority = did, origin = "example.com") => new URL(`https://${origin}/.well-known/apgateway/${authority}${path}`);
|
|
284
|
+
const objects = {
|
|
285
|
+
"compatible": () => sign(new Note({ id: compatible("/objects/compatible") })),
|
|
286
|
+
"other-gateway": () => sign(new Note({ id: compatible("/objects/other-gateway", did, "other.example") })),
|
|
287
|
+
"percent-encoded": () => sign(new Note({ id: compatible("/objects/percent-encoded", did.replaceAll(":", "%3A")) })),
|
|
288
|
+
"other-did": () => sign(new Note({ id: compatible("/objects/other-did", otherDid) }), otherKeyPair.privateKey, otherKeyId),
|
|
289
|
+
"other-path": () => sign(new Note({ id: compatible("/objects/else") })),
|
|
290
|
+
"fragment": () => sign(new Note({ id: compatible("/objects/fragment#note") })),
|
|
291
|
+
"malformed": () => sign(new Note({ id: new URL(compatible("/objects/malformed").href + "?@gateway=https%3A%2F%2Fexample.com") })),
|
|
292
|
+
"ordinary": () => sign(new Note({ id: new URL("https://example.com/objects/ordinary") })),
|
|
293
|
+
"unsigned": () => Promise.resolve(new Note({ id: compatible("/objects/unsigned") })),
|
|
294
|
+
"wrong-did": () => sign(new Note({ id: compatible("/objects/wrong-did") }), otherKeyPair.privateKey, otherKeyId),
|
|
295
|
+
"mutated-id": async () => {
|
|
296
|
+
const signed = await sign(new Note({ id: compatible("/objects/original") }));
|
|
297
|
+
const object = await Note.fromJsonLd(await signed.toJsonLd({ contextLoader: mockDocumentLoader }), {
|
|
298
|
+
contextLoader: mockDocumentLoader,
|
|
299
|
+
documentLoader: mockDocumentLoader
|
|
300
|
+
});
|
|
301
|
+
object.id.pathname = object.id.pathname.replace(/original$/, "mutated-id");
|
|
302
|
+
return object;
|
|
303
|
+
},
|
|
304
|
+
"actor": () => sign(new Person({
|
|
305
|
+
id: compatible("/objects/actor"),
|
|
306
|
+
inbox: compatible("/objects/actor/inbox"),
|
|
307
|
+
outbox: compatible("/objects/actor/outbox"),
|
|
308
|
+
gateways: [new URL("https://example.com/")]
|
|
309
|
+
}))
|
|
310
|
+
};
|
|
311
|
+
federation.setObjectDispatcher(Object$1, "/objects/{id}", (_ctx, { id }) => objects[id]?.() ?? null);
|
|
312
|
+
const expected = {
|
|
313
|
+
"compatible": 200,
|
|
314
|
+
"other-gateway": 200,
|
|
315
|
+
"percent-encoded": 200,
|
|
316
|
+
"other-did": 404,
|
|
317
|
+
"other-path": 404,
|
|
318
|
+
"fragment": 404,
|
|
319
|
+
"malformed": 404,
|
|
320
|
+
"ordinary": 404,
|
|
321
|
+
"unsigned": 500,
|
|
322
|
+
"wrong-did": 500,
|
|
323
|
+
"mutated-id": 404,
|
|
324
|
+
"actor": 200
|
|
325
|
+
};
|
|
326
|
+
for (const host of ["example.com", "other.example"]) for (const [id, status] of globalThis.Object.entries(expected)) await t.step(`${id} on ${host} → ${status}`, async () => {
|
|
327
|
+
const response = await federation.fetch(new Request(`https://${host}/.well-known/apgateway/${did}/objects/${id}`, { headers: { Accept: ACCEPT } }), { contextData: void 0 });
|
|
328
|
+
assertEquals(response.status, status);
|
|
329
|
+
if (status !== 200) return;
|
|
330
|
+
assertEquals(response.headers.get("Content-Type"), PORTABLE_OBJECT_CONTENT_TYPE);
|
|
331
|
+
assertEquals((await response.json()).id, (await objects[id]()).id?.href);
|
|
332
|
+
});
|
|
333
|
+
await t.step("authorizes compatible-ID objects on any gateway", async () => {
|
|
334
|
+
const authorized = createTestFederation();
|
|
335
|
+
authorized.setObjectDispatcher(Note, "/notes/{id}", (_ctx, values) => sign(new Note({ id: compatible(`/notes/${values.id}`) }))).authorize(async (ctx) => {
|
|
336
|
+
return (await ctx.getSignedKeyOwner())?.id?.href === "https://example.com/person2";
|
|
337
|
+
});
|
|
338
|
+
for (const host of ["example.com", "other.example"]) {
|
|
339
|
+
const url = `https://${host}/.well-known/apgateway/${did}/notes/1`;
|
|
340
|
+
const request = new Request(url, { headers: { Accept: ACCEPT } });
|
|
341
|
+
let response = await authorized.fetch(request, { contextData: void 0 });
|
|
342
|
+
assertEquals(response.status, 401, host);
|
|
343
|
+
response = await authorized.fetch(await signRequest(new Request(url, { headers: { Accept: ACCEPT } }), rsaPrivateKey3, rsaPublicKey3.id), { contextData: void 0 });
|
|
344
|
+
assertEquals(response.status, 200, host);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
});
|
|
279
348
|
test("Federation.fetch() lets application routes shadow the gateway", async () => {
|
|
280
349
|
const federation = createTestFederation();
|
|
281
350
|
const paths = [];
|
|
@@ -426,5 +495,114 @@ test("Federation.fetch() serves portable actors through WebFinger", async () =>
|
|
|
426
495
|
document: unsigned
|
|
427
496
|
}) }), null);
|
|
428
497
|
});
|
|
498
|
+
/**
|
|
499
|
+
* Captures Fedify's warnings logged while `run` executes.
|
|
500
|
+
*/
|
|
501
|
+
async function captureWarnings(run) {
|
|
502
|
+
const records = [];
|
|
503
|
+
await reset();
|
|
504
|
+
try {
|
|
505
|
+
await configure({
|
|
506
|
+
sinks: { buffer: (record) => records.push(record) },
|
|
507
|
+
filters: {},
|
|
508
|
+
loggers: [{
|
|
509
|
+
category: ["logtape", "meta"],
|
|
510
|
+
sinks: []
|
|
511
|
+
}, {
|
|
512
|
+
category: [],
|
|
513
|
+
sinks: ["buffer"],
|
|
514
|
+
lowestLevel: "warning"
|
|
515
|
+
}]
|
|
516
|
+
});
|
|
517
|
+
await run();
|
|
518
|
+
} finally {
|
|
519
|
+
await reset();
|
|
520
|
+
}
|
|
521
|
+
return records.filter((record) => record.category[0] === "fedify");
|
|
522
|
+
}
|
|
523
|
+
test("Federation.fetch() serves compatible-ID actors", async (t) => {
|
|
524
|
+
const federation = createTestFederation();
|
|
525
|
+
const actorGateways = {
|
|
526
|
+
alice: ["https://example.com"],
|
|
527
|
+
secondary: ["https://primary.example", "https://example.com"],
|
|
528
|
+
nogateway: [],
|
|
529
|
+
malformed: ["https://example.com"]
|
|
530
|
+
};
|
|
531
|
+
const getActor = async (ctx, name) => {
|
|
532
|
+
const gateways = actorGateways[name];
|
|
533
|
+
if (gateways == null) return null;
|
|
534
|
+
const id = toCompatibleEf61Id(ctx.getPortableObjectUri(Person, { name }, did), "https://example.com");
|
|
535
|
+
if (name === "malformed") id.search = "?@gateway=https%3A%2F%2Fexample.com";
|
|
536
|
+
return await sign(new Person({
|
|
537
|
+
id,
|
|
538
|
+
preferredUsername: name,
|
|
539
|
+
inbox: new URL(`${id.href}/inbox`),
|
|
540
|
+
followers: new URL(`${id.href}/followers`),
|
|
541
|
+
gateways: gateways.map((g) => new URL(g))
|
|
542
|
+
}));
|
|
543
|
+
};
|
|
544
|
+
federation.setActorDispatcher("/users/{identifier}", (ctx, identifier) => getActor(ctx, identifier)).mapHandle((_ctx, username) => username).mapAlias((_ctx, resource) => {
|
|
545
|
+
const match = resource.href.match(/\/actors\/([^/?#]+)$/);
|
|
546
|
+
return match == null || !resource.href.startsWith(gatewayUrl("/")) ? null : { identifier: match[1] };
|
|
547
|
+
});
|
|
548
|
+
federation.setFollowersDispatcher("/users/{identifier}/followers", () => ({ items: [] }));
|
|
549
|
+
federation.setObjectDispatcher(Person, "/actors/{name}", (ctx, values) => ctx.portableRequest?.authority === did ? getActor(ctx, values.name) : null);
|
|
550
|
+
const compatibleId = gatewayUrl("/actors/alice");
|
|
551
|
+
await t.step("WebFinger", async () => {
|
|
552
|
+
let records = [];
|
|
553
|
+
records = await captureWarnings(async () => {
|
|
554
|
+
for (const resource of ["acct:alice@example.com", compatibleId]) {
|
|
555
|
+
const response = await federation.fetch(new Request(`https://example.com/.well-known/webfinger?resource=${encodeURIComponent(resource)}`), { contextData: void 0 });
|
|
556
|
+
assertEquals(response.status, 200, resource);
|
|
557
|
+
const jrd = await response.json();
|
|
558
|
+
assertEquals(jrd.subject, "acct:alice@example.com", resource);
|
|
559
|
+
assertEquals(jrd.links?.[0], {
|
|
560
|
+
rel: "self",
|
|
561
|
+
href: compatibleId,
|
|
562
|
+
type: "application/activity+json"
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
assertEquals(records.map((r) => r.rawMessage), []);
|
|
567
|
+
});
|
|
568
|
+
await t.step("gateway", async () => {
|
|
569
|
+
const response = await federation.fetch(new Request(compatibleId, { headers: { Accept: ACCEPT } }), { contextData: void 0 });
|
|
570
|
+
assertEquals(response.status, 200);
|
|
571
|
+
const json = await response.json();
|
|
572
|
+
assertEquals(json.id, compatibleId);
|
|
573
|
+
const actor = await lookupObject(compatibleId, {
|
|
574
|
+
documentLoader: (url) => Promise.resolve({
|
|
575
|
+
contextUrl: null,
|
|
576
|
+
documentUrl: url,
|
|
577
|
+
document: json
|
|
578
|
+
}),
|
|
579
|
+
contextLoader: mockDocumentLoader,
|
|
580
|
+
verifyPortableObject: verifyPortableObjectProof
|
|
581
|
+
});
|
|
582
|
+
assertInstanceOf(actor, Person);
|
|
583
|
+
assertEquals(actor.id?.href, compatibleId);
|
|
584
|
+
});
|
|
585
|
+
await t.step("warns if the ID is not on the first gateway", async () => {
|
|
586
|
+
assertEquals((await captureWarnings(async () => {
|
|
587
|
+
const response = await federation.fetch(new Request("https://example.com/.well-known/webfinger?resource=acct:secondary@example.com"), { contextData: void 0 });
|
|
588
|
+
assertEquals(response.status, 200);
|
|
589
|
+
const jrd = await response.json();
|
|
590
|
+
assertEquals(jrd.subject, "acct:secondary@primary.example");
|
|
591
|
+
assertEquals(jrd.links?.[0].href, gatewayUrl("/actors/secondary"));
|
|
592
|
+
})).map((r) => r.properties.gateway), ["https://primary.example"]);
|
|
593
|
+
});
|
|
594
|
+
await t.step("warns if the actor has no gateways", async () => {
|
|
595
|
+
assertEquals((await captureWarnings(async () => {
|
|
596
|
+
assertEquals((await federation.fetch(new Request("https://example.com/users/nogateway", { headers: { Accept: ACCEPT } }), { contextData: void 0 })).status, 200);
|
|
597
|
+
})).map((r) => r.properties.actorId), [gatewayUrl("/actors/nogateway")]);
|
|
598
|
+
});
|
|
599
|
+
await t.step("warns if the ID is a malformed compatible identifier", async () => {
|
|
600
|
+
const records = await captureWarnings(async () => {
|
|
601
|
+
assertEquals((await federation.fetch(new Request("https://example.com/users/malformed", { headers: { Accept: ACCEPT } }), { contextData: void 0 })).status, 200);
|
|
602
|
+
});
|
|
603
|
+
assertEquals(records.length, 1);
|
|
604
|
+
assert(String(records[0].rawMessage).includes("malformed"));
|
|
605
|
+
});
|
|
606
|
+
});
|
|
429
607
|
//#endregion
|
|
430
608
|
export {};
|
|
@@ -10,10 +10,10 @@ import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
|
|
|
10
10
|
import "../std__assert-DvMDRPes.mjs";
|
|
11
11
|
import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
|
|
12
12
|
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
13
|
-
import { l as verifyRequest } from "../http-
|
|
13
|
+
import { l as verifyRequest } from "../http-B2ftY0Ha.mjs";
|
|
14
14
|
import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
|
|
15
|
-
import { t as doesActorOwnKey } from "../owner-
|
|
16
|
-
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-
|
|
15
|
+
import { t as doesActorOwnKey } from "../owner-Cgx2Dc7I.mjs";
|
|
16
|
+
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-D-hOvYKy.mjs";
|
|
17
17
|
import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
|
|
18
18
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
19
19
|
import { FetchError, UrlError } from "@fedify/vocab-runtime";
|
|
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as MemoryKvStore } from "../../kv-DCHkaJ9-.mjs";
|
|
5
5
|
import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-dbCShlCb.mjs";
|
|
6
6
|
import "../../testing-CO_cEvQc.mjs";
|
|
7
|
-
import { o as createFederation } from "../../middleware-
|
|
7
|
+
import { o as createFederation } from "../../middleware-E_0IH72m.mjs";
|
|
8
8
|
import { n as ParallelMessageQueue } from "../../mq-Dm1Wmtm-.mjs";
|
|
9
9
|
import { configure, reset } from "@logtape/logtape";
|
|
10
10
|
import { delay } from "es-toolkit";
|
|
@@ -4,8 +4,8 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as MemoryKvStore } from "../../kv-DCHkaJ9-.mjs";
|
|
5
5
|
import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-dbCShlCb.mjs";
|
|
6
6
|
import "../../testing-CO_cEvQc.mjs";
|
|
7
|
-
import { o as createFederation } from "../../middleware-
|
|
8
|
-
import { r as createFederationBuilder } from "../../builder-
|
|
7
|
+
import { o as createFederation } from "../../middleware-E_0IH72m.mjs";
|
|
8
|
+
import { r as createFederationBuilder } from "../../builder-Ld4Y9ymR.mjs";
|
|
9
9
|
import { t as TaskCodec } from "../../codec-C6rTrpIp.mjs";
|
|
10
10
|
import { t as InProcessMessageQueue } from "../../mq-Dm1Wmtm-.mjs";
|
|
11
11
|
import { W3CTraceContextPropagator } from "@opentelemetry/core";
|
|
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
|
|
5
5
|
import "../std__assert-DvMDRPes.mjs";
|
|
6
6
|
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
7
|
-
import { t as hasMalformedKnownTemporalLiteral } from "../temporal-
|
|
7
|
+
import { t as hasMalformedKnownTemporalLiteral } from "../temporal-CqpKGEPk.mjs";
|
|
8
8
|
import { test } from "@fedify/fixture";
|
|
9
9
|
//#region src/federation/temporal.test.ts
|
|
10
10
|
test("hasMalformedKnownTemporalLiteral() detects expanded proof timestamps", async () => {
|
|
@@ -6,7 +6,7 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
|
6
6
|
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
7
7
|
import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
|
|
8
8
|
import "../std__assert-DvMDRPes.mjs";
|
|
9
|
-
import { o as createFederation, s as handleWebFinger } from "../middleware-
|
|
9
|
+
import { o as createFederation, s as handleWebFinger } from "../middleware-E_0IH72m.mjs";
|
|
10
10
|
import { Image, Link, Person, Tombstone } from "@fedify/vocab";
|
|
11
11
|
import { createTestMeterProvider, test } from "@fedify/fixture";
|
|
12
12
|
import { parseIri } from "@fedify/vocab-runtime";
|
|
@@ -852,5 +852,93 @@ test("handleWebFinger() for FEP-ef61 portable actors", async (t) => {
|
|
|
852
852
|
assertEquals((await query("acct:alice@primary.example")).status, 404);
|
|
853
853
|
});
|
|
854
854
|
});
|
|
855
|
+
test("handleWebFinger() for FEP-ef61 compatible-ID actors", async (t) => {
|
|
856
|
+
const compatibleId = `https://example.com/.well-known/apgateway/did:key:z6Mkabc/actor`;
|
|
857
|
+
const actors = {
|
|
858
|
+
alice: {
|
|
859
|
+
id: compatibleId,
|
|
860
|
+
gateways: [new URL("https://example.com")]
|
|
861
|
+
},
|
|
862
|
+
secondary: {
|
|
863
|
+
id: compatibleId,
|
|
864
|
+
gateways: [new URL("https://primary.example"), new URL("https://example.com")]
|
|
865
|
+
},
|
|
866
|
+
malformed: {
|
|
867
|
+
id: `${compatibleId}?@gateway=https%3A%2F%2Fexample.com`,
|
|
868
|
+
gateways: [new URL("https://example.com")]
|
|
869
|
+
},
|
|
870
|
+
nogateway: {
|
|
871
|
+
id: compatibleId,
|
|
872
|
+
gateways: []
|
|
873
|
+
}
|
|
874
|
+
};
|
|
875
|
+
const actorDispatcher = (_ctx, identifier) => {
|
|
876
|
+
const actor = actors[identifier];
|
|
877
|
+
if (actor == null) return null;
|
|
878
|
+
return new Person({
|
|
879
|
+
id: new URL(actor.id),
|
|
880
|
+
preferredUsername: "alice",
|
|
881
|
+
gateways: actor.gateways
|
|
882
|
+
});
|
|
883
|
+
};
|
|
884
|
+
const actorAliasMapper = (_ctx, resource) => resource.href === compatibleId ? { identifier: "alice" } : null;
|
|
885
|
+
async function query(resource) {
|
|
886
|
+
const url = new URL("https://example.com/.well-known/webfinger");
|
|
887
|
+
url.searchParams.set("resource", resource);
|
|
888
|
+
const context = createRequestContext({
|
|
889
|
+
federation: createFederation({ kv: new MemoryKvStore() }),
|
|
890
|
+
url,
|
|
891
|
+
data: void 0,
|
|
892
|
+
getActorUri(identifier) {
|
|
893
|
+
return new URL(`${url.origin}/users/${identifier}`);
|
|
894
|
+
},
|
|
895
|
+
parseUri: () => null
|
|
896
|
+
});
|
|
897
|
+
return await handleWebFinger(context.request, {
|
|
898
|
+
context,
|
|
899
|
+
actorDispatcher,
|
|
900
|
+
actorAliasMapper,
|
|
901
|
+
onNotFound: () => new Response("Not found", { status: 404 })
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
const selfLink = {
|
|
905
|
+
rel: "self",
|
|
906
|
+
href: compatibleId,
|
|
907
|
+
type: "application/activity+json"
|
|
908
|
+
};
|
|
909
|
+
await t.step("acct: on the first gateway", async () => {
|
|
910
|
+
const response = await query("acct:alice@example.com");
|
|
911
|
+
assertEquals(response.status, 200);
|
|
912
|
+
assertEquals(await response.json(), {
|
|
913
|
+
subject: "acct:alice@example.com",
|
|
914
|
+
aliases: [compatibleId],
|
|
915
|
+
links: [selfLink]
|
|
916
|
+
});
|
|
917
|
+
});
|
|
918
|
+
await t.step("compatible identifier", async () => {
|
|
919
|
+
const response = await query(compatibleId);
|
|
920
|
+
assertEquals(response.status, 200);
|
|
921
|
+
assertEquals(await response.json(), {
|
|
922
|
+
subject: "acct:alice@example.com",
|
|
923
|
+
aliases: [compatibleId],
|
|
924
|
+
links: [selfLink]
|
|
925
|
+
});
|
|
926
|
+
});
|
|
927
|
+
await t.step("compatible identifier not on the first gateway", async () => {
|
|
928
|
+
const response = await query("acct:secondary@example.com");
|
|
929
|
+
assertEquals(response.status, 200);
|
|
930
|
+
assertEquals(await response.json(), {
|
|
931
|
+
subject: "acct:alice@primary.example",
|
|
932
|
+
aliases: [compatibleId, "acct:secondary@example.com"],
|
|
933
|
+
links: [selfLink]
|
|
934
|
+
});
|
|
935
|
+
});
|
|
936
|
+
await t.step("malformed compatible identifier", async () => {
|
|
937
|
+
assertEquals((await query("acct:malformed@example.com")).status, 404);
|
|
938
|
+
});
|
|
939
|
+
await t.step("no gateways", async () => {
|
|
940
|
+
assertEquals((await query("acct:nogateway@example.com")).status, 404);
|
|
941
|
+
});
|
|
942
|
+
});
|
|
855
943
|
//#endregion
|
|
856
944
|
export {};
|