@fedify/fedify 2.4.0-pr.936.41 → 2.5.0-dev.2271
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/README.md +50 -39
- package/dist/{assert-CJ1KXcvv.mjs → assert-D1Eafmd_.mjs} +1 -1
- package/dist/{assert_equals-BV7oLMxm.mjs → assert_equals-Z6IcEbWU.mjs} +2 -28
- package/dist/assert_exists-cffSpJ7X.mjs +28 -0
- package/dist/assert_false-CPdRnc3E.mjs +23 -0
- package/dist/assert_greater_or_equal-BlafOF4-.mjs +31 -0
- package/dist/{assert_instance_of-Q9KTBnrf.mjs → assert_instance_of-mrXWWNY-.mjs} +1 -1
- package/dist/{assert_not_equals-EPJPoxuf.mjs → assert_not_equals-CpPK2t89.mjs} +2 -1
- package/dist/assert_rejects-CTptdYck.mjs +39 -0
- package/dist/{assert_strict_equals-Ec20AUwa.mjs → assert_strict_equals-CYvBmkpC.mjs} +2 -1
- package/dist/{assert_throws-NiFfqbnH.mjs → assert_throws-BYITOvPO.mjs} +2 -1
- package/dist/assertion_error-B-cx0fFA.mjs +31 -0
- package/dist/audience-CHQ-FlLD.mjs +95 -0
- package/dist/{builder-DonuxRl_.mjs → builder-Bda6LK7A.mjs} +169 -14
- package/dist/{circuit-breaker-CBGyz5F7.mjs → circuit-breaker-XJ53md4S.mjs} +224 -17
- package/dist/circuit-breaker-test-utils-DW6YC_Pe.mjs +14 -0
- package/dist/{client-CjF5IYQS.mjs → client-CEnNz8O7.mjs} +144 -8
- package/dist/{codec-BBga-FWI.mjs → codec-C6rTrpIp.mjs} +26 -3
- package/dist/{collection-BIhlldqK.mjs → collection-CnnP1UAv.mjs} +1 -1
- package/dist/compat/mod.cjs +1 -1
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/mod.js +1 -1
- package/dist/compat/outgoing-jsonld.test.mjs +71 -3
- package/dist/compat/public-audience.test.mjs +14 -5
- package/dist/compat/secured-document.test.d.mts +2 -0
- package/dist/compat/secured-document.test.mjs +58 -0
- package/dist/compat/transformers.test.mjs +5 -5
- package/dist/compound-proof-B-9mXxFW.mjs +989 -0
- package/dist/{context-Zii1f5xS.d.ts → context-7NyiqIcg.d.ts} +1600 -25
- package/dist/{context-BJXxPElm.mjs → context-8Tu9kea0.mjs} +21 -63
- package/dist/{context-lxX6I7as.d.cts → context-tsYPJ2Ew.d.cts} +1600 -25
- package/dist/{deno-BHzKn9Ig.mjs → deno-oR5aJKpU.mjs} +1 -1
- package/dist/dist-DJQM4u7i.mjs +189 -0
- package/dist/docloader-DBoYbvYW.mjs +87 -0
- package/dist/{esm-yYQfPhIY.mjs → esm-BHXXx4Wx.mjs} +13 -6
- package/dist/federation/audience.test.d.mts +2 -0
- package/dist/federation/audience.test.mjs +130 -0
- package/dist/federation/builder.test.mjs +54 -5
- package/dist/federation/circuit-breaker.test.mjs +838 -7
- package/dist/federation/collection.test.mjs +3 -3
- package/dist/federation/compound-fanout.test.d.mts +2 -0
- package/dist/federation/compound-fanout.test.mjs +228 -0
- package/dist/federation/handler-compound-proof.test.d.mts +2 -0
- package/dist/federation/handler-compound-proof.test.mjs +384 -0
- package/dist/federation/handler.test.mjs +385 -15
- package/dist/federation/hashlink-media.test.d.mts +2 -0
- package/dist/federation/hashlink-media.test.mjs +422 -0
- package/dist/federation/idempotency.test.mjs +6 -6
- package/dist/federation/inbox-report.test.d.mts +2 -0
- package/dist/federation/inbox-report.test.mjs +475 -0
- package/dist/federation/inbox.test.mjs +3 -3
- package/dist/federation/keycache.test.mjs +215 -14
- package/dist/federation/kv.test.mjs +3 -3
- package/dist/federation/metrics.test.mjs +5 -5
- package/dist/federation/middleware.test.mjs +967 -35
- package/dist/federation/mod.cjs +18 -261
- package/dist/federation/mod.d.cts +5 -5
- package/dist/federation/mod.d.ts +5 -5
- package/dist/federation/mod.js +279 -1
- package/dist/federation/mq.test.mjs +123 -47
- package/dist/federation/negotiation.test.mjs +4 -4
- package/dist/federation/outgoing-proof.test.d.mts +2 -0
- package/dist/federation/outgoing-proof.test.mjs +931 -0
- package/dist/federation/portable-actor.test.d.mts +2 -0
- package/dist/federation/portable-actor.test.mjs +496 -0
- package/dist/federation/portable-chain.test.d.mts +2 -0
- package/dist/federation/portable-chain.test.mjs +370 -0
- package/dist/federation/portable-collection.test.d.mts +2 -0
- package/dist/federation/portable-collection.test.mjs +832 -0
- package/dist/federation/portable-delivery.test.d.mts +2 -0
- package/dist/federation/portable-delivery.test.mjs +746 -0
- package/dist/federation/portable-gateway-keys.test.d.mts +2 -0
- package/dist/federation/portable-gateway-keys.test.mjs +686 -0
- package/dist/federation/portable-inbox.test.d.mts +2 -0
- package/dist/federation/portable-inbox.test.mjs +1535 -0
- package/dist/federation/portable-parse-uri.test.d.mts +2 -0
- package/dist/federation/portable-parse-uri.test.mjs +348 -0
- package/dist/federation/portable-route-activity.test.d.mts +2 -0
- package/dist/federation/portable-route-activity.test.mjs +259 -0
- package/dist/federation/portable.test.d.mts +2 -0
- package/dist/federation/portable.test.mjs +1072 -0
- package/dist/federation/retry.test.mjs +4 -3
- package/dist/federation/router.test.mjs +9 -159
- package/dist/federation/send.test.mjs +245 -27
- package/dist/federation/tasks/codec.test.mjs +4 -4
- package/dist/federation/tasks/enqueue.test.mjs +42 -4
- package/dist/federation/tasks/tasks.test.mjs +7 -6
- package/dist/federation/temporal.test.mjs +4 -4
- package/dist/federation/webfinger.test.mjs +275 -12
- package/dist/{middleware-FrSMAYEx.cjs → federation-CkZiAcWY.cjs} +8640 -3722
- package/dist/http-B5JT115n.cjs +10630 -0
- package/dist/http-DydWCB7R.js +6527 -0
- package/dist/{http-CPVMUgsW.mjs → http-siciWkmx.mjs} +397 -168
- package/dist/key-BspPiXqn.mjs +951 -0
- package/dist/key-C1nv7_jD.d.ts +1125 -0
- package/dist/key-DYxuyd-s.d.cts +1125 -0
- package/dist/key-owner-evidence-APB6NpSk.mjs +108 -0
- package/dist/keycache-Da8A8mZa.mjs +200 -0
- package/dist/{kv-D6hNiMTK.d.ts → kv-B2mzK5tl.d.cts} +3 -2
- package/dist/{kv-gJ8LYbxX.d.cts → kv-B2mzK5tl.d.ts} +3 -2
- package/dist/kv-DBRWVx3Q.mjs +104 -0
- package/dist/{kv-cache-KyGqJqSp.mjs → kv-cache-BF1hnW65.mjs} +1 -1
- package/dist/{kv-cache-5bnX3-Jq.js → kv-cache-CTQr1tbn.js} +54 -16
- package/dist/{kv-cache-DQ29C4XL.cjs → kv-cache-DCac1nEU.cjs} +55 -19
- package/dist/{ld-Cj9F_5AP.mjs → ld-CzaP-hJl.mjs} +44 -12
- package/dist/map-local-context-conflict-DOYEEJ3u.mjs +204 -0
- package/dist/map-local-create-note-B0SeXJms.mjs +214 -0
- package/dist/{metrics-DMk-nTxI.mjs → metrics-CYg9yxBL.mjs} +3 -2
- package/dist/metrics-DVKYY6K1.d.cts +36 -0
- package/dist/metrics-DoRtI0qU.d.ts +36 -0
- package/dist/{middleware-Y-kpsp6S.js → middleware-CsDDXKuL.js} +5493 -1103
- package/dist/{middleware-CvwTUgnP.mjs → middleware-D7_LO-Vs.mjs} +3157 -272
- package/dist/{middleware-CEctxBN6.mjs → middleware-DWjrfpcz.mjs} +1 -1
- package/dist/{mod-gOlPDuT8.d.cts → mod-BojP2Bw3.d.cts} +1 -1
- package/dist/mod-CavhAgLw.d.cts +320 -0
- package/dist/mod-CtpF2BU5.d.ts +320 -0
- package/dist/{mod-BtEaIEcD.d.cts → mod-DEZXwV6J.d.ts} +10 -4
- package/dist/{mod-UMmveiO0.d.ts → mod-Dw-nPxQh.d.cts} +10 -4
- package/dist/{mod-DLc2Ot6f.d.ts → mod-Xj5P4s4d.d.ts} +1 -1
- package/dist/mod.cjs +40 -39
- package/dist/mod.d.cts +10 -11
- package/dist/mod.d.ts +10 -11
- package/dist/mod.js +9 -9
- package/dist/{mq-llHKivST.mjs → mq-Dm1Wmtm-.mjs} +6 -0
- package/dist/nodeinfo/client.test.mjs +139 -4
- package/dist/nodeinfo/handler.test.mjs +5 -5
- 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/nodeinfo/types.test.mjs +4 -4
- package/dist/otel/exporter.test.mjs +3 -3
- package/dist/otel/mod.cjs +0 -1
- package/dist/otel/mod.d.cts +1 -1
- package/dist/otel/mod.d.ts +1 -1
- package/dist/{outgoing-jsonld-URuqsICz.mjs → outgoing-jsonld-4IJqPYos.mjs} +12 -10
- package/dist/{owner-DEvZuyOE.d.cts → owner-BF0L9cMt.d.cts} +11 -0
- package/dist/{owner-CnngXDNJ.d.ts → owner-BSzr5-I8.d.ts} +11 -0
- package/dist/owner-DYgfTFHy.mjs +267 -0
- package/dist/portable-collection-BpsVsRov.js +670 -0
- package/dist/portable-collection-C74P1vtC.mjs +418 -0
- package/dist/portable-collection-DZd2bm7M.cjs +686 -0
- package/dist/portable-key-BIMLrAP4.mjs +549 -0
- package/dist/portable-key-id-Ca30-hOa.mjs +248 -0
- package/dist/proof-Dxy6ByqI.mjs +1299 -0
- package/dist/{public-audience-Cr5MsnMq.mjs → public-audience--klBIOk1.mjs} +15 -10
- package/dist/router-u5O5KCQr.mjs +156 -0
- package/dist/runtime/mod.cjs +1 -1
- package/dist/runtime/mod.js +1 -1
- package/dist/secured-document-CxzZZ9nL.mjs +35 -0
- package/dist/send-C4dDpAxm.mjs +495 -0
- package/dist/sig/accept.test.mjs +1 -1
- package/dist/sig/compound-proof-discovery.test.d.mts +2 -0
- package/dist/sig/compound-proof-discovery.test.mjs +331 -0
- package/dist/sig/compound-proof-producer.test.d.mts +2 -0
- package/dist/sig/compound-proof-producer.test.mjs +107 -0
- package/dist/sig/compound-proof-verification.test.d.mts +2 -0
- package/dist/sig/compound-proof-verification.test.mjs +833 -0
- package/dist/sig/compound-proof.test.d.mts +2 -0
- package/dist/sig/compound-proof.test.mjs +411 -0
- package/dist/sig/http.test.mjs +702 -31
- package/dist/sig/key-owner-evidence.test.d.mts +2 -0
- package/dist/sig/key-owner-evidence.test.mjs +100 -0
- package/dist/sig/key.test.mjs +220 -8
- package/dist/sig/ld.test.mjs +65 -7
- package/dist/sig/mitra-receive-interop.test.d.mts +2 -0
- package/dist/sig/mitra-receive-interop.test.mjs +260 -0
- package/dist/sig/mitra-send-interop.test.d.mts +2 -0
- package/dist/sig/mitra-send-interop.test.mjs +461 -0
- package/dist/sig/mod.cjs +18 -16
- package/dist/sig/mod.d.cts +4 -4
- package/dist/sig/mod.d.ts +4 -4
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +108 -9
- package/dist/sig/portable-collection.test.d.mts +2 -0
- package/dist/sig/portable-collection.test.mjs +697 -0
- package/dist/sig/portable-interop.test.d.mts +2 -0
- package/dist/sig/portable-interop.test.mjs +156 -0
- package/dist/sig/portable-key-id.test.d.mts +2 -0
- package/dist/sig/portable-key-id.test.mjs +67 -0
- package/dist/sig/portable-key.test.d.mts +2 -0
- package/dist/sig/portable-key.test.mjs +1439 -0
- package/dist/sig/proof.test.mjs +1753 -16
- package/dist/sig/tootik-request-interop.test.d.mts +2 -0
- package/dist/sig/tootik-request-interop.test.mjs +190 -0
- package/dist/sig/tootik-send-interop.test.d.mts +2 -0
- package/dist/sig/tootik-send-interop.test.mjs +331 -0
- package/dist/sig/verification.test.d.mts +2 -0
- package/dist/sig/verification.test.mjs +310 -0
- package/dist/std__assert-DvMDRPes.mjs +8 -0
- package/dist/{testing-CO7ftO8A.mjs → tasks-IG3pHfvZ.mjs} +2 -2
- package/dist/{temporal-fsXO5IvD.mjs → temporal-D0KJI4NF.mjs} +3 -3
- package/dist/testing/context.test.d.mts +2 -0
- package/dist/testing/context.test.mjs +19 -0
- package/dist/testing/kv.test.d.mts +2 -0
- package/dist/testing/kv.test.mjs +101 -0
- package/dist/testing/mod.d.mts +1617 -48
- package/dist/testing/mod.mjs +6 -3
- package/dist/testing-t8xDeJZ5.mjs +8 -0
- package/dist/{transformers-DGwl-hD1.cjs → transformers-BalCrSsr.cjs} +0 -1
- package/dist/{types-DC1pNgJA.js → types-_S2_MeLF.js} +144 -8
- package/dist/{types-CPym83zc.cjs → types-poIao40f.cjs} +160 -7
- package/dist/utils/docloader.test.mjs +530 -8
- package/dist/utils/kv-cache.test.mjs +2 -2
- 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/dist/verification-C3954hQh.mjs +5 -0
- package/dist/verification-Dpfcw8ml.mjs +105 -0
- package/dist/vocab/cjs.test.mjs +1 -1
- package/package.json +8 -8
- package/dist/assert_rejects-EgstcQy5.mjs +0 -82
- package/dist/docloader-e1khNUKy.mjs +0 -49
- package/dist/http-CsmQ-BQ4.js +0 -2483
- package/dist/http-VyDTd4G3.d.cts +0 -553
- package/dist/http-hlIjmewS.cjs +0 -2694
- package/dist/http-lf8Hsd91.d.ts +0 -553
- package/dist/key-DfPsX8hv.mjs +0 -453
- package/dist/keycache-_DHqz5z_.mjs +0 -97
- package/dist/metrics-CoUJLQhZ.d.cts +0 -276
- package/dist/metrics-Dc4jR-IC.d.ts +0 -276
- package/dist/mod-B0hW12_O.d.cts +0 -307
- package/dist/mod-COIAjwRS.d.ts +0 -307
- package/dist/owner-Br9YYzn_.mjs +0 -120
- package/dist/proof-B4ngoaNQ.mjs +0 -316
- package/dist/proof-B6E0XUXJ.js +0 -1415
- package/dist/proof-NN4xCQGL.cjs +0 -1562
- package/dist/send-CxHKUNzv.mjs +0 -239
- package/dist/std__assert-B2NnY9bq.mjs +0 -79
- package/dist/temporal-polyfill-BCdTHexd.cjs +0 -3565
- /package/dist/{accept-8PiolDP7.mjs → accept-CPVqki78.mjs} +0 -0
- /package/dist/{activity-listener-BtTSxZWA.mjs → activity-listener-C-BSbKI7.mjs} +0 -0
- /package/dist/{client-CAM_bQXx.d.cts → client-D-XP_Ccb.d.cts} +0 -0
- /package/dist/{client-CSddvgWN.d.ts → client-D-XP_Ccb.d.ts} +0 -0
- /package/dist/{keys-Dbyd7bhl.mjs → keys-DIJNHW-Y.mjs} +0 -0
- /package/dist/{kv-CXBAok3k.mjs → kv-DCHkaJ9-.mjs} +0 -0
- /package/dist/{negotiation-RuwrPFqi.mjs → negotiation-CCocWT_N.mjs} +0 -0
- /package/dist/{retry-DgFqeS5k.mjs → retry-zX5xw5e0.mjs} +0 -0
- /package/dist/{chunk-D5IdezsZ.js → rolldown-runtime-BC-Eo5Mj.js} +0 -0
- /package/dist/{chunk-Czpeue4M.cjs → rolldown-runtime-Dmm3t92W.cjs} +0 -0
- /package/dist/{transformers-DXze6FUB.js → transformers-DAtXl_Kc.js} +0 -0
- /package/dist/{types-CrtdVuDP.mjs → types-DE_LV1JB.mjs} +0 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
5
|
+
import { p as signJsonLd } from "../ld-CzaP-hJl.mjs";
|
|
6
|
+
import { a as signObject } from "../proof-Dxy6ByqI.mjs";
|
|
7
|
+
import { l as signRequest } from "../http-siciWkmx.mjs";
|
|
8
|
+
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
|
|
9
|
+
import { o as createFederation } from "../middleware-D7_LO-Vs.mjs";
|
|
10
|
+
import { r as createFederationBuilder } from "../builder-Bda6LK7A.mjs";
|
|
11
|
+
import { Create, Person } from "@fedify/vocab";
|
|
12
|
+
import { exportDidKey, parseIri } from "@fedify/vocab-runtime";
|
|
13
|
+
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
14
|
+
import { deepStrictEqual, ok, rejects, strictEqual } from "node:assert/strict";
|
|
15
|
+
//#region src/federation/inbox-report.test.ts
|
|
16
|
+
const loaders = {
|
|
17
|
+
documentLoader: mockDocumentLoader,
|
|
18
|
+
contextLoader: mockDocumentLoader
|
|
19
|
+
};
|
|
20
|
+
function setup(skipSignatureVerification = false) {
|
|
21
|
+
const reports = [];
|
|
22
|
+
const federation = createFederation({
|
|
23
|
+
kv: new MemoryKvStore(),
|
|
24
|
+
skipSignatureVerification,
|
|
25
|
+
documentLoaderFactory: () => mockDocumentLoader,
|
|
26
|
+
contextLoaderFactory: () => mockDocumentLoader
|
|
27
|
+
});
|
|
28
|
+
federation.setActorDispatcher("/users/{identifier}", (_ctx, id) => id === "missing" ? null : new Person({})).setKeyPairsDispatcher(() => []);
|
|
29
|
+
return {
|
|
30
|
+
federation,
|
|
31
|
+
setters: federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").onRequestFinished((_ctx, report) => {
|
|
32
|
+
reports.push(report);
|
|
33
|
+
}),
|
|
34
|
+
reports
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async function payload(id = "https://example.com/create", actor = "https://example.com/person2") {
|
|
38
|
+
return await new Create({
|
|
39
|
+
id: new URL(id),
|
|
40
|
+
actor: new URL(actor)
|
|
41
|
+
}).toJsonLd(loaders);
|
|
42
|
+
}
|
|
43
|
+
function request(body, path = "/inbox") {
|
|
44
|
+
return new Request(`https://example.com${path}`, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
body: JSON.stringify(body),
|
|
47
|
+
headers: { "Content-Type": "application/activity+json" }
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
test("onRequestFinished classifies early rejection, JSON null, and excluded routes", async () => {
|
|
51
|
+
const { federation, reports } = setup(true);
|
|
52
|
+
strictEqual((await federation.fetch(request({}, "/users/missing/inbox"), { contextData: void 0 })).status, 404);
|
|
53
|
+
deepStrictEqual(reports[0].inbox, {
|
|
54
|
+
kind: "personal",
|
|
55
|
+
recipient: "missing"
|
|
56
|
+
});
|
|
57
|
+
deepStrictEqual(reports[0].payload, { status: "unavailable" });
|
|
58
|
+
deepStrictEqual(reports[0].authentication, { status: "notDetermined" });
|
|
59
|
+
deepStrictEqual(reports[0].outcome, {
|
|
60
|
+
type: "response",
|
|
61
|
+
status: 404,
|
|
62
|
+
disposition: "rejected",
|
|
63
|
+
reason: "recipientNotFound"
|
|
64
|
+
});
|
|
65
|
+
strictEqual((await federation.fetch(request(null), { contextData: void 0 })).status, 400);
|
|
66
|
+
deepStrictEqual(reports[1].payload, {
|
|
67
|
+
status: "parsed",
|
|
68
|
+
value: null
|
|
69
|
+
});
|
|
70
|
+
deepStrictEqual(reports[1].authentication, { status: "notDetermined" });
|
|
71
|
+
await federation.fetch(new Request("https://example.com/users/alice/inbox"), { contextData: void 0 });
|
|
72
|
+
await federation.fetch(request({}, "/unmatched"), { contextData: void 0 });
|
|
73
|
+
strictEqual(reports.length, 2);
|
|
74
|
+
await federation.fetch(new Request("https://example.com/inbox", {
|
|
75
|
+
method: "POST",
|
|
76
|
+
body: "{"
|
|
77
|
+
}), { contextData: void 0 });
|
|
78
|
+
deepStrictEqual(reports[2].outcome, {
|
|
79
|
+
type: "response",
|
|
80
|
+
status: 400,
|
|
81
|
+
disposition: "rejected",
|
|
82
|
+
reason: "invalidJson"
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
test("onRequestFinished leaves deferred policy errors undetermined", async () => {
|
|
86
|
+
const did = await exportDidKey(ed25519Multikey.publicKey);
|
|
87
|
+
const keyId = parseIri(`${did}#${did.substring(8)}`);
|
|
88
|
+
const otherDid = await exportDidKey((await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"])).publicKey);
|
|
89
|
+
for (const withProof of [false, true]) {
|
|
90
|
+
const contextUrl = `https://example.com/context/deferred-${withProof}`;
|
|
91
|
+
const remote = {
|
|
92
|
+
contextUrl: null,
|
|
93
|
+
documentUrl: contextUrl,
|
|
94
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
95
|
+
};
|
|
96
|
+
const contextLoader = (url) => url === contextUrl ? Promise.resolve(remote) : mockDocumentLoader(url);
|
|
97
|
+
const activity = new Create({
|
|
98
|
+
id: parseIri(`ap+ef61://${withProof ? otherDid : did}/activities/deferred-${withProof}`),
|
|
99
|
+
actor: parseIri(`ap+ef61://${did}/users/bob`)
|
|
100
|
+
});
|
|
101
|
+
const context = [
|
|
102
|
+
"https://www.w3.org/ns/activitystreams",
|
|
103
|
+
"https://w3id.org/security/data-integrity/v1",
|
|
104
|
+
contextUrl
|
|
105
|
+
];
|
|
106
|
+
const body = withProof ? await (await signObject(activity, ed25519PrivateKey, keyId, {
|
|
107
|
+
context,
|
|
108
|
+
contextLoader
|
|
109
|
+
})).toJsonLd({
|
|
110
|
+
format: "compact",
|
|
111
|
+
context,
|
|
112
|
+
contextLoader
|
|
113
|
+
}) : await activity.toJsonLd({
|
|
114
|
+
format: "compact",
|
|
115
|
+
context,
|
|
116
|
+
contextLoader
|
|
117
|
+
});
|
|
118
|
+
body.signature = {
|
|
119
|
+
type: "RsaSignature2017",
|
|
120
|
+
creator: rsaPublicKey3.id.href,
|
|
121
|
+
created: "2024-01-01T00:00:00Z",
|
|
122
|
+
signatureValue: "bogus"
|
|
123
|
+
};
|
|
124
|
+
let failOnce = true;
|
|
125
|
+
const reports = [];
|
|
126
|
+
const federation = createFederation({
|
|
127
|
+
kv: new MemoryKvStore(),
|
|
128
|
+
documentLoaderFactory: () => mockDocumentLoader,
|
|
129
|
+
contextLoaderFactory: () => async (url) => {
|
|
130
|
+
if (url === contextUrl && failOnce) {
|
|
131
|
+
failOnce = false;
|
|
132
|
+
throw new Error("Temporary context outage");
|
|
133
|
+
}
|
|
134
|
+
return await contextLoader(url);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
federation.setActorDispatcher("/users/{identifier}", () => new Person({})).setKeyPairsDispatcher(() => []);
|
|
138
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, () => {}).onRequestFinished((_ctx, report) => {
|
|
139
|
+
reports.push(report);
|
|
140
|
+
});
|
|
141
|
+
const signed = await signRequest(request(body), rsaPrivateKey3, rsaPublicKey3.id);
|
|
142
|
+
await rejects(federation.fetch(signed, { contextData: void 0 }), (error) => {
|
|
143
|
+
strictEqual(reports.length, 1);
|
|
144
|
+
deepStrictEqual(reports[0].authentication, { status: "notDetermined" });
|
|
145
|
+
ok(reports[0].outcome.type === "exception");
|
|
146
|
+
strictEqual(reports[0].outcome.stage, "policy");
|
|
147
|
+
strictEqual(reports[0].outcome.error, error);
|
|
148
|
+
return true;
|
|
149
|
+
});
|
|
150
|
+
strictEqual(reports[0].attempts.some((attempt) => attempt.mechanism === "objectIntegrity" && attempt.status === "verified"), withProof);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
test("onRequestFinished awaits once and isolates observer failures", async () => {
|
|
154
|
+
const { federation, setters } = setup(true);
|
|
155
|
+
setters.on(Create, () => {});
|
|
156
|
+
let calls = 0;
|
|
157
|
+
let release;
|
|
158
|
+
const gate = new Promise((resolve) => {
|
|
159
|
+
release = resolve;
|
|
160
|
+
});
|
|
161
|
+
let entered;
|
|
162
|
+
const started = new Promise((resolve) => {
|
|
163
|
+
entered = resolve;
|
|
164
|
+
});
|
|
165
|
+
setters.onRequestFinished(async (_ctx, report) => {
|
|
166
|
+
calls++;
|
|
167
|
+
deepStrictEqual(report.authentication, { status: "skipped" });
|
|
168
|
+
strictEqual(report.outcome.type === "response" && report.outcome.disposition, "processed");
|
|
169
|
+
entered();
|
|
170
|
+
await gate;
|
|
171
|
+
throw new Error("observer failed");
|
|
172
|
+
});
|
|
173
|
+
let finished = false;
|
|
174
|
+
const response = federation.fetch(request(await payload()), { contextData: void 0 }).then((response) => {
|
|
175
|
+
finished = true;
|
|
176
|
+
return response;
|
|
177
|
+
});
|
|
178
|
+
await started;
|
|
179
|
+
strictEqual(finished, false);
|
|
180
|
+
release();
|
|
181
|
+
strictEqual((await response).status, 202);
|
|
182
|
+
strictEqual(calls, 1);
|
|
183
|
+
});
|
|
184
|
+
test("onRequestFinished preserves preparation exceptions and callback replacement", async () => {
|
|
185
|
+
const { federation, setters, reports } = setup();
|
|
186
|
+
const error = /* @__PURE__ */ new Error("preparation failed");
|
|
187
|
+
setters.setSharedKeyDispatcher(() => {
|
|
188
|
+
throw error;
|
|
189
|
+
});
|
|
190
|
+
setters.onRequestFinished((_ctx, report) => {
|
|
191
|
+
reports.push(report);
|
|
192
|
+
throw new Error("secondary failure");
|
|
193
|
+
});
|
|
194
|
+
await rejects(federation.fetch(request(await payload()), { contextData: void 0 }), (value) => value === error);
|
|
195
|
+
strictEqual(reports.length, 1);
|
|
196
|
+
deepStrictEqual(reports[0].outcome, {
|
|
197
|
+
type: "exception",
|
|
198
|
+
stage: "prepare",
|
|
199
|
+
error
|
|
200
|
+
});
|
|
201
|
+
deepStrictEqual(reports[0].authentication, { status: "notDetermined" });
|
|
202
|
+
});
|
|
203
|
+
test("onRequestFinished distinguishes custom responses from authentication", async () => {
|
|
204
|
+
const { federation, setters, reports } = setup();
|
|
205
|
+
setters.onUnverifiedActivity(() => new Response(null, { status: 202 }));
|
|
206
|
+
const body = await payload();
|
|
207
|
+
strictEqual((await federation.fetch(request(body), { contextData: void 0 })).status, 202);
|
|
208
|
+
deepStrictEqual(reports[0].authentication, {
|
|
209
|
+
status: "rejected",
|
|
210
|
+
reason: { type: "verificationFailed" }
|
|
211
|
+
});
|
|
212
|
+
deepStrictEqual(reports[0].outcome, {
|
|
213
|
+
type: "response",
|
|
214
|
+
status: 202,
|
|
215
|
+
disposition: "customResponse"
|
|
216
|
+
});
|
|
217
|
+
strictEqual(reports[0].attempts.at(-1)?.mechanism, "http");
|
|
218
|
+
setters.onUnverifiedActivity(() => {});
|
|
219
|
+
strictEqual((await federation.fetch(request(body), { contextData: void 0 })).status, 401);
|
|
220
|
+
strictEqual(reports[1].outcome.type === "response" && reports[1].outcome.disposition, "rejected");
|
|
221
|
+
});
|
|
222
|
+
test("onRequestFinished retains HTTP keys and separates ownership rejection", async () => {
|
|
223
|
+
const { federation, setters, reports } = setup();
|
|
224
|
+
setters.on(Create, () => {});
|
|
225
|
+
const signed = await signRequest(request(await payload()), rsaPrivateKey3, new URL("https://example.com/person2#key3"));
|
|
226
|
+
strictEqual((await federation.fetch(signed, { contextData: void 0 })).status, 202);
|
|
227
|
+
const report = reports[0];
|
|
228
|
+
strictEqual(report.authentication.status, "verified");
|
|
229
|
+
const attempt = report.attempts.find((attempt) => attempt.mechanism === "http");
|
|
230
|
+
ok(attempt?.status === "verified");
|
|
231
|
+
strictEqual(attempt.subject.id?.href, "https://example.com/create");
|
|
232
|
+
const check = attempt.checks[0];
|
|
233
|
+
ok(check.status === "verified" && check.key.type === "cryptographicKey");
|
|
234
|
+
strictEqual(check.key.publicKey.algorithm.name, "RSASSA-PKCS1-v1_5");
|
|
235
|
+
strictEqual(check.key.ownerId?.href, "https://example.com/person2");
|
|
236
|
+
strictEqual(check.key, check.triedKeys.at(-1));
|
|
237
|
+
strictEqual(attempt.signatures[0], check);
|
|
238
|
+
strictEqual(report.authentication.attempts[0], attempt);
|
|
239
|
+
await federation.fetch(signed, { contextData: void 0 });
|
|
240
|
+
deepStrictEqual(reports[1].outcome, {
|
|
241
|
+
type: "response",
|
|
242
|
+
status: 202,
|
|
243
|
+
disposition: "duplicate"
|
|
244
|
+
});
|
|
245
|
+
const mismatched = await signRequest(request(await payload("https://example.com/other", "https://example.com/person")), rsaPrivateKey3, new URL("https://example.com/person2#key3"));
|
|
246
|
+
strictEqual((await federation.fetch(mismatched, { contextData: void 0 })).status, 401);
|
|
247
|
+
strictEqual(reports[2].authentication.status, "rejected");
|
|
248
|
+
ok(reports[2].authentication.status === "rejected");
|
|
249
|
+
strictEqual(reports[2].authentication.reason.type, "actorKeyMismatch");
|
|
250
|
+
strictEqual(reports[2].attempts.at(-1)?.status, "verified");
|
|
251
|
+
});
|
|
252
|
+
test("onRequestFinished retains LDS crypto success before HTTP fallback", async () => {
|
|
253
|
+
const { federation, reports } = setup();
|
|
254
|
+
const signed = await signRequest(request(await signJsonLd(await payload("https://example.com/lds", "https://example.com/person"), rsaPrivateKey3, new URL("https://example.com/person2#key3"), loaders)), rsaPrivateKey2, new URL("https://example.com/key2"));
|
|
255
|
+
strictEqual((await federation.fetch(signed, { contextData: void 0 })).status, 202);
|
|
256
|
+
const ld = reports[0].attempts.find((attempt) => attempt.mechanism === "linkedData");
|
|
257
|
+
ok(ld?.status === "rejected");
|
|
258
|
+
strictEqual(ld.reason.type, "uncoveredAttribution");
|
|
259
|
+
strictEqual(ld.checks[0].status, "verified");
|
|
260
|
+
strictEqual(reports[0].authentication.status, "verified");
|
|
261
|
+
});
|
|
262
|
+
test("onRequestFinished keeps valid OIP checks when attributions are uncovered", async () => {
|
|
263
|
+
const { federation, reports } = setup();
|
|
264
|
+
const activity = new Create({
|
|
265
|
+
id: new URL("https://example.com/proof"),
|
|
266
|
+
actor: new URL("https://example.com/person")
|
|
267
|
+
});
|
|
268
|
+
const signed = await signObject(activity, ed25519PrivateKey, ed25519Multikey.id, loaders);
|
|
269
|
+
strictEqual((await federation.fetch(request(await signed.toJsonLd(loaders)), { contextData: void 0 })).status, 401);
|
|
270
|
+
const proof = reports[0].attempts.find((attempt) => attempt.mechanism === "objectIntegrity");
|
|
271
|
+
ok(proof?.status === "rejected");
|
|
272
|
+
strictEqual(proof.reason.type, "uncoveredAttribution");
|
|
273
|
+
const check = proof.checks[0];
|
|
274
|
+
ok(check.status === "verified" && check.key.type === "multikey");
|
|
275
|
+
strictEqual(check.key.controllerId?.href, "https://example.com/person2");
|
|
276
|
+
strictEqual(reports[0].activity?.id?.href, activity.id?.href);
|
|
277
|
+
});
|
|
278
|
+
test("onRequestFinished reports listener failures with original values", async () => {
|
|
279
|
+
const { federation, setters, reports } = setup(true);
|
|
280
|
+
const error = /* @__PURE__ */ new Error("listener failed");
|
|
281
|
+
setters.on(Create, () => {
|
|
282
|
+
throw error;
|
|
283
|
+
});
|
|
284
|
+
strictEqual((await federation.fetch(request(await payload()), { contextData: void 0 })).status, 500);
|
|
285
|
+
deepStrictEqual(reports[0].outcome, {
|
|
286
|
+
type: "response",
|
|
287
|
+
status: 500,
|
|
288
|
+
disposition: "failed",
|
|
289
|
+
reason: "listenerError",
|
|
290
|
+
error
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
test("onRequestFinished builder snapshots callbacks", async () => {
|
|
294
|
+
const builder = createFederationBuilder();
|
|
295
|
+
builder.setActorDispatcher("/users/{identifier}", () => new Person({}));
|
|
296
|
+
const reports = [];
|
|
297
|
+
const setters = builder.setInboxListeners("/users/{identifier}/inbox", "/inbox").onRequestFinished((_ctx, report) => {
|
|
298
|
+
reports.push(report);
|
|
299
|
+
});
|
|
300
|
+
const federation = await builder.build({
|
|
301
|
+
kv: new MemoryKvStore(),
|
|
302
|
+
skipSignatureVerification: true,
|
|
303
|
+
documentLoaderFactory: () => mockDocumentLoader,
|
|
304
|
+
contextLoaderFactory: () => mockDocumentLoader
|
|
305
|
+
});
|
|
306
|
+
setters.onRequestFinished(() => {
|
|
307
|
+
throw new Error("new callback must not run");
|
|
308
|
+
});
|
|
309
|
+
await federation.fetch(request(await payload()), { contextData: void 0 });
|
|
310
|
+
strictEqual(reports.length, 1);
|
|
311
|
+
});
|
|
312
|
+
test("onRequestFinished key snapshots do not alias vocabulary metadata", async () => {
|
|
313
|
+
const { snapshotKey } = await import("../verification-C3954hQh.mjs");
|
|
314
|
+
const snapshot = snapshotKey(rsaPublicKey2);
|
|
315
|
+
snapshot.id.pathname = "/changed";
|
|
316
|
+
strictEqual(rsaPublicKey2.id.href, "https://example.com/key2");
|
|
317
|
+
strictEqual(snapshotKey(rsaPublicKey3).publicKey, rsaPublicKey3.publicKey);
|
|
318
|
+
});
|
|
319
|
+
test("onRequestFinished reports enqueue acceptance without adding queue fields", async () => {
|
|
320
|
+
const messages = [];
|
|
321
|
+
const reports = [];
|
|
322
|
+
const federation = createFederation({
|
|
323
|
+
kv: new MemoryKvStore(),
|
|
324
|
+
skipSignatureVerification: true,
|
|
325
|
+
manuallyStartQueue: true,
|
|
326
|
+
queue: { inbox: {
|
|
327
|
+
enqueue: (message) => {
|
|
328
|
+
messages.push(message);
|
|
329
|
+
return Promise.resolve();
|
|
330
|
+
},
|
|
331
|
+
listen: () => Promise.resolve()
|
|
332
|
+
} },
|
|
333
|
+
documentLoaderFactory: () => mockDocumentLoader,
|
|
334
|
+
contextLoaderFactory: () => mockDocumentLoader
|
|
335
|
+
});
|
|
336
|
+
federation.setActorDispatcher("/users/{identifier}", () => new Person({}));
|
|
337
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").onRequestFinished((_ctx, report) => {
|
|
338
|
+
reports.push(report);
|
|
339
|
+
});
|
|
340
|
+
strictEqual((await federation.fetch(request(await payload()), { contextData: void 0 })).status, 202);
|
|
341
|
+
deepStrictEqual(reports[0].outcome, {
|
|
342
|
+
type: "response",
|
|
343
|
+
status: 202,
|
|
344
|
+
disposition: "enqueued"
|
|
345
|
+
});
|
|
346
|
+
strictEqual(messages.length, 1);
|
|
347
|
+
ok(!("report" in messages[0]));
|
|
348
|
+
ok(!("attempts" in messages[0]));
|
|
349
|
+
});
|
|
350
|
+
test("onRequestFinished preserves a non-Error value thrown during enqueue", async () => {
|
|
351
|
+
const reports = [];
|
|
352
|
+
const federation = createFederation({
|
|
353
|
+
kv: new MemoryKvStore(),
|
|
354
|
+
skipSignatureVerification: true,
|
|
355
|
+
manuallyStartQueue: true,
|
|
356
|
+
queue: { inbox: {
|
|
357
|
+
enqueue: () => Promise.reject(void 0),
|
|
358
|
+
listen: () => Promise.resolve()
|
|
359
|
+
} },
|
|
360
|
+
documentLoaderFactory: () => mockDocumentLoader,
|
|
361
|
+
contextLoaderFactory: () => mockDocumentLoader
|
|
362
|
+
});
|
|
363
|
+
federation.setActorDispatcher("/users/{identifier}", () => new Person({}));
|
|
364
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").onRequestFinished((_ctx, report) => {
|
|
365
|
+
reports.push(report);
|
|
366
|
+
throw new Error("observer failure");
|
|
367
|
+
});
|
|
368
|
+
let threw = false;
|
|
369
|
+
try {
|
|
370
|
+
await federation.fetch(request(await payload()), { contextData: void 0 });
|
|
371
|
+
} catch (error) {
|
|
372
|
+
threw = true;
|
|
373
|
+
strictEqual(error, void 0);
|
|
374
|
+
}
|
|
375
|
+
strictEqual(threw, true);
|
|
376
|
+
strictEqual(reports.length, 1);
|
|
377
|
+
deepStrictEqual(reports[0].outcome, {
|
|
378
|
+
type: "exception",
|
|
379
|
+
stage: "dispatch",
|
|
380
|
+
error: void 0
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
test("onRequestFinished runs with tracing disabled", async () => {
|
|
384
|
+
const { AlwaysOffSampler, BasicTracerProvider } = await import("@opentelemetry/sdk-trace-base");
|
|
385
|
+
const provider = new BasicTracerProvider({ sampler: new AlwaysOffSampler() });
|
|
386
|
+
const reports = [];
|
|
387
|
+
const federation = createFederation({
|
|
388
|
+
kv: new MemoryKvStore(),
|
|
389
|
+
tracerProvider: provider,
|
|
390
|
+
skipSignatureVerification: true,
|
|
391
|
+
documentLoaderFactory: () => mockDocumentLoader,
|
|
392
|
+
contextLoaderFactory: () => mockDocumentLoader
|
|
393
|
+
});
|
|
394
|
+
federation.setActorDispatcher("/users/{identifier}", () => new Person({}));
|
|
395
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").onRequestFinished((_ctx, report) => {
|
|
396
|
+
reports.push(report);
|
|
397
|
+
});
|
|
398
|
+
await federation.fetch(request(await payload()), { contextData: void 0 });
|
|
399
|
+
strictEqual(reports.length, 1);
|
|
400
|
+
await provider.shutdown();
|
|
401
|
+
});
|
|
402
|
+
test("onRequestFinished preserves non-POST shared inbox behavior without reports", async () => {
|
|
403
|
+
const { federation, reports } = setup(true);
|
|
404
|
+
strictEqual((await federation.fetch(new Request("https://example.com/inbox", { headers: { Accept: "application/activity+json" } }), { contextData: void 0 })).status, 400);
|
|
405
|
+
strictEqual(reports.length, 0);
|
|
406
|
+
strictEqual((await federation.fetch(new Request("https://example.com/inbox", {
|
|
407
|
+
method: "PUT",
|
|
408
|
+
body: JSON.stringify(await payload()),
|
|
409
|
+
headers: { Accept: "application/activity+json" }
|
|
410
|
+
}), { contextData: void 0 })).status, 202);
|
|
411
|
+
strictEqual(reports.length, 0);
|
|
412
|
+
});
|
|
413
|
+
test("onRequestFinished covers errors constructing the initial request context", async () => {
|
|
414
|
+
const reports = [];
|
|
415
|
+
const error = /* @__PURE__ */ new Error("loader factory failed");
|
|
416
|
+
const federation = createFederation({
|
|
417
|
+
kv: new MemoryKvStore(),
|
|
418
|
+
documentLoaderFactory: () => {
|
|
419
|
+
throw error;
|
|
420
|
+
},
|
|
421
|
+
contextLoaderFactory: () => mockDocumentLoader
|
|
422
|
+
});
|
|
423
|
+
federation.setActorDispatcher("/users/{identifier}", () => new Person({}));
|
|
424
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").onRequestFinished((ctx, report) => {
|
|
425
|
+
strictEqual(ctx.data, "data");
|
|
426
|
+
strictEqual(ctx.request.url, "https://example.com/inbox");
|
|
427
|
+
reports.push(report);
|
|
428
|
+
});
|
|
429
|
+
await rejects(federation.fetch(request({}), { contextData: "data" }), (value) => value === error);
|
|
430
|
+
strictEqual(reports.length, 1);
|
|
431
|
+
deepStrictEqual(reports[0].outcome, {
|
|
432
|
+
type: "exception",
|
|
433
|
+
stage: "prepare",
|
|
434
|
+
error
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
test("onRequestFinished observes the final response decoration result", async () => {
|
|
438
|
+
const { federation, setters, reports } = setup();
|
|
439
|
+
const redirect = Response.redirect("https://example.com/redirect", 302);
|
|
440
|
+
let immutable = false;
|
|
441
|
+
try {
|
|
442
|
+
redirect.headers.set("X-Header-Probe", "probe");
|
|
443
|
+
} catch {
|
|
444
|
+
immutable = true;
|
|
445
|
+
}
|
|
446
|
+
setters.onUnverifiedActivity(() => redirect);
|
|
447
|
+
const delivery = request(await payload());
|
|
448
|
+
delivery.headers.set("Accept", "application/activity+json");
|
|
449
|
+
let thrown;
|
|
450
|
+
let response;
|
|
451
|
+
try {
|
|
452
|
+
response = await federation.fetch(delivery, { contextData: void 0 });
|
|
453
|
+
} catch (error) {
|
|
454
|
+
thrown = error;
|
|
455
|
+
}
|
|
456
|
+
strictEqual(reports.length, 1);
|
|
457
|
+
if (immutable) {
|
|
458
|
+
ok(thrown instanceof TypeError);
|
|
459
|
+
deepStrictEqual(reports[0].outcome, {
|
|
460
|
+
type: "exception",
|
|
461
|
+
stage: "respond",
|
|
462
|
+
error: thrown
|
|
463
|
+
});
|
|
464
|
+
} else {
|
|
465
|
+
strictEqual(thrown, void 0);
|
|
466
|
+
strictEqual(response?.status, 302);
|
|
467
|
+
deepStrictEqual(reports[0].outcome, {
|
|
468
|
+
type: "response",
|
|
469
|
+
status: 302,
|
|
470
|
+
disposition: "customResponse"
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
//#endregion
|
|
475
|
+
export {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { t as assertEquals } from "../assert_equals-
|
|
5
|
-
import { t as assertThrows } from "../assert_throws-
|
|
6
|
-
import { t as ActivityListenerSet } from "../activity-listener-
|
|
4
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
5
|
+
import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
|
|
6
|
+
import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
|
|
7
7
|
import { Activity, Create, Invite, Offer, Update } from "@fedify/vocab";
|
|
8
8
|
import { test } from "@fedify/fixture";
|
|
9
9
|
//#region src/federation/inbox.test.ts
|