@fedify/fedify 2.3.0-dev.1013 → 2.3.0-dev.1034

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 (95) hide show
  1. package/dist/{builder-CROLcFVM.mjs → builder-y06Dq5bp.mjs} +14 -4
  2. package/dist/chunk-QSgtlS85.mjs +29 -0
  3. package/dist/compat/mod.d.cts +1 -1
  4. package/dist/compat/mod.d.ts +1 -1
  5. package/dist/compat/outgoing-jsonld.test.mjs +1 -1
  6. package/dist/compat/public-audience.test.mjs +1 -1
  7. package/dist/compat/transformers.test.mjs +2 -2
  8. package/dist/{context-Dk_tacqz.mjs → context-7Azky82W.mjs} +3 -2
  9. package/dist/{context-BDl7Y6f-.d.cts → context-BKLGj9QO.d.cts} +24 -1
  10. package/dist/{context-zTZAI3KP.d.ts → context-DrNqYkPw.d.ts} +24 -1
  11. package/dist/{deno-Ctd-K-t6.mjs → deno-DB1H1VHx.mjs} +1 -1
  12. package/dist/{docloader-q9QT51g3.mjs → docloader-3HwiWeYL.mjs} +2 -2
  13. package/dist/{esm-DVILvP5e.mjs → esm-DhnRLoG9.mjs} +1 -24
  14. package/dist/execAsync-eck5rbtb.mjs +13 -0
  15. package/dist/federation/builder.test.mjs +8 -3
  16. package/dist/federation/collection.test.mjs +1 -1
  17. package/dist/federation/handler.test.mjs +48 -11
  18. package/dist/federation/idempotency.test.mjs +4 -4
  19. package/dist/federation/inbox.test.mjs +1 -1
  20. package/dist/federation/keycache.test.mjs +2 -2
  21. package/dist/federation/kv.test.mjs +1 -1
  22. package/dist/federation/middleware.test.mjs +138 -15
  23. package/dist/federation/mod.cjs +1 -1
  24. package/dist/federation/mod.d.cts +2 -2
  25. package/dist/federation/mod.d.ts +2 -2
  26. package/dist/federation/mod.js +1 -1
  27. package/dist/federation/negotiation.test.mjs +1 -1
  28. package/dist/federation/retry.test.mjs +1 -1
  29. package/dist/federation/send.test.mjs +4540 -11
  30. package/dist/federation/webfinger.test.mjs +3 -3
  31. package/dist/getMachineId-bsd-DqZ4QRFp.mjs +29 -0
  32. package/dist/getMachineId-darwin-DMbbW3m7.mjs +26 -0
  33. package/dist/getMachineId-linux-lyeD2ug3.mjs +22 -0
  34. package/dist/getMachineId-unsupported-JuKr57jY.mjs +17 -0
  35. package/dist/getMachineId-win-Dxyf5pJq.mjs +28 -0
  36. package/dist/{http-1NL30qCe.js → http-BUr93aO6.js} +1 -1
  37. package/dist/{http-CX_zHeOD.mjs → http-D9zG-L9N.mjs} +3 -3
  38. package/dist/{http-Du1Jgf2P.cjs → http-FnUTcdMf.cjs} +1 -1
  39. package/dist/{key-C1Oto4it.mjs → key-CV57mOYH.mjs} +1 -1
  40. package/dist/{kv-cache-aGOwL6Vj.cjs → kv-cache-BG9O8wVV.cjs} +1 -1
  41. package/dist/{kv-cache-CsC3P4uu.js → kv-cache-C3esyJFP.js} +1 -1
  42. package/dist/{ld-Dl1HIB1a.mjs → ld-sUf94RJ8.mjs} +2 -2
  43. package/dist/{middleware-BiFLcrEX.cjs → middleware-CKkBrsOD.cjs} +203 -32
  44. package/dist/{middleware-C1cf3_6V.mjs → middleware-cMxbPxDe.mjs} +1 -1
  45. package/dist/{middleware-CKJC8DRf.js → middleware-fAuUxD9-.js} +203 -32
  46. package/dist/{middleware-BwC5U8zJ.cjs → middleware-ohzkLsW4.cjs} +1 -1
  47. package/dist/{middleware-CEWDB8EB.mjs → middleware-pb2EqN_r.mjs} +75 -27
  48. package/dist/{mod-ckCOmoCz.d.ts → mod-B8Z8mBLk.d.ts} +1 -1
  49. package/dist/{mod-BghZgD_U.d.cts → mod-DClCOv0M.d.cts} +1 -1
  50. package/dist/mod.cjs +4 -4
  51. package/dist/mod.d.cts +2 -2
  52. package/dist/mod.d.ts +2 -2
  53. package/dist/mod.js +4 -4
  54. package/dist/nodeinfo/client.test.mjs +2 -2
  55. package/dist/nodeinfo/handler.test.mjs +3 -3
  56. package/dist/nodeinfo/types.test.mjs +1 -1
  57. package/dist/otel/exporter.test.mjs +25 -22
  58. package/dist/otel/mod.cjs +6 -5
  59. package/dist/otel/mod.d.cts +3 -2
  60. package/dist/otel/mod.d.ts +3 -2
  61. package/dist/otel/mod.js +6 -5
  62. package/dist/{outgoing-jsonld-CNmZLixq.mjs → outgoing-jsonld-Bi7n-dEy.mjs} +1 -1
  63. package/dist/{owner-CEWFJlqo.mjs → owner-DsPgl527.mjs} +2 -2
  64. package/dist/{proof-CDA3f-i5.cjs → proof-BhJpq_J9.cjs} +1 -1
  65. package/dist/{proof-BFyPVl1r.mjs → proof-iVfYyJpY.mjs} +4 -4
  66. package/dist/{proof-Dedf8md5.js → proof-k4mEvvdS.js} +1 -1
  67. package/dist/send-D-vYdfC6.mjs +306 -0
  68. package/dist/sig/accept.test.mjs +1 -1
  69. package/dist/sig/http.test.mjs +4 -4
  70. package/dist/sig/key.test.mjs +2 -2
  71. package/dist/sig/ld.test.mjs +3 -3
  72. package/dist/sig/mod.cjs +2 -2
  73. package/dist/sig/mod.js +2 -2
  74. package/dist/sig/owner.test.mjs +2 -2
  75. package/dist/sig/proof.test.mjs +3 -3
  76. package/dist/testing/mod.d.mts +18 -1
  77. package/dist/testing/mod.mjs +1 -1
  78. package/dist/utils/docloader.test.mjs +4 -4
  79. package/dist/utils/kv-cache.test.mjs +1 -1
  80. package/dist/utils/mod.cjs +1 -1
  81. package/dist/utils/mod.js +1 -1
  82. package/package.json +7 -6
  83. package/dist/send-BJickEP4.mjs +0 -193
  84. /package/dist/{accept-CPkZzmGN.mjs → accept-CceiKpCy.mjs} +0 -0
  85. /package/dist/{activity-listener-ell7W1s9.mjs → activity-listener-tztVvlNb.mjs} +0 -0
  86. /package/dist/{client-D_1QpnWt.mjs → client-CIiz1WX7.mjs} +0 -0
  87. /package/dist/{collection-D-HqUuA2.mjs → collection-CA3V5zyK.mjs} +0 -0
  88. /package/dist/{keycache-EGATflN-.mjs → keycache-BeU0LCII.mjs} +0 -0
  89. /package/dist/{keys-DGu1NFwu.mjs → keys-C3kae-6B.mjs} +0 -0
  90. /package/dist/{kv-cache-U__xU4qR.mjs → kv-cache-Bmv7tUzz.mjs} +0 -0
  91. /package/dist/{kv-rV3vodCc.mjs → kv-x2IvBUyq.mjs} +0 -0
  92. /package/dist/{negotiation-SQvQgUqe.mjs → negotiation-VnHNB0Q5.mjs} +0 -0
  93. /package/dist/{public-audience-DYFHzm_c.mjs → public-audience-PVTwU_Ex.mjs} +0 -0
  94. /package/dist/{retry-bMXBL97A.mjs → retry-_VvV0h9f.mjs} +0 -0
  95. /package/dist/{types-J53Kw7so.mjs → types-BFowWFTT.mjs} +0 -0
@@ -2,13 +2,13 @@ import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  import { t as __exportAll } from "./chunk-nlSIicah.js";
4
4
  import { r as getDefaultActivityTransformers } from "./transformers-ve6e2xcg.js";
5
- import { _ as version, a as verifyRequestDetailed, d as validateCryptoKey, f as formatAcceptSignature, g as name, i as verifyRequest, n as parseRfc9421SignatureInput, o as exportJwk, t as doubleKnock, u as importJwk } from "./http-1NL30qCe.js";
6
- import { c as getKeyOwner, d as detachSignature, f as hasSignatureLike, i as verifyObject, m as verifyJsonLd, n as hasProofLike, o as normalizeOutgoingActivityJsonLd, p as signJsonLd, r as signObject, s as doesActorOwnKey } from "./proof-Dedf8md5.js";
5
+ import { _ as version, a as verifyRequestDetailed, d as validateCryptoKey, f as formatAcceptSignature, g as name, i as verifyRequest, n as parseRfc9421SignatureInput, o as exportJwk, t as doubleKnock, u as importJwk } from "./http-BUr93aO6.js";
6
+ import { c as getKeyOwner, d as detachSignature, f as hasSignatureLike, i as verifyObject, m as verifyJsonLd, n as hasProofLike, o as normalizeOutgoingActivityJsonLd, p as signJsonLd, r as signObject, s as doesActorOwnKey } from "./proof-k4mEvvdS.js";
7
7
  import { n as getNodeInfo, t as nodeInfoToJson } from "./types-hvL8ElAs.js";
8
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-CsC3P4uu.js";
8
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-C3esyJFP.js";
9
9
  import { getLogger, withContext } from "@logtape/logtape";
10
10
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
11
- import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
11
+ import { SpanKind, SpanStatusCode, context, metrics, propagation, trace } from "@opentelemetry/api";
12
12
  import { cloneDeep, uniq } from "es-toolkit";
13
13
  import { Router } from "uri-template-router";
14
14
  import { parseTemplate } from "url-template";
@@ -160,6 +160,7 @@ var RouterError = class extends Error {
160
160
  };
161
161
  //#endregion
162
162
  //#region src/federation/builder.ts
163
+ const ACTOR_ALIAS_PREFIX = "actorAlias:";
163
164
  function validateSingleIdentifierVariablePath(path, errorMessage) {
164
165
  const operatorMatches = globalThis.Array.from(path.matchAll(/{([+#./;?&]?)([A-Za-z_][A-Za-z0-9_]*)}/g));
165
166
  if (operatorMatches.length !== 1 || operatorMatches[0]?.[2] !== "identifier") throw new RouterError(errorMessage);
@@ -346,6 +347,15 @@ var FederationBuilderImpl = class {
346
347
  callbacks.aliasMapper = mapper;
347
348
  return setters;
348
349
  },
350
+ mapActorAlias: (path, identifier) => {
351
+ if (identifier === "") throw new RouterError("Identifier cannot be empty.");
352
+ if (this.router.has(`actorAlias:${identifier}`)) throw new RouterError(`Actor alias for "${identifier}" already set.`);
353
+ if (new Router$1().add(path, "temp").size > 0) throw new RouterError("Path for actor alias must have no variables.");
354
+ const existingRoute = this.router.route(path);
355
+ if (existingRoute != null) throw new RouterError(`Actor alias path "${path}" conflicts with existing route "${existingRoute.name}".`);
356
+ this.router.add(path, `${ACTOR_ALIAS_PREFIX}${identifier}`);
357
+ return setters;
358
+ },
349
359
  authorize(predicate) {
350
360
  callbacks.authorizePredicate = predicate;
351
361
  return setters;
@@ -769,8 +779,90 @@ async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
769
779
  return `collectionId="${collectionId}", url="${url}", digest="${encodeHex(await digest(actorIds))}"`;
770
780
  }
771
781
  //#endregion
782
+ //#region src/federation/metrics.ts
783
+ var FederationMetrics = class {
784
+ deliverySent;
785
+ deliveryPermanentFailure;
786
+ signatureVerificationFailure;
787
+ deliveryDuration;
788
+ inboxProcessingDuration;
789
+ constructor(meterProvider) {
790
+ const meter = meterProvider.getMeter(name, version);
791
+ this.deliverySent = meter.createCounter("activitypub.delivery.sent", {
792
+ description: "ActivityPub delivery attempts.",
793
+ unit: "{attempt}"
794
+ });
795
+ this.deliveryPermanentFailure = meter.createCounter("activitypub.delivery.permanent_failure", {
796
+ description: "ActivityPub deliveries abandoned as permanent failures.",
797
+ unit: "{failure}"
798
+ });
799
+ this.signatureVerificationFailure = meter.createCounter("activitypub.signature.verification_failure", {
800
+ description: "ActivityPub signature verification failures.",
801
+ unit: "{failure}"
802
+ });
803
+ this.deliveryDuration = meter.createHistogram("activitypub.delivery.duration", {
804
+ description: "Duration of ActivityPub delivery attempts.",
805
+ unit: "ms"
806
+ });
807
+ this.inboxProcessingDuration = meter.createHistogram("activitypub.inbox.processing_duration", {
808
+ description: "Duration of ActivityPub inbox listener processing.",
809
+ unit: "ms"
810
+ });
811
+ }
812
+ recordDelivery(inbox, durationMs, success, activityType) {
813
+ const deliveryAttributes = {
814
+ "activitypub.remote.host": getRemoteHost(inbox),
815
+ "activitypub.delivery.success": success
816
+ };
817
+ if (activityType != null) deliveryAttributes["activitypub.activity.type"] = activityType;
818
+ this.deliverySent.add(1, deliveryAttributes);
819
+ this.deliveryDuration.record(durationMs, deliveryAttributes);
820
+ }
821
+ recordPermanentFailure(inbox, statusCode) {
822
+ this.deliveryPermanentFailure.add(1, {
823
+ "activitypub.remote.host": getRemoteHost(inbox),
824
+ "http.response.status_code": statusCode
825
+ });
826
+ }
827
+ recordSignatureVerificationFailure(reason, remoteHost) {
828
+ const attributes = { "activitypub.verification.failure_reason": reason };
829
+ if (remoteHost != null) attributes["activitypub.remote.host"] = remoteHost;
830
+ this.signatureVerificationFailure.add(1, attributes);
831
+ }
832
+ recordInboxProcessingDuration(activityType, durationMs) {
833
+ this.inboxProcessingDuration.record(durationMs, { "activitypub.activity.type": activityType });
834
+ }
835
+ };
836
+ const federationMetrics = /* @__PURE__ */ new WeakMap();
837
+ /**
838
+ * Gets the cached Fedify metric instruments for a meter provider.
839
+ * @since 2.3.0
840
+ */
841
+ function getFederationMetrics(meterProvider = metrics.getMeterProvider()) {
842
+ let instruments = federationMetrics.get(meterProvider);
843
+ if (instruments == null) {
844
+ instruments = new FederationMetrics(meterProvider);
845
+ federationMetrics.set(meterProvider, instruments);
846
+ }
847
+ return instruments;
848
+ }
849
+ /**
850
+ * Gets the bounded remote host attribute value for a URL.
851
+ * @since 2.3.0
852
+ */
853
+ function getRemoteHost(url) {
854
+ return url.hostname;
855
+ }
856
+ /**
857
+ * Gets an elapsed duration in milliseconds from a `performance.now()` value.
858
+ * @since 2.3.0
859
+ */
860
+ function getDurationMs(start) {
861
+ return Math.max(0, performance.now() - start);
862
+ }
863
+ //#endregion
772
864
  //#region src/federation/inbox.ts
773
- async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, tracerProvider, idempotencyStrategy }) {
865
+ async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
774
866
  const logger = getLogger([
775
867
  "fedify",
776
868
  "federation",
@@ -871,7 +963,13 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
871
963
  const { class: cls, listener } = dispatched;
872
964
  span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
873
965
  try {
874
- await listener(inboxContextFactory(recipient, json, activity?.id?.href, getTypeId(activity).href), activity);
966
+ const activityType = getTypeId(activity).href;
967
+ const started = performance.now();
968
+ try {
969
+ await listener(inboxContextFactory(recipient, json, activity?.id?.href, activityType), activity);
970
+ } finally {
971
+ getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
972
+ }
875
973
  } catch (error) {
876
974
  try {
877
975
  await inboxErrorHandler?.(ctx, error);
@@ -1681,6 +1779,8 @@ async function handleInboxInternal(request, parameters, span) {
1681
1779
  });
1682
1780
  if (verification.verified === false) {
1683
1781
  const reason = verification.reason;
1782
+ const remoteHost = "keyId" in reason && reason.keyId != null ? getRemoteHost(reason.keyId) : void 0;
1783
+ getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure(reason.type, remoteHost);
1684
1784
  logger.error("Failed to verify the request's HTTP Signatures.", {
1685
1785
  recipient,
1686
1786
  reason: reason.type,
@@ -1767,6 +1867,7 @@ async function handleInboxInternal(request, parameters, span) {
1767
1867
  "http_signatures.key_id": httpSigKey?.id?.href ?? ""
1768
1868
  });
1769
1869
  if (httpSigKey != null && !await doesActorOwnKey(activity, httpSigKey, ctx)) {
1870
+ getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure("actorKeyMismatch", httpSigKey.id == null ? void 0 : getRemoteHost(httpSigKey.id));
1770
1871
  logger.error("The signer ({keyId}) and the actor ({actorId}) do not match.", {
1771
1872
  activity: json,
1772
1873
  recipient,
@@ -1784,6 +1885,7 @@ async function handleInboxInternal(request, parameters, span) {
1784
1885
  }
1785
1886
  if (pendingNonceLabel != null) {
1786
1887
  if (!await verifySignatureNonce(request, kv, kvPrefixes.acceptSignatureNonce, pendingNonceLabel)) {
1888
+ getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure("invalidNonce", httpSigKey?.id == null ? void 0 : getRemoteHost(httpSigKey.id));
1787
1889
  logger.error("Signature nonce verification failed (missing, expired, or replayed).", { recipient });
1788
1890
  return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
1789
1891
  }
@@ -1800,6 +1902,7 @@ async function handleInboxInternal(request, parameters, span) {
1800
1902
  kvPrefixes,
1801
1903
  queue,
1802
1904
  span,
1905
+ meterProvider: parameters.meterProvider,
1803
1906
  tracerProvider,
1804
1907
  idempotencyStrategy: parameters.idempotencyStrategy
1805
1908
  });
@@ -2447,6 +2550,25 @@ function sendActivity(options) {
2447
2550
  });
2448
2551
  }
2449
2552
  const MAX_ERROR_RESPONSE_BODY_BYTES = 1024;
2553
+ function getActivityActorId(activity) {
2554
+ if (!isRecord(activity)) return void 0;
2555
+ return getIdValue(activity.actor);
2556
+ }
2557
+ function getIdValue(value) {
2558
+ if (typeof value === "string" && value !== "") return value;
2559
+ if (value instanceof URL) return value.href;
2560
+ if (Array.isArray(value)) {
2561
+ for (const item of value) {
2562
+ const id = getIdValue(item);
2563
+ if (id != null) return id;
2564
+ }
2565
+ return;
2566
+ }
2567
+ if (isRecord(value)) return getIdValue(value.id);
2568
+ }
2569
+ function isRecord(value) {
2570
+ return typeof value === "object" && value != null;
2571
+ }
2450
2572
  async function readLimitedResponseBody(response, maxBytes) {
2451
2573
  if (response.body == null) return "";
2452
2574
  const reader = response.body.getReader();
@@ -2474,12 +2596,15 @@ async function readLimitedResponseBody(response, maxBytes) {
2474
2596
  if (truncated) result += "… (truncated)";
2475
2597
  return result;
2476
2598
  }
2477
- async function sendActivityInternal({ activity, activityId, keys, inbox, headers, specDeterminer, tracerProvider }, span) {
2599
+ async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, headers, specDeterminer, meterProvider, tracerProvider }, span) {
2478
2600
  const logger = getLogger([
2479
2601
  "fedify",
2480
2602
  "federation",
2481
2603
  "outbox"
2482
2604
  ]);
2605
+ const federationMetrics = getFederationMetrics(meterProvider);
2606
+ const started = performance.now();
2607
+ let deliverySuccess = false;
2483
2608
  headers = new Headers(headers);
2484
2609
  headers.set("Content-Type", "application/activity+json");
2485
2610
  const request = new Request(inbox, {
@@ -2511,29 +2636,38 @@ async function sendActivityInternal({ activity, activityId, keys, inbox, headers
2511
2636
  inbox: inbox.href,
2512
2637
  error
2513
2638
  });
2639
+ federationMetrics.recordDelivery(inbox, getDurationMs(started), false, activityType);
2514
2640
  throw error;
2515
2641
  }
2516
- if (!response.ok) {
2517
- let error;
2518
- try {
2519
- error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
2520
- } catch (_) {
2521
- error = "";
2642
+ try {
2643
+ if (!response.ok) {
2644
+ let error;
2645
+ try {
2646
+ error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
2647
+ } catch (_) {
2648
+ error = "";
2649
+ }
2650
+ logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
2651
+ activityId,
2652
+ inbox: inbox.href,
2653
+ status: response.status,
2654
+ statusText: response.statusText,
2655
+ error
2656
+ });
2657
+ throw new SendActivityError(inbox, response.status, `Failed to send activity ${activityId} to ${inbox.href} (${response.status} ${response.statusText}):\n${error}`, error);
2522
2658
  }
2523
- logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
2524
- activityId,
2525
- inbox: inbox.href,
2526
- status: response.status,
2527
- statusText: response.statusText,
2528
- error
2529
- });
2530
- throw new SendActivityError(inbox, response.status, `Failed to send activity ${activityId} to ${inbox.href} (${response.status} ${response.statusText}):\n${error}`, error);
2659
+ deliverySuccess = true;
2660
+ const eventAttributes = {
2661
+ "activitypub.inbox.url": inbox.href,
2662
+ "activitypub.activity.id": activityId ?? ""
2663
+ };
2664
+ if (activityType != null) eventAttributes["activitypub.activity.type"] = activityType;
2665
+ const actorId = getActivityActorId(activity);
2666
+ if (actorId != null) eventAttributes["activitypub.actor.id"] = actorId;
2667
+ span.addEvent("activitypub.activity.sent", eventAttributes);
2668
+ } finally {
2669
+ federationMetrics.recordDelivery(inbox, getDurationMs(started), deliverySuccess, activityType);
2531
2670
  }
2532
- span.addEvent("activitypub.activity.sent", {
2533
- "activitypub.activity.json": JSON.stringify(activity),
2534
- "activitypub.inbox.url": inbox.href,
2535
- "activitypub.activity.id": activityId ?? ""
2536
- });
2537
2671
  }
2538
2672
  /**
2539
2673
  * An error that is thrown when an activity fails to send to a remote inbox.
@@ -2751,6 +2885,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2751
2885
  inboxRetryPolicy;
2752
2886
  activityTransformers;
2753
2887
  _tracerProvider;
2888
+ _meterProvider;
2754
2889
  firstKnock;
2755
2890
  inboxChallengePolicy;
2756
2891
  constructor(options) {
@@ -2836,11 +2971,15 @@ var FederationImpl = class extends FederationBuilderImpl {
2836
2971
  this.inboxRetryPolicy = options.inboxRetryPolicy ?? createExponentialBackoffPolicy();
2837
2972
  this.activityTransformers = options.activityTransformers ?? getDefaultActivityTransformers();
2838
2973
  this._tracerProvider = options.tracerProvider;
2974
+ this._meterProvider = options.meterProvider;
2839
2975
  this.firstKnock = options.firstKnock;
2840
2976
  }
2841
2977
  get tracerProvider() {
2842
2978
  return this._tracerProvider ?? trace.getTracerProvider();
2843
2979
  }
2980
+ get meterProvider() {
2981
+ return this._meterProvider ?? metrics.getMeterProvider();
2982
+ }
2844
2983
  _initializeRouter() {
2845
2984
  this.router.add("/.well-known/webfinger", "webfinger");
2846
2985
  this.router.add("/.well-known/nodeinfo", "nodeInfoJrd");
@@ -3019,6 +3158,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3019
3158
  sharedInbox: message.sharedInbox,
3020
3159
  headers: new Headers(message.headers),
3021
3160
  specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, this.firstKnock),
3161
+ meterProvider: this.meterProvider,
3022
3162
  tracerProvider: this.tracerProvider
3023
3163
  });
3024
3164
  } catch (error) {
@@ -3026,6 +3166,21 @@ var FederationImpl = class extends FederationBuilderImpl {
3026
3166
  code: SpanStatusCode.ERROR,
3027
3167
  message: String(error)
3028
3168
  });
3169
+ const remoteHost = (() => {
3170
+ if (error instanceof SendActivityError) return getRemoteHost(error.inbox);
3171
+ try {
3172
+ return getRemoteHost(new URL(message.inbox));
3173
+ } catch (_) {
3174
+ logger.warn("Invalid inbox URL in queued outbox message: {inbox}", logData);
3175
+ return;
3176
+ }
3177
+ })();
3178
+ span.addEvent("activitypub.delivery.failed", {
3179
+ ...remoteHost == null ? {} : { "activitypub.remote.host": remoteHost },
3180
+ "activitypub.delivery.attempt": message.attempt,
3181
+ "activitypub.delivery.permanent_failure": error instanceof SendActivityError && this.permanentFailureStatusCodes.includes(error.statusCode),
3182
+ ...error instanceof SendActivityError ? { "http.response.status_code": error.statusCode } : {}
3183
+ });
3029
3184
  const loaderOptions = this.#getLoaderOptions(message.baseUrl);
3030
3185
  const activity = await Activity.fromJsonLd(message.activity, {
3031
3186
  contextLoader: this.contextLoaderFactory(loaderOptions),
@@ -3041,6 +3196,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3041
3196
  });
3042
3197
  }
3043
3198
  if (error instanceof SendActivityError && this.permanentFailureStatusCodes.includes(error.statusCode)) {
3199
+ getFederationMetrics(this.meterProvider).recordPermanentFailure(error.inbox, error.statusCode);
3044
3200
  logger.warn("Permanent delivery failure for activity {activityId} to {inbox} ({status}); not retrying.", {
3045
3201
  ...logData,
3046
3202
  status: error.statusCode
@@ -3149,7 +3305,13 @@ var FederationImpl = class extends FederationBuilderImpl {
3149
3305
  const { class: cls, listener } = dispatched;
3150
3306
  span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
3151
3307
  try {
3152
- await listener(context.toInboxContext(message.identifier, message.activity, activity.id?.href, getTypeId(activity).href), activity);
3308
+ const activityType = getTypeId(activity).href;
3309
+ const started = performance.now();
3310
+ try {
3311
+ await listener(context.toInboxContext(message.identifier, message.activity, activity.id?.href, activityType), activity);
3312
+ } finally {
3313
+ getFederationMetrics(this.meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
3314
+ }
3153
3315
  } catch (error) {
3154
3316
  try {
3155
3317
  await this.inboxErrorHandler?.(context, error);
@@ -3332,6 +3494,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3332
3494
  sharedInbox: inboxes[inbox].sharedInbox,
3333
3495
  headers: collectionSync == null ? void 0 : new Headers({ "Collection-Synchronization": await buildCollectionSynchronizationHeader(collectionSync, inboxes[inbox].actorIds) }),
3334
3496
  specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, this.firstKnock),
3497
+ meterProvider: this.meterProvider,
3335
3498
  tracerProvider: this.tracerProvider
3336
3499
  }));
3337
3500
  await Promise.all(promises);
@@ -3508,15 +3671,18 @@ var FederationImpl = class extends FederationBuilderImpl {
3508
3671
  if (request.method !== "POST" && !acceptsJsonLd(request)) return await onNotAcceptable(request);
3509
3672
  switch (routeName) {
3510
3673
  case "actor":
3511
- context = this.#createContext(request, contextData, { invokedFromActorDispatcher: { identifier: route.values.identifier } });
3674
+ case "actorAlias": {
3675
+ const identifier = route.name.startsWith("actorAlias:") ? route.name.substring(11) : route.values.identifier;
3676
+ context = this.#createContext(request, contextData, { invokedFromActorDispatcher: { identifier } });
3512
3677
  return await handleActor(request, {
3513
- identifier: route.values.identifier,
3678
+ identifier,
3514
3679
  context,
3515
3680
  actorDispatcher: this.actorCallbacks?.dispatcher,
3516
3681
  authorizePredicate: this.actorCallbacks?.authorizePredicate,
3517
3682
  onUnauthorized,
3518
3683
  onNotFound
3519
3684
  });
3685
+ }
3520
3686
  case "object": {
3521
3687
  const typeId = route.name.replace(/^object:/, "");
3522
3688
  const callbacks = this.objectCallbacks[typeId];
@@ -3598,6 +3764,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3598
3764
  signatureTimeWindow: this.signatureTimeWindow,
3599
3765
  skipSignatureVerification: this.skipSignatureVerification,
3600
3766
  inboxChallengePolicy: this.inboxChallengePolicy,
3767
+ meterProvider: this.meterProvider,
3601
3768
  tracerProvider: this.tracerProvider,
3602
3769
  idempotencyStrategy: this.idempotencyStrategy
3603
3770
  });
@@ -3759,13 +3926,16 @@ var ContextImpl = class ContextImpl {
3759
3926
  get tracerProvider() {
3760
3927
  return this.federation.tracerProvider;
3761
3928
  }
3929
+ get meterProvider() {
3930
+ return this.federation.meterProvider;
3931
+ }
3762
3932
  getNodeInfoUri() {
3763
3933
  const path = this.federation.router.build("nodeInfo", {});
3764
3934
  if (path == null) throw new RouterError("No NodeInfo dispatcher registered.");
3765
3935
  return new URL(path, this.canonicalOrigin);
3766
3936
  }
3767
3937
  getActorUri(identifier) {
3768
- const path = this.federation.router.build("actor", { identifier });
3938
+ const path = this.federation.router.build(`actorAlias:${identifier}`, {}) ?? this.federation.router.build("actor", { identifier });
3769
3939
  if (path == null) throw new RouterError("No actor dispatcher registered.");
3770
3940
  return new URL(path, this.canonicalOrigin);
3771
3941
  }
@@ -3831,8 +4001,8 @@ var ContextImpl = class ContextImpl {
3831
4001
  type: "inbox",
3832
4002
  identifier: void 0
3833
4003
  };
3834
- const identifier = route.values.identifier;
3835
- if (route.name === "actor") return {
4004
+ const identifier = route.name.startsWith("actorAlias:") ? route.name.substring(11) : route.values.identifier;
4005
+ if (route.name === "actor" || route.name.startsWith("actorAlias:")) return {
3836
4006
  type: "actor",
3837
4007
  identifier
3838
4008
  };
@@ -4508,6 +4678,7 @@ async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipient
4508
4678
  activityType: ctx.activityType,
4509
4679
  inbox: new URL(inbox),
4510
4680
  sharedInbox: inboxes[inbox].sharedInbox,
4681
+ meterProvider: ctx.meterProvider,
4511
4682
  tracerProvider: ctx.tracerProvider,
4512
4683
  specDeterminer: new KvSpecDeterminer(ctx.federation.kv, ctx.federation.kvPrefixes.httpMessageSignaturesSpec, ctx.federation.firstKnock)
4513
4684
  }));
@@ -1,4 +1,4 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
- const require_middleware = require("./middleware-BiFLcrEX.cjs");
3
+ const require_middleware = require("./middleware-CKkBrsOD.cjs");
4
4
  exports.FederationImpl = require_middleware.FederationImpl;