@fedify/fedify 2.0.36-dev.2637 → 2.0.36-dev.2653

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 (47) hide show
  1. package/dist/{builder-BC3eRIbe.mjs → builder-D6LGNjSj.mjs} +3 -3
  2. package/dist/compat/transformers.test.mjs +1 -1
  3. package/dist/{deno-Dv6PIwH_.mjs → deno-DnlTCwP9.mjs} +1 -1
  4. package/dist/{docloader-BgHM-JLc.mjs → docloader-ClA1cBXp.mjs} +2 -2
  5. package/dist/federation/builder.test.mjs +1 -1
  6. package/dist/federation/handler.test.mjs +6 -6
  7. package/dist/federation/idempotency.test.mjs +2 -2
  8. package/dist/federation/inbox.test.mjs +1 -1
  9. package/dist/federation/middleware.test.mjs +385 -6
  10. package/dist/federation/mod.cjs +1 -1
  11. package/dist/federation/mod.js +1 -1
  12. package/dist/federation/send.test.mjs +3 -3
  13. package/dist/federation/temporal.test.mjs +1 -1
  14. package/dist/federation/webfinger.test.mjs +1 -1
  15. package/dist/{http-D69BMfAn.cjs → http-CUyMHX68.cjs} +1 -1
  16. package/dist/{http-CrEB4I8A.js → http-Cqbz1vSQ.js} +1 -1
  17. package/dist/{http-BWoAqky4.mjs → http-pcUSzQJO.mjs} +2 -2
  18. package/dist/{inbox-0Dbv0QVc.mjs → inbox-Dvd92fR4.mjs} +1 -1
  19. package/dist/{key-ChKDZfvS.mjs → key-WbbcbEg2.mjs} +1 -1
  20. package/dist/{kv-cache-B0JZ30EF.cjs → kv-cache-C2CZ0wo0.cjs} +1 -1
  21. package/dist/{kv-cache-CDvE8__9.js → kv-cache-CFnt0ze9.js} +1 -1
  22. package/dist/{ld-DbSNiv8Q.mjs → ld-CQIFcE4D.mjs} +2 -2
  23. package/dist/{middleware-C_fDk_dx.cjs → middleware-BW7yDR0N.cjs} +46 -30
  24. package/dist/{middleware-CEufJR5k.js → middleware-C1wIK-Eo.js} +45 -29
  25. package/dist/{middleware-DCtCNota.cjs → middleware-CNSvd_Yd.cjs} +1 -1
  26. package/dist/{middleware-BA9cM-vz.mjs → middleware-DdgQlj3K.mjs} +1 -1
  27. package/dist/{middleware-C849kmHB.mjs → middleware-pG3fni-8.mjs} +53 -37
  28. package/dist/mod.cjs +4 -4
  29. package/dist/mod.js +4 -4
  30. package/dist/nodeinfo/handler.test.mjs +1 -1
  31. package/dist/{owner-BJeJfxnT.mjs → owner-U2uzi8vw.mjs} +2 -2
  32. package/dist/{proof-DNgQwz9E.cjs → proof-Czu16ywv.cjs} +1 -1
  33. package/dist/{proof-DwooE8kV.js → proof-DlcTuXU1.js} +1 -1
  34. package/dist/{proof-CaMBdk_x.mjs → proof-DuGHbMbs.mjs} +2 -2
  35. package/dist/{send-CYrAOC1K.mjs → send-JCtYxtYm.mjs} +2 -2
  36. package/dist/sig/http.test.mjs +2 -2
  37. package/dist/sig/key.test.mjs +2 -2
  38. package/dist/sig/ld.test.mjs +2 -2
  39. package/dist/sig/mod.cjs +2 -2
  40. package/dist/sig/mod.js +2 -2
  41. package/dist/sig/owner.test.mjs +1 -1
  42. package/dist/sig/proof.test.mjs +2 -2
  43. package/dist/{temporal-BJoGKOXy.mjs → temporal-B7Rg3f1o.mjs} +1 -1
  44. package/dist/utils/docloader.test.mjs +2 -2
  45. package/dist/utils/mod.cjs +1 -1
  46. package/dist/utils/mod.js +1 -1
  47. package/package.json +5 -5
@@ -2,8 +2,8 @@ import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as RouterError, t as Router } from "./router-CrMLXoOr.mjs";
5
- import { n as version, t as name } from "./deno-Dv6PIwH_.mjs";
6
- import { t as InboxListenerSet } from "./inbox-0Dbv0QVc.mjs";
5
+ import { n as version, t as name } from "./deno-DnlTCwP9.mjs";
6
+ import { t as InboxListenerSet } from "./inbox-Dvd92fR4.mjs";
7
7
  import { getTypeId } from "@fedify/vocab";
8
8
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
9
9
  import { getLogger } from "@logtape/logtape";
@@ -43,7 +43,7 @@ var FederationBuilderImpl = class {
43
43
  this.collectionTypeIds = {};
44
44
  }
45
45
  async build(options) {
46
- const { FederationImpl } = await import("./middleware-BA9cM-vz.mjs");
46
+ const { FederationImpl } = await import("./middleware-DdgQlj3K.mjs");
47
47
  const f = new FederationImpl(options);
48
48
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
49
49
  f.router = this.router.clone();
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { n as assertInstanceOf, t as assert } from "../assert-BhQd8QHP.mjs";
6
6
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
7
- import { _ as autoIdAssigner, g as actorDehydrator, n as FederationImpl } from "../middleware-C849kmHB.mjs";
7
+ import { _ as autoIdAssigner, g as actorDehydrator, n as FederationImpl } from "../middleware-pG3fni-8.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  import { Follow, Person } from "@fedify/vocab";
10
10
  //#region src/compat/transformers.test.ts
@@ -3,6 +3,6 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  //#region deno.json
5
5
  var name = "@fedify/fedify";
6
- var version = "2.0.36-dev.2637+cc2cd6fb";
6
+ var version = "2.0.36-dev.2653+6f730594";
7
7
  //#endregion
8
8
  export { version as n, name as t };
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { o as validateCryptoKey } from "./key-ChKDZfvS.mjs";
5
- import { r as doubleKnockScoped } from "./http-BWoAqky4.mjs";
4
+ import { o as validateCryptoKey } from "./key-WbbcbEg2.mjs";
5
+ import { r as doubleKnockScoped } from "./http-pcUSzQJO.mjs";
6
6
  import { curry } from "es-toolkit";
7
7
  import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest } from "@fedify/vocab-runtime";
8
8
  import { openDirectFetch, openPublicFetch } from "@fedify/vocab-runtime/internal";
@@ -5,7 +5,7 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { i as assertExists, n as assertStrictEquals } from "../std__assert-DflMZha5.mjs";
6
6
  import { t as assertThrows } from "../assert_throws-D4DNWrux.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
8
- import { n as createFederationBuilder } from "../builder-BC3eRIbe.mjs";
8
+ import { n as createFederationBuilder } from "../builder-D6LGNjSj.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Activity, Note, Person } from "@fedify/vocab";
11
11
  //#region src/federation/builder.test.ts
@@ -6,14 +6,14 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-DflMZha5.mjs";
7
7
  import { t as assert } from "../assert-BhQd8QHP.mjs";
8
8
  import { t as assertRejects } from "../assert_rejects-WyYSTDDX.mjs";
9
- import { i as generateCryptoKeyPair } from "../key-ChKDZfvS.mjs";
10
- import { c as signRequest } from "../http-BWoAqky4.mjs";
9
+ import { i as generateCryptoKeyPair } from "../key-WbbcbEg2.mjs";
10
+ import { c as signRequest } from "../http-pcUSzQJO.mjs";
11
11
  import { a as rsaPrivateKey3, c as rsaPublicKey3, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CCxRJhWa.mjs";
12
- import { a as compactJsonLd, f as signJsonLd } from "../ld-DbSNiv8Q.mjs";
13
- import { n as signObject } from "../proof-CaMBdk_x.mjs";
12
+ import { a as compactJsonLd, f as signJsonLd } from "../ld-CQIFcE4D.mjs";
13
+ import { n as signObject } from "../proof-DuGHbMbs.mjs";
14
14
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
15
- import { a as createFederation, c as handleCollection, d as handleObject, f as respondWithObject, l as handleCustomCollection, p as respondWithObjectIfAcceptable, s as handleActor, u as handleInbox } from "../middleware-C849kmHB.mjs";
16
- import { t as InboxListenerSet } from "../inbox-0Dbv0QVc.mjs";
15
+ import { a as createFederation, c as handleCollection, d as handleObject, f as respondWithObject, l as handleCustomCollection, p as respondWithObjectIfAcceptable, s as handleActor, u as handleInbox } from "../middleware-pG3fni-8.mjs";
16
+ import { t as InboxListenerSet } from "../inbox-Dvd92fR4.mjs";
17
17
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
18
18
  import { Create, CryptographicKey, Multikey, Note, Person } from "@fedify/vocab";
19
19
  //#region src/federation/handler.test.ts
@@ -4,9 +4,9 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import "../std__assert-DflMZha5.mjs";
6
6
  import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-CCxRJhWa.mjs";
7
- import { n as signObject } from "../proof-CaMBdk_x.mjs";
7
+ import { n as signObject } from "../proof-DuGHbMbs.mjs";
8
8
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
9
- import { a as createFederation } from "../middleware-C849kmHB.mjs";
9
+ import { a as createFederation } from "../middleware-pG3fni-8.mjs";
10
10
  import { mockDocumentLoader, test } from "@fedify/fixture";
11
11
  import { Create, Follow, Person } from "@fedify/vocab";
12
12
  //#region src/federation/idempotency.test.ts
@@ -3,7 +3,7 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { r as assertFalse, t as assertThrows } from "../assert_throws-D4DNWrux.mjs";
6
- import { n as actorsHaveSameOrigin, t as InboxListenerSet } from "../inbox-0Dbv0QVc.mjs";
6
+ import { n as actorsHaveSameOrigin, t as InboxListenerSet } from "../inbox-Dvd92fR4.mjs";
7
7
  import { test } from "@fedify/fixture";
8
8
  import { Activity, Create, Invite, Offer, Update } from "@fedify/vocab";
9
9
  //#region src/federation/inbox.test.ts
@@ -9,14 +9,15 @@ import { r as assertFalse, t as assertThrows } from "../assert_throws-D4DNWrux.m
9
9
  import { n as assertInstanceOf, t as assert } from "../assert-BhQd8QHP.mjs";
10
10
  import { t as assertNotEquals } from "../assert_not_equals-BkxW5QJI.mjs";
11
11
  import { t as assertRejects } from "../assert_rejects-WyYSTDDX.mjs";
12
- import { c as signRequest, u as verifyRequest } from "../http-BWoAqky4.mjs";
12
+ import { c as signRequest, u as verifyRequest } from "../http-pcUSzQJO.mjs";
13
13
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CCxRJhWa.mjs";
14
- import { n as getAuthenticatedDocumentLoader } from "../docloader-BgHM-JLc.mjs";
15
- import { a as compactJsonLd, f as signJsonLd, i as attachSignature, m as verifyJsonLd, s as detachSignature } from "../ld-DbSNiv8Q.mjs";
16
- import { t as doesActorOwnKey } from "../owner-BJeJfxnT.mjs";
17
- import { n as signObject, r as verifyObject } from "../proof-CaMBdk_x.mjs";
14
+ import { n as getAuthenticatedDocumentLoader } from "../docloader-ClA1cBXp.mjs";
15
+ import { a as compactJsonLd, f as signJsonLd, i as attachSignature, m as verifyJsonLd, s as detachSignature } from "../ld-CQIFcE4D.mjs";
16
+ import { t as doesActorOwnKey } from "../owner-U2uzi8vw.mjs";
17
+ import { n as signObject, r as verifyObject } from "../proof-DuGHbMbs.mjs";
18
18
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
19
- import { a as createFederation, i as KvSpecDeterminer, n as FederationImpl, r as InboxContextImpl, t as ContextImpl } from "../middleware-C849kmHB.mjs";
19
+ import { a as createFederation, i as KvSpecDeterminer, n as FederationImpl, r as InboxContextImpl, t as ContextImpl } from "../middleware-pG3fni-8.mjs";
20
+ import { t as SendActivityError } from "../send-JCtYxtYm.mjs";
20
21
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
21
22
  import * as vocab from "@fedify/vocab";
22
23
  import { Create, Offer, Person, getTypeId, lookupObject } from "@fedify/vocab";
@@ -195,6 +196,104 @@ test("createFederation()", async (t) => {
195
196
  }), TypeError);
196
197
  });
197
198
  });
199
+ test("FederationImpl.startQueue()", async (t) => {
200
+ function createCountingQueue() {
201
+ const messages = [];
202
+ let listenCount = 0;
203
+ return {
204
+ messages,
205
+ get listenCount() {
206
+ return listenCount;
207
+ },
208
+ enqueue(message) {
209
+ messages.push(message);
210
+ return Promise.resolve();
211
+ },
212
+ listen() {
213
+ listenCount++;
214
+ return Promise.resolve();
215
+ }
216
+ };
217
+ }
218
+ await t.step("starts a fanout-only queue with an explicit selector", async () => {
219
+ const queue = createCountingQueue();
220
+ const federation = new FederationImpl({
221
+ kv: new MemoryKvStore(),
222
+ queue: { fanout: queue },
223
+ manuallyStartQueue: true
224
+ });
225
+ await federation.startQueue(void 0, { queue: "fanout" });
226
+ assertEquals(queue.listenCount, 1);
227
+ await federation.startQueue(void 0, { queue: "fanout" });
228
+ assertEquals(queue.listenCount, 1);
229
+ });
230
+ await t.step("starts a fanout-only queue without a selector", async () => {
231
+ const queue = createCountingQueue();
232
+ await new FederationImpl({
233
+ kv: new MemoryKvStore(),
234
+ queue: { fanout: queue },
235
+ manuallyStartQueue: true
236
+ }).startQueue(void 0);
237
+ assertEquals(queue.listenCount, 1);
238
+ });
239
+ await t.step("starts a fanout-only queue after a fanout enqueue", async () => {
240
+ const queue = createCountingQueue();
241
+ const ctx = new ContextImpl({
242
+ data: void 0,
243
+ federation: new FederationImpl({
244
+ kv: new MemoryKvStore(),
245
+ contextLoaderFactory: () => mockDocumentLoader,
246
+ queue: { fanout: queue }
247
+ }),
248
+ url: new URL("https://example.com/"),
249
+ documentLoader: mockDocumentLoader,
250
+ contextLoader: mockDocumentLoader
251
+ });
252
+ const activity = new vocab.Create({
253
+ id: new URL("https://example.com/activity/1"),
254
+ actor: new URL("https://example.com/person")
255
+ });
256
+ await ctx.sendActivity([{
257
+ privateKey: rsaPrivateKey2,
258
+ keyId: rsaPublicKey2.id
259
+ }], {
260
+ id: new URL("https://example.com/recipient"),
261
+ inboxId: new URL("https://example.com/inbox")
262
+ }, activity, { fanout: "force" });
263
+ assertEquals(queue.listenCount, 1);
264
+ assertEquals(queue.messages.length, 1);
265
+ assertEquals(queue.messages[0].type, "fanout");
266
+ });
267
+ await t.step("does not start a fanout-only queue automatically when manually started", async () => {
268
+ const queue = createCountingQueue();
269
+ const ctx = new ContextImpl({
270
+ data: void 0,
271
+ federation: new FederationImpl({
272
+ kv: new MemoryKvStore(),
273
+ contextLoaderFactory: () => mockDocumentLoader,
274
+ queue: { fanout: queue },
275
+ manuallyStartQueue: true
276
+ }),
277
+ url: new URL("https://example.com/"),
278
+ documentLoader: mockDocumentLoader,
279
+ contextLoader: mockDocumentLoader
280
+ });
281
+ const activity = new vocab.Create({
282
+ id: new URL("https://example.com/activity/1"),
283
+ actor: new URL("https://example.com/person")
284
+ });
285
+ await ctx.sendActivity([{
286
+ privateKey: rsaPrivateKey2,
287
+ keyId: rsaPublicKey2.id
288
+ }], {
289
+ id: new URL("https://example.com/recipient"),
290
+ inboxId: new URL("https://example.com/inbox")
291
+ }, activity, { fanout: "force" });
292
+ assertEquals(queue.listenCount, 0);
293
+ assertEquals(queue.messages.length, 1);
294
+ assertEquals(queue.messages[0].type, "fanout");
295
+ });
296
+ });
198
297
  test({
199
298
  name: "Federation.createContext()",
200
299
  permissions: {
@@ -5074,6 +5173,286 @@ test("ContextImpl.sendActivity() honors the private-address policy", async (t) =
5074
5173
  }
5075
5174
  });
5076
5175
  });
5176
+ function deferredSignal() {
5177
+ let resolve;
5178
+ return {
5179
+ promise: new Promise((r) => resolve = r),
5180
+ resolve
5181
+ };
5182
+ }
5183
+ test("ContextImpl.sendActivity() waits for sibling deliveries on failure", async () => {
5184
+ const failingInbox = "http://127.0.0.1:3001/inbox/fail";
5185
+ const slowInbox = "http://127.0.0.1:3002/inbox/slow";
5186
+ const failingEntered = deferredSignal();
5187
+ const slowEntered = deferredSignal();
5188
+ const slowCompleted = deferredSignal();
5189
+ let releaseSlow;
5190
+ const slowGate = new Promise((resolve) => releaseSlow = resolve);
5191
+ let slowStarted = false;
5192
+ let slowSettled = false;
5193
+ esm_default.spyGlobal();
5194
+ esm_default.post(failingInbox, () => {
5195
+ failingEntered.resolve();
5196
+ return new Response(null, { status: 500 });
5197
+ });
5198
+ esm_default.post(slowInbox, async () => {
5199
+ slowStarted = true;
5200
+ slowEntered.resolve();
5201
+ await slowGate;
5202
+ slowSettled = true;
5203
+ slowCompleted.resolve();
5204
+ return new Response(null, { status: 202 });
5205
+ });
5206
+ let observed;
5207
+ let released = false;
5208
+ try {
5209
+ const ctx = createFederation({
5210
+ kv: new MemoryKvStore(),
5211
+ allowPrivateAddress: true
5212
+ }).createContext(new URL("https://example.com/"));
5213
+ const activity = new Create({
5214
+ id: new URL("https://example.com/activity/fanout"),
5215
+ actor: new URL("https://example.com/person")
5216
+ });
5217
+ let outerSettled = false;
5218
+ let outerError;
5219
+ observed = ctx.sendActivity([{
5220
+ privateKey: ed25519PrivateKey,
5221
+ keyId: ed25519Multikey.id
5222
+ }], [new Person({
5223
+ id: new URL("https://example.com/recipients/failing"),
5224
+ inbox: new URL(failingInbox)
5225
+ }), new Person({
5226
+ id: new URL("https://example.com/recipients/slow"),
5227
+ inbox: new URL(slowInbox)
5228
+ })], activity, { immediate: true }).then(() => {
5229
+ outerSettled = true;
5230
+ }, (error) => {
5231
+ outerSettled = true;
5232
+ outerError = error;
5233
+ });
5234
+ await Promise.all([failingEntered.promise, slowEntered.promise]);
5235
+ await new Promise((resolve) => setTimeout(resolve, 0));
5236
+ assertFalse(outerSettled, "sendActivity() must wait for sibling deliveries before rejecting");
5237
+ releaseSlow();
5238
+ released = true;
5239
+ await observed;
5240
+ assert(slowSettled, "the delayed delivery should have settled");
5241
+ assertInstanceOf(outerError, SendActivityError);
5242
+ } finally {
5243
+ if (!released) releaseSlow();
5244
+ if (slowStarted) await slowCompleted.promise;
5245
+ await observed;
5246
+ esm_default.hardReset();
5247
+ }
5248
+ });
5249
+ test("InboxContextImpl.forwardActivity() waits for sibling deliveries on failure", async () => {
5250
+ const failingInbox = "http://127.0.0.1:3003/inbox/fail";
5251
+ const slowInbox = "http://127.0.0.1:3004/inbox/slow";
5252
+ const failingEntered = deferredSignal();
5253
+ const slowEntered = deferredSignal();
5254
+ const slowCompleted = deferredSignal();
5255
+ let releaseSlow;
5256
+ const slowGate = new Promise((resolve) => releaseSlow = resolve);
5257
+ let slowStarted = false;
5258
+ let slowSettled = false;
5259
+ esm_default.spyGlobal();
5260
+ esm_default.post(failingInbox, () => {
5261
+ failingEntered.resolve();
5262
+ return new Response(null, { status: 500 });
5263
+ });
5264
+ esm_default.post(slowInbox, async () => {
5265
+ slowStarted = true;
5266
+ slowEntered.resolve();
5267
+ await slowGate;
5268
+ slowSettled = true;
5269
+ slowCompleted.resolve();
5270
+ return new Response(null, { status: 202 });
5271
+ });
5272
+ let observed;
5273
+ let released = false;
5274
+ try {
5275
+ const federation = new FederationImpl({
5276
+ kv: new MemoryKvStore(),
5277
+ allowPrivateAddress: true
5278
+ });
5279
+ const activity = await signObject(new vocab.Create({
5280
+ id: new URL("https://example.com/activity/forward"),
5281
+ actor: new URL("https://example.com/person2")
5282
+ }), ed25519PrivateKey, ed25519Multikey.id, {
5283
+ contextLoader: mockDocumentLoader,
5284
+ documentLoader: mockDocumentLoader
5285
+ });
5286
+ const ctx = new InboxContextImpl(null, await activity.toJsonLd({ contextLoader: mockDocumentLoader }), activity.id?.href, getTypeId(activity).href, {
5287
+ data: void 0,
5288
+ federation,
5289
+ url: new URL("https://example.com/"),
5290
+ documentLoader: mockDocumentLoader,
5291
+ contextLoader: mockDocumentLoader
5292
+ });
5293
+ let outerSettled = false;
5294
+ let outerError;
5295
+ observed = ctx.forwardActivity([{
5296
+ privateKey: ed25519PrivateKey,
5297
+ keyId: ed25519Multikey.id
5298
+ }], [{
5299
+ id: new URL("https://example.com/recipients/failing"),
5300
+ inboxId: new URL(failingInbox)
5301
+ }, {
5302
+ id: new URL("https://example.com/recipients/slow"),
5303
+ inboxId: new URL(slowInbox)
5304
+ }], {
5305
+ immediate: true,
5306
+ skipIfUnsigned: true
5307
+ }).then(() => {
5308
+ outerSettled = true;
5309
+ }, (error) => {
5310
+ outerSettled = true;
5311
+ outerError = error;
5312
+ });
5313
+ await Promise.all([failingEntered.promise, slowEntered.promise]);
5314
+ await new Promise((resolve) => setTimeout(resolve, 0));
5315
+ assertFalse(outerSettled, "forwardActivity() must wait for sibling deliveries before rejecting");
5316
+ releaseSlow();
5317
+ released = true;
5318
+ await observed;
5319
+ assert(slowSettled, "the delayed delivery should have settled");
5320
+ assertInstanceOf(outerError, SendActivityError);
5321
+ } finally {
5322
+ if (!released) releaseSlow();
5323
+ if (slowStarted) await slowCompleted.promise;
5324
+ await observed;
5325
+ esm_default.hardReset();
5326
+ }
5327
+ });
5328
+ test("FederationImpl.sendActivity() drains deliveries when fan-out setup fails", async () => {
5329
+ const slowInbox = "http://127.0.0.1:3005/inbox/slow";
5330
+ const slowEntered = deferredSignal();
5331
+ const slowCompleted = deferredSignal();
5332
+ let releaseSlow;
5333
+ const slowGate = new Promise((resolve) => releaseSlow = resolve);
5334
+ let slowStarted = false;
5335
+ let slowSettled = false;
5336
+ esm_default.spyGlobal();
5337
+ esm_default.post(slowInbox, async () => {
5338
+ slowStarted = true;
5339
+ slowEntered.resolve();
5340
+ await slowGate;
5341
+ slowSettled = true;
5342
+ slowCompleted.resolve();
5343
+ return new Response(null, { status: 202 });
5344
+ });
5345
+ let observed;
5346
+ let released = false;
5347
+ try {
5348
+ const federation = new FederationImpl({
5349
+ kv: new MemoryKvStore(),
5350
+ allowPrivateAddress: true
5351
+ });
5352
+ const context = federation.createContext(new URL("https://example.com/"));
5353
+ const activity = new Create({
5354
+ id: new URL("https://example.com/activity/setup-failure"),
5355
+ actor: new URL("https://example.com/person")
5356
+ });
5357
+ let outerSettled = false;
5358
+ let outerError;
5359
+ observed = federation.sendActivity([{
5360
+ privateKey: ed25519PrivateKey,
5361
+ keyId: ed25519Multikey.id
5362
+ }], {
5363
+ [slowInbox]: {
5364
+ actorIds: ["https://example.com/recipients/slow"],
5365
+ sharedInbox: false
5366
+ },
5367
+ "not a url": {
5368
+ actorIds: ["https://example.com/recipients/invalid"],
5369
+ sharedInbox: false
5370
+ }
5371
+ }, activity, {
5372
+ context,
5373
+ immediate: true
5374
+ }).then(() => {
5375
+ outerSettled = true;
5376
+ }, (error) => {
5377
+ outerSettled = true;
5378
+ outerError = error;
5379
+ });
5380
+ await slowEntered.promise;
5381
+ await new Promise((resolve) => setTimeout(resolve, 0));
5382
+ assertFalse(outerSettled, "sendActivity() must drain started deliveries when fan-out setup fails");
5383
+ releaseSlow();
5384
+ released = true;
5385
+ await observed;
5386
+ assert(slowSettled, "the started delivery should have settled");
5387
+ assertInstanceOf(outerError, TypeError);
5388
+ } finally {
5389
+ if (!released) releaseSlow();
5390
+ if (slowStarted) await slowCompleted.promise;
5391
+ await observed;
5392
+ esm_default.hardReset();
5393
+ }
5394
+ });
5395
+ test("FederationImpl.sendActivity() handles rejections while building headers", async () => {
5396
+ const federation = new FederationImpl({ kv: new MemoryKvStore() });
5397
+ const context = federation.createContext(new URL("https://example.com/"));
5398
+ const activity = await signObject(new Create({
5399
+ id: new URL("https://example.com/activity/header-failure"),
5400
+ actor: new URL("https://example.com/person")
5401
+ }), ed25519PrivateKey, ed25519Multikey.id, {
5402
+ contextLoader: mockDocumentLoader,
5403
+ documentLoader: mockDocumentLoader
5404
+ });
5405
+ const subtle = crypto.subtle;
5406
+ const originalDigest = subtle.digest;
5407
+ let digestCalls = 0;
5408
+ let releaseSecondDigest;
5409
+ const secondDigestGate = new Promise((resolve) => {
5410
+ releaseSecondDigest = resolve;
5411
+ });
5412
+ let secondDigestStarted;
5413
+ const secondDigestEntered = new Promise((resolve) => {
5414
+ secondDigestStarted = resolve;
5415
+ });
5416
+ subtle.digest = async (algorithm, data) => {
5417
+ digestCalls++;
5418
+ if (digestCalls === 2) {
5419
+ secondDigestStarted();
5420
+ await secondDigestGate;
5421
+ }
5422
+ return await originalDigest.call(subtle, algorithm, data);
5423
+ };
5424
+ const capture = captureUnhandledRejections();
5425
+ let released = false;
5426
+ try {
5427
+ const observed = federation.sendActivity([{
5428
+ privateKey: ed25519PrivateKey,
5429
+ keyId: ed25519Multikey.id
5430
+ }], {
5431
+ "ftp://example.com/fail": {
5432
+ actorIds: ["https://example.com/recipients/failing"],
5433
+ sharedInbox: false
5434
+ },
5435
+ "http://127.0.0.1:3006/inbox": {
5436
+ actorIds: ["https://example.com/recipients/slow"],
5437
+ sharedInbox: false
5438
+ }
5439
+ }, activity, {
5440
+ context,
5441
+ immediate: true,
5442
+ collectionSync: "https://example.com/followers"
5443
+ }).catch((error) => error);
5444
+ await secondDigestEntered;
5445
+ await capture.settle();
5446
+ assertEquals(capture.reasons, [], "an early rejection must not surface as an unhandled rejection");
5447
+ releaseSecondDigest();
5448
+ released = true;
5449
+ await observed;
5450
+ } finally {
5451
+ if (!released) releaseSecondDigest();
5452
+ subtle.digest = originalDigest;
5453
+ capture.stop();
5454
+ }
5455
+ });
5077
5456
  test("Federation.fetch() bounds inbox bodies before dispatch", async () => {
5078
5457
  let dispatched = false;
5079
5458
  const json = JSON.stringify({
@@ -2,7 +2,7 @@ const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  require("../chunk-DDcVe30Y.cjs");
5
- const require_middleware = require("../middleware-C_fDk_dx.cjs");
5
+ const require_middleware = require("../middleware-BW7yDR0N.cjs");
6
6
  let es_toolkit = require("es-toolkit");
7
7
  //#region src/federation/kv.ts
8
8
  /**
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { a as createExponentialBackoffPolicy, c as buildCollectionSynchronizationHeader, d as Router, f as RouterError, i as SendActivityError, l as digest, o as respondWithObject, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as createFederationBuilder } from "../middleware-CEufJR5k.js";
3
+ import { a as createExponentialBackoffPolicy, c as buildCollectionSynchronizationHeader, d as Router, f as RouterError, i as SendActivityError, l as digest, o as respondWithObject, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as createFederationBuilder } from "../middleware-C1wIK-Eo.js";
4
4
  import { isEqual } from "es-toolkit";
5
5
  //#region src/federation/kv.ts
6
6
  /**
@@ -8,10 +8,10 @@ import { r as assertFalse } from "../assert_throws-D4DNWrux.mjs";
8
8
  import { n as assertInstanceOf, t as assert } from "../assert-BhQd8QHP.mjs";
9
9
  import { t as assertNotEquals } from "../assert_not_equals-BkxW5QJI.mjs";
10
10
  import { t as assertRejects } from "../assert_rejects-WyYSTDDX.mjs";
11
- import { u as verifyRequest } from "../http-BWoAqky4.mjs";
11
+ import { u as verifyRequest } from "../http-pcUSzQJO.mjs";
12
12
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CCxRJhWa.mjs";
13
- import { t as doesActorOwnKey } from "../owner-BJeJfxnT.mjs";
14
- import { i as sendActivity, n as extractInboxes, r as fetchWithValidatedRedirects, t as SendActivityError } from "../send-CYrAOC1K.mjs";
13
+ import { t as doesActorOwnKey } from "../owner-U2uzi8vw.mjs";
14
+ import { i as sendActivity, n as extractInboxes, r as fetchWithValidatedRedirects, t as SendActivityError } from "../send-JCtYxtYm.mjs";
15
15
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
16
16
  import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
17
17
  import { FetchError, UrlError } from "@fedify/vocab-runtime";
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import "../std__assert-DflMZha5.mjs";
5
5
  import { r as assertFalse } from "../assert_throws-D4DNWrux.mjs";
6
6
  import { t as assert } from "../assert-BhQd8QHP.mjs";
7
- import { t as hasMalformedKnownTemporalLiteral } from "../temporal-BJoGKOXy.mjs";
7
+ import { t as hasMalformedKnownTemporalLiteral } from "../temporal-B7Rg3f1o.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  //#region src/federation/temporal.test.ts
10
10
  test("hasMalformedKnownTemporalLiteral() detects expanded proof timestamps", async () => {
@@ -5,7 +5,7 @@ import { n as createRequestContext } from "../context-Juj6bdHC.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-DflMZha5.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
8
- import { a as createFederation, o as handleWebFinger } from "../middleware-C849kmHB.mjs";
8
+ import { a as createFederation, o as handleWebFinger } from "../middleware-pG3fni-8.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Image, Link, Person } from "@fedify/vocab";
11
11
  //#region src/federation/webfinger.test.ts
@@ -11,7 +11,7 @@ let byte_encodings_base64 = require("byte-encodings/base64");
11
11
  let structured_field_values = require("structured-field-values");
12
12
  //#region deno.json
13
13
  var name = "@fedify/fedify";
14
- var version = "2.0.36-dev.2637+cc2cd6fb";
14
+ var version = "2.0.36-dev.2653+6f730594";
15
15
  //#endregion
16
16
  //#region src/sig/key.ts
17
17
  /** Classifies decoding failures without hiding unexpected loader errors. */
@@ -10,7 +10,7 @@ import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
10
10
  import { Item, decodeDict, encodeItem } from "structured-field-values";
11
11
  //#region deno.json
12
12
  var name = "@fedify/fedify";
13
- var version = "2.0.36-dev.2637+cc2cd6fb";
13
+ var version = "2.0.36-dev.2653+6f730594";
14
14
  //#endregion
15
15
  //#region src/sig/key.ts
16
16
  /** Classifies decoding failures without hiding unexpected loader errors. */
@@ -1,8 +1,8 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-Dv6PIwH_.mjs";
5
- import { o as validateCryptoKey, r as fetchKey } from "./key-ChKDZfvS.mjs";
4
+ import { n as version, t as name } from "./deno-DnlTCwP9.mjs";
5
+ import { o as validateCryptoKey, r as fetchKey } from "./key-WbbcbEg2.mjs";
6
6
  import { CryptographicKey } from "@fedify/vocab";
7
7
  import { SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { FetchError } from "@fedify/vocab-runtime";
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-Dv6PIwH_.mjs";
4
+ import { n as version, t as name } from "./deno-DnlTCwP9.mjs";
5
5
  import { Activity, getTypeId } from "@fedify/vocab";
6
6
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
7
7
  import { getLogger } from "@logtape/logtape";
@@ -1,7 +1,7 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-Dv6PIwH_.mjs";
4
+ import { n as version, t as name } from "./deno-DnlTCwP9.mjs";
5
5
  import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
6
6
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
7
7
  import { FetchError, UrlError, getDocumentLoader } from "@fedify/vocab-runtime";
@@ -1,7 +1,7 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  require("./chunk-DDcVe30Y.cjs");
4
- const require_http = require("./http-D69BMfAn.cjs");
4
+ const require_http = require("./http-CUyMHX68.cjs");
5
5
  let _logtape_logtape = require("@logtape/logtape");
6
6
  let es_toolkit = require("es-toolkit");
7
7
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { l as validateCryptoKey, t as doubleKnockScoped } from "./http-CrEB4I8A.js";
3
+ import { l as validateCryptoKey, t as doubleKnockScoped } from "./http-Cqbz1vSQ.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { curry } from "es-toolkit";
6
6
  import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts } from "@fedify/vocab-runtime";