@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
@@ -68,6 +68,28 @@ var MemoryKvStore = class {
68
68
  this.#values[encodedKey] = [newValue, expiration];
69
69
  return Promise.resolve(true);
70
70
  }
71
+ /**
72
+ * {@inheritDoc KvStore.list}
73
+ */
74
+ async *list(prefix) {
75
+ const now = Temporal.Now.instant();
76
+ for (const [encodedKey, entry] of Object.entries(this.#values)) {
77
+ const key = JSON.parse(encodedKey);
78
+ if (prefix != null) {
79
+ if (key.length < prefix.length) continue;
80
+ if (!prefix.every((p, i) => key[i] === p)) continue;
81
+ }
82
+ const [value, expiration] = entry;
83
+ if (expiration != null && now.until(expiration).sign < 0) {
84
+ delete this.#values[encodedKey];
85
+ continue;
86
+ }
87
+ yield {
88
+ key,
89
+ value
90
+ };
91
+ }
92
+ }
71
93
  };
72
94
 
73
95
  //#endregion
@@ -84,6 +106,11 @@ var InProcessMessageQueue = class {
84
106
  #monitors;
85
107
  #pollIntervalMs;
86
108
  /**
109
+ * Tracks which ordering keys are currently being processed to ensure
110
+ * sequential processing for messages with the same key.
111
+ */
112
+ #processingKeys;
113
+ /**
87
114
  * In-process message queue does not provide native retry mechanisms.
88
115
  * @since 1.7.0
89
116
  */
@@ -96,6 +123,7 @@ var InProcessMessageQueue = class {
96
123
  this.#messages = [];
97
124
  this.#monitors = {};
98
125
  this.#pollIntervalMs = Temporal.Duration.from(options.pollInterval ?? { seconds: 5 }).total("millisecond");
126
+ this.#processingKeys = /* @__PURE__ */ new Set();
99
127
  }
100
128
  enqueue(message, options) {
101
129
  const delay = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
@@ -106,7 +134,11 @@ var InProcessMessageQueue = class {
106
134
  }), delay);
107
135
  return Promise.resolve();
108
136
  }
109
- this.#messages.push(message);
137
+ const orderingKey = options?.orderingKey ?? null;
138
+ this.#messages.push({
139
+ message,
140
+ orderingKey
141
+ });
110
142
  for (const monitorId in this.#monitors) this.#monitors[monitorId]();
111
143
  return Promise.resolve();
112
144
  }
@@ -120,18 +152,29 @@ var InProcessMessageQueue = class {
120
152
  }), delay);
121
153
  return Promise.resolve();
122
154
  }
123
- this.#messages.push(...messages);
155
+ const orderingKey = options?.orderingKey ?? null;
156
+ for (const message of messages) this.#messages.push({
157
+ message,
158
+ orderingKey
159
+ });
124
160
  for (const monitorId in this.#monitors) this.#monitors[monitorId]();
125
161
  return Promise.resolve();
126
162
  }
127
163
  async listen(handler, options = {}) {
128
164
  const signal = options.signal;
129
165
  while (signal == null || !signal.aborted) {
130
- while (this.#messages.length > 0) {
131
- const message = this.#messages.shift();
132
- await handler(message);
133
- }
134
- await this.#wait(this.#pollIntervalMs, signal);
166
+ const idx = this.#messages.findIndex((m) => m.orderingKey == null || !this.#processingKeys.has(m.orderingKey));
167
+ if (idx >= 0) {
168
+ const queued = this.#messages.splice(idx, 1)[0];
169
+ const { message, orderingKey } = queued;
170
+ if (orderingKey != null) this.#processingKeys.add(orderingKey);
171
+ try {
172
+ await handler(message);
173
+ } finally {
174
+ if (orderingKey != null) this.#processingKeys.delete(orderingKey);
175
+ }
176
+ } else if (this.#messages.length === 0) await this.#wait(this.#pollIntervalMs, signal);
177
+ else await this.#wait(10, signal);
135
178
  }
136
179
  }
137
180
  #wait(ms, signal) {
@@ -163,6 +206,21 @@ var InProcessMessageQueue = class {
163
206
  * for I/O-bound tasks, but not for CPU-bound tasks, which is okay for Fedify's
164
207
  * workloads.
165
208
  *
209
+ * When using `ParallelMessageQueue`, the ordering guarantee is preserved
210
+ * *only if* the underlying queue implementation delivers messages in a wrapper
211
+ * format that includes the `__fedify_ordering_key__` property. Currently,
212
+ * only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
213
+ * For other queue implementations (e.g., `InProcessMessageQueue`,
214
+ * `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
215
+ * `AmqpMessageQueue`), the ordering key cannot be detected by
216
+ * `ParallelMessageQueue`, so ordering guarantees are handled by those
217
+ * implementations directly rather than at the `ParallelMessageQueue` level.
218
+ *
219
+ * Messages with the same ordering key will never be processed concurrently
220
+ * by different workers, ensuring sequential processing within each key.
221
+ * Messages with different ordering keys (or no ordering key) can still be
222
+ * processed in parallel.
223
+ *
166
224
  * @since 1.0.0
167
225
  */
168
226
  var ParallelMessageQueue = class ParallelMessageQueue {
@@ -174,6 +232,15 @@ var ParallelMessageQueue = class ParallelMessageQueue {
174
232
  */
175
233
  nativeRetrial;
176
234
  /**
235
+ * Tracks which ordering keys are currently being processed to ensure
236
+ * sequential processing for messages with the same key.
237
+ */
238
+ #processingKeys = /* @__PURE__ */ new Set();
239
+ /**
240
+ * Pending messages waiting for their ordering key to become available.
241
+ */
242
+ #pendingMessages = [];
243
+ /**
177
244
  * Constructs a new {@link ParallelMessageQueue} with the given queue and
178
245
  * number of workers.
179
246
  * @param queue The message queue to use under the hood. Note that
@@ -201,6 +268,25 @@ var ParallelMessageQueue = class ParallelMessageQueue {
201
268
  }
202
269
  await this.queue.enqueueMany(messages, options);
203
270
  }
271
+ /**
272
+ * Extracts ordering key from a message if present.
273
+ *
274
+ * This method only works for queue implementations that deliver messages
275
+ * in the wrapper format with `__fedify_ordering_key__` property. Currently,
276
+ * only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
277
+ *
278
+ * For other queue implementations (`InProcessMessageQueue`,
279
+ * `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
280
+ * `AmqpMessageQueue`), messages are delivered as raw payloads without the
281
+ * wrapper, so the ordering key cannot be detected here. Those
282
+ * implementations handle ordering guarantees internally.
283
+ */
284
+ #extractOrderingKey(message) {
285
+ if (message != null && typeof message === "object") {
286
+ if ("__fedify_ordering_key__" in message) return message.__fedify_ordering_key__;
287
+ }
288
+ return void 0;
289
+ }
204
290
  listen(handler, options = {}) {
205
291
  const workers = /* @__PURE__ */ new Map();
206
292
  return this.queue.listen(async (message) => {
@@ -209,13 +295,33 @@ var ParallelMessageQueue = class ParallelMessageQueue {
209
295
  workers.delete(consumedId);
210
296
  }
211
297
  const workerId = crypto.randomUUID();
212
- const promise = this.#work(workerId, handler, message);
298
+ const orderingKey = this.#extractOrderingKey(message);
299
+ if (orderingKey != null && this.#processingKeys.has(orderingKey)) await new Promise((resolve) => {
300
+ this.#pendingMessages.push({
301
+ message,
302
+ orderingKey,
303
+ resolve
304
+ });
305
+ });
306
+ if (orderingKey != null) this.#processingKeys.add(orderingKey);
307
+ const promise = this.#work(workerId, handler, message, orderingKey);
213
308
  workers.set(workerId, promise);
214
309
  }, options);
215
310
  }
216
- async #work(workerId, handler, message) {
311
+ async #work(workerId, handler, message, orderingKey) {
217
312
  await this.#sleep(0);
218
- await handler(message);
313
+ try {
314
+ await handler(message);
315
+ } finally {
316
+ if (orderingKey != null) {
317
+ this.#processingKeys.delete(orderingKey);
318
+ const pendingIdx = this.#pendingMessages.findIndex((p) => p.orderingKey === orderingKey);
319
+ if (pendingIdx >= 0) {
320
+ const pending = this.#pendingMessages.splice(pendingIdx, 1)[0];
321
+ pending.resolve();
322
+ }
323
+ }
324
+ }
219
325
  return workerId;
220
326
  }
221
327
  #sleep(ms) {
@@ -67,6 +67,28 @@ var MemoryKvStore = class {
67
67
  this.#values[encodedKey] = [newValue, expiration];
68
68
  return Promise.resolve(true);
69
69
  }
70
+ /**
71
+ * {@inheritDoc KvStore.list}
72
+ */
73
+ async *list(prefix) {
74
+ const now = Temporal.Now.instant();
75
+ for (const [encodedKey, entry] of Object.entries(this.#values)) {
76
+ const key = JSON.parse(encodedKey);
77
+ if (prefix != null) {
78
+ if (key.length < prefix.length) continue;
79
+ if (!prefix.every((p, i) => key[i] === p)) continue;
80
+ }
81
+ const [value, expiration] = entry;
82
+ if (expiration != null && now.until(expiration).sign < 0) {
83
+ delete this.#values[encodedKey];
84
+ continue;
85
+ }
86
+ yield {
87
+ key,
88
+ value
89
+ };
90
+ }
91
+ }
70
92
  };
71
93
 
72
94
  //#endregion
@@ -83,6 +105,11 @@ var InProcessMessageQueue = class {
83
105
  #monitors;
84
106
  #pollIntervalMs;
85
107
  /**
108
+ * Tracks which ordering keys are currently being processed to ensure
109
+ * sequential processing for messages with the same key.
110
+ */
111
+ #processingKeys;
112
+ /**
86
113
  * In-process message queue does not provide native retry mechanisms.
87
114
  * @since 1.7.0
88
115
  */
@@ -95,42 +122,58 @@ var InProcessMessageQueue = class {
95
122
  this.#messages = [];
96
123
  this.#monitors = {};
97
124
  this.#pollIntervalMs = Temporal.Duration.from(options.pollInterval ?? { seconds: 5 }).total("millisecond");
125
+ this.#processingKeys = /* @__PURE__ */ new Set();
98
126
  }
99
127
  enqueue(message, options) {
100
- const delay$1 = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
101
- if (delay$1 > 0) {
128
+ const delay = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
129
+ if (delay > 0) {
102
130
  setTimeout(() => this.enqueue(message, {
103
131
  ...options,
104
132
  delay: void 0
105
- }), delay$1);
133
+ }), delay);
106
134
  return Promise.resolve();
107
135
  }
108
- this.#messages.push(message);
136
+ const orderingKey = options?.orderingKey ?? null;
137
+ this.#messages.push({
138
+ message,
139
+ orderingKey
140
+ });
109
141
  for (const monitorId in this.#monitors) this.#monitors[monitorId]();
110
142
  return Promise.resolve();
111
143
  }
112
144
  enqueueMany(messages, options) {
113
145
  if (messages.length === 0) return Promise.resolve();
114
- const delay$1 = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
115
- if (delay$1 > 0) {
146
+ const delay = options?.delay == null ? 0 : Math.max(options.delay.total("millisecond"), 0);
147
+ if (delay > 0) {
116
148
  setTimeout(() => this.enqueueMany(messages, {
117
149
  ...options,
118
150
  delay: void 0
119
- }), delay$1);
151
+ }), delay);
120
152
  return Promise.resolve();
121
153
  }
122
- this.#messages.push(...messages);
154
+ const orderingKey = options?.orderingKey ?? null;
155
+ for (const message of messages) this.#messages.push({
156
+ message,
157
+ orderingKey
158
+ });
123
159
  for (const monitorId in this.#monitors) this.#monitors[monitorId]();
124
160
  return Promise.resolve();
125
161
  }
126
162
  async listen(handler, options = {}) {
127
163
  const signal = options.signal;
128
164
  while (signal == null || !signal.aborted) {
129
- while (this.#messages.length > 0) {
130
- const message = this.#messages.shift();
131
- await handler(message);
132
- }
133
- await this.#wait(this.#pollIntervalMs, signal);
165
+ const idx = this.#messages.findIndex((m) => m.orderingKey == null || !this.#processingKeys.has(m.orderingKey));
166
+ if (idx >= 0) {
167
+ const queued = this.#messages.splice(idx, 1)[0];
168
+ const { message, orderingKey } = queued;
169
+ if (orderingKey != null) this.#processingKeys.add(orderingKey);
170
+ try {
171
+ await handler(message);
172
+ } finally {
173
+ if (orderingKey != null) this.#processingKeys.delete(orderingKey);
174
+ }
175
+ } else if (this.#messages.length === 0) await this.#wait(this.#pollIntervalMs, signal);
176
+ else await this.#wait(10, signal);
134
177
  }
135
178
  }
136
179
  #wait(ms, signal) {
@@ -162,6 +205,21 @@ var InProcessMessageQueue = class {
162
205
  * for I/O-bound tasks, but not for CPU-bound tasks, which is okay for Fedify's
163
206
  * workloads.
164
207
  *
208
+ * When using `ParallelMessageQueue`, the ordering guarantee is preserved
209
+ * *only if* the underlying queue implementation delivers messages in a wrapper
210
+ * format that includes the `__fedify_ordering_key__` property. Currently,
211
+ * only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
212
+ * For other queue implementations (e.g., `InProcessMessageQueue`,
213
+ * `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
214
+ * `AmqpMessageQueue`), the ordering key cannot be detected by
215
+ * `ParallelMessageQueue`, so ordering guarantees are handled by those
216
+ * implementations directly rather than at the `ParallelMessageQueue` level.
217
+ *
218
+ * Messages with the same ordering key will never be processed concurrently
219
+ * by different workers, ensuring sequential processing within each key.
220
+ * Messages with different ordering keys (or no ordering key) can still be
221
+ * processed in parallel.
222
+ *
165
223
  * @since 1.0.0
166
224
  */
167
225
  var ParallelMessageQueue = class ParallelMessageQueue {
@@ -173,6 +231,15 @@ var ParallelMessageQueue = class ParallelMessageQueue {
173
231
  */
174
232
  nativeRetrial;
175
233
  /**
234
+ * Tracks which ordering keys are currently being processed to ensure
235
+ * sequential processing for messages with the same key.
236
+ */
237
+ #processingKeys = /* @__PURE__ */ new Set();
238
+ /**
239
+ * Pending messages waiting for their ordering key to become available.
240
+ */
241
+ #pendingMessages = [];
242
+ /**
176
243
  * Constructs a new {@link ParallelMessageQueue} with the given queue and
177
244
  * number of workers.
178
245
  * @param queue The message queue to use under the hood. Note that
@@ -200,6 +267,25 @@ var ParallelMessageQueue = class ParallelMessageQueue {
200
267
  }
201
268
  await this.queue.enqueueMany(messages, options);
202
269
  }
270
+ /**
271
+ * Extracts ordering key from a message if present.
272
+ *
273
+ * This method only works for queue implementations that deliver messages
274
+ * in the wrapper format with `__fedify_ordering_key__` property. Currently,
275
+ * only `DenoKvMessageQueue` and `WorkersMessageQueue` use this format.
276
+ *
277
+ * For other queue implementations (`InProcessMessageQueue`,
278
+ * `RedisMessageQueue`, `PostgresMessageQueue`, `SqliteMessageQueue`,
279
+ * `AmqpMessageQueue`), messages are delivered as raw payloads without the
280
+ * wrapper, so the ordering key cannot be detected here. Those
281
+ * implementations handle ordering guarantees internally.
282
+ */
283
+ #extractOrderingKey(message) {
284
+ if (message != null && typeof message === "object") {
285
+ if ("__fedify_ordering_key__" in message) return message.__fedify_ordering_key__;
286
+ }
287
+ return void 0;
288
+ }
203
289
  listen(handler, options = {}) {
204
290
  const workers = /* @__PURE__ */ new Map();
205
291
  return this.queue.listen(async (message) => {
@@ -208,13 +294,33 @@ var ParallelMessageQueue = class ParallelMessageQueue {
208
294
  workers.delete(consumedId);
209
295
  }
210
296
  const workerId = crypto.randomUUID();
211
- const promise = this.#work(workerId, handler, message);
297
+ const orderingKey = this.#extractOrderingKey(message);
298
+ if (orderingKey != null && this.#processingKeys.has(orderingKey)) await new Promise((resolve) => {
299
+ this.#pendingMessages.push({
300
+ message,
301
+ orderingKey,
302
+ resolve
303
+ });
304
+ });
305
+ if (orderingKey != null) this.#processingKeys.add(orderingKey);
306
+ const promise = this.#work(workerId, handler, message, orderingKey);
212
307
  workers.set(workerId, promise);
213
308
  }, options);
214
309
  }
215
- async #work(workerId, handler, message) {
310
+ async #work(workerId, handler, message, orderingKey) {
216
311
  await this.#sleep(0);
217
- await handler(message);
312
+ try {
313
+ await handler(message);
314
+ } finally {
315
+ if (orderingKey != null) {
316
+ this.#processingKeys.delete(orderingKey);
317
+ const pendingIdx = this.#pendingMessages.findIndex((p) => p.orderingKey === orderingKey);
318
+ if (pendingIdx >= 0) {
319
+ const pending = this.#pendingMessages.splice(pendingIdx, 1)[0];
320
+ pending.resolve();
321
+ }
322
+ }
323
+ }
218
324
  return workerId;
219
325
  }
220
326
  #sleep(ms) {
@@ -2,16 +2,133 @@
2
2
  import { Temporal } from "@js-temporal/polyfill";
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
 
5
- import { deno_default } from "./lookup-DOSnR912.js";
6
- import { CryptographicKey, Object as Object$1, isActor } from "./actor-B_gRMloq.js";
7
5
  import { getLogger } from "@logtape/logtape";
6
+ import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
8
7
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
9
- import { getDocumentLoader } from "@fedify/vocab-runtime";
10
8
  import { encodeHex } from "byte-encodings/hex";
11
9
  import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
12
10
  import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
13
11
  import { Item, decodeDict, encodeItem } from "structured-field-values";
12
+ import { getDocumentLoader } from "@fedify/vocab-runtime";
13
+
14
+ //#region deno.json
15
+ var name = "@fedify/fedify";
16
+ var version = "2.0.0-pr.559.4+6357309b";
17
+ var license = "MIT";
18
+ var exports = {
19
+ ".": "./src/mod.ts",
20
+ "./compat": "./src/compat/mod.ts",
21
+ "./federation": "./src/federation/mod.ts",
22
+ "./nodeinfo": "./src/nodeinfo/mod.ts",
23
+ "./otel": "./src/otel/mod.ts",
24
+ "./sig": "./src/sig/mod.ts",
25
+ "./utils": "./src/utils/mod.ts",
26
+ "./vocab": "./src/vocab/mod.ts"
27
+ };
28
+ var imports = {
29
+ "@multiformats/base-x": "npm:@multiformats/base-x@^4.0.1",
30
+ "@opentelemetry/core": "npm:@opentelemetry/core@^2.0.0",
31
+ "@opentelemetry/sdk-trace-base": "npm:@opentelemetry/sdk-trace-base@^2.0.0",
32
+ "@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@^1.27.0",
33
+ "@std/assert": "jsr:@std/assert@^0.226.0",
34
+ "@std/url": "jsr:@std/url@^0.225.1",
35
+ "asn1js": "npm:asn1js@^3.0.7",
36
+ "fast-check": "npm:fast-check@^3.22.0",
37
+ "fetch-mock": "npm:fetch-mock@^12.5.2",
38
+ "json-canon": "npm:json-canon@^1.0.1",
39
+ "jsonld": "npm:jsonld@^9.0.0",
40
+ "multicodec": "npm:multicodec@^3.2.1",
41
+ "pkijs": "npm:pkijs@^3.3.3",
42
+ "structured-field-values": "npm:structured-field-values@^2.0.4",
43
+ "uri-template-router": "npm:uri-template-router@^1.0.0",
44
+ "url-template": "npm:url-template@^3.1.1"
45
+ };
46
+ var exclude = [
47
+ ".test-report.xml",
48
+ "apidoc/",
49
+ "dist/",
50
+ "node_modules/",
51
+ "npm/",
52
+ "pnpm-lock.yaml",
53
+ "src/cfworkers/dist/",
54
+ "src/cfworkers/fixtures/",
55
+ "src/cfworkers/imports.ts",
56
+ "src/cfworkers/README.md",
57
+ "src/cfworkers/server.ts",
58
+ "src/cfworkers/server.js",
59
+ "src/cfworkers/server.js.map"
60
+ ];
61
+ var publish = { "exclude": [
62
+ "**/*.test.ts",
63
+ "src/testing/",
64
+ "tsdown.config.ts",
65
+ "scripts/",
66
+ "wrangler.toml"
67
+ ] };
68
+ var tasks = {
69
+ "codegen": "deno task -f @fedify/vocab compile",
70
+ "cache": {
71
+ "command": "deno cache src/mod.ts",
72
+ "dependencies": ["codegen"]
73
+ },
74
+ "check": {
75
+ "command": "deno fmt --check && deno lint && deno check src/**/*.ts",
76
+ "dependencies": ["codegen"]
77
+ },
78
+ "test": {
79
+ "command": "deno test --check --doc --allow-read --allow-write --allow-env --unstable-kv --trace-leaks --parallel",
80
+ "dependencies": ["codegen"]
81
+ },
82
+ "coverage": "deno task test --clean --coverage && deno coverage --html coverage",
83
+ "bench": {
84
+ "command": "deno bench --allow-read --allow-write --allow-net --allow-env --allow-run --unstable-kv",
85
+ "dependencies": ["codegen"]
86
+ },
87
+ "apidoc": {
88
+ "command": "deno doc --html --name=Fedify --output=apidoc/ src/mod.ts",
89
+ "dependencies": ["codegen"]
90
+ },
91
+ "publish": {
92
+ "command": "deno publish",
93
+ "dependencies": ["codegen"]
94
+ },
95
+ "pnpm:install": "pnpm install --silent",
96
+ "pnpm:build": {
97
+ "command": "pnpm exec tsdown",
98
+ "dependencies": ["pnpm:build-vocab"]
99
+ },
100
+ "test:node": {
101
+ "command": "cd dist/ && node --test",
102
+ "dependencies": ["pnpm:build"]
103
+ },
104
+ "test:bun": {
105
+ "command": "cd dist/ && bun test --timeout 60000",
106
+ "dependencies": ["pnpm:build"]
107
+ },
108
+ "test:cfworkers": {
109
+ "command": "pnpm exec wrangler deploy --dry-run --outdir src/cfworkers && node --import=tsx src/cfworkers/client.ts",
110
+ "dependencies": ["pnpm:build"]
111
+ },
112
+ "test-all": { "dependencies": [
113
+ "check",
114
+ "test",
115
+ "test:node",
116
+ "test:bun",
117
+ "test:cfworkers"
118
+ ] }
119
+ };
120
+ var deno_default = {
121
+ name,
122
+ version,
123
+ license,
124
+ exports,
125
+ imports,
126
+ exclude,
127
+ publish,
128
+ tasks
129
+ };
14
130
 
131
+ //#endregion
15
132
  //#region src/sig/key.ts
16
133
  /**
17
134
  * Checks if the given key is valid and supported. No-op if the key is valid,
@@ -285,7 +402,7 @@ async function signRequest(request, privateKey, keyId, options = {}) {
285
402
  if (span.isRecording()) {
286
403
  span.setAttribute(ATTR_HTTP_REQUEST_METHOD, signed.method);
287
404
  span.setAttribute(ATTR_URL_FULL, signed.url);
288
- for (const [name, value] of signed.headers) span.setAttribute(ATTR_HTTP_REQUEST_HEADER(name), value);
405
+ for (const [name$1, value] of signed.headers) span.setAttribute(ATTR_HTTP_REQUEST_HEADER(name$1), value);
289
406
  span.setAttribute("http_signatures.key_id", keyId.href);
290
407
  }
291
408
  return signed;
@@ -313,8 +430,8 @@ async function signRequestDraft(request, privateKey, keyId, span, currentTime, b
313
430
  }
314
431
  if (!headers.has("Date")) headers.set("Date", currentTime == null ? (/* @__PURE__ */ new Date()).toUTCString() : new Date(currentTime.toString()).toUTCString());
315
432
  const serialized = [["(request-target)", `${request.method.toLowerCase()} ${url.pathname}`], ...headers];
316
- const headerNames = serialized.map(([name]) => name);
317
- const message = serialized.map(([name, value]) => `${name}: ${value.trim()}`).join("\n");
433
+ const headerNames = serialized.map(([name$1]) => name$1);
434
+ const message = serialized.map(([name$1, value]) => `${name$1}: ${value.trim()}`).join("\n");
318
435
  const signature = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", privateKey, new TextEncoder().encode(message));
319
436
  const sigHeader = `keyId="${keyId.href}",algorithm="rsa-sha256",headers="${headerNames.join(" ")}",signature="${encodeBase64(signature)}"`;
320
437
  headers.set("Signature", sigHeader);
@@ -509,7 +626,7 @@ async function verifyRequest(request, options = {}) {
509
626
  if (span.isRecording()) {
510
627
  span.setAttribute(ATTR_HTTP_REQUEST_METHOD, request.method);
511
628
  span.setAttribute(ATTR_URL_FULL, request.url);
512
- for (const [name, value] of request.headers) span.setAttribute(ATTR_HTTP_REQUEST_HEADER(name), value);
629
+ for (const [name$1, value] of request.headers) span.setAttribute(ATTR_HTTP_REQUEST_HEADER(name$1), value);
513
630
  }
514
631
  try {
515
632
  let spec = options.spec;
@@ -692,7 +809,7 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
692
809
  logger.debug("Failed to verify; required headers missing in the Signature header: {headers}.", { headers });
693
810
  return null;
694
811
  }
695
- const message = headerNames.map((name) => `${name}: ` + (name === "(request-target)" ? `${request.method.toLowerCase()} ${new URL(request.url).pathname}` : name === "(created)" ? sigValues.created ?? "" : name === "(expires)" ? sigValues.expires ?? "" : name === "host" ? request.headers.get("host") ?? new URL(request.url).host : request.headers.get(name))).join("\n");
812
+ const message = headerNames.map((name$1) => `${name$1}: ` + (name$1 === "(request-target)" ? `${request.method.toLowerCase()} ${new URL(request.url).pathname}` : name$1 === "(created)" ? sigValues.created ?? "" : name$1 === "(expires)" ? sigValues.expires ?? "" : name$1 === "host" ? request.headers.get("host") ?? new URL(request.url).host : request.headers.get(name$1))).join("\n");
696
813
  const sig = decodeBase64(signature);
697
814
  span?.setAttribute("http_signatures.signature", encodeHex(sig));
698
815
  const verified = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, sig, new TextEncoder().encode(message));
@@ -1055,4 +1172,4 @@ function timingSafeEqual(a, b) {
1055
1172
  }
1056
1173
 
1057
1174
  //#endregion
1058
- export { doubleKnock, exportJwk, fetchKey, generateCryptoKeyPair, importJwk, signRequest, validateCryptoKey, verifyRequest };
1175
+ export { deno_default, doubleKnock, exportJwk, fetchKey, generateCryptoKeyPair, importJwk, signRequest, validateCryptoKey, verifyRequest };
@@ -1,4 +1,4 @@
1
- import { CryptographicKey, Multikey } from "./vocab-CeDBzu-f.cjs";
1
+ import { CryptographicKey, Multikey } from "@fedify/vocab";
2
2
  import { DocumentLoader } from "@fedify/vocab-runtime";
3
3
  import { TracerProvider } from "@opentelemetry/api";
4
4