@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,495 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { d as isPortableUri } from "./portable-key-id-Ca30-hOa.mjs";
5
+ import { n as version, t as name } from "./deno-oR5aJKpU.mjs";
6
+ import { n as getDurationMs, r as getFederationMetrics } from "./metrics-CYg9yxBL.mjs";
7
+ import { n as doubleKnock } from "./http-siciWkmx.mjs";
8
+ import { getLogger } from "@logtape/logtape";
9
+ import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
10
+ import { FetchError, UrlError, canonicalizePortableUri, getFe34Origin, isGatewayUrl, toCompatibleEf61Id, validatePublicUrl } from "@fedify/vocab-runtime";
11
+ import { getPortableGatewayCandidates } from "@fedify/vocab-runtime/internal/portable-dereference";
12
+ /**
13
+ * Resolves a portable inbox, i.e., an `ap:` or `ap+ef61:` URI, to
14
+ * the compatible identifiers of the inbox on the recipient's gateways.
15
+ *
16
+ * The gateways are taken from the recipient's `gateways`, or, if it has no
17
+ * valid gateway, from the `@gateway` location hints of the inbox URI. Both
18
+ * come from the recipient object, so they are as trustworthy as its inbox.
19
+ * Gateways whose origins are in `excludeBaseUris` are skipped.
20
+ * @param recipient The recipient that owns the inbox.
21
+ * @param inbox The portable inbox.
22
+ * @param excludeBaseUris The base URIs whose origins are not delivered to.
23
+ * @returns The delivery target, or `null` if the inbox is malformed, or no
24
+ * gateway is left to deliver through.
25
+ */
26
+ function resolvePortableDeliveryTarget(recipient, inbox, excludeBaseUris) {
27
+ const logger = getLogger([
28
+ "fedify",
29
+ "federation",
30
+ "outbox"
31
+ ]);
32
+ let portableInbox;
33
+ try {
34
+ portableInbox = canonicalizePortableUri(inbox.href);
35
+ } catch (error) {
36
+ if (!(error instanceof TypeError)) throw error;
37
+ logger.warn("Skipping the recipient {recipient}, as its portable inbox {inbox} is malformed.", {
38
+ recipient: recipient.id?.href,
39
+ inbox: inbox.href,
40
+ error
41
+ });
42
+ return null;
43
+ }
44
+ let gateways = (recipient.gateways ?? []).filter(isGatewayUrl);
45
+ if (gateways.length < 1) gateways = getPortableGatewayCandidates(inbox);
46
+ if (gateways.length < 1) {
47
+ logger.warn("Skipping the recipient {recipient}, as it has no gateway to deliver to its portable inbox {inbox} through.", {
48
+ recipient: recipient.id?.href,
49
+ inbox: portableInbox
50
+ });
51
+ return null;
52
+ }
53
+ const excludedOrigins = new Set(excludeBaseUris?.map((u) => u.origin));
54
+ const seen = /* @__PURE__ */ new Set();
55
+ const inboxes = [];
56
+ for (const gateway of gateways) {
57
+ if (inboxes.length >= 5) break;
58
+ if (seen.has(gateway.origin)) continue;
59
+ seen.add(gateway.origin);
60
+ if (excludedOrigins.has(gateway.origin)) continue;
61
+ try {
62
+ inboxes.push(toCompatibleEf61Id(inbox, gateway.origin));
63
+ } catch (error) {
64
+ if (!(error instanceof TypeError)) throw error;
65
+ logger.warn("Cannot deliver to the portable inbox {inbox} through the gateway {gateway}, as the inbox cannot be represented as its compatible identifier.", {
66
+ inbox: portableInbox,
67
+ gateway: gateway.origin,
68
+ error
69
+ });
70
+ }
71
+ }
72
+ if (inboxes.length < 1) {
73
+ logger.debug("Skipping the recipient {recipient}, as no gateway is left to deliver to its portable inbox {inbox} through.", {
74
+ recipient: recipient.id?.href,
75
+ inbox: portableInbox
76
+ });
77
+ return null;
78
+ }
79
+ return {
80
+ portableInbox,
81
+ inboxes
82
+ };
83
+ }
84
+ /**
85
+ * Merges two ordered lists of compatible inbox URLs, keeping the first URL
86
+ * for each gateway origin and at most {@link MAX_DELIVERY_GATEWAYS} URLs.
87
+ * @param a The preferred list.
88
+ * @param b The other list.
89
+ * @returns The merged list.
90
+ */
91
+ function mergeGatewayInboxes(a, b) {
92
+ const seen = /* @__PURE__ */ new Set();
93
+ const merged = [];
94
+ for (const inbox of [...a, ...b]) {
95
+ if (merged.length >= 5) break;
96
+ const { origin } = new URL(inbox);
97
+ if (seen.has(origin)) continue;
98
+ seen.add(origin);
99
+ merged.push(inbox);
100
+ }
101
+ return merged;
102
+ }
103
+ /**
104
+ * Gets the part of a delivery's ordering key that tells its destination
105
+ * apart: the DID of a portable inbox, so that deliveries to it stay in order
106
+ * whichever gateway they go through, or the origin of any other inbox.
107
+ * @param inbox The inbox URL.
108
+ * @param portableInbox The canonical portable inbox, if the inbox is
109
+ * the compatible identifier of one.
110
+ * @returns The destination part of the ordering key.
111
+ */
112
+ function getOrderingDestination(inbox, portableInbox) {
113
+ return portableInbox == null ? new URL(inbox).origin : getFe34Origin(portableInbox);
114
+ }
115
+ //#endregion
116
+ //#region src/federation/send.ts
117
+ /**
118
+ * Extracts the inbox URLs from recipients.
119
+ *
120
+ * An [FEP-ef61] portable inbox, i.e., an `ap:` or `ap+ef61:` URI, is resolved
121
+ * to its compatible identifiers on the recipient's gateways, or, if the
122
+ * recipient has no valid gateway, on the gateways in the `@gateway` location
123
+ * hints of the inbox URI. The inbox is keyed by the compatible identifier on
124
+ * the first gateway, and the others are listed in
125
+ * {@link ExtractedInbox.gatewayInboxes}. A recipient whose portable inbox
126
+ * has no gateway to deliver through is skipped. The shared inbox of
127
+ * a recipient with a portable inbox is never preferred, since only a portable
128
+ * inbox is synchronized between the gateways.
129
+ *
130
+ * [FEP-ef61]: https://w3id.org/fep/ef61
131
+ * @param parameters The parameters to extract the inboxes.
132
+ * See also {@link ExtractInboxesParameters}.
133
+ * @returns The inboxes as a map of inbox URL to actor URIs.
134
+ */
135
+ function extractInboxes({ recipients, preferSharedInbox, excludeBaseUris }) {
136
+ const inboxes = {};
137
+ const portableKeys = /* @__PURE__ */ new Map();
138
+ const compatibleKeys = /* @__PURE__ */ new Map();
139
+ for (const recipient of recipients) {
140
+ if (recipient.id == null) continue;
141
+ let inbox;
142
+ let sharedInbox = false;
143
+ if (preferSharedInbox && recipient.endpoints?.sharedInbox != null && (recipient.inboxId == null || !isPortableUri(recipient.inboxId))) {
144
+ inbox = recipient.endpoints.sharedInbox;
145
+ sharedInbox = true;
146
+ } else inbox = recipient.inboxId;
147
+ if (inbox == null) continue;
148
+ if (isPortableUri(inbox)) {
149
+ const target = resolvePortableDeliveryTarget(recipient, inbox, excludeBaseUris);
150
+ if (target == null) continue;
151
+ const gatewayInboxes = target.inboxes.map((u) => u.href);
152
+ const existingKeys = /* @__PURE__ */ new Set();
153
+ const portableKey = portableKeys.get(target.portableInbox);
154
+ if (portableKey != null) existingKeys.add(portableKey);
155
+ for (const i of gatewayInboxes) {
156
+ const k = compatibleKeys.get(i) ?? i;
157
+ if (Object.hasOwn(inboxes, k)) existingKeys.add(k);
158
+ }
159
+ const [key = gatewayInboxes[0], ...otherKeys] = existingKeys;
160
+ portableKeys.set(target.portableInbox, key);
161
+ const entry = inboxes[key] ??= {
162
+ actorIds: /* @__PURE__ */ new Set(),
163
+ sharedInbox
164
+ };
165
+ for (const k of otherKeys) {
166
+ for (const actorId of inboxes[k].actorIds) entry.actorIds.add(actorId);
167
+ delete inboxes[k];
168
+ }
169
+ entry.sharedInbox = false;
170
+ entry.portableInbox = target.portableInbox;
171
+ entry.gatewayInboxes = mergeGatewayInboxes(entry.gatewayInboxes ?? [key], gatewayInboxes);
172
+ for (const i of entry.gatewayInboxes) compatibleKeys.set(i, key);
173
+ entry.actorIds.add(recipient.id.href);
174
+ continue;
175
+ }
176
+ if (excludeBaseUris != null && excludeBaseUris.some((u) => u.origin === inbox?.origin)) continue;
177
+ const key = compatibleKeys.get(inbox.href) ?? inbox.href;
178
+ inboxes[key] ??= {
179
+ actorIds: /* @__PURE__ */ new Set(),
180
+ sharedInbox
181
+ };
182
+ inboxes[key].actorIds.add(recipient.id.href);
183
+ }
184
+ return inboxes;
185
+ }
186
+ /**
187
+ * Sends an {@link Activity} to an inbox.
188
+ *
189
+ * @param parameters The parameters for sending the activity.
190
+ * See also {@link SendActivityParameters}.
191
+ * @throws {Error} If the activity fails to send.
192
+ */
193
+ function sendActivity(options) {
194
+ const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
195
+ return tracerProvider.getTracer(name, version).startActiveSpan("activitypub.send_activity", {
196
+ kind: SpanKind.CLIENT,
197
+ attributes: { "activitypub.shared_inbox": options.sharedInbox ?? false }
198
+ }, async (span) => {
199
+ if (options.activityId != null) span.setAttribute("activitypub.activity.id", options.activityId);
200
+ if (options.activityType != null) span.setAttribute("activitypub.activity.type", options.activityType);
201
+ try {
202
+ await sendActivityInternal({
203
+ ...options,
204
+ tracerProvider
205
+ }, span);
206
+ } catch (e) {
207
+ span.setStatus({
208
+ code: SpanStatusCode.ERROR,
209
+ message: String(e)
210
+ });
211
+ throw e;
212
+ } finally {
213
+ span.end();
214
+ }
215
+ });
216
+ }
217
+ /**
218
+ * Sends an activity to the first of the given inboxes that accepts it, i.e.,
219
+ * the compatible identifiers of a portable inbox on its gateways, trying them
220
+ * one after another.
221
+ * @param parameters The parameters, except for the inbox.
222
+ * @param inboxes The inboxes to try, in order. Must not be empty.
223
+ * @throws {Error} The error of the last inbox if no inbox accepts it.
224
+ */
225
+ async function sendActivityThroughGateways(parameters, inboxes) {
226
+ const logger = getLogger([
227
+ "fedify",
228
+ "federation",
229
+ "outbox"
230
+ ]);
231
+ let lastError;
232
+ for (const [i, inbox] of inboxes.entries()) try {
233
+ await sendActivity({
234
+ ...parameters,
235
+ inbox
236
+ });
237
+ return;
238
+ } catch (error) {
239
+ lastError = error;
240
+ if (i < inboxes.length - 1) logger.warn("Failed to send activity {activityId} to {inbox}; trying the next gateway {next}:\n{error}", {
241
+ activityId: parameters.activityId,
242
+ inbox: inbox.href,
243
+ next: inboxes[i + 1].href,
244
+ error
245
+ });
246
+ }
247
+ throw lastError;
248
+ }
249
+ const MAX_ERROR_RESPONSE_BODY_BYTES = 1024;
250
+ function getActivityActorId(activity) {
251
+ if (!isRecord(activity)) return void 0;
252
+ return getIdValue(activity.actor);
253
+ }
254
+ function getIdValue(value) {
255
+ if (typeof value === "string" && value !== "") return value;
256
+ if (value instanceof URL) return value.href;
257
+ if (Array.isArray(value)) {
258
+ for (const item of value) {
259
+ const id = getIdValue(item);
260
+ if (id != null) return id;
261
+ }
262
+ return;
263
+ }
264
+ if (isRecord(value)) return getIdValue(value.id);
265
+ }
266
+ function isRecord(value) {
267
+ return typeof value === "object" && value != null;
268
+ }
269
+ async function readLimitedResponseBody(response, maxBytes) {
270
+ if (response.body == null) return "";
271
+ const reader = response.body.getReader();
272
+ const decoder = new TextDecoder();
273
+ const chunks = [];
274
+ let totalBytes = 0;
275
+ let truncated = false;
276
+ try {
277
+ while (true) {
278
+ const { done, value } = await reader.read();
279
+ if (done) break;
280
+ if (totalBytes + value.length > maxBytes) {
281
+ const remaining = maxBytes - totalBytes;
282
+ if (remaining > 0) chunks.push(decoder.decode(value.slice(0, remaining), { stream: true }));
283
+ truncated = true;
284
+ break;
285
+ }
286
+ chunks.push(decoder.decode(value, { stream: true }));
287
+ totalBytes += value.length;
288
+ }
289
+ } finally {
290
+ reader.releaseLock();
291
+ }
292
+ let result = chunks.join("");
293
+ if (truncated) result += "… (truncated)";
294
+ return result;
295
+ }
296
+ async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, allowPrivateAddress, headers, specDeterminer, meterProvider, tracerProvider }, span) {
297
+ const logger = getLogger([
298
+ "fedify",
299
+ "federation",
300
+ "outbox"
301
+ ]);
302
+ const federationMetrics = getFederationMetrics(meterProvider);
303
+ const started = performance.now();
304
+ let deliverySuccess = false;
305
+ async function validateUrl(url) {
306
+ if (!allowPrivateAddress) try {
307
+ await validatePublicUrl(url);
308
+ } catch (error) {
309
+ if (error instanceof UrlError && error.reason === "dns") {
310
+ logger.error("DNS resolution failed for URL: {url}", {
311
+ url,
312
+ error
313
+ });
314
+ const failure = new FetchError(url, error.message);
315
+ failure.cause = error;
316
+ throw failure;
317
+ }
318
+ if (error instanceof UrlError) logger.error("Disallowed private URL: {url}", {
319
+ url,
320
+ error
321
+ });
322
+ throw error;
323
+ }
324
+ }
325
+ try {
326
+ await validateUrl(inbox.href);
327
+ } catch (error) {
328
+ if (error instanceof FetchError) federationMetrics.recordDelivery(inbox, getDurationMs(started), false, activityType);
329
+ throw error;
330
+ }
331
+ headers = new Headers(headers);
332
+ headers.set("Content-Type", "application/activity+json");
333
+ const request = new Request(inbox, {
334
+ method: "POST",
335
+ headers,
336
+ body: JSON.stringify(activity),
337
+ redirect: "manual"
338
+ });
339
+ let rsaKey = null;
340
+ for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
341
+ rsaKey = key;
342
+ break;
343
+ }
344
+ if (rsaKey == null && keys.length < 1) logger.debug("Sending the activity {activityId} to {inbox} without a signature.", {
345
+ activityId,
346
+ inbox: inbox.href
347
+ });
348
+ else if (rsaKey == null) logger.warn("No supported key found to sign the request to {inbox}. The request will be sent without a signature. In order to sign the request, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
349
+ inbox: inbox.href,
350
+ keys: keys.map((pair) => ({
351
+ keyId: pair.keyId.href,
352
+ privateKey: pair.privateKey
353
+ }))
354
+ });
355
+ let response;
356
+ try {
357
+ response = rsaKey == null ? await fetchWithValidatedRedirects(request, validateUrl) : await doubleKnock(request, rsaKey, {
358
+ tracerProvider,
359
+ specDeterminer,
360
+ validateRedirect: validateUrl
361
+ });
362
+ } catch (error) {
363
+ const failure = error instanceof FetchError || error instanceof UrlError ? error : createFetchError(inbox.href, error);
364
+ logger.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
365
+ activityId,
366
+ inbox: inbox.href,
367
+ error: failure
368
+ });
369
+ federationMetrics.recordDelivery(inbox, getDurationMs(started), false, activityType);
370
+ throw failure;
371
+ }
372
+ try {
373
+ if (!response.ok) {
374
+ let error;
375
+ try {
376
+ error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
377
+ } catch (_) {
378
+ error = "";
379
+ }
380
+ logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
381
+ activityId,
382
+ inbox: inbox.href,
383
+ status: response.status,
384
+ statusText: response.statusText,
385
+ error
386
+ });
387
+ throw new SendActivityError(inbox, response.status, `Failed to send activity ${activityId} to ${inbox.href} (${response.status} ${response.statusText}):\n${error}`, error, response.headers);
388
+ }
389
+ deliverySuccess = true;
390
+ const eventAttributes = {
391
+ "activitypub.inbox.url": inbox.href,
392
+ "activitypub.activity.id": activityId ?? ""
393
+ };
394
+ if (activityType != null) eventAttributes["activitypub.activity.type"] = activityType;
395
+ const actorId = getActivityActorId(activity);
396
+ if (actorId != null) eventAttributes["activitypub.actor.id"] = actorId;
397
+ span.addEvent("activitypub.activity.sent", eventAttributes);
398
+ } finally {
399
+ federationMetrics.recordDelivery(inbox, getDurationMs(started), deliverySuccess, activityType);
400
+ }
401
+ }
402
+ function createFetchError(url, cause) {
403
+ const error = new FetchError(url, cause instanceof Error ? cause.message : String(cause));
404
+ error.cause = cause;
405
+ return error;
406
+ }
407
+ async function fetchWithValidatedRedirects(request, validateUrl) {
408
+ let body = await request.clone().text();
409
+ for (let redirects = 0;; redirects++) {
410
+ const response = await fetch(request, { redirect: "manual" });
411
+ const location = response.headers.get("Location");
412
+ if (![
413
+ 301,
414
+ 302,
415
+ 303,
416
+ 307,
417
+ 308
418
+ ].includes(response.status) || location == null) return response;
419
+ await response.body?.cancel();
420
+ if (redirects >= 20) throw new TypeError("Too many redirects");
421
+ const url = new URL(location, request.url);
422
+ await validateUrl(url.href);
423
+ const headers = new Headers(request.headers);
424
+ if (url.origin !== new URL(request.url).origin) {
425
+ headers.delete("Authorization");
426
+ headers.delete("Proxy-Authorization");
427
+ headers.delete("Cookie");
428
+ headers.delete("Host");
429
+ }
430
+ let method = request.method;
431
+ if ((response.status === 301 || response.status === 302) && method === "POST" || response.status === 303 && method !== "GET" && method !== "HEAD") {
432
+ method = "GET";
433
+ body = void 0;
434
+ for (const name of [
435
+ "Content-Encoding",
436
+ "Content-Language",
437
+ "Content-Length",
438
+ "Content-Location",
439
+ "Content-Type"
440
+ ]) headers.delete(name);
441
+ }
442
+ request = new Request(url, {
443
+ method,
444
+ headers,
445
+ body,
446
+ redirect: "manual"
447
+ });
448
+ }
449
+ }
450
+ /**
451
+ * An error that is thrown when an activity fails to send to a remote inbox.
452
+ * It contains structured information about the failure, including the HTTP
453
+ * status code, the inbox URL, and the response body.
454
+ * @since 2.0.0
455
+ */
456
+ var SendActivityError = class extends Error {
457
+ /**
458
+ * The inbox URL that the activity was being sent to.
459
+ */
460
+ inbox;
461
+ /**
462
+ * The HTTP status code returned by the inbox.
463
+ */
464
+ statusCode;
465
+ /**
466
+ * The response body from the inbox, if any. Note that this may be
467
+ * truncated to a maximum of 1 KiB to prevent excessive memory consumption
468
+ * when remote servers return large error pages (e.g., Cloudflare error pages).
469
+ * If truncated, the string will end with `"… (truncated)"`.
470
+ */
471
+ responseBody;
472
+ /**
473
+ * The response headers from the inbox.
474
+ * @since 2.3.0
475
+ */
476
+ responseHeaders;
477
+ /**
478
+ * Creates a new {@link SendActivityError}.
479
+ * @param inbox The inbox URL.
480
+ * @param statusCode The HTTP status code.
481
+ * @param message The error message.
482
+ * @param responseBody The response body.
483
+ * @param responseHeaders The response headers.
484
+ */
485
+ constructor(inbox, statusCode, message, responseBody, responseHeaders) {
486
+ super(message);
487
+ this.name = "SendActivityError";
488
+ this.inbox = inbox;
489
+ this.statusCode = statusCode;
490
+ this.responseBody = responseBody;
491
+ this.responseHeaders = new Headers(responseHeaders);
492
+ }
493
+ };
494
+ //#endregion
495
+ export { getOrderingDestination as a, sendActivityThroughGateways as i, extractInboxes as n, sendActivity as r, SendActivityError as t };
@@ -1,7 +1,7 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature, t as formatAcceptSignature } from "../accept-8PiolDP7.mjs";
4
+ import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature, t as formatAcceptSignature } from "../accept-CPVqki78.mjs";
5
5
  import { test } from "@fedify/fixture";
6
6
  import { deepStrictEqual, strictEqual } from "node:assert/strict";
7
7
  //#region src/sig/accept.test.ts
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";