@fedify/fedify 2.3.0-dev.1079 → 2.3.0-dev.1099

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 (86) hide show
  1. package/dist/{builder-DqT-OPZ8.mjs → builder-BkRRjxzb.mjs} +2 -2
  2. package/dist/{chunk-nlSIicah.js → chunk-CRNNMoPX.js} +2 -2
  3. package/dist/{client-z-8dc-e1.d.cts → client-CAM_bQXx.d.cts} +1 -0
  4. package/dist/{client-AtlibPOU.d.ts → client-CSddvgWN.d.ts} +1 -2
  5. package/dist/compat/mod.d.cts +2 -1
  6. package/dist/compat/mod.d.ts +2 -3
  7. package/dist/compat/mod.js +3 -3
  8. package/dist/compat/transformers.test.mjs +1 -1
  9. package/dist/{context-DrNqYkPw.d.ts → context-C0C_sRha.d.cts} +6 -7
  10. package/dist/{context-BKLGj9QO.d.cts → context-Dqgt8saU.d.ts} +6 -5
  11. package/dist/{deno-BxqkYxIf.mjs → deno-DBabeupC.mjs} +1 -1
  12. package/dist/{docloader-vsZP01ww.mjs → docloader-DA5FzJOR.mjs} +2 -2
  13. package/dist/federation/builder.test.mjs +1 -1
  14. package/dist/federation/handler.test.mjs +84 -2
  15. package/dist/federation/idempotency.test.mjs +2 -2
  16. package/dist/federation/middleware.test.mjs +383 -16
  17. package/dist/federation/mod.cjs +1 -1
  18. package/dist/federation/mod.d.cts +4 -3
  19. package/dist/federation/mod.d.ts +4 -5
  20. package/dist/federation/mod.js +2 -2
  21. package/dist/federation/send.test.mjs +3 -3
  22. package/dist/federation/webfinger.test.mjs +1 -1
  23. package/dist/{http-DM4ZAFCe.mjs → http-5G18W3NP.mjs} +58 -10
  24. package/dist/{http-CrGuipxe.d.cts → http-BDZeS5om.d.ts} +2 -1
  25. package/dist/{http-aQzN9Ayi.d.ts → http-C87EWkO0.d.cts} +2 -3
  26. package/dist/{http-b__OS_rJ.js → http-Dzy5c472.js} +58 -10
  27. package/dist/{http-DQ25_ruv.cjs → http-W2u_KBoQ.cjs} +57 -9
  28. package/dist/{key-BNp_b9oE.mjs → key-D9dUsyow.mjs} +1 -1
  29. package/dist/{kv-CbLNp3zQ.d.cts → kv-D6hNiMTK.d.ts} +1 -0
  30. package/dist/{kv-cache-DzctboPD.cjs → kv-cache-BygrlQ1c.cjs} +2 -2
  31. package/dist/{kv-cache-D5vjOi5y.js → kv-cache-CBSgxEsZ.js} +2 -2
  32. package/dist/{kv-cache-EaVpV2xQ.mjs → kv-cache-CiiNwT6W.mjs} +1 -1
  33. package/dist/{kv-GFYnFoOl.d.ts → kv-gJ8LYbxX.d.cts} +1 -3
  34. package/dist/{ld-L9w529xq.mjs → ld-hbxDLO1k.mjs} +2 -2
  35. package/dist/{middleware-CTdNwf_s.mjs → middleware-BXnhAGF9.mjs} +188 -114
  36. package/dist/{middleware-CMF242Rg.cjs → middleware-Caj827xW.cjs} +305 -107
  37. package/dist/{middleware-CpCSD43m.mjs → middleware-DZQsPMZb.mjs} +1 -1
  38. package/dist/{middleware-DZNHpEbh.js → middleware-vCF_cKAq.js} +309 -111
  39. package/dist/{mod-CLgIXe9w.d.ts → mod-B0rWmfW5.d.cts} +4 -5
  40. package/dist/{mod-CMEbIaNh.d.cts → mod-BhU_H1I_.d.ts} +4 -3
  41. package/dist/{mod-B8Z8mBLk.d.ts → mod-CLPnQPsv.d.cts} +2 -3
  42. package/dist/{mod-Cr3f-ACa.d.cts → mod-DHO9lk3D.d.ts} +3 -2
  43. package/dist/{mod-CR8soWa9.d.ts → mod-DXY9JF28.d.cts} +3 -4
  44. package/dist/{mod-DClCOv0M.d.cts → mod-Dx3-hqyo.d.ts} +2 -1
  45. package/dist/mod.cjs +4 -4
  46. package/dist/mod.d.cts +9 -8
  47. package/dist/mod.d.ts +9 -10
  48. package/dist/mod.js +9 -9
  49. package/dist/nodeinfo/handler.test.mjs +1 -1
  50. package/dist/nodeinfo/mod.d.cts +2 -1
  51. package/dist/nodeinfo/mod.d.ts +2 -3
  52. package/dist/nodeinfo/mod.js +3 -3
  53. package/dist/otel/mod.d.cts +2 -1
  54. package/dist/otel/mod.d.ts +2 -3
  55. package/dist/otel/mod.js +2 -2
  56. package/dist/{owner-CptqhsOy.d.cts → owner-CnngXDNJ.d.ts} +2 -1
  57. package/dist/{owner-74ARJ5TL.d.ts → owner-DEvZuyOE.d.cts} +2 -3
  58. package/dist/{owner-C30LGgMz.mjs → owner-DwJe0BH9.mjs} +2 -2
  59. package/dist/{proof-zrLeLWgt.cjs → proof-CZCaAURh.cjs} +1 -1
  60. package/dist/{proof-CO1qAbN8.js → proof-DMJJZnKd.js} +2 -2
  61. package/dist/{proof-BjToRsXF.mjs → proof-erpV_J_n.mjs} +2 -2
  62. package/dist/runtime/mod.d.cts +1 -0
  63. package/dist/runtime/mod.d.ts +1 -2
  64. package/dist/runtime/mod.js +3 -3
  65. package/dist/{send-DzbMznU6.mjs → send-BOwz4Hw5.mjs} +127 -3
  66. package/dist/sig/http.test.mjs +120 -2
  67. package/dist/sig/key.test.mjs +1 -1
  68. package/dist/sig/ld.test.mjs +2 -2
  69. package/dist/sig/mod.cjs +2 -2
  70. package/dist/sig/mod.d.cts +4 -3
  71. package/dist/sig/mod.d.ts +4 -5
  72. package/dist/sig/mod.js +4 -4
  73. package/dist/sig/owner.test.mjs +1 -1
  74. package/dist/sig/proof.test.mjs +1 -1
  75. package/dist/{transformers-ve6e2xcg.js → transformers-BGMIq1cs.js} +2 -2
  76. package/dist/{types-hvL8ElAs.js → types-CAY3OdLq.js} +2 -2
  77. package/dist/utils/docloader.test.mjs +2 -2
  78. package/dist/utils/kv-cache.test.mjs +1 -1
  79. package/dist/utils/mod.cjs +1 -1
  80. package/dist/utils/mod.d.cts +2 -1
  81. package/dist/utils/mod.d.ts +2 -3
  82. package/dist/utils/mod.js +3 -3
  83. package/dist/vocab/mod.d.cts +1 -0
  84. package/dist/vocab/mod.d.ts +1 -2
  85. package/dist/vocab/mod.js +2 -2
  86. package/package.json +6 -6
@@ -1,11 +1,11 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
- import "urlpattern-polyfill";
3
- import { t as __exportAll } from "./chunk-nlSIicah.js";
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-b__OS_rJ.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-CO1qAbN8.js";
7
- import { n as getNodeInfo, t as nodeInfoToJson } from "./types-hvL8ElAs.js";
8
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-D5vjOi5y.js";
2
+ import { URLPattern } from "urlpattern-polyfill";
3
+ import { t as __exportAll } from "./chunk-CRNNMoPX.js";
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-Dzy5c472.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-DMJJZnKd.js";
7
+ import { n as getNodeInfo, t as nodeInfoToJson } from "./types-CAY3OdLq.js";
8
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-CBSgxEsZ.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";
@@ -788,6 +788,12 @@ var FederationMetrics = class {
788
788
  inboxProcessingDuration;
789
789
  httpServerRequestCount;
790
790
  httpServerRequestDuration;
791
+ queueTaskEnqueued;
792
+ queueTaskStarted;
793
+ queueTaskCompleted;
794
+ queueTaskFailed;
795
+ queueTaskDuration;
796
+ queueTaskInFlight;
791
797
  constructor(meterProvider) {
792
798
  const meter = meterProvider.getMeter(name, version);
793
799
  this.deliverySent = meter.createCounter("activitypub.delivery.sent", {
@@ -834,6 +840,46 @@ var FederationMetrics = class {
834
840
  1e4
835
841
  ] }
836
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
+ });
837
883
  }
838
884
  recordDelivery(inbox, durationMs, success, activityType) {
839
885
  const deliveryAttributes = {
@@ -868,7 +914,85 @@ var FederationMetrics = class {
868
914
  this.httpServerRequestCount.add(1, attributes);
869
915
  this.httpServerRequestDuration.record(durationMs, attributes);
870
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
+ }
871
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
+ }
872
996
  const KNOWN_HTTP_METHODS = new Set([
873
997
  "CONNECT",
874
998
  "DELETE",
@@ -990,6 +1114,11 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
990
1114
  });
991
1115
  throw error;
992
1116
  }
1117
+ getFederationMetrics(meterProvider).recordQueueTaskEnqueued({
1118
+ role: "inbox",
1119
+ queue,
1120
+ activityType: getTypeId(activity).href
1121
+ }, 0);
993
1122
  logger.info("Activity {activityId} is enqueued.", {
994
1123
  activityId: activity.id?.href,
995
1124
  activity: json,
@@ -3104,78 +3233,123 @@ var FederationImpl = class extends FederationBuilderImpl {
3104
3233
  processQueuedTask(contextData, message) {
3105
3234
  const tracer = this._getTracer();
3106
3235
  const extractedContext = propagation.extract(context.active(), message.traceContext);
3236
+ const meter = getFederationMetrics(this.meterProvider);
3107
3237
  return withContext({ messageId: message.id }, async () => {
3108
- if (message.type === "fanout") await tracer.startActiveSpan("activitypub.fanout", {
3109
- kind: SpanKind.CONSUMER,
3110
- attributes: { "activitypub.activity.type": message.activityType }
3111
- }, extractedContext, async (span) => {
3112
- const spanCtx = span.spanContext();
3113
- return await withContext({
3114
- traceId: spanCtx.traceId,
3115
- spanId: spanCtx.spanId
3116
- }, async () => {
3117
- if (message.activityId != null) span.setAttribute("activitypub.activity.id", message.activityId);
3118
- try {
3119
- await this.#listenFanoutMessage(contextData, message);
3120
- } catch (e) {
3121
- span.setStatus({
3122
- code: SpanStatusCode.ERROR,
3123
- message: String(e)
3124
- });
3125
- throw e;
3126
- } finally {
3127
- span.end();
3128
- }
3238
+ if (message.type === "fanout") {
3239
+ const common = {
3240
+ role: "fanout",
3241
+ queue: this.fanoutQueue,
3242
+ activityType: message.activityType
3243
+ };
3244
+ await tracer.startActiveSpan("activitypub.fanout", {
3245
+ kind: SpanKind.CONSUMER,
3246
+ attributes: { "activitypub.activity.type": message.activityType }
3247
+ }, extractedContext, async (span) => {
3248
+ const spanCtx = span.spanContext();
3249
+ return await withContext({
3250
+ traceId: spanCtx.traceId,
3251
+ spanId: spanCtx.spanId
3252
+ }, async () => {
3253
+ if (message.activityId != null) span.setAttribute("activitypub.activity.id", message.activityId);
3254
+ meter.recordQueueTaskStarted(common);
3255
+ meter.incrementQueueTaskInFlight(common);
3256
+ const startedAt = performance.now();
3257
+ let outcome = "completed";
3258
+ try {
3259
+ await this.#listenFanoutMessage(contextData, message);
3260
+ } catch (e) {
3261
+ const aborted = isAbortError(e);
3262
+ outcome = aborted ? "aborted" : "failed";
3263
+ if (!aborted) span.setStatus({
3264
+ code: SpanStatusCode.ERROR,
3265
+ message: String(e)
3266
+ });
3267
+ throw e;
3268
+ } finally {
3269
+ meter.recordQueueTaskOutcome(common, outcome, getDurationMs(startedAt));
3270
+ meter.decrementQueueTaskInFlight(common);
3271
+ span.end();
3272
+ }
3273
+ });
3129
3274
  });
3130
- });
3131
- else if (message.type === "outbox") await tracer.startActiveSpan("activitypub.outbox", {
3132
- kind: SpanKind.CONSUMER,
3133
- attributes: {
3134
- "activitypub.activity.type": message.activityType,
3135
- "activitypub.activity.retries": message.attempt
3136
- }
3137
- }, extractedContext, async (span) => {
3138
- const spanCtx = span.spanContext();
3139
- return await withContext({
3140
- traceId: spanCtx.traceId,
3141
- spanId: spanCtx.spanId
3142
- }, async () => {
3143
- if (message.activityId != null) span.setAttribute("activitypub.activity.id", message.activityId);
3144
- try {
3145
- await this.#listenOutboxMessage(contextData, message, span);
3146
- } catch (e) {
3147
- span.setStatus({
3148
- code: SpanStatusCode.ERROR,
3149
- message: String(e)
3150
- });
3151
- throw e;
3152
- } finally {
3153
- span.end();
3275
+ } else if (message.type === "outbox") {
3276
+ const common = {
3277
+ role: "outbox",
3278
+ queue: this.outboxQueue,
3279
+ activityType: message.activityType
3280
+ };
3281
+ await tracer.startActiveSpan("activitypub.outbox", {
3282
+ kind: SpanKind.CONSUMER,
3283
+ attributes: {
3284
+ "activitypub.activity.type": message.activityType,
3285
+ "activitypub.activity.retries": message.attempt
3154
3286
  }
3287
+ }, extractedContext, async (span) => {
3288
+ const spanCtx = span.spanContext();
3289
+ return await withContext({
3290
+ traceId: spanCtx.traceId,
3291
+ spanId: spanCtx.spanId
3292
+ }, async () => {
3293
+ if (message.activityId != null) span.setAttribute("activitypub.activity.id", message.activityId);
3294
+ meter.recordQueueTaskStarted(common);
3295
+ meter.incrementQueueTaskInFlight(common);
3296
+ const startedAt = performance.now();
3297
+ let outcome = "completed";
3298
+ try {
3299
+ await this.#listenOutboxMessage(contextData, message, span);
3300
+ } catch (e) {
3301
+ const aborted = isAbortError(e);
3302
+ outcome = aborted ? "aborted" : "failed";
3303
+ if (!aborted) span.setStatus({
3304
+ code: SpanStatusCode.ERROR,
3305
+ message: String(e)
3306
+ });
3307
+ throw e;
3308
+ } finally {
3309
+ meter.recordQueueTaskOutcome(common, outcome, getDurationMs(startedAt));
3310
+ meter.decrementQueueTaskInFlight(common);
3311
+ span.end();
3312
+ }
3313
+ });
3155
3314
  });
3156
- });
3157
- else if (message.type === "inbox") await tracer.startActiveSpan("activitypub.inbox", {
3158
- kind: SpanKind.CONSUMER,
3159
- attributes: { "activitypub.shared_inbox": message.identifier == null }
3160
- }, extractedContext, async (span) => {
3161
- const spanCtx = span.spanContext();
3162
- return await withContext({
3163
- traceId: spanCtx.traceId,
3164
- spanId: spanCtx.spanId
3165
- }, async () => {
3166
- try {
3167
- await this.#listenInboxMessage(contextData, message, span);
3168
- } catch (e) {
3169
- span.setStatus({
3170
- code: SpanStatusCode.ERROR,
3171
- message: String(e)
3172
- });
3173
- throw e;
3174
- } finally {
3175
- span.end();
3176
- }
3315
+ } else if (message.type === "inbox") {
3316
+ const common = {
3317
+ role: "inbox",
3318
+ queue: this.inboxQueue
3319
+ };
3320
+ await tracer.startActiveSpan("activitypub.inbox", {
3321
+ kind: SpanKind.CONSUMER,
3322
+ attributes: { "activitypub.shared_inbox": message.identifier == null }
3323
+ }, extractedContext, async (span) => {
3324
+ const spanCtx = span.spanContext();
3325
+ return await withContext({
3326
+ traceId: spanCtx.traceId,
3327
+ spanId: spanCtx.spanId
3328
+ }, async () => {
3329
+ meter.recordQueueTaskStarted(common);
3330
+ meter.incrementQueueTaskInFlight(common);
3331
+ const startedAt = performance.now();
3332
+ let outcome = "completed";
3333
+ try {
3334
+ await this.#listenInboxMessage(contextData, message, span, (activityType) => {
3335
+ common.activityType = activityType;
3336
+ });
3337
+ } catch (e) {
3338
+ const aborted = isAbortError(e);
3339
+ outcome = aborted ? "aborted" : "failed";
3340
+ if (!aborted) span.setStatus({
3341
+ code: SpanStatusCode.ERROR,
3342
+ message: String(e)
3343
+ });
3344
+ throw e;
3345
+ } finally {
3346
+ meter.recordQueueTaskOutcome(common, outcome, getDurationMs(startedAt));
3347
+ meter.decrementQueueTaskInFlight(common);
3348
+ span.end();
3349
+ }
3350
+ });
3177
3351
  });
3178
- });
3352
+ }
3179
3353
  });
3180
3354
  }
3181
3355
  async #listenFanoutMessage(data, message) {
@@ -3332,10 +3506,19 @@ var FederationImpl = class extends FederationBuilderImpl {
3332
3506
  ...logData,
3333
3507
  error
3334
3508
  });
3335
- await this.outboxQueue?.enqueue({
3509
+ const retryMessage = {
3336
3510
  ...message,
3337
3511
  attempt: message.attempt + 1
3338
- }, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
3512
+ };
3513
+ const { outboxQueue } = this;
3514
+ if (outboxQueue != null) {
3515
+ await outboxQueue.enqueue(retryMessage, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
3516
+ getFederationMetrics(this.meterProvider).recordQueueTaskEnqueued({
3517
+ role: "outbox",
3518
+ queue: outboxQueue,
3519
+ activityType: retryMessage.activityType
3520
+ }, retryMessage.attempt);
3521
+ }
3339
3522
  } else logger.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
3340
3523
  ...logData,
3341
3524
  error
@@ -3344,7 +3527,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3344
3527
  }
3345
3528
  logger.info("Successfully sent activity {activityId} to {inbox}.", { ...logData });
3346
3529
  }
3347
- async #listenInboxMessage(ctxData, message, span) {
3530
+ async #listenInboxMessage(ctxData, message, span, onActivityType) {
3348
3531
  const logger = getLogger([
3349
3532
  "fedify",
3350
3533
  "federation",
@@ -3358,7 +3541,9 @@ var FederationImpl = class extends FederationBuilderImpl {
3358
3541
  if (identity != null) context = this.#createContext(baseUrl, ctxData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
3359
3542
  }
3360
3543
  const activity = await Activity.fromJsonLd(message.activity, context);
3361
- span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
3544
+ const activityType = getTypeId(activity).href;
3545
+ span.setAttribute("activitypub.activity.type", activityType);
3546
+ onActivityType?.(activityType);
3362
3547
  if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
3363
3548
  const cacheKey = activity.id == null ? null : [
3364
3549
  ...this.kvPrefixes.activityIdempotence,
@@ -3386,7 +3571,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3386
3571
  });
3387
3572
  span.setStatus({
3388
3573
  code: SpanStatusCode.ERROR,
3389
- message: `Unsupported activity type: ${getTypeId(activity).href}`
3574
+ message: `Unsupported activity type: ${activityType}`
3390
3575
  });
3391
3576
  span.end();
3392
3577
  return;
@@ -3394,7 +3579,6 @@ var FederationImpl = class extends FederationBuilderImpl {
3394
3579
  const { class: cls, listener } = dispatched;
3395
3580
  span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
3396
3581
  try {
3397
- const activityType = getTypeId(activity).href;
3398
3582
  const started = performance.now();
3399
3583
  try {
3400
3584
  await listener(context.toInboxContext(message.identifier, message.activity, activity.id?.href, activityType), activity);
@@ -3439,10 +3623,19 @@ var FederationImpl = class extends FederationBuilderImpl {
3439
3623
  activity: message.activity,
3440
3624
  recipient: message.identifier
3441
3625
  });
3442
- await this.inboxQueue?.enqueue({
3626
+ const retryMessage = {
3443
3627
  ...message,
3444
3628
  attempt: message.attempt + 1
3445
- }, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
3629
+ };
3630
+ const { inboxQueue } = this;
3631
+ if (inboxQueue != null) {
3632
+ await inboxQueue.enqueue(retryMessage, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
3633
+ getFederationMetrics(this.meterProvider).recordQueueTaskEnqueued({
3634
+ role: "inbox",
3635
+ queue: inboxQueue,
3636
+ activityType
3637
+ }, retryMessage.attempt);
3638
+ }
3446
3639
  } else logger.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
3447
3640
  error,
3448
3641
  activityId: activity.id?.href,
@@ -3631,9 +3824,11 @@ var FederationImpl = class extends FederationBuilderImpl {
3631
3824
  });
3632
3825
  }
3633
3826
  const { outboxQueue } = this;
3634
- if (outboxQueue.enqueueMany == null) {
3635
- const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
3636
- const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
3827
+ if (outboxQueue.enqueueMany == null || orderingKey != null) {
3828
+ const errors = (await Promise.allSettled(messages.map(async (m) => {
3829
+ await outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey });
3830
+ recordOutboxEnqueue(this.meterProvider, outboxQueue, m.message);
3831
+ }))).filter((r) => r.status === "rejected").map((r) => r.reason);
3637
3832
  if (errors.length > 0) {
3638
3833
  logger.error("Failed to enqueue activity {activityId} to send later: {errors}", {
3639
3834
  activityId: activity.id.href,
@@ -3642,25 +3837,17 @@ var FederationImpl = class extends FederationBuilderImpl {
3642
3837
  if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${activityId} to send later.`);
3643
3838
  throw errors[0];
3644
3839
  }
3645
- } else if (orderingKey != null) {
3646
- const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
3647
- const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
3648
- if (errors.length > 0) {
3649
- logger.error("Failed to enqueue activity {activityId} to send later: {errors}", {
3840
+ } else {
3841
+ try {
3842
+ await outboxQueue.enqueueMany(messages.map((m) => m.message));
3843
+ } catch (error) {
3844
+ logger.error("Failed to enqueue activity {activityId} to send later: {error}", {
3650
3845
  activityId: activity.id.href,
3651
- errors
3846
+ error
3652
3847
  });
3653
- if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${activityId} to send later.`);
3654
- throw errors[0];
3848
+ throw error;
3655
3849
  }
3656
- } else try {
3657
- await outboxQueue.enqueueMany(messages.map((m) => m.message));
3658
- } catch (error) {
3659
- logger.error("Failed to enqueue activity {activityId} to send later: {error}", {
3660
- activityId: activity.id.href,
3661
- error
3662
- });
3663
- throw error;
3850
+ for (const m of messages) recordOutboxEnqueue(this.meterProvider, outboxQueue, m.message);
3664
3851
  }
3665
3852
  }
3666
3853
  fetch(request, options) {
@@ -4476,6 +4663,11 @@ var ContextImpl = class ContextImpl {
4476
4663
  };
4477
4664
  if (!this.federation.manuallyStartQueue) this.federation._startQueueInternal(this.data);
4478
4665
  await this.federation.fanoutQueue.enqueue(message, { orderingKey: options.orderingKey });
4666
+ getFederationMetrics(this.federation.meterProvider).recordQueueTaskEnqueued({
4667
+ role: "fanout",
4668
+ queue: this.federation.fanoutQueue,
4669
+ activityType: message.activityType
4670
+ }, 0);
4479
4671
  return true;
4480
4672
  }
4481
4673
  async *getFollowers(identifier) {
@@ -4606,6 +4798,7 @@ var ContextImpl = class ContextImpl {
4606
4798
  kvPrefixes: this.federation.kvPrefixes,
4607
4799
  queue: this.federation.inboxQueue,
4608
4800
  span,
4801
+ meterProvider: this.federation.meterProvider,
4609
4802
  tracerProvider: options.tracerProvider ?? this.tracerProvider,
4610
4803
  idempotencyStrategy: this.federation.idempotencyStrategy
4611
4804
  });
@@ -4855,8 +5048,10 @@ async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipient
4855
5048
  }
4856
5049
  const { outboxQueue } = ctx.federation;
4857
5050
  if (outboxQueue.enqueueMany == null || orderingKey != null) {
4858
- const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
4859
- const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
5051
+ const errors = (await Promise.allSettled(messages.map(async (m) => {
5052
+ await outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey });
5053
+ recordOutboxEnqueue(ctx.federation.meterProvider, outboxQueue, m.message);
5054
+ }))).filter((r) => r.status === "rejected").map((r) => r.reason);
4860
5055
  if (errors.length > 0) {
4861
5056
  logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
4862
5057
  activityId: ctx.activityId,
@@ -4865,14 +5060,17 @@ async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipient
4865
5060
  if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${ctx.activityId} to forward later.`);
4866
5061
  throw errors[0];
4867
5062
  }
4868
- } else try {
4869
- await outboxQueue.enqueueMany(messages.map((m) => m.message));
4870
- } catch (error) {
4871
- logger.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
4872
- activityId: ctx.activityId,
4873
- error
4874
- });
4875
- throw error;
5063
+ } else {
5064
+ try {
5065
+ await outboxQueue.enqueueMany(messages.map((m) => m.message));
5066
+ } catch (error) {
5067
+ logger.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
5068
+ activityId: ctx.activityId,
5069
+ error
5070
+ });
5071
+ throw error;
5072
+ }
5073
+ for (const m of messages) recordOutboxEnqueue(ctx.federation.meterProvider, outboxQueue, m.message);
4876
5074
  }
4877
5075
  return true;
4878
5076
  }
@@ -1,9 +1,8 @@
1
- import { Temporal } from "@js-temporal/polyfill";
2
- import { URLPattern } from "urlpattern-polyfill";
3
- import { n as HttpMessageSignaturesSpecDeterminer } from "./http-aQzN9Ayi.js";
4
- import { n as KvStore, t as KvKey } from "./kv-GFYnFoOl.js";
5
- import { TracerProvider } from "@opentelemetry/api";
1
+ /// <reference lib="esnext.temporal" />
2
+ import { n as HttpMessageSignaturesSpecDeterminer } from "./http-C87EWkO0.cjs";
3
+ import { n as KvStore, t as KvKey } from "./kv-gJ8LYbxX.cjs";
6
4
  import { DocumentLoader, DocumentLoaderFactoryOptions } from "@fedify/vocab-runtime";
5
+ import { TracerProvider } from "@opentelemetry/api";
7
6
 
8
7
  //#region src/utils/docloader.d.ts
9
8
  /**
@@ -1,7 +1,8 @@
1
- import { n as HttpMessageSignaturesSpecDeterminer } from "./http-CrGuipxe.cjs";
2
- import { n as KvStore, t as KvKey } from "./kv-CbLNp3zQ.cjs";
3
- import { DocumentLoader, DocumentLoaderFactoryOptions } from "@fedify/vocab-runtime";
1
+ /// <reference lib="esnext.temporal" />
2
+ import { n as HttpMessageSignaturesSpecDeterminer } from "./http-BDZeS5om.js";
3
+ import { n as KvStore, t as KvKey } from "./kv-D6hNiMTK.js";
4
4
  import { TracerProvider } from "@opentelemetry/api";
5
+ import { DocumentLoader, DocumentLoaderFactoryOptions } from "@fedify/vocab-runtime";
5
6
 
6
7
  //#region src/utils/docloader.d.ts
7
8
  /**
@@ -1,6 +1,5 @@
1
- import { Temporal } from "@js-temporal/polyfill";
2
- import { URLPattern } from "urlpattern-polyfill";
3
- import { Ct as WebFingerLinksDispatcher, et as ActorAliasMapper, l as RequestContext, nt as ActorHandleMapper, tt as ActorDispatcher } from "./context-DrNqYkPw.js";
1
+ /// <reference lib="esnext.temporal" />
2
+ import { Ct as WebFingerLinksDispatcher, et as ActorAliasMapper, l as RequestContext, nt as ActorHandleMapper, tt as ActorDispatcher } from "./context-C0C_sRha.cjs";
4
3
  import { Span, Tracer } from "@opentelemetry/api";
5
4
 
6
5
  //#region src/federation/webfinger.d.ts
@@ -1,7 +1,8 @@
1
- import { S as KeyCache } from "./http-CrGuipxe.cjs";
1
+ /// <reference lib="esnext.temporal" />
2
+ import { S as KeyCache } from "./http-BDZeS5om.js";
2
3
  import { CryptographicKey, DataIntegrityProof, Multikey, Object as Object$1 } from "@fedify/vocab";
3
- import { DocumentLoader } from "@fedify/vocab-runtime";
4
4
  import { TracerProvider } from "@opentelemetry/api";
5
+ import { DocumentLoader } from "@fedify/vocab-runtime";
5
6
 
6
7
  //#region src/sig/ld.d.ts
7
8
  /**
@@ -1,9 +1,8 @@
1
- import { Temporal } from "@js-temporal/polyfill";
2
- import { URLPattern } from "urlpattern-polyfill";
3
- import { S as KeyCache } from "./http-aQzN9Ayi.js";
1
+ /// <reference lib="esnext.temporal" />
2
+ import { S as KeyCache } from "./http-C87EWkO0.cjs";
4
3
  import { CryptographicKey, DataIntegrityProof, Multikey, Object as Object$1 } from "@fedify/vocab";
5
- import { TracerProvider } from "@opentelemetry/api";
6
4
  import { DocumentLoader } from "@fedify/vocab-runtime";
5
+ import { TracerProvider } from "@opentelemetry/api";
7
6
 
8
7
  //#region src/sig/ld.d.ts
9
8
  /**
@@ -1,4 +1,5 @@
1
- import { Ct as WebFingerLinksDispatcher, et as ActorAliasMapper, l as RequestContext, nt as ActorHandleMapper, tt as ActorDispatcher } from "./context-BKLGj9QO.cjs";
1
+ /// <reference lib="esnext.temporal" />
2
+ import { Ct as WebFingerLinksDispatcher, et as ActorAliasMapper, l as RequestContext, nt as ActorHandleMapper, tt as ActorDispatcher } from "./context-Dqgt8saU.js";
2
3
  import { Span, Tracer } from "@opentelemetry/api";
3
4
 
4
5
  //#region src/federation/webfinger.d.ts
package/dist/mod.cjs CHANGED
@@ -4,11 +4,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  require("./chunk-DDcVe30Y.cjs");
5
5
  const require_transformers = require("./transformers-NeAONrAq.cjs");
6
6
  require("./compat/mod.cjs");
7
- const require_http = require("./http-DQ25_ruv.cjs");
8
- const require_middleware = require("./middleware-CMF242Rg.cjs");
9
- const require_proof = require("./proof-zrLeLWgt.cjs");
7
+ const require_http = require("./http-W2u_KBoQ.cjs");
8
+ const require_middleware = require("./middleware-Caj827xW.cjs");
9
+ const require_proof = require("./proof-CZCaAURh.cjs");
10
10
  const require_types = require("./types-KC4QAoxe.cjs");
11
- const require_kv_cache = require("./kv-cache-DzctboPD.cjs");
11
+ const require_kv_cache = require("./kv-cache-BygrlQ1c.cjs");
12
12
  const require_federation_mod = require("./federation/mod.cjs");
13
13
  require("./nodeinfo/mod.cjs");
14
14
  require("./runtime/mod.cjs");