@fedify/fedify 2.5.0-dev.2448 → 2.5.0-dev.2494

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 (126) hide show
  1. package/README.md +3 -0
  2. package/dist/{builder-DbC8bn6k.mjs → builder-Y3pokaG2.mjs} +2 -2
  3. package/dist/{client-D-XP_Ccb.d.cts → client-3InDZbba.d.cts} +0 -1
  4. package/dist/{client-D-XP_Ccb.d.ts → client-3InDZbba.d.ts} +0 -1
  5. package/dist/{client-CEnNz8O7.mjs → client-5SIFzW3q.mjs} +113 -64
  6. package/dist/compat/mod.d.cts +1 -1
  7. package/dist/compat/mod.d.ts +1 -1
  8. package/dist/compat/transformers.test.mjs +1 -1
  9. package/dist/{compound-proof-heeXW2_A.mjs → compound-proof-BM7AINht.mjs} +2 -2
  10. package/dist/{context-GlHrzdSP.d.ts → context-DQJE9YfH.d.ts} +29 -3
  11. package/dist/{context-BazwQrHn.d.cts → context-DSuL3YWx.d.cts} +29 -3
  12. package/dist/{deno-Dpewt_jJ.mjs → deno-D7blYLbb.mjs} +1 -1
  13. package/dist/docloader-DbBbDaY-.mjs +136 -0
  14. package/dist/{exporter-DThFfe7P.mjs → exporter-DVG68ozH.mjs} +36 -30
  15. package/dist/federation/builder.test.mjs +1 -1
  16. package/dist/federation/compound-fanout.test.mjs +6 -3
  17. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  18. package/dist/federation/handler.test.mjs +5 -5
  19. package/dist/federation/hashlink-media.test.mjs +2 -2
  20. package/dist/federation/idempotency.test.mjs +2 -2
  21. package/dist/federation/inbox-actorless.test.mjs +5 -5
  22. package/dist/federation/inbox-report.test.mjs +5 -5
  23. package/dist/federation/inbox-view.test.mjs +1 -1
  24. package/dist/federation/inbox.test.mjs +1 -1
  25. package/dist/federation/metrics.test.mjs +1 -1
  26. package/dist/federation/middleware.test.mjs +468 -33
  27. package/dist/federation/mod.cjs +1 -1
  28. package/dist/federation/mod.d.cts +3 -3
  29. package/dist/federation/mod.d.ts +3 -3
  30. package/dist/federation/mod.js +1 -1
  31. package/dist/federation/mq.test.mjs +1 -1
  32. package/dist/federation/outgoing-proof.test.mjs +9 -6
  33. package/dist/federation/portable-actor.test.mjs +3 -3
  34. package/dist/federation/portable-chain.test.mjs +3 -3
  35. package/dist/federation/portable-collection.test.mjs +4 -4
  36. package/dist/federation/portable-delivery.test.mjs +2 -2
  37. package/dist/federation/portable-gateway-keys.test.mjs +11 -5
  38. package/dist/federation/portable-inbox.test.mjs +4 -4
  39. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  40. package/dist/federation/portable-route-activity.test.mjs +2 -2
  41. package/dist/federation/portable.test.mjs +3 -3
  42. package/dist/federation/send.test.mjs +502 -93
  43. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  44. package/dist/federation/tasks/tasks.test.mjs +2 -2
  45. package/dist/federation/temporal.test.mjs +1 -1
  46. package/dist/federation/webfinger.test.mjs +1 -1
  47. package/dist/{federation-D8YexkP1.cjs → federation-CFaTsSMN.cjs} +246 -108
  48. package/dist/{http-CAl7Num5.cjs → http-2SwIR4b-.cjs} +179 -110
  49. package/dist/{http-BBr-VLil.js → http-CrYxBKqG.js} +178 -109
  50. package/dist/{http-BZkyQIkb.mjs → http-CxKd10Lq.mjs} +204 -106
  51. package/dist/{inbox-wDuMKaFW.mjs → inbox-CM0UktG8.mjs} +2 -2
  52. package/dist/{inbox-view-NE9wXdLo.mjs → inbox-view-BJ5DBPFo.mjs} +1 -1
  53. package/dist/{key-nYGY4QB9.d.cts → key-DbLNT-vA.d.ts} +1 -2
  54. package/dist/{key-DhsBxxZN.d.ts → key-DlHT4R6k.d.cts} +1 -2
  55. package/dist/{kv-cache-CTQSfkmp.mjs → kv-cache-CIdGATck.mjs} +1 -1
  56. package/dist/{kv-cache-CjgCOz3i.cjs → kv-cache-Cd8xKv-x.cjs} +72 -27
  57. package/dist/{kv-cache-U7uz5nx4.js → kv-cache-DbgFe35y.js} +73 -28
  58. package/dist/{ld-Cel1RRZ5.d.cts → ld-CWVpo_NM.d.cts} +1 -1
  59. package/dist/{ld-CQFuiah9.mjs → ld-DUeBHFWH.mjs} +2 -2
  60. package/dist/{ld-CcuZ4NZa.d.ts → ld-Inagww4R.d.ts} +1 -1
  61. package/dist/{metrics-B-QxRSSR.mjs → metrics-B7TvoAP7.mjs} +1 -1
  62. package/dist/{middleware-BdlNa8BD.mjs → middleware-BE4JXx-U.mjs} +1 -1
  63. package/dist/{middleware-CuVa0UuS.mjs → middleware-BEaUojg7.mjs} +89 -49
  64. package/dist/{middleware-P-AZEQMn.js → middleware-CId6efsJ.js} +247 -109
  65. package/dist/{mod-DvWOv2Ho.d.cts → mod-6TQ4UQZb.d.cts} +1 -1
  66. package/dist/{mod-BHQE-7WQ.d.ts → mod-B4kBw1-8.d.ts} +1 -1
  67. package/dist/{mod-DkhfZC1u.d.ts → mod-B7j30IQf.d.ts} +2 -9
  68. package/dist/{mod-B2783_3v.d.cts → mod-C1nT74IG.d.cts} +1 -1
  69. package/dist/{mod-DjZfJj9R.d.ts → mod-CIeBgsOC.d.ts} +1 -1
  70. package/dist/{mod-B5-wdzmJ.d.cts → mod-YvA4Rk8J.d.cts} +2 -9
  71. package/dist/mod.cjs +5 -5
  72. package/dist/mod.d.cts +7 -7
  73. package/dist/mod.d.ts +7 -7
  74. package/dist/mod.js +5 -5
  75. package/dist/nodeinfo/client.test.mjs +337 -19
  76. package/dist/nodeinfo/handler.test.mjs +1 -1
  77. package/dist/nodeinfo/mod.cjs +1 -1
  78. package/dist/nodeinfo/mod.d.cts +1 -1
  79. package/dist/nodeinfo/mod.d.ts +1 -1
  80. package/dist/nodeinfo/mod.js +1 -1
  81. package/dist/otel/exporter.test.mjs +257 -1
  82. package/dist/otel/mod.cjs +36 -30
  83. package/dist/otel/mod.d.cts +13 -0
  84. package/dist/otel/mod.d.ts +13 -0
  85. package/dist/otel/mod.js +36 -30
  86. package/dist/{owner-ccKkQOAf.mjs → owner-CwijV0c5.mjs} +3 -3
  87. package/dist/{portable-collection-DUlQ3s2M.js → portable-collection-2jn_4V7Y.js} +1 -1
  88. package/dist/{portable-collection-CndnpXfo.mjs → portable-collection-B023xqhZ.mjs} +2 -2
  89. package/dist/{portable-collection-TC-kvkVI.cjs → portable-collection-BeS88JJp.cjs} +1 -1
  90. package/dist/{portable-key-D5tFOoV-.mjs → portable-key-BpBucRfb.mjs} +1 -1
  91. package/dist/{proof-DPoF06nz.mjs → proof-DQh2z9fx.mjs} +3 -3
  92. package/dist/{send-CF44O1Df.mjs → send-DN5HNBv-.mjs} +174 -76
  93. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  94. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  95. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  96. package/dist/sig/compound-proof.test.mjs +2 -2
  97. package/dist/sig/http.test.mjs +553 -2
  98. package/dist/sig/key.test.mjs +1 -1
  99. package/dist/sig/ld.test.mjs +1 -1
  100. package/dist/sig/mitra-receive-interop.test.mjs +4 -4
  101. package/dist/sig/mitra-send-interop.test.mjs +3 -3
  102. package/dist/sig/mod.cjs +2 -2
  103. package/dist/sig/mod.d.cts +3 -3
  104. package/dist/sig/mod.d.ts +3 -3
  105. package/dist/sig/mod.js +2 -2
  106. package/dist/sig/owner.test.mjs +1 -1
  107. package/dist/sig/portable-collection.test.mjs +1 -1
  108. package/dist/sig/portable-interop.test.mjs +3 -3
  109. package/dist/sig/portable-key.test.mjs +5 -5
  110. package/dist/sig/proof.test.mjs +4 -4
  111. package/dist/sig/tootik-request-interop.test.mjs +4 -4
  112. package/dist/sig/tootik-send-interop.test.mjs +3 -3
  113. package/dist/sig/verification.test.mjs +3 -3
  114. package/dist/{temporal-XlZ4Wb94.mjs → temporal-yl7Z7A6y.mjs} +1 -1
  115. package/dist/testing/kv.test.mjs +1 -1
  116. package/dist/testing/mod.d.mts +26 -0
  117. package/dist/{types-_S2_MeLF.js → types-DUjZb4i6.js} +112 -63
  118. package/dist/{types-poIao40f.cjs → types-hKCCjii0.cjs} +111 -62
  119. package/dist/utils/docloader.test.mjs +344 -36
  120. package/dist/utils/kv-cache.test.mjs +1 -1
  121. package/dist/utils/mod.cjs +1 -1
  122. package/dist/utils/mod.d.cts +1 -1
  123. package/dist/utils/mod.d.ts +1 -1
  124. package/dist/utils/mod.js +1 -1
  125. package/package.json +8 -8
  126. package/dist/docloader-CpsDyqVN.mjs +0 -91
@@ -2,13 +2,14 @@ import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { d as isPortableUri } from "./portable-key-id-Ca30-hOa.mjs";
5
- import { n as version, t as name } from "./deno-Dpewt_jJ.mjs";
6
- import { n as getDurationMs, r as getFederationMetrics } from "./metrics-B-QxRSSR.mjs";
7
- import { n as doubleKnock } from "./http-BZkyQIkb.mjs";
5
+ import { n as version, t as name } from "./deno-D7blYLbb.mjs";
6
+ import { n as getDurationMs, r as getFederationMetrics } from "./metrics-B7TvoAP7.mjs";
7
+ import { r as doubleKnockScoped } from "./http-CxKd10Lq.mjs";
8
8
  import { getLogger, withContext } from "@logtape/logtape";
9
9
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
10
- import { FetchError, UrlError, canonicalizePortableUri, getFe34Origin, isGatewayUrl, toCompatibleEf61Id, validatePublicUrl } from "@fedify/vocab-runtime";
10
+ import { FetchError, UrlError, canonicalizePortableUri, getFe34Origin, isGatewayUrl, toCompatibleEf61Id } from "@fedify/vocab-runtime";
11
11
  import { getPortableGatewayCandidates } from "@fedify/vocab-runtime/internal/portable-dereference";
12
+ import { openDirectFetch, openPublicFetch } from "@fedify/vocab-runtime/internal";
12
13
  /**
13
14
  * Resolves a portable inbox, i.e., an `ap:` or `ap+ef61:` URI, to
14
15
  * the compatible identifiers of the inbox on the recipient's gateways.
@@ -188,9 +189,10 @@ function extractInboxes({ recipients, preferSharedInbox, excludeBaseUris }) {
188
189
  *
189
190
  * @param parameters The parameters for sending the activity.
190
191
  * See also {@link SendActivityParameters}.
192
+ * @param deps The dependencies, for tests.
191
193
  * @throws {Error} If the activity fails to send.
192
194
  */
193
- function sendActivity(options) {
195
+ function sendActivity(options, deps = {}) {
194
196
  const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
195
197
  return tracerProvider.getTracer(name, version).startActiveSpan("activitypub.send_activity", {
196
198
  kind: SpanKind.CLIENT,
@@ -206,7 +208,7 @@ function sendActivity(options) {
206
208
  }, () => sendActivityInternal({
207
209
  ...options,
208
210
  tracerProvider
209
- }, span));
211
+ }, span, deps));
210
212
  } catch (e) {
211
213
  span.setStatus({
212
214
  code: SpanStatusCode.ERROR,
@@ -291,13 +293,14 @@ async function readLimitedResponseBody(response, maxBytes) {
291
293
  totalBytes += value.length;
292
294
  }
293
295
  } finally {
296
+ if (truncated) reader.cancel().catch(() => {});
294
297
  reader.releaseLock();
295
298
  }
296
299
  let result = chunks.join("");
297
300
  if (truncated) result += "… (truncated)";
298
301
  return result;
299
302
  }
300
- async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, allowPrivateAddress, headers, specDeterminer, meterProvider, tracerProvider }, span) {
303
+ async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, allowPrivateAddress, headers, specDeterminer, meterProvider, tracerProvider }, span, deps) {
301
304
  const logger = getLogger([
302
305
  "fedify",
303
306
  "federation",
@@ -306,9 +309,11 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
306
309
  const federationMetrics = getFederationMetrics(meterProvider);
307
310
  const started = performance.now();
308
311
  let deliverySuccess = false;
309
- async function validateUrl(url) {
310
- if (!allowPrivateAddress) try {
311
- await validatePublicUrl(url);
312
+ const openUnvalidatedHop = deps.openHop ?? (allowPrivateAddress ? openDirectFetch : (url) => openPublicFetch(url));
313
+ async function openHop(url) {
314
+ let session;
315
+ try {
316
+ session = await openUnvalidatedHop(url);
312
317
  } catch (error) {
313
318
  if (error instanceof UrlError && error.reason === "dns") {
314
319
  logger.error("DNS resolution failed for URL: {url}", {
@@ -325,82 +330,90 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
325
330
  });
326
331
  throw error;
327
332
  }
333
+ return session;
328
334
  }
335
+ let initialSession;
329
336
  try {
330
- await validateUrl(inbox.href);
337
+ initialSession = await openHop(inbox.href);
331
338
  } catch (error) {
332
339
  if (error instanceof FetchError) federationMetrics.recordDelivery(inbox, getDurationMs(started), false, activityType);
333
340
  throw error;
334
341
  }
335
- headers = new Headers(headers);
336
- headers.set("Content-Type", "application/activity+json");
337
- const request = new Request(inbox, {
338
- method: "POST",
339
- headers,
340
- body: JSON.stringify(activity),
341
- redirect: "manual"
342
- });
343
- let rsaKey = null;
344
- for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
345
- rsaKey = key;
346
- break;
347
- }
348
- if (rsaKey == null && keys.length < 1) logger.debug("Sending the activity {activityId} to {inbox} without a signature.", {
349
- activityId,
350
- inbox: inbox.href
351
- });
352
- else if (rsaKey == null) logger.warn("No supported key found to sign the request to {inbox}. The request will be sent without a signature. In order to sign the request, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
353
- inbox: inbox.href,
354
- keys: keys.map((pair) => ({
355
- keyId: pair.keyId.href,
356
- privateKey: pair.privateKey
357
- }))
358
- });
359
- let response;
342
+ let initialPending = true;
343
+ const openDeliveryHop = (url) => {
344
+ if (initialPending) {
345
+ initialPending = false;
346
+ return Promise.resolve(initialSession);
347
+ }
348
+ return openHop(url);
349
+ };
360
350
  try {
361
- response = rsaKey == null ? await fetchWithValidatedRedirects(request, validateUrl) : await doubleKnock(request, rsaKey, {
362
- tracerProvider,
363
- specDeterminer,
364
- validateRedirect: validateUrl
351
+ headers = new Headers(headers);
352
+ headers.set("Content-Type", "application/activity+json");
353
+ const request = new Request(inbox, {
354
+ method: "POST",
355
+ headers,
356
+ body: JSON.stringify(activity),
357
+ redirect: "manual"
365
358
  });
366
- } catch (error) {
367
- const failure = error instanceof FetchError || error instanceof UrlError ? error : createFetchError(inbox.href, error);
368
- logger.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
359
+ let rsaKey = null;
360
+ for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
361
+ rsaKey = key;
362
+ break;
363
+ }
364
+ if (rsaKey == null && keys.length < 1) logger.debug("Sending the activity {activityId} to {inbox} without a signature.", {
369
365
  activityId,
366
+ inbox: inbox.href
367
+ });
368
+ else if (rsaKey == null) logger.warn("No supported key found to sign the request to {inbox}. The request will be sent without a signature. In order to sign the request, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
370
369
  inbox: inbox.href,
371
- error: failure
370
+ keys: keys.map((pair) => ({
371
+ keyId: pair.keyId.href,
372
+ privateKey: pair.privateKey
373
+ }))
372
374
  });
373
- federationMetrics.recordDelivery(inbox, getDurationMs(started), false, activityType);
374
- throw failure;
375
- }
376
- try {
377
- if (!response.ok) {
378
- let error;
379
- try {
380
- error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
381
- } catch (_) {
382
- error = "";
383
- }
384
- logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
375
+ let outcome;
376
+ try {
377
+ outcome = rsaKey == null ? await fetchWithValidatedRedirects(request, openDeliveryHop, (response) => consumeSendActivityResponse(response, inbox)) : await doubleKnockScoped(request, rsaKey, {
378
+ tracerProvider,
379
+ specDeterminer
380
+ }, openDeliveryHop, (response) => consumeSendActivityResponse(response, inbox));
381
+ } catch (error) {
382
+ const failure = error instanceof FetchError || error instanceof UrlError ? error : createFetchError(inbox.href, error);
383
+ logger.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
385
384
  activityId,
386
385
  inbox: inbox.href,
387
- status: response.status,
388
- statusText: response.statusText,
389
- error
386
+ error: failure
390
387
  });
391
- throw new SendActivityError(inbox, response.status, `Failed to send activity ${activityId} to ${inbox.href} (${response.status} ${response.statusText}):\n${error}`, error, response.headers);
388
+ federationMetrics.recordDelivery(inbox, getDurationMs(started), false, activityType);
389
+ throw failure;
390
+ }
391
+ try {
392
+ if (!outcome.ok) {
393
+ const { status, statusText, error, responseHeaders } = outcome;
394
+ logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
395
+ activityId,
396
+ inbox: inbox.href,
397
+ status,
398
+ statusText,
399
+ error
400
+ });
401
+ throw new SendActivityError(inbox, status, `Failed to send activity ${activityId} to ${inbox.href} (${status} ${statusText}):\n${error}`, error, responseHeaders);
402
+ }
403
+ deliverySuccess = true;
404
+ const eventAttributes = {
405
+ "activitypub.inbox.url": inbox.href,
406
+ "activitypub.activity.id": activityId ?? ""
407
+ };
408
+ if (activityType != null) eventAttributes["activitypub.activity.type"] = activityType;
409
+ const actorId = getActivityActorId(activity);
410
+ if (actorId != null) eventAttributes["activitypub.actor.id"] = actorId;
411
+ span.addEvent("activitypub.activity.sent", eventAttributes);
412
+ } finally {
413
+ federationMetrics.recordDelivery(inbox, getDurationMs(started), deliverySuccess, activityType);
392
414
  }
393
- deliverySuccess = true;
394
- const eventAttributes = {
395
- "activitypub.inbox.url": inbox.href,
396
- "activitypub.activity.id": activityId ?? ""
397
- };
398
- if (activityType != null) eventAttributes["activitypub.activity.type"] = activityType;
399
- const actorId = getActivityActorId(activity);
400
- if (actorId != null) eventAttributes["activitypub.actor.id"] = actorId;
401
- span.addEvent("activitypub.activity.sent", eventAttributes);
402
415
  } finally {
403
- federationMetrics.recordDelivery(inbox, getDurationMs(started), deliverySuccess, activityType);
416
+ if (initialPending) await initialSession.close();
404
417
  }
405
418
  }
406
419
  function createFetchError(url, cause) {
@@ -408,10 +421,75 @@ function createFetchError(url, cause) {
408
421
  error.cause = cause;
409
422
  return error;
410
423
  }
411
- async function fetchWithValidatedRedirects(request, validateUrl) {
424
+ /**
425
+ * Starts cancelling the body of a response without waiting for it to settle.
426
+ * The cancellation of a branch of a tee (e.g., a body cloned by a fetch mock)
427
+ * settles only after the other branch is cancelled too, per the Streams
428
+ * standard, so awaiting it could block forever. Failures are only logged.
429
+ */
430
+ function cancelResponseBody(response, url) {
431
+ response.body?.cancel().catch((error) => {
432
+ getLogger([
433
+ "fedify",
434
+ "federation",
435
+ "outbox"
436
+ ]).debug("Failed to cancel the response body from {url}: {error}", {
437
+ url,
438
+ error
439
+ });
440
+ });
441
+ }
442
+ /**
443
+ * Reads what is needed from the final response of a delivery while its
444
+ * session is open: the body of a successful response is cancelled, and at
445
+ * most 1 KiB of an error response body is read.
446
+ */
447
+ async function consumeSendActivityResponse(response, inbox) {
448
+ if (response.ok) {
449
+ cancelResponseBody(response, response.url || inbox.href);
450
+ return { ok: true };
451
+ }
452
+ let error;
453
+ try {
454
+ error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
455
+ } catch (_) {
456
+ error = "";
457
+ }
458
+ return {
459
+ ok: false,
460
+ status: response.status,
461
+ statusText: response.statusText,
462
+ error,
463
+ responseHeaders: new Headers(response.headers)
464
+ };
465
+ }
466
+ /**
467
+ * Sends a request without signing it, following redirects manually like the
468
+ * Fetch standard does. Every hop is opened with `openHop()`, which validates
469
+ * the destination, after the previous hop is closed. The final response is
470
+ * passed to `consume()`, and its session is closed after that.
471
+ * This is exported only for tests; it is not exported from any entry point.
472
+ * @param request The request to send.
473
+ * @param openHop The function to open a session for each hop.
474
+ * @param consume The function to consume the final response.
475
+ * @returns The value returned by `consume`.
476
+ */
477
+ async function fetchWithValidatedRedirects(request, openHop, consume) {
478
+ const logger = getLogger([
479
+ "fedify",
480
+ "federation",
481
+ "outbox"
482
+ ]);
412
483
  let body = await request.clone().text();
413
484
  for (let redirects = 0;; redirects++) {
414
- const response = await fetch(request, { redirect: "manual" });
485
+ const session = await openHop(request.url);
486
+ let response;
487
+ try {
488
+ response = await session.fetch(request, { redirect: "manual" });
489
+ } catch (error) {
490
+ await closeAfterError(session, logger);
491
+ throw error;
492
+ }
415
493
  const location = response.headers.get("Location");
416
494
  if (![
417
495
  301,
@@ -419,11 +497,21 @@ async function fetchWithValidatedRedirects(request, validateUrl) {
419
497
  303,
420
498
  307,
421
499
  308
422
- ].includes(response.status) || location == null) return response;
423
- await response.body?.cancel();
500
+ ].includes(response.status) || location == null) try {
501
+ return await consume(response);
502
+ } catch (error) {
503
+ logger.debug("Failed to consume the response from {url}; closing the session: {error}", {
504
+ url: request.url,
505
+ error
506
+ });
507
+ throw error;
508
+ } finally {
509
+ await session.close();
510
+ }
511
+ cancelResponseBody(response, request.url);
512
+ await session.close();
424
513
  if (redirects >= 20) throw new TypeError("Too many redirects");
425
514
  const url = new URL(location, request.url);
426
- await validateUrl(url.href);
427
515
  const headers = new Headers(request.headers);
428
516
  if (url.origin !== new URL(request.url).origin) {
429
517
  headers.delete("Authorization");
@@ -451,6 +539,16 @@ async function fetchWithValidatedRedirects(request, validateUrl) {
451
539
  });
452
540
  }
453
541
  }
542
+ async function closeAfterError(session, logger) {
543
+ try {
544
+ await session.close();
545
+ } catch (error) {
546
+ logger.debug("Failed to close the session for {url}: {error}", {
547
+ url: session.url.href,
548
+ error
549
+ });
550
+ }
551
+ }
454
552
  /**
455
553
  * An error that is thrown when an activity fails to send to a remote inbox.
456
554
  * It contains structured information about the failure, including the HTTP
@@ -496,4 +594,4 @@ var SendActivityError = class extends Error {
496
594
  }
497
595
  };
498
596
  //#endregion
499
- export { getOrderingDestination as a, sendActivityThroughGateways as i, extractInboxes as n, sendActivity as r, SendActivityError as t };
597
+ export { sendActivityThroughGateways as a, sendActivity as i, extractInboxes as n, getOrderingDestination as o, fetchWithValidatedRedirects as r, SendActivityError as t };
@@ -3,7 +3,7 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
5
5
  import "../std__assert-DvMDRPes.mjs";
6
- import { a as inspectCompoundPortableObjectApplicability, n as discoverCompoundProofDocuments } from "../compound-proof-heeXW2_A.mjs";
6
+ import { a as inspectCompoundPortableObjectApplicability, n as discoverCompoundProofDocuments } from "../compound-proof-BM7AINht.mjs";
7
7
  import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  //#region src/sig/compound-proof-discovery.test.ts
@@ -3,7 +3,7 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
5
5
  import "../std__assert-DvMDRPes.mjs";
6
- import { a as inspectCompoundPortableObjectApplicability, i as findUnsupportedCompoundProofShape, t as containsCompoundPortableObject } from "../compound-proof-heeXW2_A.mjs";
6
+ import { a as inspectCompoundPortableObjectApplicability, i as findUnsupportedCompoundProofShape, t as containsCompoundPortableObject } from "../compound-proof-BM7AINht.mjs";
7
7
  import { test } from "@fedify/fixture";
8
8
  //#region src/sig/compound-proof-producer.test.ts
9
9
  const portableId = "ap+ef61://did:key:z6MkAlice/objects/1";
@@ -4,9 +4,9 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
5
5
  import "../std__assert-DvMDRPes.mjs";
6
6
  import { t as assert } from "../assert-D1Eafmd_.mjs";
7
- import { c as verifyMapLocalProof, f as verifyPortableObjectProofPolicy } from "../proof-DPoF06nz.mjs";
7
+ import { c as verifyMapLocalProof, f as verifyPortableObjectProofPolicy } from "../proof-DQh2z9fx.mjs";
8
8
  import { n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
9
- import { a as inspectCompoundPortableObjectApplicability, o as verifyCompoundPortableObjectProofs, s as verifyCompoundProofDocuments, t as containsCompoundPortableObject } from "../compound-proof-heeXW2_A.mjs";
9
+ import { a as inspectCompoundPortableObjectApplicability, o as verifyCompoundPortableObjectProofs, s as verifyCompoundProofDocuments, t as containsCompoundPortableObject } from "../compound-proof-BM7AINht.mjs";
10
10
  import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
11
11
  import { t as map_local_context_conflict_default } from "../map-local-context-conflict-DOYEEJ3u.mjs";
12
12
  import { CryptographicKey, Multikey, Person } from "@fedify/vocab";
@@ -6,9 +6,9 @@ import "../std__assert-DvMDRPes.mjs";
6
6
  import { t as assert } from "../assert-D1Eafmd_.mjs";
7
7
  import { n as preloadedOnlyDocumentLoader } from "../public-audience--klBIOk1.mjs";
8
8
  import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-4IJqPYos.mjs";
9
- import { h as verifyProof, s as signObject } from "../proof-DPoF06nz.mjs";
9
+ import { h as verifyProof, s as signObject } from "../proof-DQh2z9fx.mjs";
10
10
  import { n as ed25519PrivateKey, r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
11
- import { s as verifyCompoundProofDocuments } from "../compound-proof-heeXW2_A.mjs";
11
+ import { s as verifyCompoundProofDocuments } from "../compound-proof-BM7AINht.mjs";
12
12
  import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
13
13
  import { t as map_local_context_conflict_default } from "../map-local-context-conflict-DOYEEJ3u.mjs";
14
14
  import { Create, DataIntegrityProof, Note, Question } from "@fedify/vocab";