@fedify/fedify 2.3.0-dev.1119 → 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 (68) hide show
  1. package/dist/{builder-Ond_h57y.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-DVsHS7rA.mjs → deno--CS-SBS9.mjs} +1 -1
  8. package/dist/{docloader-WsWfKaE5.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.mjs +229 -1
  13. package/dist/federation/middleware.test.mjs +64 -6
  14. package/dist/federation/mod.cjs +1 -1
  15. package/dist/federation/mod.d.cts +3 -2
  16. package/dist/federation/mod.d.ts +3 -2
  17. package/dist/federation/mod.js +1 -1
  18. package/dist/federation/send.test.mjs +3 -3
  19. package/dist/federation/webfinger.test.mjs +1 -1
  20. package/dist/{http-CubOB9wq.cjs → http-CJfvRL7D.cjs} +263 -19
  21. package/dist/{http-DUV8ysti.mjs → http-IywnQdiX.mjs} +7 -5
  22. package/dist/{http-D6LP89UO.d.ts → http-VyDTd4G3.d.cts} +8 -1
  23. package/dist/{http-CouJSFVK.js → http-cqujdCRz.js} +252 -20
  24. package/dist/{http-D6aw3j2U.d.cts → http-lf8Hsd91.d.ts} +8 -1
  25. package/dist/{key-BoWaYRHm.mjs → key-Df3tMleh.mjs} +42 -17
  26. package/dist/{kv-cache-Dz31ATUT.cjs → kv-cache-L0SMQkcd.cjs} +19 -2
  27. package/dist/{kv-cache-DBNpsneh.js → kv-cache-pEejzYq4.js} +19 -2
  28. package/dist/{kv-cache-DihufyAQ.mjs → kv-cache-q9Ec2ryS.mjs} +19 -1
  29. package/dist/{ld-B5K1mSuG.mjs → ld-BGwiJpl3.mjs} +3 -3
  30. package/dist/{metrics-C4attqv0.mjs → metrics-BTOMkW8C.mjs} +209 -2
  31. package/dist/{middleware-CmsDtIHI.cjs → middleware-B2rtdpFV.cjs} +45 -17
  32. package/dist/{middleware-t0jC8I99.mjs → middleware-BB0IbDow.mjs} +54 -26
  33. package/dist/{middleware-BDKFRjue.mjs → middleware-Dnql59Y8.mjs} +1 -1
  34. package/dist/{middleware-Dtjz-hSk.js → middleware-DtOddSVg.js} +45 -17
  35. package/dist/{mod-BDhgfjP7.d.cts → mod-B0hW12_O.d.cts} +1 -1
  36. package/dist/{mod-B-Lin9Sy.d.ts → mod-COIAjwRS.d.ts} +1 -1
  37. package/dist/{mod-C6E8rkcz.d.ts → mod-CajNYYkt.d.ts} +1 -1
  38. package/dist/{mod-DLrRb0dx.d.ts → mod-DFvNJcNb.d.ts} +54 -3
  39. package/dist/{mod-P9tE2WmM.d.cts → mod-DnzgcPcy.d.cts} +1 -1
  40. package/dist/{mod-BR_BB0bh.d.cts → mod-yvIXFAEi.d.cts} +54 -3
  41. package/dist/mod.cjs +4 -4
  42. package/dist/mod.d.cts +6 -5
  43. package/dist/mod.d.ts +6 -5
  44. package/dist/mod.js +4 -4
  45. package/dist/mq-D-nlpY04.d.ts +208 -0
  46. package/dist/mq-D8uSFzxe.d.cts +208 -0
  47. package/dist/nodeinfo/handler.test.mjs +1 -1
  48. package/dist/{owner-hDxI0ufu.mjs → owner-CIt4hvmM.mjs} +2 -2
  49. package/dist/{proof-BUWfVr6Q.cjs → proof-B1_u25UV.cjs} +1 -1
  50. package/dist/{proof-DhVuz4bc.mjs → proof-BYlrRSmZ.mjs} +3 -3
  51. package/dist/{proof-n60t8o9P.js → proof-DMGIjHYH.js} +1 -1
  52. package/dist/{send-BPhyR5Oo.mjs → send-DJFpze7B.mjs} +3 -3
  53. package/dist/sig/http.test.mjs +6 -2
  54. package/dist/sig/key.test.mjs +99 -2
  55. package/dist/sig/ld.test.mjs +2 -2
  56. package/dist/sig/mod.cjs +2 -2
  57. package/dist/sig/mod.d.cts +2 -2
  58. package/dist/sig/mod.d.ts +2 -2
  59. package/dist/sig/mod.js +2 -2
  60. package/dist/sig/owner.test.mjs +1 -1
  61. package/dist/sig/proof.test.mjs +1 -1
  62. package/dist/utils/docloader.test.mjs +2 -2
  63. package/dist/utils/kv-cache.test.mjs +67 -2
  64. package/dist/utils/mod.cjs +1 -1
  65. package/dist/utils/mod.d.cts +1 -1
  66. package/dist/utils/mod.d.ts +1 -1
  67. package/dist/utils/mod.js +1 -1
  68. package/package.json +6 -6
@@ -1,8 +1,9 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-DVsHS7rA.mjs";
4
+ import { n as version, t as name } from "./deno--CS-SBS9.mjs";
5
5
  import { metrics } from "@opentelemetry/api";
6
+ import { FetchError } from "@fedify/vocab-runtime";
6
7
  //#region src/federation/metrics.ts
7
8
  var FederationMetrics = class {
8
9
  deliverySent;
@@ -23,6 +24,11 @@ var FederationMetrics = class {
23
24
  fanoutRecipients;
24
25
  inboxActivity;
25
26
  outboxActivity;
27
+ keyLookup;
28
+ keyLookupDuration;
29
+ documentFetch;
30
+ documentFetchDuration;
31
+ documentCache;
26
32
  constructor(meterProvider) {
27
33
  const meter = meterProvider.getMeter(name, version);
28
34
  this.deliverySent = meter.createCounter("activitypub.delivery.sent", {
@@ -129,6 +135,58 @@ var FederationMetrics = class {
129
135
  description: "ActivityPub activities observed at the outbox lifecycle level: queued, retried, or abandoned. Per-recipient delivery counters live on `activitypub.delivery.*`.",
130
136
  unit: "{activity}"
131
137
  });
138
+ this.keyLookup = meter.createCounter("activitypub.key.lookup", {
139
+ description: "Public-key lookup attempts performed by Fedify, including both cache hits and remote fetches.",
140
+ unit: "{lookup}"
141
+ });
142
+ this.keyLookupDuration = meter.createHistogram("activitypub.key.lookup.duration", {
143
+ description: "Duration of public-key lookups performed by Fedify, including any remote fetch.",
144
+ unit: "ms",
145
+ advice: { explicitBucketBoundaries: [
146
+ 5,
147
+ 10,
148
+ 25,
149
+ 50,
150
+ 75,
151
+ 100,
152
+ 250,
153
+ 500,
154
+ 750,
155
+ 1e3,
156
+ 2500,
157
+ 5e3,
158
+ 7500,
159
+ 1e4
160
+ ] }
161
+ });
162
+ this.documentFetch = meter.createCounter("activitypub.document.fetch", {
163
+ description: "Remote JSON-LD document loader invocations made by Fedify-wrapped loaders.",
164
+ unit: "{fetch}"
165
+ });
166
+ this.documentFetchDuration = meter.createHistogram("activitypub.document.fetch.duration", {
167
+ description: "Duration of remote JSON-LD document loader invocations made by Fedify-wrapped loaders.",
168
+ unit: "ms",
169
+ advice: { explicitBucketBoundaries: [
170
+ 5,
171
+ 10,
172
+ 25,
173
+ 50,
174
+ 75,
175
+ 100,
176
+ 250,
177
+ 500,
178
+ 750,
179
+ 1e3,
180
+ 2500,
181
+ 5e3,
182
+ 7500,
183
+ 1e4
184
+ ] }
185
+ });
186
+ this.documentCache = meter.createCounter("activitypub.document.cache", {
187
+ description: "KV-backed document loader cache lookups, with `hit` or `miss` classification.",
188
+ unit: "{lookup}"
189
+ });
132
190
  }
133
191
  recordDelivery(inbox, durationMs, success, activityType) {
134
192
  const deliveryAttributes = {
@@ -212,6 +270,36 @@ var FederationMetrics = class {
212
270
  recordOutboxActivity(result, activityType) {
213
271
  this.outboxActivity.add(1, buildActivityLifecycleAttributes(result, activityType));
214
272
  }
273
+ recordKeyLookup(attrs) {
274
+ const attributes = {
275
+ "activitypub.lookup.kind": "public_key",
276
+ "activitypub.lookup.result": attrs.result,
277
+ "activitypub.cache.enabled": attrs.cacheEnabled
278
+ };
279
+ if (attrs.remoteUrl != null) attributes["activitypub.remote.host"] = getRemoteHost(attrs.remoteUrl);
280
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
281
+ this.keyLookup.add(1, attributes);
282
+ this.keyLookupDuration.record(attrs.durationMs, attributes);
283
+ }
284
+ recordDocumentFetch(attrs) {
285
+ const attributes = {
286
+ "activitypub.lookup.kind": attrs.kind,
287
+ "activitypub.lookup.result": attrs.result
288
+ };
289
+ if (attrs.remoteUrl != null) attributes["activitypub.remote.host"] = getRemoteHost(attrs.remoteUrl);
290
+ if (attrs.cacheEnabled != null) attributes["activitypub.cache.enabled"] = attrs.cacheEnabled;
291
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
292
+ this.documentFetch.add(1, attributes);
293
+ this.documentFetchDuration.record(attrs.durationMs, attributes);
294
+ }
295
+ recordDocumentCache(attrs) {
296
+ const attributes = {
297
+ "activitypub.lookup.kind": attrs.kind,
298
+ "activitypub.lookup.result": attrs.result
299
+ };
300
+ if (attrs.remoteUrl != null) attributes["activitypub.remote.host"] = getRemoteHost(attrs.remoteUrl);
301
+ this.documentCache.add(1, attributes);
302
+ }
215
303
  };
216
304
  function buildActivityLifecycleAttributes(result, activityType) {
217
305
  const attributes = { "activitypub.processing.result": result };
@@ -300,6 +388,125 @@ function recordOutboxActivity(meterProvider, result, activityType) {
300
388
  getFederationMetrics(meterProvider).recordOutboxActivity(result, activityType);
301
389
  }
302
390
  /**
391
+ * Records one measurement on `activitypub.key.lookup` (counter) and
392
+ * `activitypub.key.lookup.duration` (histogram) for a public-key lookup.
393
+ *
394
+ * `activitypub.lookup.kind` is always recorded as `public_key`; the result
395
+ * classification, remote host, HTTP status code (when an HTTP response was
396
+ * received), and `activitypub.cache.enabled` are recorded as attributes on
397
+ * both measurements. Full key URLs and key IDs are deliberately omitted to
398
+ * keep cardinality bounded.
399
+ * @since 2.3.0
400
+ */
401
+ function recordKeyLookup(meterProvider, attrs) {
402
+ getFederationMetrics(meterProvider).recordKeyLookup(attrs);
403
+ }
404
+ /**
405
+ * Records one measurement each on `activitypub.document.fetch` (counter)
406
+ * and `activitypub.document.fetch.duration` (histogram) for one remote
407
+ * JSON-LD document loader invocation, with bounded
408
+ * `activitypub.lookup.kind` and `activitypub.lookup.result` attributes
409
+ * plus the optional remote-host, cache-enabled, and HTTP status-code
410
+ * attributes. Counter and histogram are always recorded together so
411
+ * aggregate rate and latency views stay in sync.
412
+ * @since 2.3.0
413
+ */
414
+ function recordDocumentFetch(meterProvider, attrs) {
415
+ getFederationMetrics(meterProvider).recordDocumentFetch(attrs);
416
+ }
417
+ /**
418
+ * Records one `activitypub.document.cache` measurement, classifying the
419
+ * lookup as `hit` (the cache returned an entry) or `miss` (the cache was
420
+ * consulted and returned nothing, prompting a delegate fetch).
421
+ * @since 2.3.0
422
+ */
423
+ function recordDocumentCache(meterProvider, attrs) {
424
+ getFederationMetrics(meterProvider).recordDocumentCache(attrs);
425
+ }
426
+ /**
427
+ * Classifies a thrown value from a key or document fetch into the bounded
428
+ * {@link LookupResult} taxonomy and, when an HTTP response was received,
429
+ * surfaces its status code.
430
+ *
431
+ * - `FetchError` with a `Response` whose status is `404` or `410`:
432
+ * `result=not_found` and the response status code.
433
+ * - `FetchError` with any other `Response`: `result=error` and the
434
+ * response status code.
435
+ * - `FetchError` without a `Response`: `result=network_error`.
436
+ * - An `AbortError` (typically from a cancelled fetch): `result=network_error`.
437
+ * - A bare `TypeError` (the shape native `fetch()` raises on DNS, connect,
438
+ * and TLS failures before any response is observed):
439
+ * `result=network_error`.
440
+ * - Any other value: `result=error`.
441
+ * @since 2.3.0
442
+ */
443
+ function classifyFetchError(error) {
444
+ if (error instanceof FetchError) {
445
+ if (error.response != null) {
446
+ const status = error.response.status;
447
+ return {
448
+ result: status === 404 || status === 410 ? "not_found" : "error",
449
+ statusCode: status
450
+ };
451
+ }
452
+ return { result: "network_error" };
453
+ }
454
+ if (isAbortError(error)) return { result: "network_error" };
455
+ if (error instanceof TypeError) return { result: "network_error" };
456
+ return { result: "error" };
457
+ }
458
+ /**
459
+ * Wraps a {@link DocumentLoader} so each invocation records one
460
+ * measurement on `activitypub.document.fetch` (counter) and one on
461
+ * `activitypub.document.fetch.duration` (histogram), classifying the
462
+ * outcome via {@link classifyFetchError} when the wrapped loader throws
463
+ * and as `fetched` on success. The wrapper rethrows whatever the
464
+ * wrapped loader throws so caller behavior is unchanged.
465
+ *
466
+ * The wrapper records the hostname of the requested URL on
467
+ * `activitypub.remote.host` when the URL parses; full URLs, paths, and
468
+ * query strings are deliberately excluded to keep cardinality bounded.
469
+ * HTTP status codes are recorded only when the failure carries a
470
+ * `Response` (currently, when the wrapped loader throws a
471
+ * {@link FetchError} with a non-`null` `response`).
472
+ * @since 2.3.0
473
+ */
474
+ function instrumentDocumentLoader(loader, options) {
475
+ const meterProvider = options.meterProvider;
476
+ if (meterProvider == null) return loader;
477
+ return async (url, opts) => {
478
+ const start = performance.now();
479
+ let remoteUrl;
480
+ try {
481
+ remoteUrl = new URL(url);
482
+ } catch {
483
+ remoteUrl = void 0;
484
+ }
485
+ try {
486
+ const result = await loader(url, opts);
487
+ recordDocumentFetch(meterProvider, {
488
+ durationMs: getDurationMs(start),
489
+ kind: options.kind,
490
+ result: "fetched",
491
+ remoteUrl,
492
+ cacheEnabled: options.cacheEnabled
493
+ });
494
+ return result;
495
+ } catch (error) {
496
+ const classified = classifyFetchError(error);
497
+ recordDocumentFetch(meterProvider, {
498
+ durationMs: getDurationMs(start),
499
+ kind: options.kind,
500
+ result: classified.result,
501
+ remoteUrl,
502
+ cacheEnabled: options.cacheEnabled,
503
+ statusCode: classified.statusCode
504
+ });
505
+ throw error;
506
+ }
507
+ };
508
+ }
509
+ /**
303
510
  * Times an awaited public key fetch and records exactly one
304
511
  * `activitypub.signature.key_fetch.duration` measurement, classifying the
305
512
  * outcome as `hit`, `fetched`, or `error` based on the `cached` flag and
@@ -379,4 +586,4 @@ function getDurationMs(start) {
379
586
  return Math.max(0, performance.now() - start);
380
587
  }
381
588
  //#endregion
382
- export { measureSignatureKeyFetch as a, recordOutboxActivity as c, isAbortError as i, recordOutboxEnqueue as l, getFederationMetrics as n, recordFanoutRecipients as o, getRemoteHost as r, recordInboxActivity as s, getDurationMs as t };
589
+ export { instrumentDocumentLoader as a, recordDocumentCache as c, recordInboxActivity as d, recordKeyLookup as f, getRemoteHost as i, recordDocumentFetch as l, recordOutboxEnqueue as m, getDurationMs as n, isAbortError as o, recordOutboxActivity as p, getFederationMetrics as r, measureSignatureKeyFetch as s, classifyFetchError as t, recordFanoutRecipients as u };
@@ -2,10 +2,10 @@ const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  const require_chunk = require("./chunk-DDcVe30Y.cjs");
4
4
  const require_transformers = require("./transformers-NeAONrAq.cjs");
5
- const require_http = require("./http-CubOB9wq.cjs");
6
- const require_proof = require("./proof-BUWfVr6Q.cjs");
5
+ const require_http = require("./http-CJfvRL7D.cjs");
6
+ const require_proof = require("./proof-B1_u25UV.cjs");
7
7
  const require_types = require("./types-KC4QAoxe.cjs");
8
- const require_kv_cache = require("./kv-cache-Dz31ATUT.cjs");
8
+ const require_kv_cache = require("./kv-cache-L0SMQkcd.cjs");
9
9
  let _logtape_logtape = require("@logtape/logtape");
10
10
  let _fedify_vocab = require("@fedify/vocab");
11
11
  let _opentelemetry_api = require("@opentelemetry/api");
@@ -2913,23 +2913,50 @@ var FederationImpl = class extends FederationBuilderImpl {
2913
2913
  }
2914
2914
  const { allowPrivateAddress, userAgent } = options;
2915
2915
  this.allowPrivateAddress = allowPrivateAddress ?? false;
2916
- this.documentLoaderFactory = options.documentLoaderFactory ?? ((opts) => {
2917
- return require_kv_cache.kvCache({
2918
- loader: (0, _fedify_vocab_runtime.getDocumentLoader)({
2919
- allowPrivateAddress: opts?.allowPrivateAddress ?? allowPrivateAddress,
2920
- userAgent: opts?.userAgent ?? userAgent
2921
- }),
2922
- kv: options.kv,
2923
- prefix: this.kvPrefixes.remoteDocument
2924
- });
2916
+ const userDocumentLoaderFactory = options.documentLoaderFactory;
2917
+ const userContextLoaderFactory = options.contextLoaderFactory;
2918
+ const userAuthFactory = options.authenticatedDocumentLoaderFactory;
2919
+ const builtinDocumentLoaderFactory = (opts) => require_kv_cache.kvCache({
2920
+ loader: (0, _fedify_vocab_runtime.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) => require_kv_cache.kvCache({
2930
+ loader: (0, _fedify_vocab_runtime.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"
2925
2938
  });
2926
- this.contextLoaderFactory = options.contextLoaderFactory ?? this.documentLoaderFactory;
2927
- this.authenticatedDocumentLoaderFactory = options.authenticatedDocumentLoaderFactory ?? ((identity) => require_kv_cache.getAuthenticatedDocumentLoader(identity, {
2928
- allowPrivateAddress,
2929
- userAgent,
2939
+ this.documentLoaderFactory = (opts) => require_http.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) => require_http.instrumentDocumentLoader(resolvedContextLoaderFactory(opts), {
2946
+ meterProvider: this._meterProvider,
2947
+ kind: "context",
2948
+ cacheEnabled: userContextLoaderFactory == null && userDocumentLoaderFactory == null ? true : void 0
2949
+ });
2950
+ this.authenticatedDocumentLoaderFactory = (identity, factoryOpts) => require_http.instrumentDocumentLoader(userAuthFactory != null ? userAuthFactory(identity, factoryOpts) : require_kv_cache.getAuthenticatedDocumentLoader(identity, {
2951
+ allowPrivateAddress: factoryOpts?.allowPrivateAddress ?? allowPrivateAddress,
2952
+ userAgent: factoryOpts?.userAgent ?? userAgent,
2930
2953
  specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, options.firstKnock),
2931
2954
  tracerProvider: this.tracerProvider
2932
- }));
2955
+ }), {
2956
+ meterProvider: this._meterProvider,
2957
+ kind: "object",
2958
+ cacheEnabled: userAuthFactory == null ? false : void 0
2959
+ });
2933
2960
  this.userAgent = userAgent;
2934
2961
  this.onOutboxError = options.onOutboxError;
2935
2962
  this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
@@ -4244,6 +4271,7 @@ var ContextImpl = class ContextImpl {
4244
4271
  contextLoader: options.contextLoader ?? this.contextLoader,
4245
4272
  userAgent: options.userAgent ?? this.federation.userAgent,
4246
4273
  tracerProvider: options.tracerProvider ?? this.tracerProvider,
4274
+ meterProvider: options.meterProvider ?? this.meterProvider,
4247
4275
  allowPrivateAddress: this.federation.allowPrivateAddress
4248
4276
  });
4249
4277
  }
@@ -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-DVsHS7rA.mjs";
6
- import { c as recordOutboxActivity, i as isAbortError, l as recordOutboxEnqueue, n as getFederationMetrics, o as recordFanoutRecipients, r as getRemoteHost, s as recordInboxActivity, t as getDurationMs } from "./metrics-C4attqv0.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-BoWaYRHm.mjs";
9
- import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-DUV8ysti.mjs";
10
- import { t as getAuthenticatedDocumentLoader } from "./docloader-WsWfKaE5.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-B5K1mSuG.mjs";
13
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-hDxI0ufu.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-DhVuz4bc.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-Ond_h57y.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-BPhyR5Oo.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";
@@ -1815,23 +1815,50 @@ var FederationImpl = class extends FederationBuilderImpl {
1815
1815
  }
1816
1816
  const { allowPrivateAddress, userAgent } = options;
1817
1817
  this.allowPrivateAddress = allowPrivateAddress ?? false;
1818
- this.documentLoaderFactory = options.documentLoaderFactory ?? ((opts) => {
1819
- return kvCache({
1820
- loader: getDocumentLoader({
1821
- allowPrivateAddress: opts?.allowPrivateAddress ?? allowPrivateAddress,
1822
- userAgent: opts?.userAgent ?? userAgent
1823
- }),
1824
- kv: options.kv,
1825
- prefix: this.kvPrefixes.remoteDocument
1826
- });
1827
- });
1828
- this.contextLoaderFactory = options.contextLoaderFactory ?? this.documentLoaderFactory;
1829
- this.authenticatedDocumentLoaderFactory = options.authenticatedDocumentLoaderFactory ?? ((identity) => getAuthenticatedDocumentLoader(identity, {
1830
- allowPrivateAddress,
1831
- 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,
1832
1855
  specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, options.firstKnock),
1833
1856
  tracerProvider: this.tracerProvider
1834
- }));
1857
+ }), {
1858
+ meterProvider: this._meterProvider,
1859
+ kind: "object",
1860
+ cacheEnabled: userAuthFactory == null ? false : void 0
1861
+ });
1835
1862
  this.userAgent = userAgent;
1836
1863
  this.onOutboxError = options.onOutboxError;
1837
1864
  this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
@@ -3146,6 +3173,7 @@ var ContextImpl = class ContextImpl {
3146
3173
  contextLoader: options.contextLoader ?? this.contextLoader,
3147
3174
  userAgent: options.userAgent ?? this.federation.userAgent,
3148
3175
  tracerProvider: options.tracerProvider ?? this.tracerProvider,
3176
+ meterProvider: options.meterProvider ?? this.meterProvider,
3149
3177
  allowPrivateAddress: this.federation.allowPrivateAddress
3150
3178
  });
3151
3179
  }
@@ -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-t0jC8I99.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 { 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";
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-DBNpsneh.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";
@@ -2913,23 +2913,50 @@ var FederationImpl = class extends FederationBuilderImpl {
2913
2913
  }
2914
2914
  const { allowPrivateAddress, userAgent } = options;
2915
2915
  this.allowPrivateAddress = allowPrivateAddress ?? false;
2916
- this.documentLoaderFactory = options.documentLoaderFactory ?? ((opts) => {
2917
- return kvCache({
2918
- loader: getDocumentLoader({
2919
- allowPrivateAddress: opts?.allowPrivateAddress ?? allowPrivateAddress,
2920
- userAgent: opts?.userAgent ?? userAgent
2921
- }),
2922
- kv: options.kv,
2923
- prefix: this.kvPrefixes.remoteDocument
2924
- });
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"
2925
2938
  });
2926
- this.contextLoaderFactory = options.contextLoaderFactory ?? this.documentLoaderFactory;
2927
- this.authenticatedDocumentLoaderFactory = options.authenticatedDocumentLoaderFactory ?? ((identity) => getAuthenticatedDocumentLoader(identity, {
2928
- allowPrivateAddress,
2929
- userAgent,
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
2949
+ });
2950
+ this.authenticatedDocumentLoaderFactory = (identity, factoryOpts) => instrumentDocumentLoader(userAuthFactory != null ? userAuthFactory(identity, factoryOpts) : getAuthenticatedDocumentLoader(identity, {
2951
+ allowPrivateAddress: factoryOpts?.allowPrivateAddress ?? allowPrivateAddress,
2952
+ userAgent: factoryOpts?.userAgent ?? userAgent,
2930
2953
  specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, options.firstKnock),
2931
2954
  tracerProvider: this.tracerProvider
2932
- }));
2955
+ }), {
2956
+ meterProvider: this._meterProvider,
2957
+ kind: "object",
2958
+ cacheEnabled: userAuthFactory == null ? false : void 0
2959
+ });
2933
2960
  this.userAgent = userAgent;
2934
2961
  this.onOutboxError = options.onOutboxError;
2935
2962
  this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
@@ -4244,6 +4271,7 @@ var ContextImpl = class ContextImpl {
4244
4271
  contextLoader: options.contextLoader ?? this.contextLoader,
4245
4272
  userAgent: options.userAgent ?? this.federation.userAgent,
4246
4273
  tracerProvider: options.tracerProvider ?? this.tracerProvider,
4274
+ meterProvider: options.meterProvider ?? this.meterProvider,
4247
4275
  allowPrivateAddress: this.federation.allowPrivateAddress
4248
4276
  });
4249
4277
  }
@@ -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 };