@fedify/fedify 2.0.0-pr.490.2 → 2.0.0-pr.559.4

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 (267) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +64 -37
  3. package/dist/{builder-4syLV1-z.js → builder-DTlQwmVF.js} +10 -3
  4. package/dist/{client-BsGzbnV-.d.ts → client-CUTUGgvJ.d.ts} +18 -18
  5. package/dist/{client-pY7-3icS.js → client-Dg7OfUDA.js} +28 -23
  6. package/dist/{client-94iWEfQa.d.cts → client-by-PEGAJ.d.cts} +18 -18
  7. package/dist/compat/mod.cjs +1 -1
  8. package/dist/compat/mod.d.cts +6 -10
  9. package/dist/compat/mod.d.ts +6 -10
  10. package/dist/compat/mod.js +1 -1
  11. package/dist/compat/transformers.test.js +22 -21
  12. package/dist/{context-PxGADCsD.d.cts → context-B6X-7loD.d.cts} +206 -74
  13. package/dist/{context-V-XS2_6O.d.ts → context-CJaICYPw.d.ts} +206 -74
  14. package/dist/context-CZ5llAss.js +109 -0
  15. package/dist/deno-DGx1JZHr.js +124 -0
  16. package/dist/{testing-BslrM_9E.js → dist-B5f6a8Tt.js} +90 -110
  17. package/dist/{docloader-DndkGj0O.js → docloader-D8UHsyqD.js} +3 -3
  18. package/dist/{esm-VlKMJQqV.js → esm-DGl7uK1r.js} +1 -1
  19. package/dist/federation/builder.test.js +7 -5
  20. package/dist/federation/collection.test.js +2 -3
  21. package/dist/federation/handler.test.js +24 -23
  22. package/dist/federation/idempotency.test.js +59 -22
  23. package/dist/federation/inbox.test.js +4 -3
  24. package/dist/federation/keycache.test.js +4 -4
  25. package/dist/federation/kv.test.js +56 -3
  26. package/dist/federation/middleware.test.js +307 -93
  27. package/dist/federation/mod.cjs +9 -10
  28. package/dist/federation/mod.d.cts +7 -11
  29. package/dist/federation/mod.d.ts +7 -11
  30. package/dist/federation/mod.js +8 -11
  31. package/dist/federation/mq.test.js +167 -16
  32. package/dist/federation/negotiation.test.js +2 -3
  33. package/dist/federation/retry.test.js +2 -3
  34. package/dist/federation/router.test.js +2 -2
  35. package/dist/federation/send.test.js +93 -11
  36. package/dist/{webfinger/handler.test.js → federation/webfinger.test.js} +24 -22
  37. package/dist/{federation-CRpdnOMS.cjs → federation-CE0CJ_0G.cjs} +116 -10
  38. package/dist/{federation-jcR8-ZxP.js → federation-D6FVaeAR.js} +122 -16
  39. package/dist/{http-YhR_TMMQ.js → http-CL3G0rnf.js} +126 -9
  40. package/dist/{http-M8k5mKc0.d.cts → http-ClB3pLcL.d.cts} +1 -1
  41. package/dist/{http-Dxpqz4hE.cjs → http-DKBUv5zZ.cjs} +139 -16
  42. package/dist/{http-BbO0ejuk.d.ts → http-DLBDPal9.d.ts} +1 -1
  43. package/dist/{http-DH47B-h3.js → http-LGtYlSfN.js} +3 -2
  44. package/dist/{inbox-CEyHvxOo.js → inbox-DbtWQY2D.js} +2 -1
  45. package/dist/{key-x7E5PYI0.js → key-BCtt1Ugy.js} +3 -3
  46. package/dist/{keycache-BRXuBDuy.js → keycache-DRxpZ5r9.js} +1 -1
  47. package/dist/{keys-DLk_8H-l.js → keys-ZbcByPg9.js} +1 -1
  48. package/dist/{kv-Bxr0Q87_.d.cts → kv-B4vFhIYL.d.cts} +30 -1
  49. package/dist/{kv-BKNZ-Tb-.d.ts → kv-CYySNrsn.d.ts} +30 -1
  50. package/dist/{kv-CRZrzyXm.js → kv-QzKcOQgP.js} +22 -0
  51. package/dist/{kv-cache-HFnFIjSD.js → kv-cache-0786BfqY.js} +3 -3
  52. package/dist/{kv-cache-DN9pfMBe.js → kv-cache-B__dHl7g.js} +15 -2
  53. package/dist/{kv-cache-BMpfJFTx.cjs → kv-cache-DCJojeTn.cjs} +3 -3
  54. package/dist/{ld-CRPaU6c8.js → ld-QlZPwGEH.js} +4 -3
  55. package/dist/middleware-B3jUPnDa.js +12 -0
  56. package/dist/middleware-BFiwWMA2.cjs +12 -0
  57. package/dist/middleware-DMx6DyIw.js +26 -0
  58. package/dist/{middleware-DfLpMu7C.js → middleware-Dm58nObp.js} +280 -166
  59. package/dist/{middleware-BIqFwRwI.js → middleware-WokE4qxc.js} +245 -178
  60. package/dist/{middleware-Ck7O6mb0.cjs → middleware-hWyKOO_6.cjs} +332 -206
  61. package/dist/{mod-DMpuiKXi.d.cts → mod-BHXq4Q3x.d.cts} +7 -7
  62. package/dist/{mod-DgxG-byT.d.cts → mod-BrS8tiad.d.cts} +2 -2
  63. package/dist/mod-CoMP50Rf.d.ts +64 -0
  64. package/dist/{mod-BoRKfJPE.d.cts → mod-DScazwCW.d.cts} +4 -4
  65. package/dist/mod-DTzN6Pv3.d.cts +62 -0
  66. package/dist/{mod-aAE2wOWV.d.ts → mod-DZmuPaKv.d.ts} +7 -7
  67. package/dist/{mod-D5Z2tISD.d.ts → mod-jOa7W503.d.ts} +2 -2
  68. package/dist/{mod-Cdo6SYlJ.d.ts → mod-xKJ57rwu.d.ts} +4 -4
  69. package/dist/mod.cjs +12 -93
  70. package/dist/mod.d.cts +11 -15
  71. package/dist/mod.d.ts +11 -15
  72. package/dist/mod.js +11 -15
  73. package/dist/nodeinfo/client.test.js +3 -4
  74. package/dist/nodeinfo/handler.test.js +22 -21
  75. package/dist/nodeinfo/mod.cjs +2 -2
  76. package/dist/nodeinfo/mod.d.cts +2 -2
  77. package/dist/nodeinfo/mod.d.ts +2 -2
  78. package/dist/nodeinfo/mod.js +2 -2
  79. package/dist/nodeinfo/types.test.js +2 -3
  80. package/dist/otel/exporter.test.js +893 -0
  81. package/dist/otel/mod.cjs +256 -0
  82. package/dist/otel/mod.d.cts +230 -0
  83. package/dist/otel/mod.d.ts +232 -0
  84. package/dist/otel/mod.js +255 -0
  85. package/dist/{owner-kQRGVXG1.d.ts → owner-BgI8C-VY.d.ts} +1 -2
  86. package/dist/{owner-B4HbyP8s.d.cts → owner-C-zfmVAD.d.cts} +1 -2
  87. package/dist/{owner-CIWnopkT.js → owner-Cejm-F7S.js} +2 -2
  88. package/dist/{proof-D-5ri6rf.js → proof-BOQBHd-i.js} +3 -2
  89. package/dist/{proof-fEwcA7LA.cjs → proof-Bmi8ZIcW.cjs} +24 -25
  90. package/dist/{proof-C8-2l0zH.js → proof-CnaEQ_Ev.js} +4 -5
  91. package/dist/router-D9eI0s4b.js +118 -0
  92. package/dist/{send-CPGk9QKZ.js → send-jFxXfsN8.js} +38 -4
  93. package/dist/sig/http.test.js +6 -7
  94. package/dist/sig/key.test.js +5 -5
  95. package/dist/sig/ld.test.js +6 -6
  96. package/dist/sig/mod.cjs +3 -5
  97. package/dist/sig/mod.d.cts +3 -5
  98. package/dist/sig/mod.d.ts +3 -5
  99. package/dist/sig/mod.js +3 -5
  100. package/dist/sig/owner.test.js +6 -7
  101. package/dist/sig/proof.test.js +6 -6
  102. package/dist/testing/mod.d.ts +173 -7006
  103. package/dist/testing/mod.js +4 -3
  104. package/dist/{transformers-CoBS-oFG.cjs → transformers-BjBg6Lag.cjs} +2 -2
  105. package/dist/{transformers-BFT6d7J5.js → transformers-N_ip_y4P.js} +2 -2
  106. package/dist/{types-BtUjyi5y.js → types-8l28uC8o.js} +30 -25
  107. package/dist/{types-CWgzGaqk.cjs → types-B6z6CqIz.cjs} +30 -25
  108. package/dist/{types-C2XVl6gj.js → types-CPz01LGH.js} +3 -3
  109. package/dist/utils/docloader.test.js +7 -8
  110. package/dist/utils/kv-cache.test.js +5 -3
  111. package/dist/utils/mod.cjs +3 -5
  112. package/dist/utils/mod.d.cts +3 -4
  113. package/dist/utils/mod.d.ts +3 -4
  114. package/dist/utils/mod.js +3 -5
  115. package/dist/vocab/mod.cjs +8 -81
  116. package/dist/vocab/mod.d.cts +1 -4
  117. package/dist/vocab/mod.d.ts +1 -4
  118. package/dist/vocab/mod.js +1 -5
  119. package/package.json +27 -27
  120. package/dist/actor-BT-e5fn9.js +0 -146
  121. package/dist/actor-B_gRMloq.js +0 -41647
  122. package/dist/actor-CBfPjuWj.cjs +0 -42079
  123. package/dist/actor-DqFajh9s.d.ts +0 -130
  124. package/dist/actor-f2NtjyCg.d.cts +0 -128
  125. package/dist/fixtures/activitypub.academy/users/brauca_darradiul.json +0 -83
  126. package/dist/fixtures/example.com/announce.json +0 -6
  127. package/dist/fixtures/example.com/collection.json +0 -19
  128. package/dist/fixtures/example.com/create.json +0 -6
  129. package/dist/fixtures/example.com/cross-origin-actor.json +0 -6
  130. package/dist/fixtures/example.com/hong-gildong.json +0 -11
  131. package/dist/fixtures/example.com/invite.json +0 -7
  132. package/dist/fixtures/example.com/key.json +0 -7
  133. package/dist/fixtures/example.com/key2.json +0 -6
  134. package/dist/fixtures/example.com/object.json +0 -6
  135. package/dist/fixtures/example.com/orderedcollectionpage.json +0 -24
  136. package/dist/fixtures/example.com/paged/a.json +0 -13
  137. package/dist/fixtures/example.com/paged/b.json +0 -16
  138. package/dist/fixtures/example.com/paged-collection.json +0 -6
  139. package/dist/fixtures/example.com/person.json +0 -22
  140. package/dist/fixtures/example.com/person2.json +0 -40
  141. package/dist/fixtures/example.com/test.json +0 -5
  142. package/dist/fixtures/example.com/users/handle.json +0 -16
  143. package/dist/fixtures/example.com/wrong-type.json +0 -3
  144. package/dist/fixtures/media.example.com/avatars/test-avatar.jpg.json +0 -6
  145. package/dist/fixtures/oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd.json +0 -24
  146. package/dist/fixtures/remote.domain/users/bob.json +0 -20
  147. package/dist/fixtures/server.example/users/alice.json +0 -20
  148. package/dist/fixtures/w3id.org/identity/v1.json +0 -152
  149. package/dist/fixtures/w3id.org/security/data-integrity/v1.json +0 -74
  150. package/dist/fixtures/w3id.org/security/multikey/v1.json +0 -35
  151. package/dist/fixtures/w3id.org/security/v1.json +0 -50
  152. package/dist/fixtures/wizard.casa/users/hongminhee.json +0 -69
  153. package/dist/fixtures/www.w3.org/ns/activitystreams.json +0 -379
  154. package/dist/fixtures/www.w3.org/ns/did/v1.json +0 -58
  155. package/dist/lookup-BTqtVATt.cjs +0 -266
  156. package/dist/lookup-DOSnR912.js +0 -254
  157. package/dist/lookup-Dj9-mgOn.js +0 -42184
  158. package/dist/middleware-CxswDtQn.js +0 -15
  159. package/dist/middleware-CyITsnX0.js +0 -26
  160. package/dist/middleware-Z8lc_drL.cjs +0 -15
  161. package/dist/mod-BlVovdcy.d.ts +0 -309
  162. package/dist/mod-BxRCHTz-.d.cts +0 -307
  163. package/dist/mod-C58MZ7Wx.d.cts +0 -113
  164. package/dist/mod-CcDPcLJW.d.cts +0 -1
  165. package/dist/mod-Ds0mpFZU.d.ts +0 -115
  166. package/dist/mod-bjzj5QIb.d.ts +0 -2
  167. package/dist/otel-1BmGPuZc.js +0 -64
  168. package/dist/src/vocab/accept.yaml +0 -15
  169. package/dist/src/vocab/activity.yaml +0 -98
  170. package/dist/src/vocab/add.yaml +0 -16
  171. package/dist/src/vocab/announce.yaml +0 -30
  172. package/dist/src/vocab/application.yaml +0 -324
  173. package/dist/src/vocab/arrive.yaml +0 -15
  174. package/dist/src/vocab/article.yaml +0 -46
  175. package/dist/src/vocab/audio.yaml +0 -11
  176. package/dist/src/vocab/block.yaml +0 -16
  177. package/dist/src/vocab/chatmessage.yaml +0 -50
  178. package/dist/src/vocab/collection.yaml +0 -154
  179. package/dist/src/vocab/collectionpage.yaml +0 -55
  180. package/dist/src/vocab/create.yaml +0 -28
  181. package/dist/src/vocab/dataintegrityproof.yaml +0 -56
  182. package/dist/src/vocab/delete.yaml +0 -27
  183. package/dist/src/vocab/didservice.yaml +0 -22
  184. package/dist/src/vocab/dislike.yaml +0 -14
  185. package/dist/src/vocab/document.yaml +0 -31
  186. package/dist/src/vocab/emoji.yaml +0 -12
  187. package/dist/src/vocab/emojireact.yaml +0 -17
  188. package/dist/src/vocab/endpoints.yaml +0 -85
  189. package/dist/src/vocab/event.yaml +0 -11
  190. package/dist/src/vocab/export.yaml +0 -9
  191. package/dist/src/vocab/flag.yaml +0 -15
  192. package/dist/src/vocab/follow.yaml +0 -19
  193. package/dist/src/vocab/group.yaml +0 -324
  194. package/dist/src/vocab/hashtag.yaml +0 -14
  195. package/dist/src/vocab/ignore.yaml +0 -14
  196. package/dist/src/vocab/image.yaml +0 -9
  197. package/dist/src/vocab/intransitiveactivity.yaml +0 -15
  198. package/dist/src/vocab/invite.yaml +0 -14
  199. package/dist/src/vocab/join.yaml +0 -14
  200. package/dist/src/vocab/key.yaml +0 -28
  201. package/dist/src/vocab/leave.yaml +0 -14
  202. package/dist/src/vocab/like.yaml +0 -16
  203. package/dist/src/vocab/link.yaml +0 -101
  204. package/dist/src/vocab/listen.yaml +0 -12
  205. package/dist/src/vocab/mention.yaml +0 -9
  206. package/dist/src/vocab/move.yaml +0 -15
  207. package/dist/src/vocab/multikey.yaml +0 -36
  208. package/dist/src/vocab/note.yaml +0 -48
  209. package/dist/src/vocab/object.yaml +0 -404
  210. package/dist/src/vocab/offer.yaml +0 -15
  211. package/dist/src/vocab/orderedcollection.yaml +0 -39
  212. package/dist/src/vocab/orderedcollectionpage.yaml +0 -50
  213. package/dist/src/vocab/organization.yaml +0 -324
  214. package/dist/src/vocab/page.yaml +0 -11
  215. package/dist/src/vocab/person.yaml +0 -324
  216. package/dist/src/vocab/place.yaml +0 -75
  217. package/dist/src/vocab/profile.yaml +0 -26
  218. package/dist/src/vocab/propertyvalue.yaml +0 -32
  219. package/dist/src/vocab/question.yaml +0 -103
  220. package/dist/src/vocab/read.yaml +0 -13
  221. package/dist/src/vocab/reject.yaml +0 -14
  222. package/dist/src/vocab/relationship.yaml +0 -52
  223. package/dist/src/vocab/remove.yaml +0 -14
  224. package/dist/src/vocab/service.yaml +0 -324
  225. package/dist/src/vocab/source.yaml +0 -26
  226. package/dist/src/vocab/tentativeaccept.yaml +0 -14
  227. package/dist/src/vocab/tentativereject.yaml +0 -14
  228. package/dist/src/vocab/tombstone.yaml +0 -24
  229. package/dist/src/vocab/travel.yaml +0 -16
  230. package/dist/src/vocab/undo.yaml +0 -26
  231. package/dist/src/vocab/update.yaml +0 -58
  232. package/dist/src/vocab/video.yaml +0 -11
  233. package/dist/src/vocab/view.yaml +0 -13
  234. package/dist/testing/docloader.test.js +0 -22
  235. package/dist/vocab/actor.test.js +0 -5963
  236. package/dist/vocab/lookup.test.d.ts +0 -3
  237. package/dist/vocab/lookup.test.js +0 -476
  238. package/dist/vocab/type.test.d.ts +0 -3
  239. package/dist/vocab/type.test.js +0 -24
  240. package/dist/vocab/vocab.test.d.ts +0 -3
  241. package/dist/vocab/vocab.test.js +0 -9397
  242. package/dist/vocab-BCWe1Ih5.d.ts +0 -14905
  243. package/dist/vocab-ByUp-A2_.js +0 -260
  244. package/dist/vocab-CeDBzu-f.d.cts +0 -14903
  245. package/dist/vocab-X_X5T8D3.cjs +0 -296
  246. package/dist/webfinger/handler.test.d.ts +0 -3
  247. package/dist/webfinger/lookup.test.d.ts +0 -3
  248. package/dist/webfinger/lookup.test.js +0 -193
  249. package/dist/webfinger/mod.cjs +0 -8
  250. package/dist/webfinger/mod.d.cts +0 -2
  251. package/dist/webfinger/mod.d.ts +0 -4
  252. package/dist/webfinger/mod.js +0 -8
  253. package/dist/webfinger-C72Y8lrh.js +0 -4
  254. package/dist/webfinger-vAtLmxOF.cjs +0 -4
  255. /package/dist/{collection-BzWsN9pB.js → collection-CcnIw1qY.js} +0 -0
  256. /package/dist/{testing/docloader.test.d.ts → federation/webfinger.test.d.ts} +0 -0
  257. /package/dist/{mod-CVgZgliM.d.ts → mod-1E3W847c.d.ts} +0 -0
  258. /package/dist/{mod-B-hUPT2N.d.cts → mod-C81L6_lQ.d.cts} +0 -0
  259. /package/dist/{negotiation-C4nFufNk.js → negotiation-5NPJL6zp.js} +0 -0
  260. /package/dist/{nodeinfo-BnthBobC.js → nodeinfo-BlLsRSiT.js} +0 -0
  261. /package/dist/{nodeinfo-CdN0rEnZ.cjs → nodeinfo-DuMYTpbZ.cjs} +0 -0
  262. /package/dist/{vocab/actor.test.d.ts → otel/exporter.test.d.ts} +0 -0
  263. /package/dist/{retry-CfF8Gn4d.js → retry-D4GJ670a.js} +0 -0
  264. /package/dist/{sig-C34-oHBl.js → sig-CwuONEzF.js} +0 -0
  265. /package/dist/{sig-YYj5tCnr.cjs → sig-DeXX2xnj.cjs} +0 -0
  266. /package/dist/{utils-DyRU1gdZ.cjs → utils-Db0ZmjcD.cjs} +0 -0
  267. /package/dist/{utils-D-Va7aXC.js → utils-Wranxuoe.js} +0 -0
@@ -2,22 +2,21 @@
2
2
  import { Temporal } from "@js-temporal/polyfill";
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
 
5
- import { getDefaultActivityTransformers } from "./transformers-BFT6d7J5.js";
6
- import { deno_default, lookupWebFinger } from "./lookup-DOSnR912.js";
7
- import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, getTypeId } from "./actor-B_gRMloq.js";
8
- import { doubleKnock, exportJwk, importJwk, validateCryptoKey, verifyRequest } from "./http-YhR_TMMQ.js";
9
- import { detachSignature, doesActorOwnKey, getKeyOwner, hasSignature, signJsonLd, signObject, verifyJsonLd, verifyObject } from "./proof-C8-2l0zH.js";
10
- import { getNodeInfo, nodeInfoToJson } from "./types-BtUjyi5y.js";
11
- import { getAuthenticatedDocumentLoader, kvCache } from "./kv-cache-HFnFIjSD.js";
12
- import { lookupObject, traverseCollection } from "./vocab-ByUp-A2_.js";
5
+ import { getDefaultActivityTransformers } from "./transformers-N_ip_y4P.js";
6
+ import { deno_default, doubleKnock, exportJwk, importJwk, validateCryptoKey, verifyRequest } from "./http-CL3G0rnf.js";
7
+ import { detachSignature, doesActorOwnKey, getKeyOwner, hasSignature, signJsonLd, signObject, verifyJsonLd, verifyObject } from "./proof-CnaEQ_Ev.js";
8
+ import { getNodeInfo, nodeInfoToJson } from "./types-8l28uC8o.js";
9
+ import { getAuthenticatedDocumentLoader, kvCache } from "./kv-cache-0786BfqY.js";
13
10
  import { getLogger, withContext } from "@logtape/logtape";
11
+ import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
14
12
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
15
- import { getDocumentLoader } from "@fedify/vocab-runtime";
16
13
  import { cloneDeep } from "es-toolkit";
17
14
  import { Router } from "uri-template-router";
18
15
  import { parseTemplate } from "url-template";
19
16
  import { encodeHex } from "byte-encodings/hex";
20
17
  import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_HEADER, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
18
+ import { getDocumentLoader } from "@fedify/vocab-runtime";
19
+ import { lookupWebFinger } from "@fedify/webfinger";
21
20
  import { domainToASCII } from "node:url";
22
21
 
23
22
  //#region src/federation/inbox.ts
@@ -323,6 +322,7 @@ var FederationBuilderImpl = class {
323
322
  inboxListeners;
324
323
  inboxErrorHandler;
325
324
  sharedInboxKeyDispatcher;
325
+ outboxPermanentFailureHandler;
326
326
  idempotencyStrategy;
327
327
  collectionTypeIds;
328
328
  collectionCallbacks;
@@ -338,7 +338,7 @@ var FederationBuilderImpl = class {
338
338
  this.collectionTypeIds = {};
339
339
  }
340
340
  async build(options) {
341
- const { FederationImpl: FederationImpl$1 } = await import("./middleware-CxswDtQn.js");
341
+ const { FederationImpl: FederationImpl$1 } = await import("./middleware-B3jUPnDa.js");
342
342
  const f = new FederationImpl$1(options);
343
343
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
344
344
  f.router = this.router.clone();
@@ -360,6 +360,7 @@ var FederationBuilderImpl = class {
360
360
  f.inboxListeners = this.inboxListeners?.clone();
361
361
  f.inboxErrorHandler = this.inboxErrorHandler;
362
362
  f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
363
+ f.outboxPermanentFailureHandler = this.outboxPermanentFailureHandler;
363
364
  f.idempotencyStrategy = this.idempotencyStrategy;
364
365
  return f;
365
366
  }
@@ -841,6 +842,9 @@ var FederationBuilderImpl = class {
841
842
  const path = this.router.build(`collection:${routeName}`, values) ?? this.router.build(`orderedCollection:${routeName}`, values);
842
843
  return path;
843
844
  }
845
+ setOutboxPermanentFailureHandler(handler) {
846
+ this.outboxPermanentFailureHandler = handler;
847
+ }
844
848
  /**
845
849
  * Converts a name (string or symbol) to a unique string identifier.
846
850
  * For symbols, generates and caches a UUID if not already present.
@@ -2037,139 +2041,6 @@ function handleNodeInfoJrd(_request, context$1) {
2037
2041
  return Promise.resolve(response);
2038
2042
  }
2039
2043
 
2040
- //#endregion
2041
- //#region src/webfinger/handler.ts
2042
- const logger = getLogger([
2043
- "fedify",
2044
- "webfinger",
2045
- "server"
2046
- ]);
2047
- /**
2048
- * Handles a WebFinger request. You would not typically call this function
2049
- * directly, but instead use {@link Federation.fetch} method.
2050
- * @param request The WebFinger request to handle.
2051
- * @param parameters The parameters for handling the request.
2052
- * @returns The response to the request.
2053
- */
2054
- async function handleWebFinger(request, options) {
2055
- if (options.tracer == null) return await handleWebFingerInternal(request, options);
2056
- return await options.tracer.startActiveSpan("webfinger.handle", { kind: SpanKind.SERVER }, async (span) => {
2057
- try {
2058
- const response = await handleWebFingerInternal(request, options);
2059
- span.setStatus({ code: response.ok ? SpanStatusCode.UNSET : SpanStatusCode.ERROR });
2060
- return response;
2061
- } catch (error) {
2062
- span.setStatus({
2063
- code: SpanStatusCode.ERROR,
2064
- message: String(error)
2065
- });
2066
- throw error;
2067
- } finally {
2068
- span.end();
2069
- }
2070
- });
2071
- }
2072
- async function handleWebFingerInternal(request, { context: context$1, host, actorDispatcher, actorHandleMapper, actorAliasMapper, onNotFound, span, webFingerLinksDispatcher }) {
2073
- if (actorDispatcher == null) {
2074
- logger.error("Actor dispatcher is not set.");
2075
- return await onNotFound(request);
2076
- }
2077
- const resource = context$1.url.searchParams.get("resource");
2078
- if (resource == null) return new Response("Missing resource parameter.", { status: 400 });
2079
- span?.setAttribute("webfinger.resource", resource);
2080
- let resourceUrl;
2081
- try {
2082
- resourceUrl = new URL(resource);
2083
- } catch (e) {
2084
- if (e instanceof TypeError) return new Response("Invalid resource URL.", { status: 400 });
2085
- throw e;
2086
- }
2087
- span?.setAttribute("webfinger.resource.scheme", resourceUrl.protocol.replace(/:$/, ""));
2088
- async function mapUsernameToIdentifier(username) {
2089
- if (actorHandleMapper == null) {
2090
- logger.error("No actor handle mapper is set; use the WebFinger username {username} as the actor's internal identifier.", { username });
2091
- return username;
2092
- }
2093
- const identifier$1 = await actorHandleMapper(context$1, username);
2094
- if (identifier$1 == null) {
2095
- logger.error("Actor {username} not found.", { username });
2096
- return null;
2097
- }
2098
- return identifier$1;
2099
- }
2100
- let identifier = null;
2101
- const uriParsed = context$1.parseUri(resourceUrl);
2102
- if (uriParsed?.type != "actor") {
2103
- const match = /^acct:([^@]+)@([^@]+)$/.exec(resource);
2104
- if (match == null) {
2105
- const result = await actorAliasMapper?.(context$1, resourceUrl);
2106
- if (result == null) return await onNotFound(request);
2107
- if ("identifier" in result) identifier = result.identifier;
2108
- else identifier = await mapUsernameToIdentifier(result.username);
2109
- } else {
2110
- const portMatch = /:\d+$/.exec(match[2]);
2111
- const normalizedHost = portMatch == null ? domainToASCII(match[2].toLowerCase()) : domainToASCII(match[2].substring(0, portMatch.index).toLowerCase()) + portMatch[0];
2112
- if (normalizedHost != context$1.url.host && normalizedHost != host) return await onNotFound(request);
2113
- else {
2114
- identifier = await mapUsernameToIdentifier(match[1]);
2115
- resourceUrl = new URL(`acct:${match[1]}@${normalizedHost}`);
2116
- }
2117
- }
2118
- } else identifier = uriParsed.identifier;
2119
- if (identifier == null) return await onNotFound(request);
2120
- const actor = await actorDispatcher(context$1, identifier);
2121
- if (actor == null) {
2122
- logger.error("Actor {identifier} not found.", { identifier });
2123
- return await onNotFound(request);
2124
- }
2125
- const links = [{
2126
- rel: "self",
2127
- href: context$1.getActorUri(identifier).href,
2128
- type: "application/activity+json"
2129
- }];
2130
- for (const url of actor.urls) if (url instanceof Link && url.href != null) links.push({
2131
- rel: url.rel ?? "http://webfinger.net/rel/profile-page",
2132
- href: url.href.href,
2133
- type: url.mediaType == null ? void 0 : url.mediaType
2134
- });
2135
- else if (url instanceof URL) links.push({
2136
- rel: "http://webfinger.net/rel/profile-page",
2137
- href: url.href
2138
- });
2139
- for await (const image of actor.getIcons()) {
2140
- if (image.url?.href == null) continue;
2141
- const link = {
2142
- rel: "http://webfinger.net/rel/avatar",
2143
- href: image.url.href.toString()
2144
- };
2145
- if (image.mediaType != null) link.type = image.mediaType;
2146
- links.push(link);
2147
- }
2148
- if (webFingerLinksDispatcher != null) {
2149
- const customLinks = await webFingerLinksDispatcher(context$1, resourceUrl);
2150
- if (customLinks != null) for (const link of customLinks) links.push(link);
2151
- }
2152
- const aliases = [];
2153
- if (resourceUrl.protocol != "acct:" && actor.preferredUsername != null) {
2154
- aliases.push(`acct:${actor.preferredUsername}@${host ?? context$1.url.host}`);
2155
- if (host != null && host !== context$1.url.host) aliases.push(`acct:${actor.preferredUsername}@${context$1.url.host}`);
2156
- }
2157
- if (resourceUrl.href !== context$1.getActorUri(identifier).href) aliases.push(context$1.getActorUri(identifier).href);
2158
- if (resourceUrl.protocol === "acct:" && host != null && host !== context$1.url.host && !resourceUrl.href.endsWith(`@${host}`)) {
2159
- const username = resourceUrl.href.replace(/^acct:/, "").replace(/@.*$/, "");
2160
- aliases.push(`acct:${username}@${host}`);
2161
- }
2162
- const jrd = {
2163
- subject: resourceUrl.href,
2164
- aliases,
2165
- links
2166
- };
2167
- return new Response(JSON.stringify(jrd), { headers: {
2168
- "Content-Type": "application/jrd+json",
2169
- "Access-Control-Allow-Origin": "*"
2170
- } });
2171
- }
2172
-
2173
2044
  //#endregion
2174
2045
  //#region src/federation/retry.ts
2175
2046
  /**
@@ -2196,8 +2067,8 @@ function createExponentialBackoffPolicy(options = {}) {
2196
2067
  milliseconds *= 1 + Math.random();
2197
2068
  milliseconds = Math.round(milliseconds);
2198
2069
  }
2199
- const delay$1 = Temporal.Duration.from({ milliseconds });
2200
- return Temporal.Duration.compare(delay$1, maxDelay) > 0 ? maxDelay : delay$1;
2070
+ const delay = Temporal.Duration.from({ milliseconds });
2071
+ return Temporal.Duration.compare(delay, maxDelay) > 0 ? maxDelay : delay;
2201
2072
  };
2202
2073
  }
2203
2074
 
@@ -2314,7 +2185,7 @@ async function sendActivityInternal({ activity, activityId, keys, inbox, headers
2314
2185
  statusText: response.statusText,
2315
2186
  error
2316
2187
  });
2317
- throw new Error(`Failed to send activity ${activityId} to ${inbox.href} (${response.status} ${response.statusText}):\n${error}`);
2188
+ throw new SendActivityError(inbox, response.status, `Failed to send activity ${activityId} to ${inbox.href} (${response.status} ${response.statusText}):\n${error}`, error);
2318
2189
  }
2319
2190
  span.addEvent("activitypub.activity.sent", {
2320
2191
  "activitypub.activity.json": JSON.stringify(activity),
@@ -2322,6 +2193,172 @@ async function sendActivityInternal({ activity, activityId, keys, inbox, headers
2322
2193
  "activitypub.activity.id": activityId ?? ""
2323
2194
  });
2324
2195
  }
2196
+ /**
2197
+ * An error that is thrown when an activity fails to send to a remote inbox.
2198
+ * It contains structured information about the failure, including the HTTP
2199
+ * status code, the inbox URL, and the response body.
2200
+ * @since 2.0.0
2201
+ */
2202
+ var SendActivityError = class extends Error {
2203
+ /**
2204
+ * The inbox URL that the activity was being sent to.
2205
+ */
2206
+ inbox;
2207
+ /**
2208
+ * The HTTP status code returned by the inbox.
2209
+ */
2210
+ statusCode;
2211
+ /**
2212
+ * The response body from the inbox, if any.
2213
+ */
2214
+ responseBody;
2215
+ /**
2216
+ * Creates a new {@link SendActivityError}.
2217
+ * @param inbox The inbox URL.
2218
+ * @param statusCode The HTTP status code.
2219
+ * @param message The error message.
2220
+ * @param responseBody The response body.
2221
+ */
2222
+ constructor(inbox, statusCode, message, responseBody) {
2223
+ super(message);
2224
+ this.name = "SendActivityError";
2225
+ this.inbox = inbox;
2226
+ this.statusCode = statusCode;
2227
+ this.responseBody = responseBody;
2228
+ }
2229
+ };
2230
+
2231
+ //#endregion
2232
+ //#region src/federation/webfinger.ts
2233
+ const logger = getLogger([
2234
+ "fedify",
2235
+ "webfinger",
2236
+ "server"
2237
+ ]);
2238
+ /**
2239
+ * Handles a WebFinger request. You would not typically call this function
2240
+ * directly, but instead use {@link Federation.fetch} method.
2241
+ * @param request The WebFinger request to handle.
2242
+ * @param parameters The parameters for handling the request.
2243
+ * @returns The response to the request.
2244
+ */
2245
+ async function handleWebFinger(request, options) {
2246
+ if (options.tracer == null) return await handleWebFingerInternal(request, options);
2247
+ return await options.tracer.startActiveSpan("webfinger.handle", { kind: SpanKind.SERVER }, async (span) => {
2248
+ try {
2249
+ const response = await handleWebFingerInternal(request, options);
2250
+ span.setStatus({ code: response.ok ? SpanStatusCode.UNSET : SpanStatusCode.ERROR });
2251
+ return response;
2252
+ } catch (error) {
2253
+ span.setStatus({
2254
+ code: SpanStatusCode.ERROR,
2255
+ message: String(error)
2256
+ });
2257
+ throw error;
2258
+ } finally {
2259
+ span.end();
2260
+ }
2261
+ });
2262
+ }
2263
+ async function handleWebFingerInternal(request, { context: context$1, host, actorDispatcher, actorHandleMapper, actorAliasMapper, onNotFound, span, webFingerLinksDispatcher }) {
2264
+ if (actorDispatcher == null) {
2265
+ logger.error("Actor dispatcher is not set.");
2266
+ return await onNotFound(request);
2267
+ }
2268
+ const resource = context$1.url.searchParams.get("resource");
2269
+ if (resource == null) return new Response("Missing resource parameter.", { status: 400 });
2270
+ span?.setAttribute("webfinger.resource", resource);
2271
+ let resourceUrl;
2272
+ try {
2273
+ resourceUrl = new URL(resource);
2274
+ } catch (e) {
2275
+ if (e instanceof TypeError) return new Response("Invalid resource URL.", { status: 400 });
2276
+ throw e;
2277
+ }
2278
+ span?.setAttribute("webfinger.resource.scheme", resourceUrl.protocol.replace(/:$/, ""));
2279
+ async function mapUsernameToIdentifier(username) {
2280
+ if (actorHandleMapper == null) {
2281
+ logger.error("No actor handle mapper is set; use the WebFinger username {username} as the actor's internal identifier.", { username });
2282
+ return username;
2283
+ }
2284
+ const identifier$1 = await actorHandleMapper(context$1, username);
2285
+ if (identifier$1 == null) {
2286
+ logger.error("Actor {username} not found.", { username });
2287
+ return null;
2288
+ }
2289
+ return identifier$1;
2290
+ }
2291
+ let identifier = null;
2292
+ const uriParsed = context$1.parseUri(resourceUrl);
2293
+ if (uriParsed?.type != "actor") {
2294
+ const match = /^acct:([^@]+)@([^@]+)$/.exec(resource);
2295
+ if (match == null) {
2296
+ const result = await actorAliasMapper?.(context$1, resourceUrl);
2297
+ if (result == null) return await onNotFound(request);
2298
+ if ("identifier" in result) identifier = result.identifier;
2299
+ else identifier = await mapUsernameToIdentifier(result.username);
2300
+ } else {
2301
+ const portMatch = /:\d+$/.exec(match[2]);
2302
+ const normalizedHost = portMatch == null ? domainToASCII(match[2].toLowerCase()) : domainToASCII(match[2].substring(0, portMatch.index).toLowerCase()) + portMatch[0];
2303
+ if (normalizedHost != context$1.url.host && normalizedHost != host) return await onNotFound(request);
2304
+ else {
2305
+ identifier = await mapUsernameToIdentifier(match[1]);
2306
+ resourceUrl = new URL(`acct:${match[1]}@${normalizedHost}`);
2307
+ }
2308
+ }
2309
+ } else identifier = uriParsed.identifier;
2310
+ if (identifier == null) return await onNotFound(request);
2311
+ const actor = await actorDispatcher(context$1, identifier);
2312
+ if (actor == null) {
2313
+ logger.error("Actor {identifier} not found.", { identifier });
2314
+ return await onNotFound(request);
2315
+ }
2316
+ const links = [{
2317
+ rel: "self",
2318
+ href: context$1.getActorUri(identifier).href,
2319
+ type: "application/activity+json"
2320
+ }];
2321
+ for (const url of actor.urls) if (url instanceof Link && url.href != null) links.push({
2322
+ rel: url.rel ?? "http://webfinger.net/rel/profile-page",
2323
+ href: url.href.href,
2324
+ type: url.mediaType == null ? void 0 : url.mediaType
2325
+ });
2326
+ else if (url instanceof URL) links.push({
2327
+ rel: "http://webfinger.net/rel/profile-page",
2328
+ href: url.href
2329
+ });
2330
+ for await (const image of actor.getIcons()) {
2331
+ if (image.url?.href == null) continue;
2332
+ links.push({
2333
+ rel: "http://webfinger.net/rel/avatar",
2334
+ href: image.url.href.toString(),
2335
+ ...image.mediaType != null && { type: image.mediaType }
2336
+ });
2337
+ }
2338
+ if (webFingerLinksDispatcher != null) {
2339
+ const customLinks = await webFingerLinksDispatcher(context$1, resourceUrl);
2340
+ if (customLinks != null) for (const link of customLinks) links.push(link);
2341
+ }
2342
+ const aliases = [];
2343
+ if (resourceUrl.protocol != "acct:" && actor.preferredUsername != null) {
2344
+ aliases.push(`acct:${actor.preferredUsername}@${host ?? context$1.url.host}`);
2345
+ if (host != null && host !== context$1.url.host) aliases.push(`acct:${actor.preferredUsername}@${context$1.url.host}`);
2346
+ }
2347
+ if (resourceUrl.href !== context$1.getActorUri(identifier).href) aliases.push(context$1.getActorUri(identifier).href);
2348
+ if (resourceUrl.protocol === "acct:" && host != null && host !== context$1.url.host && !resourceUrl.href.endsWith(`@${host}`)) {
2349
+ const username = resourceUrl.href.replace(/^acct:/, "").replace(/@.*$/, "");
2350
+ aliases.push(`acct:${username}@${host}`);
2351
+ }
2352
+ const jrd = {
2353
+ subject: resourceUrl.href,
2354
+ aliases,
2355
+ links
2356
+ };
2357
+ return new Response(JSON.stringify(jrd), { headers: {
2358
+ "Content-Type": "application/jrd+json",
2359
+ "Access-Control-Allow-Origin": "*"
2360
+ } });
2361
+ }
2325
2362
 
2326
2363
  //#endregion
2327
2364
  //#region src/federation/middleware.ts
@@ -2351,6 +2388,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2351
2388
  allowPrivateAddress;
2352
2389
  userAgent;
2353
2390
  onOutboxError;
2391
+ permanentFailureStatusCodes;
2354
2392
  signatureTimeWindow;
2355
2393
  skipSignatureVerification;
2356
2394
  outboxRetryPolicy;
@@ -2432,6 +2470,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2432
2470
  }));
2433
2471
  this.userAgent = userAgent;
2434
2472
  this.onOutboxError = options.onOutboxError;
2473
+ this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
2435
2474
  this.signatureTimeWindow = options.signatureTimeWindow ?? { hours: 1 };
2436
2475
  this.skipSignatureVerification = options.skipSignatureVerification ?? false;
2437
2476
  this.outboxRetryPolicy = options.outboxRetryPolicy ?? createExponentialBackoffPolicy();
@@ -2562,6 +2601,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2562
2601
  }) });
2563
2602
  await this.sendActivity(keys, message.inboxes, activity, {
2564
2603
  collectionSync: message.collectionSync,
2604
+ orderingKey: message.orderingKey,
2565
2605
  context: context$1
2566
2606
  });
2567
2607
  }
@@ -2613,13 +2653,44 @@ var FederationImpl = class extends FederationBuilderImpl {
2613
2653
  tracerProvider: this.tracerProvider
2614
2654
  });
2615
2655
  try {
2616
- this.onOutboxError?.(error, activity);
2656
+ await this.onOutboxError?.(error, activity);
2617
2657
  } catch (error$1) {
2618
2658
  logger$1.error("An unexpected error occurred in onError handler:\n{error}", {
2619
2659
  ...logData,
2620
2660
  error: error$1
2621
2661
  });
2622
2662
  }
2663
+ if (error instanceof SendActivityError && this.permanentFailureStatusCodes.includes(error.statusCode)) {
2664
+ logger$1.warn("Permanent delivery failure for activity {activityId} to {inbox} ({status}); not retrying.", {
2665
+ ...logData,
2666
+ status: error.statusCode
2667
+ });
2668
+ if (this.outboxPermanentFailureHandler != null) {
2669
+ const ctx = this.#createContext(new URL(message.baseUrl), _, { documentLoader: this.documentLoaderFactory(loaderOptions) });
2670
+ try {
2671
+ await this.outboxPermanentFailureHandler(ctx, {
2672
+ inbox: new URL(message.inbox),
2673
+ activity,
2674
+ error,
2675
+ statusCode: error.statusCode,
2676
+ actorIds: (message.actorIds ?? []).flatMap((id) => {
2677
+ try {
2678
+ return [new URL(id)];
2679
+ } catch {
2680
+ logger$1.warn("Invalid actorId URL in OutboxMessage: {id}", { id });
2681
+ return [];
2682
+ }
2683
+ })
2684
+ });
2685
+ } catch (handlerError) {
2686
+ logger$1.error("An unexpected error occurred in outboxPermanentFailureHandler:\n{error}", {
2687
+ ...logData,
2688
+ error: handlerError
2689
+ });
2690
+ }
2691
+ }
2692
+ return;
2693
+ }
2623
2694
  if (this.outboxQueue?.nativeRetrial) {
2624
2695
  logger$1.error("Failed to send activity {activityId} to {inbox}; backend will handle retry:\n{error}", {
2625
2696
  ...logData,
@@ -2627,11 +2698,11 @@ var FederationImpl = class extends FederationBuilderImpl {
2627
2698
  });
2628
2699
  throw error;
2629
2700
  }
2630
- const delay$1 = this.outboxRetryPolicy({
2701
+ const delay = this.outboxRetryPolicy({
2631
2702
  elapsedTime: Temporal.Instant.from(message.started).until(Temporal.Now.instant()),
2632
2703
  attempts: message.attempt
2633
2704
  });
2634
- if (delay$1 != null) {
2705
+ if (delay != null) {
2635
2706
  logger$1.error("Failed to send activity {activityId} to {inbox} (attempt #{attempt}); retry...:\n{error}", {
2636
2707
  ...logData,
2637
2708
  error
@@ -2639,7 +2710,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2639
2710
  await this.outboxQueue?.enqueue({
2640
2711
  ...message,
2641
2712
  attempt: message.attempt + 1
2642
- }, { delay: Temporal.Duration.compare(delay$1, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay$1 });
2713
+ }, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
2643
2714
  } else logger$1.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
2644
2715
  ...logData,
2645
2716
  error
@@ -2726,11 +2797,11 @@ var FederationImpl = class extends FederationBuilderImpl {
2726
2797
  span$1.end();
2727
2798
  throw error;
2728
2799
  }
2729
- const delay$1 = this.inboxRetryPolicy({
2800
+ const delay = this.inboxRetryPolicy({
2730
2801
  elapsedTime: Temporal.Instant.from(message.started).until(Temporal.Now.instant()),
2731
2802
  attempts: message.attempt
2732
2803
  });
2733
- if (delay$1 != null) {
2804
+ if (delay != null) {
2734
2805
  logger$1.error("Failed to process the incoming activity {activityId} (attempt #{attempt}); retry...:\n{error}", {
2735
2806
  error,
2736
2807
  attempt: message.attempt,
@@ -2741,7 +2812,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2741
2812
  await this.inboxQueue?.enqueue({
2742
2813
  ...message,
2743
2814
  attempt: message.attempt + 1
2744
- }, { delay: Temporal.Duration.compare(delay$1, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay$1 });
2815
+ }, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
2745
2816
  } else logger$1.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
2746
2817
  error,
2747
2818
  activityId: activity.id?.href,
@@ -2810,7 +2881,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2810
2881
  "federation",
2811
2882
  "outbox"
2812
2883
  ]);
2813
- const { immediate, collectionSync, context: ctx } = options;
2884
+ const { immediate, collectionSync, orderingKey, context: ctx } = options;
2814
2885
  if (activity.id == null) throw new TypeError("The activity to send must have an id.");
2815
2886
  if (activity.actorId == null) throw new TypeError("The activity to send must have at least one actor property.");
2816
2887
  else if (keys.length < 1) throw new TypeError("The keys must not be empty.");
@@ -2898,6 +2969,8 @@ var FederationImpl = class extends FederationBuilderImpl {
2898
2969
  propagation.inject(context.active(), carrier);
2899
2970
  const messages = [];
2900
2971
  for (const inbox in inboxes) {
2972
+ const inboxOrigin = new URL(inbox).origin;
2973
+ const messageOrderingKey = orderingKey == null ? void 0 : `${orderingKey}\n${inboxOrigin}`;
2901
2974
  const message = {
2902
2975
  type: "outbox",
2903
2976
  id: crypto.randomUUID(),
@@ -2908,16 +2981,33 @@ var FederationImpl = class extends FederationBuilderImpl {
2908
2981
  activityType: getTypeId(activity).href,
2909
2982
  inbox,
2910
2983
  sharedInbox: inboxes[inbox].sharedInbox,
2984
+ actorIds: [...inboxes[inbox].actorIds],
2911
2985
  started: (/* @__PURE__ */ new Date()).toISOString(),
2912
2986
  attempt: 0,
2913
2987
  headers: collectionSync == null ? {} : { "Collection-Synchronization": await buildCollectionSynchronizationHeader(collectionSync, inboxes[inbox].actorIds) },
2988
+ orderingKey: messageOrderingKey,
2914
2989
  traceContext: carrier
2915
2990
  };
2916
- messages.push(message);
2991
+ messages.push({
2992
+ message,
2993
+ orderingKey: messageOrderingKey
2994
+ });
2917
2995
  }
2918
2996
  const { outboxQueue } = this;
2919
2997
  if (outboxQueue.enqueueMany == null) {
2920
- const promises = messages.map((m) => outboxQueue.enqueue(m));
2998
+ const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
2999
+ const results = await Promise.allSettled(promises);
3000
+ const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
3001
+ if (errors.length > 0) {
3002
+ logger$1.error("Failed to enqueue activity {activityId} to send later: {errors}", {
3003
+ activityId: activity.id.href,
3004
+ errors
3005
+ });
3006
+ if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${activityId} to send later.`);
3007
+ throw errors[0];
3008
+ }
3009
+ } else if (orderingKey != null) {
3010
+ const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
2921
3011
  const results = await Promise.allSettled(promises);
2922
3012
  const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
2923
3013
  if (errors.length > 0) {
@@ -2929,7 +3019,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2929
3019
  throw errors[0];
2930
3020
  }
2931
3021
  } else try {
2932
- await outboxQueue.enqueueMany(messages);
3022
+ await outboxQueue.enqueueMany(messages.map((m) => m.message));
2933
3023
  } catch (error) {
2934
3024
  logger$1.error("Failed to enqueue activity {activityId} to send later: {error}", {
2935
3025
  activityId: activity.id.href,
@@ -3645,20 +3735,25 @@ var ContextImpl = class ContextImpl {
3645
3735
  } else keys = [sender];
3646
3736
  if (keys.length < 1) throw new TypeError("The sender's keys must not be empty.");
3647
3737
  for (const { privateKey } of keys) validateCryptoKey(privateKey, "private");
3648
- const opts = { context: this };
3649
3738
  let expandedRecipients;
3739
+ let collectionSync;
3650
3740
  if (Array.isArray(recipients)) expandedRecipients = recipients;
3651
3741
  else if (recipients === "followers") {
3652
3742
  if (identifier == null) throw new Error("If recipients is \"followers\", sender must be an actor identifier or username.");
3653
3743
  expandedRecipients = [];
3654
3744
  for await (const recipient of this.getFollowers(identifier)) expandedRecipients.push(recipient);
3655
3745
  if (options.syncCollection) try {
3656
- opts.collectionSync = this.getFollowersUri(identifier).href;
3746
+ collectionSync = this.getFollowersUri(identifier).href;
3657
3747
  } catch (error) {
3658
- if (error instanceof RouterError) opts.collectionSync = void 0;
3659
- else throw error;
3748
+ if (!(error instanceof RouterError)) throw error;
3660
3749
  }
3661
3750
  } else expandedRecipients = [recipients];
3751
+ const opts = {
3752
+ context: this,
3753
+ orderingKey: options.orderingKey,
3754
+ collectionSync,
3755
+ immediate: options.immediate
3756
+ };
3662
3757
  span.setAttribute("activitypub.inboxes", expandedRecipients.length);
3663
3758
  for (const activityTransformer of this.federation.activityTransformers) activity = activityTransformer(activity, this);
3664
3759
  span?.setAttribute("activitypub.activity.id", activity?.id?.href ?? "");
@@ -3705,10 +3800,11 @@ var ContextImpl = class ContextImpl {
3705
3800
  activityId: activity.id?.href,
3706
3801
  activityType: getTypeId(activity).href,
3707
3802
  collectionSync: opts.collectionSync,
3803
+ orderingKey: options.orderingKey,
3708
3804
  traceContext: carrier
3709
3805
  };
3710
3806
  if (!this.federation.manuallyStartQueue) this.federation._startQueueInternal(this.data);
3711
- this.federation.fanoutQueue.enqueue(message);
3807
+ this.federation.fanoutQueue.enqueue(message, { orderingKey: options.orderingKey });
3712
3808
  }
3713
3809
  async *getFollowers(identifier) {
3714
3810
  if (this.federation.followersCallbacks == null) throw new Error("No followers collection dispatcher registered.");
@@ -4060,8 +4156,10 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
4060
4156
  }
4061
4157
  const carrier = {};
4062
4158
  propagation.inject(context.active(), carrier);
4159
+ const orderingKey = options?.orderingKey;
4063
4160
  const messages = [];
4064
4161
  for (const inbox in inboxes) {
4162
+ const inboxUrl = new URL(inbox);
4065
4163
  const message = {
4066
4164
  type: "outbox",
4067
4165
  id: crypto.randomUUID(),
@@ -4075,13 +4173,29 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
4075
4173
  started: (/* @__PURE__ */ new Date()).toISOString(),
4076
4174
  attempt: 0,
4077
4175
  headers: {},
4176
+ orderingKey: orderingKey == null ? void 0 : `${orderingKey}\n${inboxUrl.origin}`,
4078
4177
  traceContext: carrier
4079
4178
  };
4080
- messages.push(message);
4179
+ messages.push({
4180
+ message,
4181
+ orderingKey: message.orderingKey
4182
+ });
4081
4183
  }
4082
4184
  const { outboxQueue } = this.federation;
4083
4185
  if (outboxQueue.enqueueMany == null) {
4084
- const promises = messages.map((m) => outboxQueue.enqueue(m));
4186
+ const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
4187
+ const results = await Promise.allSettled(promises);
4188
+ const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
4189
+ if (errors.length > 0) {
4190
+ logger$1.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
4191
+ activityId: this.activityId,
4192
+ errors
4193
+ });
4194
+ if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${this.activityId} to forward later.`);
4195
+ throw errors[0];
4196
+ }
4197
+ } else if (orderingKey != null) {
4198
+ const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
4085
4199
  const results = await Promise.allSettled(promises);
4086
4200
  const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
4087
4201
  if (errors.length > 0) {
@@ -4093,7 +4207,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
4093
4207
  throw errors[0];
4094
4208
  }
4095
4209
  } else try {
4096
- await outboxQueue.enqueueMany(messages);
4210
+ await outboxQueue.enqueueMany(messages.map((m) => m.message));
4097
4211
  } catch (error) {
4098
4212
  logger$1.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
4099
4213
  activityId: this.activityId,
@@ -4156,4 +4270,4 @@ function getRequestId(request) {
4156
4270
  }
4157
4271
 
4158
4272
  //#endregion
4159
- export { ContextImpl, FederationImpl, InboxContextImpl, KvSpecDeterminer, Router$1 as Router, RouterError, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, respondWithObject, respondWithObjectIfAcceptable };
4273
+ export { ContextImpl, FederationImpl, InboxContextImpl, KvSpecDeterminer, Router$1 as Router, RouterError, SendActivityError, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, handleWebFinger, respondWithObject, respondWithObjectIfAcceptable };