@fedify/fedify 2.3.0-dev.1110 → 2.3.0-dev.1119

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 (97) hide show
  1. package/dist/{assert_rejects-B-qJtC9Z.mjs → assert_rejects-DQP-q39h.mjs} +27 -2
  2. package/dist/{builder-B-Y6fwSu.mjs → builder-Ond_h57y.mjs} +3 -3
  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-C0C_sRha.d.cts → context-Ch-ZLyTQ.d.cts} +1 -1
  9. package/dist/{context-Dqgt8saU.d.ts → context-cSUMk2da.d.ts} +1 -1
  10. package/dist/{deno-hqC7tKJn.mjs → deno-DVsHS7rA.mjs} +1 -1
  11. package/dist/{docloader-BOEuuXkX.mjs → docloader-WsWfKaE5.mjs} +2 -2
  12. package/dist/federation/builder.test.mjs +3 -3
  13. package/dist/federation/collection.test.mjs +2 -2
  14. package/dist/federation/handler.test.mjs +8 -7
  15. package/dist/federation/idempotency.test.mjs +5 -5
  16. package/dist/federation/inbox.test.mjs +1 -1
  17. package/dist/federation/keycache.test.mjs +1 -1
  18. package/dist/federation/kv.test.mjs +2 -2
  19. package/dist/federation/metrics.test.d.mts +2 -0
  20. package/dist/federation/metrics.test.mjs +107 -0
  21. package/dist/federation/middleware.test.mjs +390 -10
  22. package/dist/federation/mod.cjs +1 -1
  23. package/dist/federation/mod.d.cts +2 -2
  24. package/dist/federation/mod.d.ts +2 -2
  25. package/dist/federation/mod.js +1 -1
  26. package/dist/federation/mq.test.mjs +2 -2
  27. package/dist/federation/negotiation.test.mjs +2 -2
  28. package/dist/federation/router.test.mjs +2 -2
  29. package/dist/federation/send.test.mjs +11 -11
  30. package/dist/federation/webfinger.test.mjs +3 -3
  31. package/dist/{getMachineId-bsd-etIyxDet.mjs → getMachineId-bsd-BY01PL1n.mjs} +1 -1
  32. package/dist/{getMachineId-darwin-D23zTf4g.mjs → getMachineId-darwin-Dr1gkBkp.mjs} +1 -1
  33. package/dist/{getMachineId-win-Dpap6v5i.mjs → getMachineId-win-QEYwcJiy.mjs} +1 -1
  34. package/dist/{http-O8MYWwk8.js → http-CouJSFVK.js} +461 -37
  35. package/dist/{http-DV0il3vk.cjs → http-CubOB9wq.cjs} +513 -35
  36. package/dist/{http-BDZeS5om.d.ts → http-D6LP89UO.d.ts} +7 -1
  37. package/dist/{http-C87EWkO0.d.cts → http-D6aw3j2U.d.cts} +7 -1
  38. package/dist/{http-BLopFpvC.mjs → http-DUV8ysti.mjs} +86 -37
  39. package/dist/{key-DW1EVmtP.mjs → key-BoWaYRHm.mjs} +1 -1
  40. package/dist/{kv-cache-C3NWWiTg.js → kv-cache-DBNpsneh.js} +1 -1
  41. package/dist/{kv-cache-Dya-TWMe.cjs → kv-cache-Dz31ATUT.cjs} +1 -1
  42. package/dist/{ld-BNkk2Yal.mjs → ld-B5K1mSuG.mjs} +60 -9
  43. package/dist/{send-hokVCPu6.mjs → metrics-C4attqv0.mjs} +124 -224
  44. package/dist/{middleware-D6FbOjuK.mjs → middleware-BDKFRjue.mjs} +1 -1
  45. package/dist/{middleware-DUWeXjZR.cjs → middleware-CmsDtIHI.cjs} +75 -309
  46. package/dist/{middleware-CjzI3aYo.js → middleware-Dtjz-hSk.js} +46 -280
  47. package/dist/{middleware-DA2WTBr4.mjs → middleware-t0jC8I99.mjs} +59 -34
  48. package/dist/{mod-DXY9JF28.d.cts → mod-B-Lin9Sy.d.ts} +25 -2
  49. package/dist/{mod-DHO9lk3D.d.ts → mod-BDhgfjP7.d.cts} +25 -2
  50. package/dist/{mod-B0rWmfW5.d.cts → mod-BR_BB0bh.d.cts} +1 -1
  51. package/dist/{mod-Dx3-hqyo.d.ts → mod-C6E8rkcz.d.ts} +1 -1
  52. package/dist/{mod-BhU_H1I_.d.ts → mod-DLrRb0dx.d.ts} +1 -1
  53. package/dist/{mod-CLPnQPsv.d.cts → mod-P9tE2WmM.d.cts} +1 -1
  54. package/dist/mod.cjs +4 -4
  55. package/dist/mod.d.cts +5 -5
  56. package/dist/mod.d.ts +5 -5
  57. package/dist/mod.js +4 -4
  58. package/dist/nodeinfo/client.test.mjs +2 -2
  59. package/dist/nodeinfo/handler.test.mjs +3 -3
  60. package/dist/nodeinfo/types.test.mjs +2 -2
  61. package/dist/otel/exporter.test.mjs +2 -2
  62. package/dist/{outgoing-jsonld-BgFLCJQ_.mjs → outgoing-jsonld-BNL8AC14.mjs} +1 -1
  63. package/dist/{owner-jvJAtR5O.mjs → owner-hDxI0ufu.mjs} +2 -2
  64. package/dist/{proof-BD92WeqV.cjs → proof-BUWfVr6Q.cjs} +78 -11
  65. package/dist/{proof-mfmHH9j0.mjs → proof-DhVuz4bc.mjs} +25 -7
  66. package/dist/{proof-5kT7OUPV.js → proof-n60t8o9P.js} +78 -11
  67. package/dist/send-BPhyR5Oo.mjs +225 -0
  68. package/dist/sig/accept.test.mjs +1 -1
  69. package/dist/sig/http.test.mjs +212 -6
  70. package/dist/sig/key.test.mjs +4 -4
  71. package/dist/sig/ld.test.mjs +138 -5
  72. package/dist/sig/mod.cjs +2 -2
  73. package/dist/sig/mod.d.cts +2 -2
  74. package/dist/sig/mod.d.ts +2 -2
  75. package/dist/sig/mod.js +2 -2
  76. package/dist/sig/owner.test.mjs +4 -4
  77. package/dist/sig/proof.test.mjs +167 -6
  78. package/dist/{std__assert-CRDpx_HF.mjs → std__assert-BTEgfoJo.mjs} +2 -27
  79. package/dist/utils/docloader.test.mjs +5 -5
  80. package/dist/utils/kv-cache.test.mjs +1 -1
  81. package/dist/utils/mod.cjs +1 -1
  82. package/dist/utils/mod.d.cts +1 -1
  83. package/dist/utils/mod.d.ts +1 -1
  84. package/dist/utils/mod.js +1 -1
  85. package/package.json +5 -5
  86. /package/dist/{accept-CceiKpCy.mjs → accept-CgDcxvjV.mjs} +0 -0
  87. /package/dist/{activity-listener-tztVvlNb.mjs → activity-listener-BeTGV3wc.mjs} +0 -0
  88. /package/dist/{client-B_A6mfn3.mjs → client-Bneh_DYR.mjs} +0 -0
  89. /package/dist/{collection-CA3V5zyK.mjs → collection-Cc3DVAhE.mjs} +0 -0
  90. /package/dist/{execAsync-DCBrgFiV.mjs → execAsync-Dxb7rNf3.mjs} +0 -0
  91. /package/dist/{getMachineId-linux-ObI47Hql.mjs → getMachineId-linux-Bbhofx-s.mjs} +0 -0
  92. /package/dist/{getMachineId-unsupported-Ddu-PFeh.mjs → getMachineId-unsupported-dIOte2Ct.mjs} +0 -0
  93. /package/dist/{keys-C3kae-6B.mjs → keys-CSYsOMFG.mjs} +0 -0
  94. /package/dist/{kv-x2IvBUyq.mjs → kv-QHE0oeM3.mjs} +0 -0
  95. /package/dist/{kv-cache-CiiNwT6W.mjs → kv-cache-DihufyAQ.mjs} +0 -0
  96. /package/dist/{public-audience-N3pyOx2p.mjs → public-audience-c9zmYKgA.mjs} +0 -0
  97. /package/dist/{types-BFowWFTT.mjs → types-D09GN0uZ.mjs} +0 -0
@@ -2,10 +2,10 @@ import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  import { t as __exportAll } from "./chunk-CRNNMoPX.js";
4
4
  import { r as getDefaultActivityTransformers } from "./transformers-BGMIq1cs.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-O8MYWwk8.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-5kT7OUPV.js";
5
+ import { E as version, T as name, _ as recordFanoutRecipients, a as verifyRequestDetailed, b as recordOutboxEnqueue, d as validateCryptoKey, f as getDurationMs, h as isAbortError, i as verifyRequest, m as getRemoteHost, n as parseRfc9421SignatureInput, o as exportJwk, p as getFederationMetrics, t as doubleKnock, u as importJwk, v as recordInboxActivity, x as formatAcceptSignature, y as recordOutboxActivity } from "./http-CouJSFVK.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-n60t8o9P.js";
7
7
  import { n as getNodeInfo, t as nodeInfoToJson } from "./types-CAY3OdLq.js";
8
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-C3NWWiTg.js";
8
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-DBNpsneh.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
11
  import { SpanKind, SpanStatusCode, context, metrics, propagation, trace } from "@opentelemetry/api";
@@ -779,264 +779,6 @@ async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
779
779
  return `collectionId="${collectionId}", url="${url}", digest="${encodeHex(await digest(actorIds))}"`;
780
780
  }
781
781
  //#endregion
782
- //#region src/federation/metrics.ts
783
- var FederationMetrics = class {
784
- deliverySent;
785
- deliveryPermanentFailure;
786
- signatureVerificationFailure;
787
- deliveryDuration;
788
- inboxProcessingDuration;
789
- httpServerRequestCount;
790
- httpServerRequestDuration;
791
- queueTaskEnqueued;
792
- queueTaskStarted;
793
- queueTaskCompleted;
794
- queueTaskFailed;
795
- queueTaskDuration;
796
- queueTaskInFlight;
797
- constructor(meterProvider) {
798
- const meter = meterProvider.getMeter(name, version);
799
- this.deliverySent = meter.createCounter("activitypub.delivery.sent", {
800
- description: "ActivityPub delivery attempts.",
801
- unit: "{attempt}"
802
- });
803
- this.deliveryPermanentFailure = meter.createCounter("activitypub.delivery.permanent_failure", {
804
- description: "ActivityPub deliveries abandoned as permanent failures.",
805
- unit: "{failure}"
806
- });
807
- this.signatureVerificationFailure = meter.createCounter("activitypub.signature.verification_failure", {
808
- description: "ActivityPub signature verification failures.",
809
- unit: "{failure}"
810
- });
811
- this.deliveryDuration = meter.createHistogram("activitypub.delivery.duration", {
812
- description: "Duration of ActivityPub delivery attempts.",
813
- unit: "ms"
814
- });
815
- this.inboxProcessingDuration = meter.createHistogram("activitypub.inbox.processing_duration", {
816
- description: "Duration of ActivityPub inbox listener processing.",
817
- unit: "ms"
818
- });
819
- this.httpServerRequestCount = meter.createCounter("fedify.http.server.request.count", {
820
- description: "HTTP requests handled by Federation.fetch().",
821
- unit: "{request}"
822
- });
823
- this.httpServerRequestDuration = meter.createHistogram("fedify.http.server.request.duration", {
824
- description: "Duration of HTTP requests handled by Federation.fetch().",
825
- unit: "ms",
826
- advice: { explicitBucketBoundaries: [
827
- 5,
828
- 10,
829
- 25,
830
- 50,
831
- 75,
832
- 100,
833
- 250,
834
- 500,
835
- 750,
836
- 1e3,
837
- 2500,
838
- 5e3,
839
- 7500,
840
- 1e4
841
- ] }
842
- });
843
- this.queueTaskEnqueued = meter.createCounter("fedify.queue.task.enqueued", {
844
- description: "Tasks Fedify enqueued for inbox, outbox, or fanout work.",
845
- unit: "{task}"
846
- });
847
- this.queueTaskStarted = meter.createCounter("fedify.queue.task.started", {
848
- description: "Tasks Fedify began processing as a queue worker.",
849
- unit: "{task}"
850
- });
851
- this.queueTaskCompleted = meter.createCounter("fedify.queue.task.completed", {
852
- description: "Queue tasks Fedify finished processing without throwing.",
853
- unit: "{task}"
854
- });
855
- this.queueTaskFailed = meter.createCounter("fedify.queue.task.failed", {
856
- description: "Queue tasks Fedify abandoned because processing threw.",
857
- unit: "{task}"
858
- });
859
- this.queueTaskDuration = meter.createHistogram("fedify.queue.task.duration", {
860
- description: "Duration of queue task processing in Fedify workers.",
861
- unit: "ms",
862
- advice: { explicitBucketBoundaries: [
863
- 5,
864
- 10,
865
- 25,
866
- 50,
867
- 75,
868
- 100,
869
- 250,
870
- 500,
871
- 750,
872
- 1e3,
873
- 2500,
874
- 5e3,
875
- 7500,
876
- 1e4
877
- ] }
878
- });
879
- this.queueTaskInFlight = meter.createUpDownCounter("fedify.queue.task.in_flight", {
880
- description: "Queue tasks currently being processed in this Fedify process.",
881
- unit: "{task}"
882
- });
883
- }
884
- recordDelivery(inbox, durationMs, success, activityType) {
885
- const deliveryAttributes = {
886
- "activitypub.remote.host": getRemoteHost(inbox),
887
- "activitypub.delivery.success": success
888
- };
889
- if (activityType != null) deliveryAttributes["activitypub.activity.type"] = activityType;
890
- this.deliverySent.add(1, deliveryAttributes);
891
- this.deliveryDuration.record(durationMs, deliveryAttributes);
892
- }
893
- recordPermanentFailure(inbox, statusCode) {
894
- this.deliveryPermanentFailure.add(1, {
895
- "activitypub.remote.host": getRemoteHost(inbox),
896
- "http.response.status_code": statusCode
897
- });
898
- }
899
- recordSignatureVerificationFailure(reason, remoteHost) {
900
- const attributes = { "activitypub.verification.failure_reason": reason };
901
- if (remoteHost != null) attributes["activitypub.remote.host"] = remoteHost;
902
- this.signatureVerificationFailure.add(1, attributes);
903
- }
904
- recordInboxProcessingDuration(activityType, durationMs) {
905
- this.inboxProcessingDuration.record(durationMs, { "activitypub.activity.type": activityType });
906
- }
907
- recordHttpServerRequest(method, endpoint, durationMs, options = {}) {
908
- const attributes = {
909
- "http.request.method": normalizeHttpMethod(method),
910
- "fedify.endpoint": endpoint
911
- };
912
- if (options.statusCode != null) attributes["http.response.status_code"] = options.statusCode;
913
- if (options.routeTemplate != null) attributes["fedify.route.template"] = options.routeTemplate;
914
- this.httpServerRequestCount.add(1, attributes);
915
- this.httpServerRequestDuration.record(durationMs, attributes);
916
- }
917
- recordQueueTaskEnqueued(common, attempt) {
918
- const attributes = buildQueueTaskAttributes(common);
919
- attributes["fedify.queue.task.attempt"] = attempt;
920
- this.queueTaskEnqueued.add(1, attributes);
921
- }
922
- recordQueueTaskStarted(common) {
923
- this.queueTaskStarted.add(1, buildQueueTaskAttributes(common));
924
- }
925
- incrementQueueTaskInFlight(common) {
926
- this.queueTaskInFlight.add(1, buildQueueTaskInFlightAttributes(common));
927
- }
928
- decrementQueueTaskInFlight(common) {
929
- this.queueTaskInFlight.add(-1, buildQueueTaskInFlightAttributes(common));
930
- }
931
- recordQueueTaskOutcome(common, result, durationMs) {
932
- const attributes = buildQueueTaskAttributes(common);
933
- attributes["fedify.queue.task.result"] = result;
934
- if (result === "completed") this.queueTaskCompleted.add(1, attributes);
935
- else if (result === "failed") this.queueTaskFailed.add(1, attributes);
936
- this.queueTaskDuration.record(durationMs, attributes);
937
- }
938
- };
939
- function buildQueueTaskAttributes(common) {
940
- const attributes = { "fedify.queue.role": common.role };
941
- const backend = getQueueBackend(common.queue);
942
- if (backend != null) attributes["fedify.queue.backend"] = backend;
943
- const nativeRetrial = common.queue?.nativeRetrial;
944
- if (typeof nativeRetrial === "boolean") attributes["fedify.queue.native_retrial"] = nativeRetrial;
945
- if (common.activityType != null) attributes["activitypub.activity.type"] = common.activityType;
946
- return attributes;
947
- }
948
- function buildQueueTaskInFlightAttributes(common) {
949
- return buildQueueTaskAttributes({
950
- role: common.role,
951
- queue: common.queue
952
- });
953
- }
954
- /**
955
- * Returns the constructor name of the given message queue, when it is a
956
- * meaningful identifier. Used as a best-effort `fedify.queue.backend`
957
- * attribute on queue task metrics; returns `undefined` for plain object
958
- * literals (whose constructor is `Object`) so the attribute does not appear
959
- * with a non-informative value.
960
- * @since 2.3.0
961
- */
962
- function getQueueBackend(queue) {
963
- const name = queue?.constructor?.name;
964
- if (name == null || name === "" || name === "Object") return void 0;
965
- return name;
966
- }
967
- /**
968
- * Records `fedify.queue.task.enqueued` for an outgoing outbox enqueue.
969
- *
970
- * Both `Context.sendActivity()` and `OutboxContext.forwardActivity()` enqueue
971
- * outbox messages with the same metric attributes (role, queue, activity
972
- * type, attempt), so they share this helper rather than each defining a local
973
- * closure.
974
- * @since 2.3.0
975
- */
976
- function recordOutboxEnqueue(meterProvider, outboxQueue, message) {
977
- getFederationMetrics(meterProvider).recordQueueTaskEnqueued({
978
- role: "outbox",
979
- queue: outboxQueue,
980
- activityType: message.activityType
981
- }, message.attempt);
982
- }
983
- /**
984
- * Whether the given thrown value is an `AbortError`.
985
- *
986
- * `processQueuedTask` distinguishes aborted tasks (recorded as
987
- * `fedify.queue.task.result=aborted`) from other failures so that backend
988
- * shutdown signals do not inflate the `fedify.queue.task.failed` counter.
989
- * @since 2.3.0
990
- */
991
- function isAbortError(error) {
992
- if (error == null || typeof error !== "object") return false;
993
- const name = error.name;
994
- return typeof name === "string" && name === "AbortError";
995
- }
996
- const KNOWN_HTTP_METHODS = new Set([
997
- "CONNECT",
998
- "DELETE",
999
- "GET",
1000
- "HEAD",
1001
- "OPTIONS",
1002
- "PATCH",
1003
- "POST",
1004
- "PUT",
1005
- "QUERY",
1006
- "TRACE"
1007
- ]);
1008
- function normalizeHttpMethod(method) {
1009
- const upper = method.toUpperCase();
1010
- return KNOWN_HTTP_METHODS.has(upper) ? upper : "_OTHER";
1011
- }
1012
- const federationMetrics = /* @__PURE__ */ new WeakMap();
1013
- /**
1014
- * Gets the cached Fedify metric instruments for a meter provider.
1015
- * @since 2.3.0
1016
- */
1017
- function getFederationMetrics(meterProvider = metrics.getMeterProvider()) {
1018
- let instruments = federationMetrics.get(meterProvider);
1019
- if (instruments == null) {
1020
- instruments = new FederationMetrics(meterProvider);
1021
- federationMetrics.set(meterProvider, instruments);
1022
- }
1023
- return instruments;
1024
- }
1025
- /**
1026
- * Gets the bounded remote host attribute value for a URL.
1027
- * @since 2.3.0
1028
- */
1029
- function getRemoteHost(url) {
1030
- return url.hostname;
1031
- }
1032
- /**
1033
- * Gets an elapsed duration in milliseconds from a `performance.now()` value.
1034
- * @since 2.3.0
1035
- */
1036
- function getDurationMs(start) {
1037
- return Math.max(0, performance.now() - start);
1038
- }
1039
- //#endregion
1040
782
  //#region src/federation/inbox.ts
1041
783
  async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
1042
784
  const logger = getLogger([
@@ -1075,6 +817,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
1075
817
  code: SpanStatusCode.UNSET,
1076
818
  message: `Activity ${activity.id?.href} has already been processed.`
1077
819
  });
820
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
1078
821
  return "alreadyProcessed";
1079
822
  }
1080
823
  }
@@ -1084,6 +827,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
1084
827
  code: SpanStatusCode.ERROR,
1085
828
  message: "Missing actor."
1086
829
  });
830
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
1087
831
  return "missingActor";
1088
832
  }
1089
833
  span.setAttribute("activitypub.actor.id", activity.actorId.href);
@@ -1119,6 +863,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
1119
863
  queue,
1120
864
  activityType: getTypeId(activity).href
1121
865
  }, 0);
866
+ recordInboxActivity(meterProvider, "queued", getTypeId(activity).href);
1122
867
  logger.info("Activity {activityId} is enqueued.", {
1123
868
  activityId: activity.id?.href,
1124
869
  activity: json,
@@ -1138,6 +883,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
1138
883
  code: SpanStatusCode.UNSET,
1139
884
  message: `Unsupported activity type: ${getTypeId(activity).href}`
1140
885
  });
886
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
1141
887
  span.end();
1142
888
  return "unsupportedActivity";
1143
889
  }
@@ -1147,7 +893,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
1147
893
  const activityType = getTypeId(activity).href;
1148
894
  const started = performance.now();
1149
895
  try {
1150
- await listener(inboxContextFactory(recipient, json, activity?.id?.href, activityType), activity);
896
+ await listener(inboxContextFactory(recipient, json, activity.id?.href, activityType), activity);
1151
897
  } finally {
1152
898
  getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
1153
899
  }
@@ -1172,9 +918,11 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
1172
918
  code: SpanStatusCode.ERROR,
1173
919
  message: String(error)
1174
920
  });
921
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
1175
922
  span.end();
1176
923
  return "error";
1177
924
  }
925
+ recordInboxActivity(meterProvider, "processed", getTypeId(activity).href);
1178
926
  if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
1179
927
  logger.info("Activity {activityId} has been processed.", {
1180
928
  activityId: activity.id?.href,
@@ -1442,8 +1190,8 @@ async function handleObject(request, { values, context, objectDispatcher, author
1442
1190
  * @param parameters The parameters for handling the collection.
1443
1191
  * @returns A promise that resolves to an HTTP response.
1444
1192
  */
1445
- async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, onUnauthorized, onNotFound }) {
1446
- const spanName = name$1.trim().replace(/\s+/g, "_");
1193
+ async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, onUnauthorized, onNotFound }) {
1194
+ const spanName = name$2.trim().replace(/\s+/g, "_");
1447
1195
  tracerProvider = tracerProvider ?? trace.getTracerProvider();
1448
1196
  const tracer = tracerProvider.getTracer(name, version);
1449
1197
  const cursor = new URL(request.url).searchParams.get("cursor");
@@ -1485,7 +1233,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
1485
1233
  collection = new OrderedCollection({
1486
1234
  id: baseUri,
1487
1235
  totalItems: totalItems == null ? null : Number(totalItems),
1488
- items: filterCollectionItems(itemsOrResponse, name$1, filterPredicate)
1236
+ items: filterCollectionItems(itemsOrResponse, name$2, filterPredicate)
1489
1237
  });
1490
1238
  } else {
1491
1239
  const lastCursor = await collectionCallbacks.lastCursor?.(context, identifier);
@@ -1506,7 +1254,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
1506
1254
  } else {
1507
1255
  const uri = new URL(baseUri);
1508
1256
  uri.searchParams.set("cursor", cursor);
1509
- const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
1257
+ const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
1510
1258
  kind: SpanKind.SERVER,
1511
1259
  attributes: {
1512
1260
  "activitypub.collection.id": uri.href,
@@ -1550,7 +1298,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
1550
1298
  id: uri,
1551
1299
  prev,
1552
1300
  next,
1553
- items: filterCollectionItems(items, name$1, filterPredicate),
1301
+ items: filterCollectionItems(items, name$2, filterPredicate),
1554
1302
  partOf
1555
1303
  });
1556
1304
  }
@@ -1825,7 +1573,7 @@ async function handleInbox(request, options) {
1825
1573
  * @returns A promise that resolves to an HTTP response.
1826
1574
  */
1827
1575
  async function handleInboxInternal(request, parameters, span) {
1828
- const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, inboxChallengePolicy, tracerProvider } = parameters;
1576
+ const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, inboxChallengePolicy, meterProvider, tracerProvider } = parameters;
1829
1577
  const logger = getLogger([
1830
1578
  "fedify",
1831
1579
  "federation",
@@ -1903,6 +1651,7 @@ async function handleInboxInternal(request, parameters, span) {
1903
1651
  contextLoader: ctx.contextLoader,
1904
1652
  documentLoader: ctx.documentLoader,
1905
1653
  keyCache,
1654
+ meterProvider,
1906
1655
  tracerProvider
1907
1656
  });
1908
1657
  } catch (error) {
@@ -1938,6 +1687,7 @@ async function handleInboxInternal(request, parameters, span) {
1938
1687
  contextLoader: ctx.contextLoader,
1939
1688
  documentLoader: ctx.documentLoader,
1940
1689
  keyCache,
1690
+ meterProvider,
1941
1691
  tracerProvider
1942
1692
  });
1943
1693
  } catch (error) {
@@ -1985,6 +1735,7 @@ async function handleInboxInternal(request, parameters, span) {
1985
1735
  documentLoader: ctx.documentLoader,
1986
1736
  timeWindow: signatureTimeWindow,
1987
1737
  keyCache,
1738
+ meterProvider,
1988
1739
  tracerProvider
1989
1740
  });
1990
1741
  if (verification.verified === false) {
@@ -2233,8 +1984,8 @@ var CustomCollectionHandler = class {
2233
1984
  * @param CollectionPage The CollectionPage constructor.
2234
1985
  * @param filterPredicate Optional filter predicate for items.
2235
1986
  */
2236
- constructor(name$2, values, context, callbacks, tracerProvider = trace.getTracerProvider(), Collection, CollectionPage, filterPredicate) {
2237
- this.name = name$2;
1987
+ constructor(name$1, values, context, callbacks, tracerProvider = trace.getTracerProvider(), Collection, CollectionPage, filterPredicate) {
1988
+ this.name = name$1;
2238
1989
  this.values = values;
2239
1990
  this.context = context;
2240
1991
  this.callbacks = callbacks;
@@ -3488,6 +3239,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3488
3239
  });
3489
3240
  }
3490
3241
  }
3242
+ recordOutboxActivity(this.meterProvider, "abandoned", message.activityType);
3491
3243
  return;
3492
3244
  }
3493
3245
  if (this.outboxQueue?.nativeRetrial) {
@@ -3518,11 +3270,15 @@ var FederationImpl = class extends FederationBuilderImpl {
3518
3270
  queue: outboxQueue,
3519
3271
  activityType: retryMessage.activityType
3520
3272
  }, retryMessage.attempt);
3273
+ recordOutboxActivity(this.meterProvider, "retried", retryMessage.activityType);
3521
3274
  }
3522
- } else logger.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
3523
- ...logData,
3524
- error
3525
- });
3275
+ } else {
3276
+ logger.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
3277
+ ...logData,
3278
+ error
3279
+ });
3280
+ recordOutboxActivity(this.meterProvider, "abandoned", message.activityType);
3281
+ }
3526
3282
  return;
3527
3283
  }
3528
3284
  logger.info("Successfully sent activity {activityId} to {inbox}.", { ...logData });
@@ -3557,6 +3313,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3557
3313
  activity: message.activity,
3558
3314
  recipient: message.identifier
3559
3315
  });
3316
+ recordInboxActivity(this.meterProvider, "rejected", activityType);
3560
3317
  return;
3561
3318
  }
3562
3319
  }
@@ -3573,6 +3330,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3573
3330
  code: SpanStatusCode.ERROR,
3574
3331
  message: `Unsupported activity type: ${activityType}`
3575
3332
  });
3333
+ recordInboxActivity(this.meterProvider, "rejected", activityType);
3576
3334
  span.end();
3577
3335
  return;
3578
3336
  }
@@ -3585,6 +3343,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3585
3343
  } finally {
3586
3344
  getFederationMetrics(this.meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
3587
3345
  }
3346
+ recordInboxActivity(this.meterProvider, "processed", activityType);
3588
3347
  } catch (error) {
3589
3348
  try {
3590
3349
  await this.inboxErrorHandler?.(context, error);
@@ -3635,13 +3394,17 @@ var FederationImpl = class extends FederationBuilderImpl {
3635
3394
  queue: inboxQueue,
3636
3395
  activityType
3637
3396
  }, retryMessage.attempt);
3397
+ recordInboxActivity(this.meterProvider, "retried", activityType);
3638
3398
  }
3639
- } else logger.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
3640
- error,
3641
- activityId: activity.id?.href,
3642
- activity: message.activity,
3643
- recipient: message.identifier
3644
- });
3399
+ } else {
3400
+ logger.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
3401
+ error,
3402
+ activityId: activity.id?.href,
3403
+ activity: message.activity,
3404
+ recipient: message.identifier
3405
+ });
3406
+ recordInboxActivity(this.meterProvider, "abandoned", activityType);
3407
+ }
3645
3408
  span.setStatus({
3646
3409
  code: SpanStatusCode.ERROR,
3647
3410
  message: String(error)
@@ -4668,6 +4431,7 @@ var ContextImpl = class ContextImpl {
4668
4431
  queue: this.federation.fanoutQueue,
4669
4432
  activityType: message.activityType
4670
4433
  }, 0);
4434
+ recordFanoutRecipients(this.federation.meterProvider, globalThis.Object.keys(message.inboxes).length, message.activityType);
4671
4435
  return true;
4672
4436
  }
4673
4437
  async *getFollowers(identifier) {
@@ -4731,6 +4495,7 @@ var ContextImpl = class ContextImpl {
4731
4495
  if (await verifyObject(Activity, json, {
4732
4496
  contextLoader,
4733
4497
  documentLoader: options.documentLoader ?? this.documentLoader,
4498
+ meterProvider: this.meterProvider,
4734
4499
  tracerProvider: options.tracerProvider ?? this.tracerProvider,
4735
4500
  keyCache
4736
4501
  }) == null) {
@@ -4873,6 +4638,7 @@ var RequestContextImpl = class RequestContextImpl extends ContextImpl {
4873
4638
  contextLoader: options.contextLoader ?? this.contextLoader,
4874
4639
  documentLoader: options.documentLoader ?? this.documentLoader,
4875
4640
  timeWindow: this.federation.signatureTimeWindow,
4641
+ meterProvider: this.meterProvider,
4876
4642
  tracerProvider: options.tracerProvider ?? this.tracerProvider
4877
4643
  });
4878
4644
  }