@fedify/fedify 2.4.5-dev.2462 → 2.4.5

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 (119) hide show
  1. package/README.md +3 -0
  2. package/dist/{builder-BBUGd9wr.mjs → builder-CUAVOHg6.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-CariICma.mjs → compound-proof-BNjXclzO.mjs} +2 -2
  10. package/dist/{context-7NyiqIcg.d.ts → context-CVOOd9pD.d.ts} +28 -2
  11. package/dist/{context-tsYPJ2Ew.d.cts → context-D0gFIgev.d.cts} +28 -2
  12. package/dist/{deno-BO7-a3hz.mjs → deno-DcJISdwu.mjs} +1 -1
  13. package/dist/docloader-CP1UxMLL.mjs +136 -0
  14. package/dist/federation/builder.test.mjs +1 -1
  15. package/dist/federation/compound-fanout.test.mjs +6 -3
  16. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  17. package/dist/federation/handler.test.mjs +4 -4
  18. package/dist/federation/hashlink-media.test.mjs +2 -2
  19. package/dist/federation/idempotency.test.mjs +2 -2
  20. package/dist/federation/inbox-report.test.mjs +5 -5
  21. package/dist/federation/inbox.test.mjs +1 -1
  22. package/dist/federation/metrics.test.mjs +1 -1
  23. package/dist/federation/middleware.test.mjs +465 -32
  24. package/dist/federation/mod.cjs +1 -1
  25. package/dist/federation/mod.d.cts +3 -3
  26. package/dist/federation/mod.d.ts +3 -3
  27. package/dist/federation/mod.js +1 -1
  28. package/dist/federation/outgoing-proof.test.mjs +9 -6
  29. package/dist/federation/portable-actor.test.mjs +3 -3
  30. package/dist/federation/portable-chain.test.mjs +3 -3
  31. package/dist/federation/portable-collection.test.mjs +4 -4
  32. package/dist/federation/portable-delivery.test.mjs +2 -2
  33. package/dist/federation/portable-gateway-keys.test.mjs +11 -5
  34. package/dist/federation/portable-inbox.test.mjs +4 -4
  35. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  36. package/dist/federation/portable-route-activity.test.mjs +2 -2
  37. package/dist/federation/portable.test.mjs +3 -3
  38. package/dist/federation/send.test.mjs +498 -88
  39. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  40. package/dist/federation/tasks/tasks.test.mjs +2 -2
  41. package/dist/federation/temporal.test.mjs +1 -1
  42. package/dist/federation/webfinger.test.mjs +1 -1
  43. package/dist/{federation-Dd9IwOEi.cjs → federation-Bkfq2YpS.cjs} +240 -102
  44. package/dist/{http-DLf8QYD6.cjs → http-DHVH0DqC.cjs} +179 -110
  45. package/dist/{http-D7L2pqLT.mjs → http-DuGmr8J8.mjs} +204 -106
  46. package/dist/{http-BVrOR6MG.js → http-R0ty0Udi.js} +178 -109
  47. package/dist/{inbox-DtEKOr3W.mjs → inbox-Bl1Y0s_A.mjs} +2 -2
  48. package/dist/{key-DYxuyd-s.d.cts → key-CyCcut3h.d.ts} +1 -2
  49. package/dist/{key-C1nv7_jD.d.ts → key-VEL1QqHF.d.cts} +1 -2
  50. package/dist/{kv-cache-Dkzuqs8B.cjs → kv-cache-BtBSBZi9.cjs} +72 -27
  51. package/dist/{kv-cache-m3RIr811.mjs → kv-cache-DAV2_U44.mjs} +1 -1
  52. package/dist/{kv-cache-DoZoRS9p.js → kv-cache-FnLtmATH.js} +73 -28
  53. package/dist/{ld-PJLGA2MJ.mjs → ld-Cy5GqbvS.mjs} +2 -2
  54. package/dist/{metrics-CGR9cI-Y.mjs → metrics-CW8LoN7n.mjs} +1 -1
  55. package/dist/{middleware-DPI-Jp9w.mjs → middleware-CdP-lz8Q.mjs} +1 -1
  56. package/dist/{middleware-ByanhTvb.mjs → middleware-D2fk45uN.mjs} +82 -42
  57. package/dist/{middleware-BxhBd7pw.js → middleware-D4rQARvq.js} +241 -103
  58. package/dist/{mod-DEZXwV6J.d.ts → mod-B1Qt5cQQ.d.ts} +2 -9
  59. package/dist/{mod-Xj5P4s4d.d.ts → mod-B5COx5Wd.d.ts} +1 -1
  60. package/dist/{mod-CavhAgLw.d.cts → mod-BTxJG7ZH.d.cts} +1 -1
  61. package/dist/{mod-CtpF2BU5.d.ts → mod-CpyjFA9s.d.ts} +1 -1
  62. package/dist/{mod-BojP2Bw3.d.cts → mod-makPMVMg.d.cts} +1 -1
  63. package/dist/{mod-Dw-nPxQh.d.cts → mod-ss0wBUH1.d.cts} +2 -9
  64. package/dist/mod.cjs +5 -5
  65. package/dist/mod.d.cts +6 -6
  66. package/dist/mod.d.ts +6 -6
  67. package/dist/mod.js +5 -5
  68. package/dist/nodeinfo/client.test.mjs +337 -19
  69. package/dist/nodeinfo/handler.test.mjs +1 -1
  70. package/dist/nodeinfo/mod.cjs +1 -1
  71. package/dist/nodeinfo/mod.d.cts +1 -1
  72. package/dist/nodeinfo/mod.d.ts +1 -1
  73. package/dist/nodeinfo/mod.js +1 -1
  74. package/dist/otel/exporter.test.mjs +292 -30
  75. package/dist/otel/mod.cjs +36 -30
  76. package/dist/otel/mod.d.cts +13 -0
  77. package/dist/otel/mod.d.ts +13 -0
  78. package/dist/otel/mod.js +36 -30
  79. package/dist/{owner-B5c276hH.mjs → owner-CbQZqFim.mjs} +3 -3
  80. package/dist/{portable-collection-BMZY9qT-.js → portable-collection-B5Aryi8I.js} +1 -1
  81. package/dist/{portable-collection-CR5uwjws.cjs → portable-collection-C_Kmvl3q.cjs} +1 -1
  82. package/dist/{portable-collection-C88tBiy0.mjs → portable-collection-D2JYDadi.mjs} +2 -2
  83. package/dist/{portable-key-DxTXUpCK.mjs → portable-key-Cc-ctNU2.mjs} +1 -1
  84. package/dist/{proof-DNu-qP0i.mjs → proof-UN4CW55z.mjs} +3 -3
  85. package/dist/{send-CdCifQs4.mjs → send-BzbX2e6v.mjs} +174 -76
  86. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  87. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  88. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  89. package/dist/sig/compound-proof.test.mjs +2 -2
  90. package/dist/sig/http.test.mjs +553 -2
  91. package/dist/sig/key.test.mjs +1 -1
  92. package/dist/sig/ld.test.mjs +1 -1
  93. package/dist/sig/mitra-receive-interop.test.mjs +4 -4
  94. package/dist/sig/mitra-send-interop.test.mjs +3 -3
  95. package/dist/sig/mod.cjs +2 -2
  96. package/dist/sig/mod.d.cts +2 -2
  97. package/dist/sig/mod.d.ts +2 -2
  98. package/dist/sig/mod.js +2 -2
  99. package/dist/sig/owner.test.mjs +1 -1
  100. package/dist/sig/portable-collection.test.mjs +1 -1
  101. package/dist/sig/portable-interop.test.mjs +3 -3
  102. package/dist/sig/portable-key.test.mjs +5 -5
  103. package/dist/sig/proof.test.mjs +4 -4
  104. package/dist/sig/tootik-request-interop.test.mjs +4 -4
  105. package/dist/sig/tootik-send-interop.test.mjs +3 -3
  106. package/dist/sig/verification.test.mjs +3 -3
  107. package/dist/{temporal-BuMu2bgP.mjs → temporal-B7Wvxrjs.mjs} +1 -1
  108. package/dist/testing/kv.test.mjs +1 -1
  109. package/dist/testing/mod.d.mts +26 -0
  110. package/dist/{types-_S2_MeLF.js → types-DUjZb4i6.js} +112 -63
  111. package/dist/{types-poIao40f.cjs → types-hKCCjii0.cjs} +111 -62
  112. package/dist/utils/docloader.test.mjs +344 -36
  113. package/dist/utils/kv-cache.test.mjs +1 -1
  114. package/dist/utils/mod.cjs +1 -1
  115. package/dist/utils/mod.d.cts +1 -1
  116. package/dist/utils/mod.d.ts +1 -1
  117. package/dist/utils/mod.js +1 -1
  118. package/package.json +7 -7
  119. package/dist/docloader-DQNRDYDb.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-BO7-a3hz.mjs";
6
- import { n as getDurationMs, r as getFederationMetrics } from "./metrics-CGR9cI-Y.mjs";
7
- import { n as doubleKnock } from "./http-D7L2pqLT.mjs";
5
+ import { n as version, t as name } from "./deno-DcJISdwu.mjs";
6
+ import { n as getDurationMs, r as getFederationMetrics } from "./metrics-CW8LoN7n.mjs";
7
+ import { r as doubleKnockScoped } from "./http-DuGmr8J8.mjs";
8
8
  import { getLogger } 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,
@@ -202,7 +204,7 @@ function sendActivity(options) {
202
204
  await sendActivityInternal({
203
205
  ...options,
204
206
  tracerProvider
205
- }, span);
207
+ }, span, deps);
206
208
  } catch (e) {
207
209
  span.setStatus({
208
210
  code: SpanStatusCode.ERROR,
@@ -287,13 +289,14 @@ async function readLimitedResponseBody(response, maxBytes) {
287
289
  totalBytes += value.length;
288
290
  }
289
291
  } finally {
292
+ if (truncated) reader.cancel().catch(() => {});
290
293
  reader.releaseLock();
291
294
  }
292
295
  let result = chunks.join("");
293
296
  if (truncated) result += "… (truncated)";
294
297
  return result;
295
298
  }
296
- async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, allowPrivateAddress, headers, specDeterminer, meterProvider, tracerProvider }, span) {
299
+ async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, allowPrivateAddress, headers, specDeterminer, meterProvider, tracerProvider }, span, deps) {
297
300
  const logger = getLogger([
298
301
  "fedify",
299
302
  "federation",
@@ -302,9 +305,11 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
302
305
  const federationMetrics = getFederationMetrics(meterProvider);
303
306
  const started = performance.now();
304
307
  let deliverySuccess = false;
305
- async function validateUrl(url) {
306
- if (!allowPrivateAddress) try {
307
- await validatePublicUrl(url);
308
+ const openUnvalidatedHop = deps.openHop ?? (allowPrivateAddress ? openDirectFetch : (url) => openPublicFetch(url));
309
+ async function openHop(url) {
310
+ let session;
311
+ try {
312
+ session = await openUnvalidatedHop(url);
308
313
  } catch (error) {
309
314
  if (error instanceof UrlError && error.reason === "dns") {
310
315
  logger.error("DNS resolution failed for URL: {url}", {
@@ -321,82 +326,90 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
321
326
  });
322
327
  throw error;
323
328
  }
329
+ return session;
324
330
  }
331
+ let initialSession;
325
332
  try {
326
- await validateUrl(inbox.href);
333
+ initialSession = await openHop(inbox.href);
327
334
  } catch (error) {
328
335
  if (error instanceof FetchError) federationMetrics.recordDelivery(inbox, getDurationMs(started), false, activityType);
329
336
  throw error;
330
337
  }
331
- headers = new Headers(headers);
332
- headers.set("Content-Type", "application/activity+json");
333
- const request = new Request(inbox, {
334
- method: "POST",
335
- headers,
336
- body: JSON.stringify(activity),
337
- redirect: "manual"
338
- });
339
- let rsaKey = null;
340
- for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
341
- rsaKey = key;
342
- break;
343
- }
344
- if (rsaKey == null && keys.length < 1) logger.debug("Sending the activity {activityId} to {inbox} without a signature.", {
345
- activityId,
346
- inbox: inbox.href
347
- });
348
- else if (rsaKey == null) logger.warn("No supported key found to sign the request to {inbox}. The request will be sent without a signature. In order to sign the request, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
349
- inbox: inbox.href,
350
- keys: keys.map((pair) => ({
351
- keyId: pair.keyId.href,
352
- privateKey: pair.privateKey
353
- }))
354
- });
355
- let response;
338
+ let initialPending = true;
339
+ const openDeliveryHop = (url) => {
340
+ if (initialPending) {
341
+ initialPending = false;
342
+ return Promise.resolve(initialSession);
343
+ }
344
+ return openHop(url);
345
+ };
356
346
  try {
357
- response = rsaKey == null ? await fetchWithValidatedRedirects(request, validateUrl) : await doubleKnock(request, rsaKey, {
358
- tracerProvider,
359
- specDeterminer,
360
- validateRedirect: validateUrl
347
+ headers = new Headers(headers);
348
+ headers.set("Content-Type", "application/activity+json");
349
+ const request = new Request(inbox, {
350
+ method: "POST",
351
+ headers,
352
+ body: JSON.stringify(activity),
353
+ redirect: "manual"
361
354
  });
362
- } catch (error) {
363
- const failure = error instanceof FetchError || error instanceof UrlError ? error : createFetchError(inbox.href, error);
364
- logger.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
355
+ let rsaKey = null;
356
+ for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
357
+ rsaKey = key;
358
+ break;
359
+ }
360
+ if (rsaKey == null && keys.length < 1) logger.debug("Sending the activity {activityId} to {inbox} without a signature.", {
365
361
  activityId,
362
+ inbox: inbox.href
363
+ });
364
+ 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.", {
366
365
  inbox: inbox.href,
367
- error: failure
366
+ keys: keys.map((pair) => ({
367
+ keyId: pair.keyId.href,
368
+ privateKey: pair.privateKey
369
+ }))
368
370
  });
369
- federationMetrics.recordDelivery(inbox, getDurationMs(started), false, activityType);
370
- throw failure;
371
- }
372
- try {
373
- if (!response.ok) {
374
- let error;
375
- try {
376
- error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
377
- } catch (_) {
378
- error = "";
379
- }
380
- logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
371
+ let outcome;
372
+ try {
373
+ outcome = rsaKey == null ? await fetchWithValidatedRedirects(request, openDeliveryHop, (response) => consumeSendActivityResponse(response, inbox)) : await doubleKnockScoped(request, rsaKey, {
374
+ tracerProvider,
375
+ specDeterminer
376
+ }, openDeliveryHop, (response) => consumeSendActivityResponse(response, inbox));
377
+ } catch (error) {
378
+ const failure = error instanceof FetchError || error instanceof UrlError ? error : createFetchError(inbox.href, error);
379
+ logger.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
381
380
  activityId,
382
381
  inbox: inbox.href,
383
- status: response.status,
384
- statusText: response.statusText,
385
- error
382
+ error: failure
386
383
  });
387
- throw new SendActivityError(inbox, response.status, `Failed to send activity ${activityId} to ${inbox.href} (${response.status} ${response.statusText}):\n${error}`, error, response.headers);
384
+ federationMetrics.recordDelivery(inbox, getDurationMs(started), false, activityType);
385
+ throw failure;
386
+ }
387
+ try {
388
+ if (!outcome.ok) {
389
+ const { status, statusText, error, responseHeaders } = outcome;
390
+ logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
391
+ activityId,
392
+ inbox: inbox.href,
393
+ status,
394
+ statusText,
395
+ error
396
+ });
397
+ throw new SendActivityError(inbox, status, `Failed to send activity ${activityId} to ${inbox.href} (${status} ${statusText}):\n${error}`, error, responseHeaders);
398
+ }
399
+ deliverySuccess = true;
400
+ const eventAttributes = {
401
+ "activitypub.inbox.url": inbox.href,
402
+ "activitypub.activity.id": activityId ?? ""
403
+ };
404
+ if (activityType != null) eventAttributes["activitypub.activity.type"] = activityType;
405
+ const actorId = getActivityActorId(activity);
406
+ if (actorId != null) eventAttributes["activitypub.actor.id"] = actorId;
407
+ span.addEvent("activitypub.activity.sent", eventAttributes);
408
+ } finally {
409
+ federationMetrics.recordDelivery(inbox, getDurationMs(started), deliverySuccess, activityType);
388
410
  }
389
- deliverySuccess = true;
390
- const eventAttributes = {
391
- "activitypub.inbox.url": inbox.href,
392
- "activitypub.activity.id": activityId ?? ""
393
- };
394
- if (activityType != null) eventAttributes["activitypub.activity.type"] = activityType;
395
- const actorId = getActivityActorId(activity);
396
- if (actorId != null) eventAttributes["activitypub.actor.id"] = actorId;
397
- span.addEvent("activitypub.activity.sent", eventAttributes);
398
411
  } finally {
399
- federationMetrics.recordDelivery(inbox, getDurationMs(started), deliverySuccess, activityType);
412
+ if (initialPending) await initialSession.close();
400
413
  }
401
414
  }
402
415
  function createFetchError(url, cause) {
@@ -404,10 +417,75 @@ function createFetchError(url, cause) {
404
417
  error.cause = cause;
405
418
  return error;
406
419
  }
407
- async function fetchWithValidatedRedirects(request, validateUrl) {
420
+ /**
421
+ * Starts cancelling the body of a response without waiting for it to settle.
422
+ * The cancellation of a branch of a tee (e.g., a body cloned by a fetch mock)
423
+ * settles only after the other branch is cancelled too, per the Streams
424
+ * standard, so awaiting it could block forever. Failures are only logged.
425
+ */
426
+ function cancelResponseBody(response, url) {
427
+ response.body?.cancel().catch((error) => {
428
+ getLogger([
429
+ "fedify",
430
+ "federation",
431
+ "outbox"
432
+ ]).debug("Failed to cancel the response body from {url}: {error}", {
433
+ url,
434
+ error
435
+ });
436
+ });
437
+ }
438
+ /**
439
+ * Reads what is needed from the final response of a delivery while its
440
+ * session is open: the body of a successful response is cancelled, and at
441
+ * most 1 KiB of an error response body is read.
442
+ */
443
+ async function consumeSendActivityResponse(response, inbox) {
444
+ if (response.ok) {
445
+ cancelResponseBody(response, response.url || inbox.href);
446
+ return { ok: true };
447
+ }
448
+ let error;
449
+ try {
450
+ error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
451
+ } catch (_) {
452
+ error = "";
453
+ }
454
+ return {
455
+ ok: false,
456
+ status: response.status,
457
+ statusText: response.statusText,
458
+ error,
459
+ responseHeaders: new Headers(response.headers)
460
+ };
461
+ }
462
+ /**
463
+ * Sends a request without signing it, following redirects manually like the
464
+ * Fetch standard does. Every hop is opened with `openHop()`, which validates
465
+ * the destination, after the previous hop is closed. The final response is
466
+ * passed to `consume()`, and its session is closed after that.
467
+ * This is exported only for tests; it is not exported from any entry point.
468
+ * @param request The request to send.
469
+ * @param openHop The function to open a session for each hop.
470
+ * @param consume The function to consume the final response.
471
+ * @returns The value returned by `consume`.
472
+ */
473
+ async function fetchWithValidatedRedirects(request, openHop, consume) {
474
+ const logger = getLogger([
475
+ "fedify",
476
+ "federation",
477
+ "outbox"
478
+ ]);
408
479
  let body = await request.clone().text();
409
480
  for (let redirects = 0;; redirects++) {
410
- const response = await fetch(request, { redirect: "manual" });
481
+ const session = await openHop(request.url);
482
+ let response;
483
+ try {
484
+ response = await session.fetch(request, { redirect: "manual" });
485
+ } catch (error) {
486
+ await closeAfterError(session, logger);
487
+ throw error;
488
+ }
411
489
  const location = response.headers.get("Location");
412
490
  if (![
413
491
  301,
@@ -415,11 +493,21 @@ async function fetchWithValidatedRedirects(request, validateUrl) {
415
493
  303,
416
494
  307,
417
495
  308
418
- ].includes(response.status) || location == null) return response;
419
- await response.body?.cancel();
496
+ ].includes(response.status) || location == null) try {
497
+ return await consume(response);
498
+ } catch (error) {
499
+ logger.debug("Failed to consume the response from {url}; closing the session: {error}", {
500
+ url: request.url,
501
+ error
502
+ });
503
+ throw error;
504
+ } finally {
505
+ await session.close();
506
+ }
507
+ cancelResponseBody(response, request.url);
508
+ await session.close();
420
509
  if (redirects >= 20) throw new TypeError("Too many redirects");
421
510
  const url = new URL(location, request.url);
422
- await validateUrl(url.href);
423
511
  const headers = new Headers(request.headers);
424
512
  if (url.origin !== new URL(request.url).origin) {
425
513
  headers.delete("Authorization");
@@ -447,6 +535,16 @@ async function fetchWithValidatedRedirects(request, validateUrl) {
447
535
  });
448
536
  }
449
537
  }
538
+ async function closeAfterError(session, logger) {
539
+ try {
540
+ await session.close();
541
+ } catch (error) {
542
+ logger.debug("Failed to close the session for {url}: {error}", {
543
+ url: session.url.href,
544
+ error
545
+ });
546
+ }
547
+ }
450
548
  /**
451
549
  * An error that is thrown when an activity fails to send to a remote inbox.
452
550
  * It contains structured information about the failure, including the HTTP
@@ -492,4 +590,4 @@ var SendActivityError = class extends Error {
492
590
  }
493
591
  };
494
592
  //#endregion
495
- export { getOrderingDestination as a, sendActivityThroughGateways as i, extractInboxes as n, sendActivity as r, SendActivityError as t };
593
+ 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-CariICma.mjs";
6
+ import { a as inspectCompoundPortableObjectApplicability, n as discoverCompoundProofDocuments } from "../compound-proof-BNjXclzO.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-CariICma.mjs";
6
+ import { a as inspectCompoundPortableObjectApplicability, i as findUnsupportedCompoundProofShape, t as containsCompoundPortableObject } from "../compound-proof-BNjXclzO.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 { l as verifyPortableObjectProofPolicy, o as verifyMapLocalProof } from "../proof-DNu-qP0i.mjs";
7
+ import { l as verifyPortableObjectProofPolicy, o as verifyMapLocalProof } from "../proof-UN4CW55z.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-CariICma.mjs";
9
+ import { a as inspectCompoundPortableObjectApplicability, o as verifyCompoundPortableObjectProofs, s as verifyCompoundProofDocuments, t as containsCompoundPortableObject } from "../compound-proof-BNjXclzO.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 { a as signObject, d as verifyProof } from "../proof-DNu-qP0i.mjs";
9
+ import { a as signObject, d as verifyProof } from "../proof-UN4CW55z.mjs";
10
10
  import { n as ed25519PrivateKey, r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
11
- import { s as verifyCompoundProofDocuments } from "../compound-proof-CariICma.mjs";
11
+ import { s as verifyCompoundProofDocuments } from "../compound-proof-BNjXclzO.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";