@fedify/fedify 2.3.0-dev.1114 → 2.3.0-dev.1131

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 (69) hide show
  1. package/dist/{builder-YlEusQth.mjs → builder-DckAhD27.mjs} +2 -2
  2. package/dist/compat/mod.d.cts +1 -1
  3. package/dist/compat/mod.d.ts +1 -1
  4. package/dist/compat/transformers.test.mjs +1 -1
  5. package/dist/{context-cSUMk2da.d.ts → context-Cq18Gplu.d.ts} +3 -208
  6. package/dist/{context-Ch-ZLyTQ.d.cts → context-tc6VOOOL.d.cts} +3 -208
  7. package/dist/{deno-CF3jMgip.mjs → deno--CS-SBS9.mjs} +1 -1
  8. package/dist/{docloader-BENj6vQ4.mjs → docloader-k6huZLQL.mjs} +2 -2
  9. package/dist/federation/builder.test.mjs +1 -1
  10. package/dist/federation/handler.test.mjs +2 -2
  11. package/dist/federation/idempotency.test.mjs +2 -2
  12. package/dist/federation/metrics.test.d.mts +2 -0
  13. package/dist/federation/metrics.test.mjs +335 -0
  14. package/dist/federation/middleware.test.mjs +444 -6
  15. package/dist/federation/mod.cjs +1 -1
  16. package/dist/federation/mod.d.cts +3 -2
  17. package/dist/federation/mod.d.ts +3 -2
  18. package/dist/federation/mod.js +1 -1
  19. package/dist/federation/send.test.mjs +3 -3
  20. package/dist/federation/webfinger.test.mjs +1 -1
  21. package/dist/{http-CKCgOPkX.cjs → http-CJfvRL7D.cjs} +352 -22
  22. package/dist/{http-BmOZYc-8.mjs → http-IywnQdiX.mjs} +7 -5
  23. package/dist/{http-D6LP89UO.d.ts → http-VyDTd4G3.d.cts} +8 -1
  24. package/dist/{http-CpzZ9zsb.js → http-cqujdCRz.js} +323 -23
  25. package/dist/{http-D6aw3j2U.d.cts → http-lf8Hsd91.d.ts} +8 -1
  26. package/dist/{key-B4I8H5Lc.mjs → key-Df3tMleh.mjs} +42 -17
  27. package/dist/{kv-cache-DY-XWOqM.cjs → kv-cache-L0SMQkcd.cjs} +19 -2
  28. package/dist/{kv-cache-Wc5ezcVW.js → kv-cache-pEejzYq4.js} +19 -2
  29. package/dist/{kv-cache-DihufyAQ.mjs → kv-cache-q9Ec2ryS.mjs} +19 -1
  30. package/dist/{ld-B5D5THhl.mjs → ld-BGwiJpl3.mjs} +3 -3
  31. package/dist/{metrics-ek3ilf6c.mjs → metrics-BTOMkW8C.mjs} +280 -5
  32. package/dist/{middleware-EqTYPG4F.cjs → middleware-B2rtdpFV.cjs} +75 -28
  33. package/dist/{middleware-DlcecZMq.mjs → middleware-BB0IbDow.mjs} +84 -37
  34. package/dist/{middleware-EI7OU6BR.mjs → middleware-Dnql59Y8.mjs} +1 -1
  35. package/dist/{middleware-CuZbBw-N.js → middleware-DtOddSVg.js} +75 -28
  36. package/dist/{mod-BDhgfjP7.d.cts → mod-B0hW12_O.d.cts} +1 -1
  37. package/dist/{mod-B-Lin9Sy.d.ts → mod-COIAjwRS.d.ts} +1 -1
  38. package/dist/{mod-C6E8rkcz.d.ts → mod-CajNYYkt.d.ts} +1 -1
  39. package/dist/{mod-DLrRb0dx.d.ts → mod-DFvNJcNb.d.ts} +54 -3
  40. package/dist/{mod-P9tE2WmM.d.cts → mod-DnzgcPcy.d.cts} +1 -1
  41. package/dist/{mod-BR_BB0bh.d.cts → mod-yvIXFAEi.d.cts} +54 -3
  42. package/dist/mod.cjs +4 -4
  43. package/dist/mod.d.cts +6 -5
  44. package/dist/mod.d.ts +6 -5
  45. package/dist/mod.js +4 -4
  46. package/dist/mq-D-nlpY04.d.ts +208 -0
  47. package/dist/mq-D8uSFzxe.d.cts +208 -0
  48. package/dist/nodeinfo/handler.test.mjs +1 -1
  49. package/dist/{owner-DO810N24.mjs → owner-CIt4hvmM.mjs} +2 -2
  50. package/dist/{proof-DIoqrKnX.cjs → proof-B1_u25UV.cjs} +1 -1
  51. package/dist/{proof-BgfyWv7b.mjs → proof-BYlrRSmZ.mjs} +3 -3
  52. package/dist/{proof-Vd8-1EWh.js → proof-DMGIjHYH.js} +1 -1
  53. package/dist/{send-CAYXdUTk.mjs → send-DJFpze7B.mjs} +3 -3
  54. package/dist/sig/http.test.mjs +6 -2
  55. package/dist/sig/key.test.mjs +99 -2
  56. package/dist/sig/ld.test.mjs +2 -2
  57. package/dist/sig/mod.cjs +2 -2
  58. package/dist/sig/mod.d.cts +2 -2
  59. package/dist/sig/mod.d.ts +2 -2
  60. package/dist/sig/mod.js +2 -2
  61. package/dist/sig/owner.test.mjs +1 -1
  62. package/dist/sig/proof.test.mjs +1 -1
  63. package/dist/utils/docloader.test.mjs +2 -2
  64. package/dist/utils/kv-cache.test.mjs +67 -2
  65. package/dist/utils/mod.cjs +1 -1
  66. package/dist/utils/mod.d.cts +1 -1
  67. package/dist/utils/mod.d.ts +1 -1
  68. package/dist/utils/mod.js +1 -1
  69. package/package.json +6 -6
@@ -2,25 +2,25 @@ import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as RouterError } from "./router-BT_F5748.mjs";
5
- import { n as version, t as name } from "./deno-CF3jMgip.mjs";
6
- import { i as isAbortError, n as getFederationMetrics, o as recordOutboxEnqueue, r as getRemoteHost, t as getDurationMs } from "./metrics-ek3ilf6c.mjs";
5
+ import { n as version, t as name } from "./deno--CS-SBS9.mjs";
6
+ import { a as instrumentDocumentLoader, d as recordInboxActivity, i as getRemoteHost, m as recordOutboxEnqueue, n as getDurationMs, o as isAbortError, p as recordOutboxActivity, r as getFederationMetrics, u as recordFanoutRecipients } from "./metrics-BTOMkW8C.mjs";
7
7
  import { t as formatAcceptSignature } from "./accept-CgDcxvjV.mjs";
8
- import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-B4I8H5Lc.mjs";
9
- import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-BmOZYc-8.mjs";
10
- import { t as getAuthenticatedDocumentLoader } from "./docloader-BENj6vQ4.mjs";
11
- import { n as kvCache } from "./kv-cache-DihufyAQ.mjs";
12
- import { a as signJsonLd, i as hasSignatureLike, o as verifyJsonLd, r as detachSignature } from "./ld-B5D5THhl.mjs";
13
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-DO810N24.mjs";
8
+ import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-Df3tMleh.mjs";
9
+ import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-IywnQdiX.mjs";
10
+ import { t as getAuthenticatedDocumentLoader } from "./docloader-k6huZLQL.mjs";
11
+ import { n as kvCache } from "./kv-cache-q9Ec2ryS.mjs";
12
+ import { a as signJsonLd, i as hasSignatureLike, o as verifyJsonLd, r as detachSignature } from "./ld-BGwiJpl3.mjs";
13
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-CIt4hvmM.mjs";
14
14
  import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-BNL8AC14.mjs";
15
- import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-BgfyWv7b.mjs";
15
+ import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-BYlrRSmZ.mjs";
16
16
  import { t as getNodeInfo } from "./client-Bneh_DYR.mjs";
17
17
  import { t as nodeInfoToJson } from "./types-D09GN0uZ.mjs";
18
- import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-YlEusQth.mjs";
18
+ import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-DckAhD27.mjs";
19
19
  import { t as buildCollectionSynchronizationHeader } from "./collection-Cc3DVAhE.mjs";
20
20
  import { t as KvKeyCache } from "./keycache-BeU0LCII.mjs";
21
21
  import { t as acceptsJsonLd } from "./negotiation-DDstyBvc.mjs";
22
22
  import { t as createExponentialBackoffPolicy } from "./retry-_VvV0h9f.mjs";
23
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-CAYXdUTk.mjs";
23
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-DJFpze7B.mjs";
24
24
  import { getLogger, withContext } from "@logtape/logtape";
25
25
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
26
26
  import { lookupWebFinger } from "@fedify/webfinger";
@@ -192,6 +192,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
192
192
  code: SpanStatusCode.UNSET,
193
193
  message: `Activity ${activity.id?.href} has already been processed.`
194
194
  });
195
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
195
196
  return "alreadyProcessed";
196
197
  }
197
198
  }
@@ -201,6 +202,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
201
202
  code: SpanStatusCode.ERROR,
202
203
  message: "Missing actor."
203
204
  });
205
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
204
206
  return "missingActor";
205
207
  }
206
208
  span.setAttribute("activitypub.actor.id", activity.actorId.href);
@@ -236,6 +238,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
236
238
  queue,
237
239
  activityType: getTypeId(activity).href
238
240
  }, 0);
241
+ recordInboxActivity(meterProvider, "queued", getTypeId(activity).href);
239
242
  logger.info("Activity {activityId} is enqueued.", {
240
243
  activityId: activity.id?.href,
241
244
  activity: json,
@@ -255,6 +258,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
255
258
  code: SpanStatusCode.UNSET,
256
259
  message: `Unsupported activity type: ${getTypeId(activity).href}`
257
260
  });
261
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
258
262
  span.end();
259
263
  return "unsupportedActivity";
260
264
  }
@@ -264,7 +268,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
264
268
  const activityType = getTypeId(activity).href;
265
269
  const started = performance.now();
266
270
  try {
267
- await listener(inboxContextFactory(recipient, json, activity?.id?.href, activityType), activity);
271
+ await listener(inboxContextFactory(recipient, json, activity.id?.href, activityType), activity);
268
272
  } finally {
269
273
  getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
270
274
  }
@@ -289,9 +293,11 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
289
293
  code: SpanStatusCode.ERROR,
290
294
  message: String(error)
291
295
  });
296
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
292
297
  span.end();
293
298
  return "error";
294
299
  }
300
+ recordInboxActivity(meterProvider, "processed", getTypeId(activity).href);
295
301
  if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
296
302
  logger.info("Activity {activityId} has been processed.", {
297
303
  activityId: activity.id?.href,
@@ -1809,23 +1815,50 @@ var FederationImpl = class extends FederationBuilderImpl {
1809
1815
  }
1810
1816
  const { allowPrivateAddress, userAgent } = options;
1811
1817
  this.allowPrivateAddress = allowPrivateAddress ?? false;
1812
- this.documentLoaderFactory = options.documentLoaderFactory ?? ((opts) => {
1813
- return kvCache({
1814
- loader: getDocumentLoader({
1815
- allowPrivateAddress: opts?.allowPrivateAddress ?? allowPrivateAddress,
1816
- userAgent: opts?.userAgent ?? userAgent
1817
- }),
1818
- kv: options.kv,
1819
- prefix: this.kvPrefixes.remoteDocument
1820
- });
1821
- });
1822
- this.contextLoaderFactory = options.contextLoaderFactory ?? this.documentLoaderFactory;
1823
- this.authenticatedDocumentLoaderFactory = options.authenticatedDocumentLoaderFactory ?? ((identity) => getAuthenticatedDocumentLoader(identity, {
1824
- allowPrivateAddress,
1825
- userAgent,
1818
+ const userDocumentLoaderFactory = options.documentLoaderFactory;
1819
+ const userContextLoaderFactory = options.contextLoaderFactory;
1820
+ const userAuthFactory = options.authenticatedDocumentLoaderFactory;
1821
+ const builtinDocumentLoaderFactory = (opts) => kvCache({
1822
+ loader: getDocumentLoader({
1823
+ allowPrivateAddress: opts?.allowPrivateAddress ?? allowPrivateAddress,
1824
+ userAgent: opts?.userAgent ?? userAgent
1825
+ }),
1826
+ kv: options.kv,
1827
+ prefix: this.kvPrefixes.remoteDocument,
1828
+ meterProvider: this._meterProvider,
1829
+ kind: "object"
1830
+ });
1831
+ const builtinContextLoaderFactory = (opts) => kvCache({
1832
+ loader: getDocumentLoader({
1833
+ allowPrivateAddress: opts?.allowPrivateAddress ?? allowPrivateAddress,
1834
+ userAgent: opts?.userAgent ?? userAgent
1835
+ }),
1836
+ kv: options.kv,
1837
+ prefix: this.kvPrefixes.remoteDocument,
1838
+ meterProvider: this._meterProvider,
1839
+ kind: "context"
1840
+ });
1841
+ this.documentLoaderFactory = (opts) => instrumentDocumentLoader((userDocumentLoaderFactory ?? builtinDocumentLoaderFactory)(opts), {
1842
+ meterProvider: this._meterProvider,
1843
+ kind: "object",
1844
+ cacheEnabled: userDocumentLoaderFactory == null ? true : void 0
1845
+ });
1846
+ const resolvedContextLoaderFactory = userContextLoaderFactory ?? userDocumentLoaderFactory ?? builtinContextLoaderFactory;
1847
+ this.contextLoaderFactory = (opts) => instrumentDocumentLoader(resolvedContextLoaderFactory(opts), {
1848
+ meterProvider: this._meterProvider,
1849
+ kind: "context",
1850
+ cacheEnabled: userContextLoaderFactory == null && userDocumentLoaderFactory == null ? true : void 0
1851
+ });
1852
+ this.authenticatedDocumentLoaderFactory = (identity, factoryOpts) => instrumentDocumentLoader(userAuthFactory != null ? userAuthFactory(identity, factoryOpts) : getAuthenticatedDocumentLoader(identity, {
1853
+ allowPrivateAddress: factoryOpts?.allowPrivateAddress ?? allowPrivateAddress,
1854
+ userAgent: factoryOpts?.userAgent ?? userAgent,
1826
1855
  specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, options.firstKnock),
1827
1856
  tracerProvider: this.tracerProvider
1828
- }));
1857
+ }), {
1858
+ meterProvider: this._meterProvider,
1859
+ kind: "object",
1860
+ cacheEnabled: userAuthFactory == null ? false : void 0
1861
+ });
1829
1862
  this.userAgent = userAgent;
1830
1863
  this.onOutboxError = options.onOutboxError;
1831
1864
  this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
@@ -2135,6 +2168,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2135
2168
  });
2136
2169
  }
2137
2170
  }
2171
+ recordOutboxActivity(this.meterProvider, "abandoned", message.activityType);
2138
2172
  return;
2139
2173
  }
2140
2174
  if (this.outboxQueue?.nativeRetrial) {
@@ -2165,11 +2199,15 @@ var FederationImpl = class extends FederationBuilderImpl {
2165
2199
  queue: outboxQueue,
2166
2200
  activityType: retryMessage.activityType
2167
2201
  }, retryMessage.attempt);
2202
+ recordOutboxActivity(this.meterProvider, "retried", retryMessage.activityType);
2168
2203
  }
2169
- } else logger.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
2170
- ...logData,
2171
- error
2172
- });
2204
+ } else {
2205
+ logger.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
2206
+ ...logData,
2207
+ error
2208
+ });
2209
+ recordOutboxActivity(this.meterProvider, "abandoned", message.activityType);
2210
+ }
2173
2211
  return;
2174
2212
  }
2175
2213
  logger.info("Successfully sent activity {activityId} to {inbox}.", { ...logData });
@@ -2204,6 +2242,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2204
2242
  activity: message.activity,
2205
2243
  recipient: message.identifier
2206
2244
  });
2245
+ recordInboxActivity(this.meterProvider, "rejected", activityType);
2207
2246
  return;
2208
2247
  }
2209
2248
  }
@@ -2220,6 +2259,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2220
2259
  code: SpanStatusCode.ERROR,
2221
2260
  message: `Unsupported activity type: ${activityType}`
2222
2261
  });
2262
+ recordInboxActivity(this.meterProvider, "rejected", activityType);
2223
2263
  span.end();
2224
2264
  return;
2225
2265
  }
@@ -2232,6 +2272,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2232
2272
  } finally {
2233
2273
  getFederationMetrics(this.meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
2234
2274
  }
2275
+ recordInboxActivity(this.meterProvider, "processed", activityType);
2235
2276
  } catch (error) {
2236
2277
  try {
2237
2278
  await this.inboxErrorHandler?.(context, error);
@@ -2282,13 +2323,17 @@ var FederationImpl = class extends FederationBuilderImpl {
2282
2323
  queue: inboxQueue,
2283
2324
  activityType
2284
2325
  }, retryMessage.attempt);
2326
+ recordInboxActivity(this.meterProvider, "retried", activityType);
2285
2327
  }
2286
- } else logger.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
2287
- error,
2288
- activityId: activity.id?.href,
2289
- activity: message.activity,
2290
- recipient: message.identifier
2291
- });
2328
+ } else {
2329
+ logger.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
2330
+ error,
2331
+ activityId: activity.id?.href,
2332
+ activity: message.activity,
2333
+ recipient: message.identifier
2334
+ });
2335
+ recordInboxActivity(this.meterProvider, "abandoned", activityType);
2336
+ }
2292
2337
  span.setStatus({
2293
2338
  code: SpanStatusCode.ERROR,
2294
2339
  message: String(error)
@@ -3128,6 +3173,7 @@ var ContextImpl = class ContextImpl {
3128
3173
  contextLoader: options.contextLoader ?? this.contextLoader,
3129
3174
  userAgent: options.userAgent ?? this.federation.userAgent,
3130
3175
  tracerProvider: options.tracerProvider ?? this.tracerProvider,
3176
+ meterProvider: options.meterProvider ?? this.meterProvider,
3131
3177
  allowPrivateAddress: this.federation.allowPrivateAddress
3132
3178
  });
3133
3179
  }
@@ -3315,6 +3361,7 @@ var ContextImpl = class ContextImpl {
3315
3361
  queue: this.federation.fanoutQueue,
3316
3362
  activityType: message.activityType
3317
3363
  }, 0);
3364
+ recordFanoutRecipients(this.federation.meterProvider, globalThis.Object.keys(message.inboxes).length, message.activityType);
3318
3365
  return true;
3319
3366
  }
3320
3367
  async *getFollowers(identifier) {
@@ -1,5 +1,5 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as FederationImpl } from "./middleware-DlcecZMq.mjs";
4
+ import { n as FederationImpl } from "./middleware-BB0IbDow.mjs";
5
5
  export { FederationImpl };
@@ -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 { C as version, S as name, _ as recordOutboxEnqueue, a as verifyRequestDetailed, 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 formatAcceptSignature } from "./http-CpzZ9zsb.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-Vd8-1EWh.js";
5
+ import { C as formatAcceptSignature, D as name, O as version, S as recordOutboxEnqueue, a as verifyRequestDetailed, b as recordInboxActivity, d as validateCryptoKey, f as getDurationMs, g as isAbortError, h as instrumentDocumentLoader, i as verifyRequest, m as getRemoteHost, n as parseRfc9421SignatureInput, o as exportJwk, p as getFederationMetrics, t as doubleKnock, u as importJwk, x as recordOutboxActivity, y as recordFanoutRecipients } from "./http-cqujdCRz.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-DMGIjHYH.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-Wc5ezcVW.js";
8
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-pEejzYq4.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";
@@ -817,6 +817,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
817
817
  code: SpanStatusCode.UNSET,
818
818
  message: `Activity ${activity.id?.href} has already been processed.`
819
819
  });
820
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
820
821
  return "alreadyProcessed";
821
822
  }
822
823
  }
@@ -826,6 +827,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
826
827
  code: SpanStatusCode.ERROR,
827
828
  message: "Missing actor."
828
829
  });
830
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
829
831
  return "missingActor";
830
832
  }
831
833
  span.setAttribute("activitypub.actor.id", activity.actorId.href);
@@ -861,6 +863,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
861
863
  queue,
862
864
  activityType: getTypeId(activity).href
863
865
  }, 0);
866
+ recordInboxActivity(meterProvider, "queued", getTypeId(activity).href);
864
867
  logger.info("Activity {activityId} is enqueued.", {
865
868
  activityId: activity.id?.href,
866
869
  activity: json,
@@ -880,6 +883,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
880
883
  code: SpanStatusCode.UNSET,
881
884
  message: `Unsupported activity type: ${getTypeId(activity).href}`
882
885
  });
886
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
883
887
  span.end();
884
888
  return "unsupportedActivity";
885
889
  }
@@ -889,7 +893,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
889
893
  const activityType = getTypeId(activity).href;
890
894
  const started = performance.now();
891
895
  try {
892
- await listener(inboxContextFactory(recipient, json, activity?.id?.href, activityType), activity);
896
+ await listener(inboxContextFactory(recipient, json, activity.id?.href, activityType), activity);
893
897
  } finally {
894
898
  getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
895
899
  }
@@ -914,9 +918,11 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
914
918
  code: SpanStatusCode.ERROR,
915
919
  message: String(error)
916
920
  });
921
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
917
922
  span.end();
918
923
  return "error";
919
924
  }
925
+ recordInboxActivity(meterProvider, "processed", getTypeId(activity).href);
920
926
  if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
921
927
  logger.info("Activity {activityId} has been processed.", {
922
928
  activityId: activity.id?.href,
@@ -2907,23 +2913,50 @@ var FederationImpl = class extends FederationBuilderImpl {
2907
2913
  }
2908
2914
  const { allowPrivateAddress, userAgent } = options;
2909
2915
  this.allowPrivateAddress = allowPrivateAddress ?? false;
2910
- this.documentLoaderFactory = options.documentLoaderFactory ?? ((opts) => {
2911
- return kvCache({
2912
- loader: getDocumentLoader({
2913
- allowPrivateAddress: opts?.allowPrivateAddress ?? allowPrivateAddress,
2914
- userAgent: opts?.userAgent ?? userAgent
2915
- }),
2916
- kv: options.kv,
2917
- prefix: this.kvPrefixes.remoteDocument
2918
- });
2916
+ const userDocumentLoaderFactory = options.documentLoaderFactory;
2917
+ const userContextLoaderFactory = options.contextLoaderFactory;
2918
+ const userAuthFactory = options.authenticatedDocumentLoaderFactory;
2919
+ const builtinDocumentLoaderFactory = (opts) => kvCache({
2920
+ loader: getDocumentLoader({
2921
+ allowPrivateAddress: opts?.allowPrivateAddress ?? allowPrivateAddress,
2922
+ userAgent: opts?.userAgent ?? userAgent
2923
+ }),
2924
+ kv: options.kv,
2925
+ prefix: this.kvPrefixes.remoteDocument,
2926
+ meterProvider: this._meterProvider,
2927
+ kind: "object"
2928
+ });
2929
+ const builtinContextLoaderFactory = (opts) => kvCache({
2930
+ loader: getDocumentLoader({
2931
+ allowPrivateAddress: opts?.allowPrivateAddress ?? allowPrivateAddress,
2932
+ userAgent: opts?.userAgent ?? userAgent
2933
+ }),
2934
+ kv: options.kv,
2935
+ prefix: this.kvPrefixes.remoteDocument,
2936
+ meterProvider: this._meterProvider,
2937
+ kind: "context"
2938
+ });
2939
+ this.documentLoaderFactory = (opts) => instrumentDocumentLoader((userDocumentLoaderFactory ?? builtinDocumentLoaderFactory)(opts), {
2940
+ meterProvider: this._meterProvider,
2941
+ kind: "object",
2942
+ cacheEnabled: userDocumentLoaderFactory == null ? true : void 0
2943
+ });
2944
+ const resolvedContextLoaderFactory = userContextLoaderFactory ?? userDocumentLoaderFactory ?? builtinContextLoaderFactory;
2945
+ this.contextLoaderFactory = (opts) => instrumentDocumentLoader(resolvedContextLoaderFactory(opts), {
2946
+ meterProvider: this._meterProvider,
2947
+ kind: "context",
2948
+ cacheEnabled: userContextLoaderFactory == null && userDocumentLoaderFactory == null ? true : void 0
2919
2949
  });
2920
- this.contextLoaderFactory = options.contextLoaderFactory ?? this.documentLoaderFactory;
2921
- this.authenticatedDocumentLoaderFactory = options.authenticatedDocumentLoaderFactory ?? ((identity) => getAuthenticatedDocumentLoader(identity, {
2922
- allowPrivateAddress,
2923
- userAgent,
2950
+ this.authenticatedDocumentLoaderFactory = (identity, factoryOpts) => instrumentDocumentLoader(userAuthFactory != null ? userAuthFactory(identity, factoryOpts) : getAuthenticatedDocumentLoader(identity, {
2951
+ allowPrivateAddress: factoryOpts?.allowPrivateAddress ?? allowPrivateAddress,
2952
+ userAgent: factoryOpts?.userAgent ?? userAgent,
2924
2953
  specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, options.firstKnock),
2925
2954
  tracerProvider: this.tracerProvider
2926
- }));
2955
+ }), {
2956
+ meterProvider: this._meterProvider,
2957
+ kind: "object",
2958
+ cacheEnabled: userAuthFactory == null ? false : void 0
2959
+ });
2927
2960
  this.userAgent = userAgent;
2928
2961
  this.onOutboxError = options.onOutboxError;
2929
2962
  this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
@@ -3233,6 +3266,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3233
3266
  });
3234
3267
  }
3235
3268
  }
3269
+ recordOutboxActivity(this.meterProvider, "abandoned", message.activityType);
3236
3270
  return;
3237
3271
  }
3238
3272
  if (this.outboxQueue?.nativeRetrial) {
@@ -3263,11 +3297,15 @@ var FederationImpl = class extends FederationBuilderImpl {
3263
3297
  queue: outboxQueue,
3264
3298
  activityType: retryMessage.activityType
3265
3299
  }, retryMessage.attempt);
3300
+ recordOutboxActivity(this.meterProvider, "retried", retryMessage.activityType);
3266
3301
  }
3267
- } else logger.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
3268
- ...logData,
3269
- error
3270
- });
3302
+ } else {
3303
+ logger.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
3304
+ ...logData,
3305
+ error
3306
+ });
3307
+ recordOutboxActivity(this.meterProvider, "abandoned", message.activityType);
3308
+ }
3271
3309
  return;
3272
3310
  }
3273
3311
  logger.info("Successfully sent activity {activityId} to {inbox}.", { ...logData });
@@ -3302,6 +3340,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3302
3340
  activity: message.activity,
3303
3341
  recipient: message.identifier
3304
3342
  });
3343
+ recordInboxActivity(this.meterProvider, "rejected", activityType);
3305
3344
  return;
3306
3345
  }
3307
3346
  }
@@ -3318,6 +3357,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3318
3357
  code: SpanStatusCode.ERROR,
3319
3358
  message: `Unsupported activity type: ${activityType}`
3320
3359
  });
3360
+ recordInboxActivity(this.meterProvider, "rejected", activityType);
3321
3361
  span.end();
3322
3362
  return;
3323
3363
  }
@@ -3330,6 +3370,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3330
3370
  } finally {
3331
3371
  getFederationMetrics(this.meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
3332
3372
  }
3373
+ recordInboxActivity(this.meterProvider, "processed", activityType);
3333
3374
  } catch (error) {
3334
3375
  try {
3335
3376
  await this.inboxErrorHandler?.(context, error);
@@ -3380,13 +3421,17 @@ var FederationImpl = class extends FederationBuilderImpl {
3380
3421
  queue: inboxQueue,
3381
3422
  activityType
3382
3423
  }, retryMessage.attempt);
3424
+ recordInboxActivity(this.meterProvider, "retried", activityType);
3383
3425
  }
3384
- } else logger.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
3385
- error,
3386
- activityId: activity.id?.href,
3387
- activity: message.activity,
3388
- recipient: message.identifier
3389
- });
3426
+ } else {
3427
+ logger.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
3428
+ error,
3429
+ activityId: activity.id?.href,
3430
+ activity: message.activity,
3431
+ recipient: message.identifier
3432
+ });
3433
+ recordInboxActivity(this.meterProvider, "abandoned", activityType);
3434
+ }
3390
3435
  span.setStatus({
3391
3436
  code: SpanStatusCode.ERROR,
3392
3437
  message: String(error)
@@ -4226,6 +4271,7 @@ var ContextImpl = class ContextImpl {
4226
4271
  contextLoader: options.contextLoader ?? this.contextLoader,
4227
4272
  userAgent: options.userAgent ?? this.federation.userAgent,
4228
4273
  tracerProvider: options.tracerProvider ?? this.tracerProvider,
4274
+ meterProvider: options.meterProvider ?? this.meterProvider,
4229
4275
  allowPrivateAddress: this.federation.allowPrivateAddress
4230
4276
  });
4231
4277
  }
@@ -4413,6 +4459,7 @@ var ContextImpl = class ContextImpl {
4413
4459
  queue: this.federation.fanoutQueue,
4414
4460
  activityType: message.activityType
4415
4461
  }, 0);
4462
+ recordFanoutRecipients(this.federation.meterProvider, globalThis.Object.keys(message.inboxes).length, message.activityType);
4416
4463
  return true;
4417
4464
  }
4418
4465
  async *getFollowers(identifier) {
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { S as KeyCache } from "./http-D6aw3j2U.cjs";
2
+ import { S as KeyCache } from "./http-VyDTd4G3.cjs";
3
3
  import { CryptographicKey, DataIntegrityProof, Multikey, Object as Object$1 } from "@fedify/vocab";
4
4
  import { DocumentLoader } from "@fedify/vocab-runtime";
5
5
  import { MeterProvider, TracerProvider } from "@opentelemetry/api";
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { S as KeyCache } from "./http-D6LP89UO.js";
2
+ import { S as KeyCache } from "./http-lf8Hsd91.js";
3
3
  import { CryptographicKey, DataIntegrityProof, Multikey, Object as Object$1 } from "@fedify/vocab";
4
4
  import { MeterProvider, TracerProvider } from "@opentelemetry/api";
5
5
  import { DocumentLoader } from "@fedify/vocab-runtime";
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { Ct as WebFingerLinksDispatcher, et as ActorAliasMapper, l as RequestContext, nt as ActorHandleMapper, tt as ActorDispatcher } from "./context-cSUMk2da.js";
2
+ import { J as ActorDispatcher, Y as ActorHandleMapper, gt as WebFingerLinksDispatcher, l as RequestContext, q as ActorAliasMapper } from "./context-Cq18Gplu.js";
3
3
  import { Span, Tracer } from "@opentelemetry/api";
4
4
 
5
5
  //#region src/federation/webfinger.d.ts
@@ -1,7 +1,7 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as HttpMessageSignaturesSpecDeterminer } from "./http-D6LP89UO.js";
2
+ import { n as HttpMessageSignaturesSpecDeterminer } from "./http-lf8Hsd91.js";
3
3
  import { n as KvStore, t as KvKey } from "./kv-D6hNiMTK.js";
4
- import { TracerProvider } from "@opentelemetry/api";
4
+ import { MeterProvider, TracerProvider } from "@opentelemetry/api";
5
5
  import { DocumentLoader, DocumentLoaderFactoryOptions } from "@fedify/vocab-runtime";
6
6
 
7
7
  //#region src/utils/docloader.d.ts
@@ -48,6 +48,37 @@ declare function getAuthenticatedDocumentLoader(identity: {
48
48
  tracerProvider
49
49
  }?: GetAuthenticatedDocumentLoaderOptions): DocumentLoader;
50
50
  //#endregion
51
+ //#region src/federation/metrics.d.ts
52
+ /**
53
+ * The kind of remote ActivityPub lookup, recorded as
54
+ * `activitypub.lookup.kind` on the public-key lookup and remote document
55
+ * fetch metric families.
56
+ *
57
+ * - `public_key`: a public key lookup performed by `fetchKey` /
58
+ * `fetchKeyDetailed` (always recorded on `activitypub.key.lookup*`).
59
+ * - `actor`: a document fetch whose resolved value is an Actor. The
60
+ * bucket exists in the taxonomy for future actor-aware call sites;
61
+ * today, actor documents fetched through Fedify's generic document
62
+ * loader are still classified as `object` because the kind is decided
63
+ * at the loader boundary, before the response is parsed.
64
+ * - `object`: a generic ActivityPub object fetch through Fedify's
65
+ * document loader. This is the default classification for
66
+ * `documentLoader` invocations that do not match a more specific
67
+ * bucket.
68
+ * - `context`: a JSON-LD `@context` document fetch through Fedify's
69
+ * context loader.
70
+ * - `other`: a fetch that does not fit any of the above classifications.
71
+ * @since 2.3.0
72
+ */
73
+ type LookupKind = "public_key" | "actor" | "object" | "context" | "other";
74
+ /**
75
+ * The {@link LookupKind} values that can appear on remote document fetch
76
+ * metrics. `public_key` lookups are reported on the
77
+ * `activitypub.key.lookup` metric family instead, so it is excluded here.
78
+ * @since 2.3.0
79
+ */
80
+ type DocumentFetchKind = Exclude<LookupKind, "public_key">;
81
+ //#endregion
51
82
  //#region src/utils/kv-cache.d.ts
52
83
  /**
53
84
  * The parameters for {@link kvCache} function.
@@ -75,6 +106,24 @@ interface KvCacheParameters {
75
106
  * By default, 5 minutes for all URLs.
76
107
  */
77
108
  readonly rules?: readonly [string | URL | URLPattern, Temporal.Duration | Temporal.DurationLike][];
109
+ /**
110
+ * The OpenTelemetry meter provider used to record
111
+ * `activitypub.document.cache` measurements. When omitted, the wrapper
112
+ * does not emit any metric measurements, preserving the previous
113
+ * unobserved-cache behavior.
114
+ * @since 2.3.0
115
+ */
116
+ readonly meterProvider?: MeterProvider;
117
+ /**
118
+ * The lookup kind to record on the `activitypub.lookup.kind` attribute of
119
+ * `activitypub.document.cache` measurements. Defaults to `"object"` so
120
+ * the generic document loader case does not require an explicit option.
121
+ * Set to `"context"` for context-loader wrappers; the
122
+ * authenticated-loader path does not use `kvCache()` and is therefore
123
+ * out of scope.
124
+ * @since 2.3.0
125
+ */
126
+ readonly kind?: DocumentFetchKind;
78
127
  }
79
128
  /**
80
129
  * Decorates a {@link DocumentLoader} with a cache backed by a {@link KvStore}.
@@ -85,7 +134,9 @@ declare function kvCache({
85
134
  loader,
86
135
  kv,
87
136
  prefix,
88
- rules
137
+ rules,
138
+ meterProvider,
139
+ kind
89
140
  }: KvCacheParameters): DocumentLoader;
90
141
  //#endregion
91
142
  export { getAuthenticatedDocumentLoader as n, kvCache as t };
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { Ct as WebFingerLinksDispatcher, et as ActorAliasMapper, l as RequestContext, nt as ActorHandleMapper, tt as ActorDispatcher } from "./context-Ch-ZLyTQ.cjs";
2
+ import { J as ActorDispatcher, Y as ActorHandleMapper, gt as WebFingerLinksDispatcher, l as RequestContext, q as ActorAliasMapper } from "./context-tc6VOOOL.cjs";
3
3
  import { Span, Tracer } from "@opentelemetry/api";
4
4
 
5
5
  //#region src/federation/webfinger.d.ts