@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.
Files changed (244) hide show
  1. package/README.md +50 -39
  2. package/dist/{assert-CJ1KXcvv.mjs → assert-D1Eafmd_.mjs} +1 -1
  3. package/dist/{assert_equals-BV7oLMxm.mjs → assert_equals-Z6IcEbWU.mjs} +2 -28
  4. package/dist/assert_exists-cffSpJ7X.mjs +28 -0
  5. package/dist/assert_false-CPdRnc3E.mjs +23 -0
  6. package/dist/assert_greater_or_equal-BlafOF4-.mjs +31 -0
  7. package/dist/{assert_instance_of-Q9KTBnrf.mjs → assert_instance_of-mrXWWNY-.mjs} +1 -1
  8. package/dist/{assert_not_equals-EPJPoxuf.mjs → assert_not_equals-CpPK2t89.mjs} +2 -1
  9. package/dist/assert_rejects-CTptdYck.mjs +39 -0
  10. package/dist/{assert_strict_equals-Ec20AUwa.mjs → assert_strict_equals-CYvBmkpC.mjs} +2 -1
  11. package/dist/{assert_throws-NiFfqbnH.mjs → assert_throws-BYITOvPO.mjs} +2 -1
  12. package/dist/assertion_error-B-cx0fFA.mjs +31 -0
  13. package/dist/audience-CHQ-FlLD.mjs +95 -0
  14. package/dist/{builder-DonuxRl_.mjs → builder-Bda6LK7A.mjs} +169 -14
  15. package/dist/{circuit-breaker-CBGyz5F7.mjs → circuit-breaker-XJ53md4S.mjs} +224 -17
  16. package/dist/circuit-breaker-test-utils-DW6YC_Pe.mjs +14 -0
  17. package/dist/{client-CjF5IYQS.mjs → client-CEnNz8O7.mjs} +144 -8
  18. package/dist/{codec-BBga-FWI.mjs → codec-C6rTrpIp.mjs} +26 -3
  19. package/dist/{collection-BIhlldqK.mjs → collection-CnnP1UAv.mjs} +1 -1
  20. package/dist/compat/mod.cjs +1 -1
  21. package/dist/compat/mod.d.cts +1 -1
  22. package/dist/compat/mod.d.ts +1 -1
  23. package/dist/compat/mod.js +1 -1
  24. package/dist/compat/outgoing-jsonld.test.mjs +71 -3
  25. package/dist/compat/public-audience.test.mjs +14 -5
  26. package/dist/compat/secured-document.test.d.mts +2 -0
  27. package/dist/compat/secured-document.test.mjs +58 -0
  28. package/dist/compat/transformers.test.mjs +5 -5
  29. package/dist/compound-proof-B-9mXxFW.mjs +989 -0
  30. package/dist/{context-Zii1f5xS.d.ts → context-7NyiqIcg.d.ts} +1600 -25
  31. package/dist/{context-BJXxPElm.mjs → context-8Tu9kea0.mjs} +21 -63
  32. package/dist/{context-lxX6I7as.d.cts → context-tsYPJ2Ew.d.cts} +1600 -25
  33. package/dist/{deno-BHzKn9Ig.mjs → deno-oR5aJKpU.mjs} +1 -1
  34. package/dist/dist-DJQM4u7i.mjs +189 -0
  35. package/dist/docloader-DBoYbvYW.mjs +87 -0
  36. package/dist/{esm-yYQfPhIY.mjs → esm-BHXXx4Wx.mjs} +13 -6
  37. package/dist/federation/audience.test.d.mts +2 -0
  38. package/dist/federation/audience.test.mjs +130 -0
  39. package/dist/federation/builder.test.mjs +54 -5
  40. package/dist/federation/circuit-breaker.test.mjs +838 -7
  41. package/dist/federation/collection.test.mjs +3 -3
  42. package/dist/federation/compound-fanout.test.d.mts +2 -0
  43. package/dist/federation/compound-fanout.test.mjs +228 -0
  44. package/dist/federation/handler-compound-proof.test.d.mts +2 -0
  45. package/dist/federation/handler-compound-proof.test.mjs +384 -0
  46. package/dist/federation/handler.test.mjs +385 -15
  47. package/dist/federation/hashlink-media.test.d.mts +2 -0
  48. package/dist/federation/hashlink-media.test.mjs +422 -0
  49. package/dist/federation/idempotency.test.mjs +6 -6
  50. package/dist/federation/inbox-report.test.d.mts +2 -0
  51. package/dist/federation/inbox-report.test.mjs +475 -0
  52. package/dist/federation/inbox.test.mjs +3 -3
  53. package/dist/federation/keycache.test.mjs +215 -14
  54. package/dist/federation/kv.test.mjs +3 -3
  55. package/dist/federation/metrics.test.mjs +5 -5
  56. package/dist/federation/middleware.test.mjs +967 -35
  57. package/dist/federation/mod.cjs +18 -261
  58. package/dist/federation/mod.d.cts +5 -5
  59. package/dist/federation/mod.d.ts +5 -5
  60. package/dist/federation/mod.js +279 -1
  61. package/dist/federation/mq.test.mjs +123 -47
  62. package/dist/federation/negotiation.test.mjs +4 -4
  63. package/dist/federation/outgoing-proof.test.d.mts +2 -0
  64. package/dist/federation/outgoing-proof.test.mjs +931 -0
  65. package/dist/federation/portable-actor.test.d.mts +2 -0
  66. package/dist/federation/portable-actor.test.mjs +496 -0
  67. package/dist/federation/portable-chain.test.d.mts +2 -0
  68. package/dist/federation/portable-chain.test.mjs +370 -0
  69. package/dist/federation/portable-collection.test.d.mts +2 -0
  70. package/dist/federation/portable-collection.test.mjs +832 -0
  71. package/dist/federation/portable-delivery.test.d.mts +2 -0
  72. package/dist/federation/portable-delivery.test.mjs +746 -0
  73. package/dist/federation/portable-gateway-keys.test.d.mts +2 -0
  74. package/dist/federation/portable-gateway-keys.test.mjs +686 -0
  75. package/dist/federation/portable-inbox.test.d.mts +2 -0
  76. package/dist/federation/portable-inbox.test.mjs +1535 -0
  77. package/dist/federation/portable-parse-uri.test.d.mts +2 -0
  78. package/dist/federation/portable-parse-uri.test.mjs +348 -0
  79. package/dist/federation/portable-route-activity.test.d.mts +2 -0
  80. package/dist/federation/portable-route-activity.test.mjs +259 -0
  81. package/dist/federation/portable.test.d.mts +2 -0
  82. package/dist/federation/portable.test.mjs +1072 -0
  83. package/dist/federation/retry.test.mjs +4 -3
  84. package/dist/federation/router.test.mjs +9 -159
  85. package/dist/federation/send.test.mjs +245 -27
  86. package/dist/federation/tasks/codec.test.mjs +4 -4
  87. package/dist/federation/tasks/enqueue.test.mjs +42 -4
  88. package/dist/federation/tasks/tasks.test.mjs +7 -6
  89. package/dist/federation/temporal.test.mjs +4 -4
  90. package/dist/federation/webfinger.test.mjs +275 -12
  91. package/dist/{middleware-FrSMAYEx.cjs → federation-CkZiAcWY.cjs} +8640 -3722
  92. package/dist/http-B5JT115n.cjs +10630 -0
  93. package/dist/http-DydWCB7R.js +6527 -0
  94. package/dist/{http-CPVMUgsW.mjs → http-siciWkmx.mjs} +397 -168
  95. package/dist/key-BspPiXqn.mjs +951 -0
  96. package/dist/key-C1nv7_jD.d.ts +1125 -0
  97. package/dist/key-DYxuyd-s.d.cts +1125 -0
  98. package/dist/key-owner-evidence-APB6NpSk.mjs +108 -0
  99. package/dist/keycache-Da8A8mZa.mjs +200 -0
  100. package/dist/{kv-D6hNiMTK.d.ts → kv-B2mzK5tl.d.cts} +3 -2
  101. package/dist/{kv-gJ8LYbxX.d.cts → kv-B2mzK5tl.d.ts} +3 -2
  102. package/dist/kv-DBRWVx3Q.mjs +104 -0
  103. package/dist/{kv-cache-KyGqJqSp.mjs → kv-cache-BF1hnW65.mjs} +1 -1
  104. package/dist/{kv-cache-5bnX3-Jq.js → kv-cache-CTQr1tbn.js} +54 -16
  105. package/dist/{kv-cache-DQ29C4XL.cjs → kv-cache-DCac1nEU.cjs} +55 -19
  106. package/dist/{ld-Cj9F_5AP.mjs → ld-CzaP-hJl.mjs} +44 -12
  107. package/dist/map-local-context-conflict-DOYEEJ3u.mjs +204 -0
  108. package/dist/map-local-create-note-B0SeXJms.mjs +214 -0
  109. package/dist/{metrics-DMk-nTxI.mjs → metrics-CYg9yxBL.mjs} +3 -2
  110. package/dist/metrics-DVKYY6K1.d.cts +36 -0
  111. package/dist/metrics-DoRtI0qU.d.ts +36 -0
  112. package/dist/{middleware-Y-kpsp6S.js → middleware-CsDDXKuL.js} +5493 -1103
  113. package/dist/{middleware-CvwTUgnP.mjs → middleware-D7_LO-Vs.mjs} +3157 -272
  114. package/dist/{middleware-CEctxBN6.mjs → middleware-DWjrfpcz.mjs} +1 -1
  115. package/dist/{mod-gOlPDuT8.d.cts → mod-BojP2Bw3.d.cts} +1 -1
  116. package/dist/mod-CavhAgLw.d.cts +320 -0
  117. package/dist/mod-CtpF2BU5.d.ts +320 -0
  118. package/dist/{mod-BtEaIEcD.d.cts → mod-DEZXwV6J.d.ts} +10 -4
  119. package/dist/{mod-UMmveiO0.d.ts → mod-Dw-nPxQh.d.cts} +10 -4
  120. package/dist/{mod-DLc2Ot6f.d.ts → mod-Xj5P4s4d.d.ts} +1 -1
  121. package/dist/mod.cjs +40 -39
  122. package/dist/mod.d.cts +10 -11
  123. package/dist/mod.d.ts +10 -11
  124. package/dist/mod.js +9 -9
  125. package/dist/{mq-llHKivST.mjs → mq-Dm1Wmtm-.mjs} +6 -0
  126. package/dist/nodeinfo/client.test.mjs +139 -4
  127. package/dist/nodeinfo/handler.test.mjs +5 -5
  128. package/dist/nodeinfo/mod.cjs +1 -1
  129. package/dist/nodeinfo/mod.d.cts +1 -1
  130. package/dist/nodeinfo/mod.d.ts +1 -1
  131. package/dist/nodeinfo/mod.js +1 -1
  132. package/dist/nodeinfo/types.test.mjs +4 -4
  133. package/dist/otel/exporter.test.mjs +3 -3
  134. package/dist/otel/mod.cjs +0 -1
  135. package/dist/otel/mod.d.cts +1 -1
  136. package/dist/otel/mod.d.ts +1 -1
  137. package/dist/{outgoing-jsonld-URuqsICz.mjs → outgoing-jsonld-4IJqPYos.mjs} +12 -10
  138. package/dist/{owner-DEvZuyOE.d.cts → owner-BF0L9cMt.d.cts} +11 -0
  139. package/dist/{owner-CnngXDNJ.d.ts → owner-BSzr5-I8.d.ts} +11 -0
  140. package/dist/owner-DYgfTFHy.mjs +267 -0
  141. package/dist/portable-collection-BpsVsRov.js +670 -0
  142. package/dist/portable-collection-C74P1vtC.mjs +418 -0
  143. package/dist/portable-collection-DZd2bm7M.cjs +686 -0
  144. package/dist/portable-key-BIMLrAP4.mjs +549 -0
  145. package/dist/portable-key-id-Ca30-hOa.mjs +248 -0
  146. package/dist/proof-Dxy6ByqI.mjs +1299 -0
  147. package/dist/{public-audience-Cr5MsnMq.mjs → public-audience--klBIOk1.mjs} +15 -10
  148. package/dist/router-u5O5KCQr.mjs +156 -0
  149. package/dist/runtime/mod.cjs +1 -1
  150. package/dist/runtime/mod.js +1 -1
  151. package/dist/secured-document-CxzZZ9nL.mjs +35 -0
  152. package/dist/send-C4dDpAxm.mjs +495 -0
  153. package/dist/sig/accept.test.mjs +1 -1
  154. package/dist/sig/compound-proof-discovery.test.d.mts +2 -0
  155. package/dist/sig/compound-proof-discovery.test.mjs +331 -0
  156. package/dist/sig/compound-proof-producer.test.d.mts +2 -0
  157. package/dist/sig/compound-proof-producer.test.mjs +107 -0
  158. package/dist/sig/compound-proof-verification.test.d.mts +2 -0
  159. package/dist/sig/compound-proof-verification.test.mjs +833 -0
  160. package/dist/sig/compound-proof.test.d.mts +2 -0
  161. package/dist/sig/compound-proof.test.mjs +411 -0
  162. package/dist/sig/http.test.mjs +702 -31
  163. package/dist/sig/key-owner-evidence.test.d.mts +2 -0
  164. package/dist/sig/key-owner-evidence.test.mjs +100 -0
  165. package/dist/sig/key.test.mjs +220 -8
  166. package/dist/sig/ld.test.mjs +65 -7
  167. package/dist/sig/mitra-receive-interop.test.d.mts +2 -0
  168. package/dist/sig/mitra-receive-interop.test.mjs +260 -0
  169. package/dist/sig/mitra-send-interop.test.d.mts +2 -0
  170. package/dist/sig/mitra-send-interop.test.mjs +461 -0
  171. package/dist/sig/mod.cjs +18 -16
  172. package/dist/sig/mod.d.cts +4 -4
  173. package/dist/sig/mod.d.ts +4 -4
  174. package/dist/sig/mod.js +3 -3
  175. package/dist/sig/owner.test.mjs +108 -9
  176. package/dist/sig/portable-collection.test.d.mts +2 -0
  177. package/dist/sig/portable-collection.test.mjs +697 -0
  178. package/dist/sig/portable-interop.test.d.mts +2 -0
  179. package/dist/sig/portable-interop.test.mjs +156 -0
  180. package/dist/sig/portable-key-id.test.d.mts +2 -0
  181. package/dist/sig/portable-key-id.test.mjs +67 -0
  182. package/dist/sig/portable-key.test.d.mts +2 -0
  183. package/dist/sig/portable-key.test.mjs +1439 -0
  184. package/dist/sig/proof.test.mjs +1753 -16
  185. package/dist/sig/tootik-request-interop.test.d.mts +2 -0
  186. package/dist/sig/tootik-request-interop.test.mjs +190 -0
  187. package/dist/sig/tootik-send-interop.test.d.mts +2 -0
  188. package/dist/sig/tootik-send-interop.test.mjs +331 -0
  189. package/dist/sig/verification.test.d.mts +2 -0
  190. package/dist/sig/verification.test.mjs +310 -0
  191. package/dist/std__assert-DvMDRPes.mjs +8 -0
  192. package/dist/{testing-CO7ftO8A.mjs → tasks-IG3pHfvZ.mjs} +2 -2
  193. package/dist/{temporal-fsXO5IvD.mjs → temporal-D0KJI4NF.mjs} +3 -3
  194. package/dist/testing/context.test.d.mts +2 -0
  195. package/dist/testing/context.test.mjs +19 -0
  196. package/dist/testing/kv.test.d.mts +2 -0
  197. package/dist/testing/kv.test.mjs +101 -0
  198. package/dist/testing/mod.d.mts +1617 -48
  199. package/dist/testing/mod.mjs +6 -3
  200. package/dist/testing-t8xDeJZ5.mjs +8 -0
  201. package/dist/{transformers-DGwl-hD1.cjs → transformers-BalCrSsr.cjs} +0 -1
  202. package/dist/{types-DC1pNgJA.js → types-_S2_MeLF.js} +144 -8
  203. package/dist/{types-CPym83zc.cjs → types-poIao40f.cjs} +160 -7
  204. package/dist/utils/docloader.test.mjs +530 -8
  205. package/dist/utils/kv-cache.test.mjs +2 -2
  206. package/dist/utils/mod.cjs +1 -1
  207. package/dist/utils/mod.d.cts +1 -1
  208. package/dist/utils/mod.d.ts +1 -1
  209. package/dist/utils/mod.js +1 -1
  210. package/dist/verification-C3954hQh.mjs +5 -0
  211. package/dist/verification-Dpfcw8ml.mjs +105 -0
  212. package/dist/vocab/cjs.test.mjs +1 -1
  213. package/package.json +8 -8
  214. package/dist/assert_rejects-EgstcQy5.mjs +0 -82
  215. package/dist/docloader-e1khNUKy.mjs +0 -49
  216. package/dist/http-CsmQ-BQ4.js +0 -2483
  217. package/dist/http-VyDTd4G3.d.cts +0 -553
  218. package/dist/http-hlIjmewS.cjs +0 -2694
  219. package/dist/http-lf8Hsd91.d.ts +0 -553
  220. package/dist/key-DfPsX8hv.mjs +0 -453
  221. package/dist/keycache-_DHqz5z_.mjs +0 -97
  222. package/dist/metrics-CoUJLQhZ.d.cts +0 -276
  223. package/dist/metrics-Dc4jR-IC.d.ts +0 -276
  224. package/dist/mod-B0hW12_O.d.cts +0 -307
  225. package/dist/mod-COIAjwRS.d.ts +0 -307
  226. package/dist/owner-Br9YYzn_.mjs +0 -120
  227. package/dist/proof-B4ngoaNQ.mjs +0 -316
  228. package/dist/proof-B6E0XUXJ.js +0 -1415
  229. package/dist/proof-NN4xCQGL.cjs +0 -1562
  230. package/dist/send-CxHKUNzv.mjs +0 -239
  231. package/dist/std__assert-B2NnY9bq.mjs +0 -79
  232. package/dist/temporal-polyfill-BCdTHexd.cjs +0 -3565
  233. /package/dist/{accept-8PiolDP7.mjs → accept-CPVqki78.mjs} +0 -0
  234. /package/dist/{activity-listener-BtTSxZWA.mjs → activity-listener-C-BSbKI7.mjs} +0 -0
  235. /package/dist/{client-CAM_bQXx.d.cts → client-D-XP_Ccb.d.cts} +0 -0
  236. /package/dist/{client-CSddvgWN.d.ts → client-D-XP_Ccb.d.ts} +0 -0
  237. /package/dist/{keys-Dbyd7bhl.mjs → keys-DIJNHW-Y.mjs} +0 -0
  238. /package/dist/{kv-CXBAok3k.mjs → kv-DCHkaJ9-.mjs} +0 -0
  239. /package/dist/{negotiation-RuwrPFqi.mjs → negotiation-CCocWT_N.mjs} +0 -0
  240. /package/dist/{retry-DgFqeS5k.mjs → retry-zX5xw5e0.mjs} +0 -0
  241. /package/dist/{chunk-D5IdezsZ.js → rolldown-runtime-BC-Eo5Mj.js} +0 -0
  242. /package/dist/{chunk-Czpeue4M.cjs → rolldown-runtime-Dmm3t92W.cjs} +0 -0
  243. /package/dist/{transformers-DXze6FUB.js → transformers-DAtXl_Kc.js} +0 -0
  244. /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-BV7oLMxm.mjs";
5
- import { t as assertThrows } from "../assert_throws-NiFfqbnH.mjs";
6
- import { t as ActivityListenerSet } from "../activity-listener-BtTSxZWA.mjs";
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