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

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 (104) hide show
  1. package/README.md +3 -0
  2. package/dist/{assert-DikXweDx.mjs → assert-OguE97r2.mjs} +1 -1
  3. package/dist/{assert_instance_of-C4Ri6VuN.mjs → assert_instance_of-DBC5X09g.mjs} +1 -1
  4. package/dist/{assert_not_equals--wG9hV7u.mjs → assert_not_equals-DkVK8oqV.mjs} +1 -1
  5. package/dist/{assert_rejects-DQP-q39h.mjs → assert_rejects-DN60FHPX.mjs} +2 -2
  6. package/dist/{assert_strict_equals-Dmjbg-bA.mjs → assert_strict_equals-XEgZAlrj.mjs} +1 -1
  7. package/dist/{assert_throws-4NwKEy2q.mjs → assert_throws-BOkhLGYc.mjs} +1 -1
  8. package/dist/{builder-DckAhD27.mjs → builder-ShiR1K6b.mjs} +60 -41
  9. package/dist/compat/mod.d.cts +1 -1
  10. package/dist/compat/mod.d.ts +1 -1
  11. package/dist/compat/outgoing-jsonld.test.mjs +3 -3
  12. package/dist/compat/public-audience.test.mjs +3 -3
  13. package/dist/compat/transformers.test.mjs +5 -5
  14. package/dist/{context-Cq18Gplu.d.ts → context-DCtsSHDv.d.ts} +2 -86
  15. package/dist/{context-tc6VOOOL.d.cts → context-DI2gRbyN.d.cts} +1 -87
  16. package/dist/{context-BAE7AKLA.mjs → context-DVoTs_wM.mjs} +1 -1
  17. package/dist/{deno--CS-SBS9.mjs → deno-h0TWFuEz.mjs} +1 -1
  18. package/dist/{docloader-k6huZLQL.mjs → docloader-BdDN0Aqx.mjs} +3 -3
  19. package/dist/federation/builder.test.mjs +138 -10
  20. package/dist/federation/collection.test.mjs +3 -3
  21. package/dist/federation/handler.test.mjs +12 -12
  22. package/dist/federation/idempotency.test.mjs +6 -6
  23. package/dist/federation/inbox.test.mjs +3 -3
  24. package/dist/federation/keycache.test.mjs +5 -5
  25. package/dist/federation/kv.test.mjs +3 -3
  26. package/dist/federation/metrics.test.mjs +63 -4
  27. package/dist/federation/middleware.test.mjs +30 -18
  28. package/dist/federation/mod.cjs +155 -3
  29. package/dist/federation/mod.d.cts +2 -2
  30. package/dist/federation/mod.d.ts +2 -2
  31. package/dist/federation/mod.js +153 -1
  32. package/dist/federation/mq.test.mjs +5 -5
  33. package/dist/federation/negotiation.test.mjs +4 -4
  34. package/dist/federation/retry.test.mjs +3 -3
  35. package/dist/federation/router.test.mjs +190 -9
  36. package/dist/federation/send.test.mjs +16 -16
  37. package/dist/federation/webfinger.test.mjs +151 -6
  38. package/dist/{getMachineId-bsd-BY01PL1n.mjs → getMachineId-bsd-etIyxDet.mjs} +1 -1
  39. package/dist/{getMachineId-darwin-Dr1gkBkp.mjs → getMachineId-darwin-D23zTf4g.mjs} +1 -1
  40. package/dist/{getMachineId-win-QEYwcJiy.mjs → getMachineId-win-Dpap6v5i.mjs} +1 -1
  41. package/dist/{http-CJfvRL7D.cjs → http-7kAB7PVx.cjs} +53 -1
  42. package/dist/{http-cqujdCRz.js → http-B2hxA7dO.js} +48 -2
  43. package/dist/{http-IywnQdiX.mjs → http-QzW9IWfs.mjs} +4 -4
  44. package/dist/{key-Df3tMleh.mjs → key-Dh2OK1XQ.mjs} +2 -2
  45. package/dist/{kv-cache-L0SMQkcd.cjs → kv-cache-DCPp-MT0.cjs} +1 -1
  46. package/dist/{kv-cache-q9Ec2ryS.mjs → kv-cache-EZRIPZXD.mjs} +1 -1
  47. package/dist/{kv-cache-pEejzYq4.js → kv-cache-b22dNkjt.js} +1 -1
  48. package/dist/{ld-BGwiJpl3.mjs → ld-eZbar1rr.mjs} +3 -3
  49. package/dist/{metrics-BTOMkW8C.mjs → metrics-E0hAHtLZ.mjs} +48 -2
  50. package/dist/{middleware-B2rtdpFV.cjs → middleware-BUl1BH4x.cjs} +163 -212
  51. package/dist/{middleware-DtOddSVg.js → middleware-BrGIM_Ra.js} +127 -164
  52. package/dist/{middleware-BB0IbDow.mjs → middleware-CyJDCmNg.mjs} +89 -37
  53. package/dist/{middleware-Dnql59Y8.mjs → middleware-mToCR2tG.mjs} +1 -1
  54. package/dist/mod-CI9fduEi.d.cts +182 -0
  55. package/dist/mod-CkRiJHGA.d.ts +182 -0
  56. package/dist/mod.cjs +6 -6
  57. package/dist/mod.d.cts +2 -2
  58. package/dist/mod.d.ts +2 -2
  59. package/dist/mod.js +5 -5
  60. package/dist/nodeinfo/client.test.mjs +4 -4
  61. package/dist/nodeinfo/handler.test.mjs +5 -5
  62. package/dist/nodeinfo/types.test.mjs +4 -4
  63. package/dist/otel/exporter.test.mjs +3 -3
  64. package/dist/{outgoing-jsonld-BNL8AC14.mjs → outgoing-jsonld-BgFLCJQ_.mjs} +1 -1
  65. package/dist/{owner-CIt4hvmM.mjs → owner-ByO_Fw6U.mjs} +2 -2
  66. package/dist/{proof-DMGIjHYH.js → proof-BkRyFchv.js} +1 -1
  67. package/dist/{proof-BYlrRSmZ.mjs → proof-CSo0S8OK.mjs} +5 -5
  68. package/dist/{proof-B1_u25UV.cjs → proof-jVqClF49.cjs} +1 -1
  69. package/dist/{send-DJFpze7B.mjs → send-jzrTV1FU.mjs} +3 -3
  70. package/dist/sig/accept.test.mjs +1 -1
  71. package/dist/sig/http.test.mjs +9 -9
  72. package/dist/sig/key.test.mjs +6 -6
  73. package/dist/sig/ld.test.mjs +7 -7
  74. package/dist/sig/mod.cjs +2 -2
  75. package/dist/sig/mod.js +2 -2
  76. package/dist/sig/owner.test.mjs +6 -6
  77. package/dist/sig/proof.test.mjs +8 -8
  78. package/dist/{std__assert-BTEgfoJo.mjs → std__assert-BBjXFNOb.mjs} +4 -4
  79. package/dist/testing/mod.d.mts +1 -0
  80. package/dist/testing/mod.mjs +1 -1
  81. package/dist/utils/docloader.test.mjs +7 -7
  82. package/dist/utils/kv-cache.test.mjs +1 -1
  83. package/dist/utils/mod.cjs +1 -1
  84. package/dist/utils/mod.js +1 -1
  85. package/package.json +6 -7
  86. package/dist/mod-CajNYYkt.d.ts +0 -63
  87. package/dist/mod-DnzgcPcy.d.cts +0 -63
  88. package/dist/router-BT_F5748.mjs +0 -114
  89. /package/dist/{accept-CgDcxvjV.mjs → accept-CceiKpCy.mjs} +0 -0
  90. /package/dist/{activity-listener-BeTGV3wc.mjs → activity-listener-tztVvlNb.mjs} +0 -0
  91. /package/dist/{assert_equals-Ew3jOFa3.mjs → assert_equals-C-ZRDbaf.mjs} +0 -0
  92. /package/dist/{client-Bneh_DYR.mjs → client-B_A6mfn3.mjs} +0 -0
  93. /package/dist/{collection-Cc3DVAhE.mjs → collection-CA3V5zyK.mjs} +0 -0
  94. /package/dist/{esm-sdtqOUPu.mjs → esm-BQRw925N.mjs} +0 -0
  95. /package/dist/{execAsync-Dxb7rNf3.mjs → execAsync-DCBrgFiV.mjs} +0 -0
  96. /package/dist/{getMachineId-linux-Bbhofx-s.mjs → getMachineId-linux-ObI47Hql.mjs} +0 -0
  97. /package/dist/{getMachineId-unsupported-dIOte2Ct.mjs → getMachineId-unsupported-Ddu-PFeh.mjs} +0 -0
  98. /package/dist/{keycache-BeU0LCII.mjs → keycache-BYMd8q7F.mjs} +0 -0
  99. /package/dist/{keys-CSYsOMFG.mjs → keys-C3kae-6B.mjs} +0 -0
  100. /package/dist/{kv-QHE0oeM3.mjs → kv-x2IvBUyq.mjs} +0 -0
  101. /package/dist/{negotiation-DDstyBvc.mjs → negotiation-CDW-_gUU.mjs} +0 -0
  102. /package/dist/{public-audience-c9zmYKgA.mjs → public-audience-N3pyOx2p.mjs} +0 -0
  103. /package/dist/{retry-_VvV0h9f.mjs → retry-v_sGLH1d.mjs} +0 -0
  104. /package/dist/{types-D09GN0uZ.mjs → types-BFowWFTT.mjs} +0 -0
@@ -1,13 +1,14 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { r as createRequestContext } from "../context-BAE7AKLA.mjs";
5
- import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
- import "../std__assert-BTEgfoJo.mjs";
7
- import { t as MemoryKvStore } from "../kv-QHE0oeM3.mjs";
8
- import { o as createFederation, s as handleWebFinger } from "../middleware-BB0IbDow.mjs";
4
+ import { r as createRequestContext } from "../context-DVoTs_wM.mjs";
5
+ import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
6
+ import "../std__assert-BBjXFNOb.mjs";
7
+ import { t as assertNotEquals } from "../assert_not_equals-DkVK8oqV.mjs";
8
+ import { t as MemoryKvStore } from "../kv-x2IvBUyq.mjs";
9
+ import { o as createFederation, s as handleWebFinger } from "../middleware-CyJDCmNg.mjs";
9
10
  import { Image, Link, Person, Tombstone } from "@fedify/vocab";
10
- import { test } from "@fedify/fixture";
11
+ import { createTestMeterProvider, test } from "@fedify/fixture";
11
12
  //#region src/federation/webfinger.test.ts
12
13
  test("handleWebFinger()", async (t) => {
13
14
  const url = new URL("https://example.com/.well-known/webfinger");
@@ -532,5 +533,149 @@ test("handleWebFinger()", async (t) => {
532
533
  });
533
534
  });
534
535
  });
536
+ test("handleWebFinger() records webfinger.handle counter and duration", async (t) => {
537
+ const url = new URL("https://example.com/.well-known/webfinger");
538
+ const actorDispatcher = (ctx, identifier) => {
539
+ if (identifier === "gone") return new Tombstone({ id: ctx.getActorUri(identifier) });
540
+ if (identifier !== "someone") return null;
541
+ return new Person({
542
+ id: ctx.getActorUri(identifier),
543
+ preferredUsername: "someone"
544
+ });
545
+ };
546
+ const onNotFound = () => new Response("Not found", { status: 404 });
547
+ function createContext(u) {
548
+ return createRequestContext({
549
+ federation: createFederation({ kv: new MemoryKvStore() }),
550
+ url: u,
551
+ data: void 0,
552
+ getActorUri(identifier) {
553
+ return new URL(`${u.origin}/users/${identifier}`);
554
+ },
555
+ async getActor(handle) {
556
+ const actor = await actorDispatcher(this, handle);
557
+ return actor instanceof Tombstone ? null : actor;
558
+ },
559
+ parseUri(uri) {
560
+ if (uri == null) return null;
561
+ if (uri.protocol === "acct:") return null;
562
+ if (!uri.pathname.startsWith("/users/")) return null;
563
+ return {
564
+ type: "actor",
565
+ identifier: uri.pathname.split("/").pop()
566
+ };
567
+ }
568
+ });
569
+ }
570
+ await t.step("records result=resolved for a 200 response", async () => {
571
+ const u = new URL(url);
572
+ u.searchParams.set("resource", "acct:someone@example.com");
573
+ const context = createContext(u);
574
+ const [meterProvider, recorder] = createTestMeterProvider();
575
+ assertEquals((await handleWebFinger(context.request, {
576
+ context,
577
+ actorDispatcher,
578
+ onNotFound,
579
+ meterProvider
580
+ })).status, 200);
581
+ const counter = recorder.getMeasurement("webfinger.handle");
582
+ assertNotEquals(counter, void 0);
583
+ assertEquals(counter?.type, "counter");
584
+ assertEquals(counter?.value, 1);
585
+ assertEquals(counter?.attributes["webfinger.handle.result"], "resolved");
586
+ assertEquals(counter?.attributes["webfinger.resource.scheme"], "acct");
587
+ assertEquals(counter?.attributes["http.response.status_code"], 200);
588
+ const duration = recorder.getMeasurement("webfinger.handle.duration");
589
+ assertNotEquals(duration, void 0);
590
+ assertEquals(duration?.type, "histogram");
591
+ assertEquals(duration?.attributes["webfinger.handle.result"], "resolved");
592
+ });
593
+ await t.step("records result=invalid for a 400 response", async () => {
594
+ const context = createContext(new URL(url));
595
+ const [meterProvider, recorder] = createTestMeterProvider();
596
+ assertEquals((await handleWebFinger(context.request, {
597
+ context,
598
+ actorDispatcher,
599
+ onNotFound,
600
+ meterProvider
601
+ })).status, 400);
602
+ const counter = recorder.getMeasurement("webfinger.handle");
603
+ assertEquals(counter?.attributes["webfinger.handle.result"], "invalid");
604
+ assertEquals(counter?.attributes["http.response.status_code"], 400);
605
+ assertEquals("webfinger.resource.scheme" in (counter?.attributes ?? {}), false, "missing resource has no scheme attribute");
606
+ });
607
+ await t.step("records result=not_found for a 404 response", async () => {
608
+ const u = new URL(url);
609
+ u.searchParams.set("resource", "acct:absent@example.com");
610
+ const context = createContext(u);
611
+ const [meterProvider, recorder] = createTestMeterProvider();
612
+ assertEquals((await handleWebFinger(context.request, {
613
+ context,
614
+ actorDispatcher,
615
+ onNotFound,
616
+ meterProvider
617
+ })).status, 404);
618
+ const counter = recorder.getMeasurement("webfinger.handle");
619
+ assertEquals(counter?.attributes["webfinger.handle.result"], "not_found");
620
+ assertEquals(counter?.attributes["http.response.status_code"], 404);
621
+ assertEquals(counter?.attributes["webfinger.resource.scheme"], "acct");
622
+ });
623
+ await t.step("records result=tombstoned for a 410 response", async () => {
624
+ const u = new URL(url);
625
+ u.searchParams.set("resource", "acct:gone@example.com");
626
+ const context = createContext(u);
627
+ const [meterProvider, recorder] = createTestMeterProvider();
628
+ assertEquals((await handleWebFinger(context.request, {
629
+ context,
630
+ actorDispatcher,
631
+ onNotFound,
632
+ meterProvider
633
+ })).status, 410);
634
+ const counter = recorder.getMeasurement("webfinger.handle");
635
+ assertEquals(counter?.attributes["webfinger.handle.result"], "tombstoned");
636
+ assertEquals(counter?.attributes["http.response.status_code"], 410);
637
+ });
638
+ await t.step("records result=not_found when onNotFound returns 200", async () => {
639
+ const u = new URL(url);
640
+ u.searchParams.set("resource", "acct:absent@example.com");
641
+ const context = createContext(u);
642
+ const [meterProvider, recorder] = createTestMeterProvider();
643
+ assertEquals((await handleWebFinger(context.request, {
644
+ context,
645
+ actorDispatcher,
646
+ onNotFound: () => new Response("custom fallback", { status: 200 }),
647
+ meterProvider
648
+ })).status, 200);
649
+ const counter = recorder.getMeasurement("webfinger.handle");
650
+ assertEquals(counter?.attributes["webfinger.handle.result"], "not_found");
651
+ assertEquals(counter?.attributes["http.response.status_code"], 200);
652
+ });
653
+ await t.step("buckets unknown resource schemes as 'other' to keep metric cardinality bounded", async () => {
654
+ const u = new URL(url);
655
+ u.searchParams.set("resource", "ssh:nobody@example.com");
656
+ const context = createContext(u);
657
+ const [meterProvider, recorder] = createTestMeterProvider();
658
+ await handleWebFinger(context.request, {
659
+ context,
660
+ actorDispatcher,
661
+ onNotFound,
662
+ meterProvider
663
+ });
664
+ assertEquals(recorder.getMeasurement("webfinger.handle")?.attributes["webfinger.resource.scheme"], "other");
665
+ });
666
+ await t.step("omits measurements when no meterProvider is provided", async () => {
667
+ const u = new URL(url);
668
+ u.searchParams.set("resource", "acct:someone@example.com");
669
+ const context = createContext(u);
670
+ const [_unused, recorder] = createTestMeterProvider();
671
+ await handleWebFinger(context.request, {
672
+ context,
673
+ actorDispatcher,
674
+ onNotFound
675
+ });
676
+ assertEquals(recorder.getMeasurements("webfinger.handle").length, 0);
677
+ assertEquals(recorder.getMeasurements("webfinger.handle.duration").length, 0);
678
+ });
679
+ });
535
680
  //#endregion
536
681
  export {};
@@ -2,7 +2,7 @@ import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as __require, t as __commonJSMin } from "./chunk-DNRtMIoB.mjs";
5
- import { t as require_execAsync } from "./execAsync-Dxb7rNf3.mjs";
5
+ import { t as require_execAsync } from "./execAsync-DCBrgFiV.mjs";
6
6
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.1_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-bsd.js
7
7
  var require_getMachineId_bsd = /* @__PURE__ */ __commonJSMin(((exports) => {
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -2,7 +2,7 @@ import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as __require, t as __commonJSMin } from "./chunk-DNRtMIoB.mjs";
5
- import { t as require_execAsync } from "./execAsync-Dxb7rNf3.mjs";
5
+ import { t as require_execAsync } from "./execAsync-DCBrgFiV.mjs";
6
6
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.1_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-darwin.js
7
7
  var require_getMachineId_darwin = /* @__PURE__ */ __commonJSMin(((exports) => {
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -2,7 +2,7 @@ import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as __require, t as __commonJSMin } from "./chunk-DNRtMIoB.mjs";
5
- import { t as require_execAsync } from "./execAsync-Dxb7rNf3.mjs";
5
+ import { t as require_execAsync } from "./execAsync-DCBrgFiV.mjs";
6
6
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.1_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-win.js
7
7
  var require_getMachineId_win = /* @__PURE__ */ __commonJSMin(((exports) => {
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -11,7 +11,7 @@ let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conve
11
11
  let byte_encodings_base64 = require("byte-encodings/base64");
12
12
  //#region deno.json
13
13
  var name = "@fedify/fedify";
14
- var version = "2.3.0-dev.1131+553b59b8";
14
+ var version = "2.3.0-dev.1145+3fe78022";
15
15
  //#endregion
16
16
  //#region src/sig/accept.ts
17
17
  /**
@@ -177,6 +177,8 @@ var FederationMetrics = class {
177
177
  documentFetch;
178
178
  documentFetchDuration;
179
179
  documentCache;
180
+ webFingerHandle;
181
+ webFingerHandleDuration;
180
182
  constructor(meterProvider) {
181
183
  const meter = meterProvider.getMeter(name, version);
182
184
  this.deliverySent = meter.createCounter("activitypub.delivery.sent", {
@@ -335,6 +337,30 @@ var FederationMetrics = class {
335
337
  description: "KV-backed document loader cache lookups, with `hit` or `miss` classification.",
336
338
  unit: "{lookup}"
337
339
  });
340
+ this.webFingerHandle = meter.createCounter("webfinger.handle", {
341
+ description: "Incoming WebFinger requests handled by Fedify, classified by terminal outcome.",
342
+ unit: "{request}"
343
+ });
344
+ this.webFingerHandleDuration = meter.createHistogram("webfinger.handle.duration", {
345
+ description: "Duration of incoming WebFinger request handling in Fedify.",
346
+ unit: "ms",
347
+ advice: { explicitBucketBoundaries: [
348
+ 5,
349
+ 10,
350
+ 25,
351
+ 50,
352
+ 75,
353
+ 100,
354
+ 250,
355
+ 500,
356
+ 750,
357
+ 1e3,
358
+ 2500,
359
+ 5e3,
360
+ 7500,
361
+ 1e4
362
+ ] }
363
+ });
338
364
  }
339
365
  recordDelivery(inbox, durationMs, success, activityType) {
340
366
  const deliveryAttributes = {
@@ -448,6 +474,13 @@ var FederationMetrics = class {
448
474
  if (attrs.remoteUrl != null) attributes["activitypub.remote.host"] = getRemoteHost(attrs.remoteUrl);
449
475
  this.documentCache.add(1, attributes);
450
476
  }
477
+ recordWebFingerHandle(attrs) {
478
+ const attributes = { "webfinger.handle.result": attrs.result };
479
+ if (attrs.scheme != null) attributes["webfinger.resource.scheme"] = attrs.scheme;
480
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
481
+ this.webFingerHandle.add(1, attributes);
482
+ this.webFingerHandleDuration.record(attrs.durationMs, attributes);
483
+ }
451
484
  };
452
485
  function buildActivityLifecycleAttributes(result, activityType) {
453
486
  const attributes = { "activitypub.processing.result": result };
@@ -572,6 +605,19 @@ function recordDocumentCache(meterProvider, attrs) {
572
605
  getFederationMetrics(meterProvider).recordDocumentCache(attrs);
573
606
  }
574
607
  /**
608
+ * Records one measurement on `webfinger.handle` (counter) and
609
+ * `webfinger.handle.duration` (histogram) for an incoming WebFinger
610
+ * request handled by Fedify. Counter and histogram are always recorded
611
+ * together, with `webfinger.handle.result` set to one of `resolved`,
612
+ * `invalid`, `not_found`, `tombstoned`, or `error`. The queried
613
+ * resource string is deliberately excluded; it remains on the
614
+ * `webfinger.handle` span for trace-level investigation.
615
+ * @since 2.3.0
616
+ */
617
+ function recordWebFingerHandle(meterProvider, attrs) {
618
+ getFederationMetrics(meterProvider).recordWebFingerHandle(attrs);
619
+ }
620
+ /**
575
621
  * Classifies a thrown value from a key or document fetch into the bounded
576
622
  * {@link LookupResult} taxonomy and, when an HTTP response was received,
577
623
  * surfaces its status code.
@@ -2309,6 +2355,12 @@ Object.defineProperty(exports, "recordOutboxEnqueue", {
2309
2355
  return recordOutboxEnqueue;
2310
2356
  }
2311
2357
  });
2358
+ Object.defineProperty(exports, "recordWebFingerHandle", {
2359
+ enumerable: true,
2360
+ get: function() {
2361
+ return recordWebFingerHandle;
2362
+ }
2363
+ });
2312
2364
  Object.defineProperty(exports, "signRequest", {
2313
2365
  enumerable: true,
2314
2366
  get: function() {
@@ -10,7 +10,7 @@ import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } fro
10
10
  import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
11
11
  //#region deno.json
12
12
  var name = "@fedify/fedify";
13
- var version = "2.3.0-dev.1131+553b59b8";
13
+ var version = "2.3.0-dev.1145+3fe78022";
14
14
  //#endregion
15
15
  //#region src/sig/accept.ts
16
16
  /**
@@ -176,6 +176,8 @@ var FederationMetrics = class {
176
176
  documentFetch;
177
177
  documentFetchDuration;
178
178
  documentCache;
179
+ webFingerHandle;
180
+ webFingerHandleDuration;
179
181
  constructor(meterProvider) {
180
182
  const meter = meterProvider.getMeter(name, version);
181
183
  this.deliverySent = meter.createCounter("activitypub.delivery.sent", {
@@ -334,6 +336,30 @@ var FederationMetrics = class {
334
336
  description: "KV-backed document loader cache lookups, with `hit` or `miss` classification.",
335
337
  unit: "{lookup}"
336
338
  });
339
+ this.webFingerHandle = meter.createCounter("webfinger.handle", {
340
+ description: "Incoming WebFinger requests handled by Fedify, classified by terminal outcome.",
341
+ unit: "{request}"
342
+ });
343
+ this.webFingerHandleDuration = meter.createHistogram("webfinger.handle.duration", {
344
+ description: "Duration of incoming WebFinger request handling in Fedify.",
345
+ unit: "ms",
346
+ advice: { explicitBucketBoundaries: [
347
+ 5,
348
+ 10,
349
+ 25,
350
+ 50,
351
+ 75,
352
+ 100,
353
+ 250,
354
+ 500,
355
+ 750,
356
+ 1e3,
357
+ 2500,
358
+ 5e3,
359
+ 7500,
360
+ 1e4
361
+ ] }
362
+ });
337
363
  }
338
364
  recordDelivery(inbox, durationMs, success, activityType) {
339
365
  const deliveryAttributes = {
@@ -447,6 +473,13 @@ var FederationMetrics = class {
447
473
  if (attrs.remoteUrl != null) attributes["activitypub.remote.host"] = getRemoteHost(attrs.remoteUrl);
448
474
  this.documentCache.add(1, attributes);
449
475
  }
476
+ recordWebFingerHandle(attrs) {
477
+ const attributes = { "webfinger.handle.result": attrs.result };
478
+ if (attrs.scheme != null) attributes["webfinger.resource.scheme"] = attrs.scheme;
479
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
480
+ this.webFingerHandle.add(1, attributes);
481
+ this.webFingerHandleDuration.record(attrs.durationMs, attributes);
482
+ }
450
483
  };
451
484
  function buildActivityLifecycleAttributes(result, activityType) {
452
485
  const attributes = { "activitypub.processing.result": result };
@@ -571,6 +604,19 @@ function recordDocumentCache(meterProvider, attrs) {
571
604
  getFederationMetrics(meterProvider).recordDocumentCache(attrs);
572
605
  }
573
606
  /**
607
+ * Records one measurement on `webfinger.handle` (counter) and
608
+ * `webfinger.handle.duration` (histogram) for an incoming WebFinger
609
+ * request handled by Fedify. Counter and histogram are always recorded
610
+ * together, with `webfinger.handle.result` set to one of `resolved`,
611
+ * `invalid`, `not_found`, `tombstoned`, or `error`. The queried
612
+ * resource string is deliberately excluded; it remains on the
613
+ * `webfinger.handle` span for trace-level investigation.
614
+ * @since 2.3.0
615
+ */
616
+ function recordWebFingerHandle(meterProvider, attrs) {
617
+ getFederationMetrics(meterProvider).recordWebFingerHandle(attrs);
618
+ }
619
+ /**
574
620
  * Classifies a thrown value from a key or document fetch into the bounded
575
621
  * {@link LookupResult} taxonomy and, when an HTTP response was received,
576
622
  * surfaces its status code.
@@ -2176,4 +2222,4 @@ function timingSafeEqual(a, b) {
2176
2222
  return result === 0;
2177
2223
  }
2178
2224
  //#endregion
2179
- export { formatAcceptSignature as C, name as D, validateAcceptSignature as E, version as O, recordOutboxEnqueue as S, parseAcceptSignature as T, measureSignatureKeyFetch as _, verifyRequestDetailed as a, recordInboxActivity as b, fetchKeyDetailed as c, validateCryptoKey as d, getDurationMs as f, isAbortError$1 as g, instrumentDocumentLoader as h, verifyRequest as i, generateCryptoKeyPair as l, getRemoteHost as m, parseRfc9421SignatureInput as n, exportJwk as o, getFederationMetrics as p, signRequest as r, fetchKey as s, doubleKnock as t, importJwk as u, recordDocumentCache as v, fulfillAcceptSignature as w, recordOutboxActivity as x, recordFanoutRecipients as y };
2225
+ export { recordWebFingerHandle as C, validateAcceptSignature as D, parseAcceptSignature as E, name as O, recordOutboxEnqueue as S, fulfillAcceptSignature as T, measureSignatureKeyFetch as _, verifyRequestDetailed as a, recordInboxActivity as b, fetchKeyDetailed as c, validateCryptoKey as d, getDurationMs as f, isAbortError$1 as g, instrumentDocumentLoader as h, verifyRequest as i, version as k, generateCryptoKeyPair as l, getRemoteHost as m, parseRfc9421SignatureInput as n, exportJwk as o, getFederationMetrics as p, signRequest as r, fetchKey as s, doubleKnock as t, importJwk as u, recordDocumentCache as v, formatAcceptSignature as w, recordOutboxActivity as x, recordFanoutRecipients as y };
@@ -1,10 +1,10 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno--CS-SBS9.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-BTOMkW8C.mjs";
6
- import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-CgDcxvjV.mjs";
7
- import { o as validateCryptoKey, r as fetchKeyDetailed } from "./key-Df3tMleh.mjs";
4
+ import { n as version, t as name } from "./deno-h0TWFuEz.mjs";
5
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-E0hAHtLZ.mjs";
6
+ import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-CceiKpCy.mjs";
7
+ import { o as validateCryptoKey, r as fetchKeyDetailed } from "./key-Dh2OK1XQ.mjs";
8
8
  import { getLogger } from "@logtape/logtape";
9
9
  import { CryptographicKey } from "@fedify/vocab";
10
10
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -1,8 +1,8 @@
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--CS-SBS9.mjs";
5
- import { f as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-BTOMkW8C.mjs";
4
+ import { n as version, t as name } from "./deno-h0TWFuEz.mjs";
5
+ import { f as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-E0hAHtLZ.mjs";
6
6
  import { getLogger } from "@logtape/logtape";
7
7
  import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
8
8
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -1,7 +1,7 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  require("./chunk-DDcVe30Y.cjs");
4
- const require_http = require("./http-CJfvRL7D.cjs");
4
+ const require_http = require("./http-7kAB7PVx.cjs");
5
5
  let _logtape_logtape = require("@logtape/logtape");
6
6
  let es_toolkit = require("es-toolkit");
7
7
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { c as recordDocumentCache } from "./metrics-BTOMkW8C.mjs";
4
+ import { c as recordDocumentCache } from "./metrics-E0hAHtLZ.mjs";
5
5
  import { getLogger } from "@logtape/logtape";
6
6
  import { preloadedContexts } from "@fedify/vocab-runtime";
7
7
  //#region src/utils/kv-cache.ts
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { d as validateCryptoKey, t as doubleKnock, v as recordDocumentCache } from "./http-cqujdCRz.js";
3
+ import { d as validateCryptoKey, t as doubleKnock, v as recordDocumentCache } from "./http-B2hxA7dO.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { curry } from "es-toolkit";
6
6
  import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
@@ -1,9 +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--CS-SBS9.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-BTOMkW8C.mjs";
6
- import { n as fetchKey, o as validateCryptoKey } from "./key-Df3tMleh.mjs";
4
+ import { n as version, t as name } from "./deno-h0TWFuEz.mjs";
5
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-E0hAHtLZ.mjs";
6
+ import { n as fetchKey, o as validateCryptoKey } from "./key-Dh2OK1XQ.mjs";
7
7
  import { getLogger } from "@logtape/logtape";
8
8
  import { Activity, CryptographicKey, Object as Object$1, getTypeId } from "@fedify/vocab";
9
9
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -1,7 +1,7 @@
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--CS-SBS9.mjs";
4
+ import { n as version, t as name } from "./deno-h0TWFuEz.mjs";
5
5
  import { metrics } from "@opentelemetry/api";
6
6
  import { FetchError } from "@fedify/vocab-runtime";
7
7
  //#region src/federation/metrics.ts
@@ -29,6 +29,8 @@ var FederationMetrics = class {
29
29
  documentFetch;
30
30
  documentFetchDuration;
31
31
  documentCache;
32
+ webFingerHandle;
33
+ webFingerHandleDuration;
32
34
  constructor(meterProvider) {
33
35
  const meter = meterProvider.getMeter(name, version);
34
36
  this.deliverySent = meter.createCounter("activitypub.delivery.sent", {
@@ -187,6 +189,30 @@ var FederationMetrics = class {
187
189
  description: "KV-backed document loader cache lookups, with `hit` or `miss` classification.",
188
190
  unit: "{lookup}"
189
191
  });
192
+ this.webFingerHandle = meter.createCounter("webfinger.handle", {
193
+ description: "Incoming WebFinger requests handled by Fedify, classified by terminal outcome.",
194
+ unit: "{request}"
195
+ });
196
+ this.webFingerHandleDuration = meter.createHistogram("webfinger.handle.duration", {
197
+ description: "Duration of incoming WebFinger request handling in Fedify.",
198
+ unit: "ms",
199
+ advice: { explicitBucketBoundaries: [
200
+ 5,
201
+ 10,
202
+ 25,
203
+ 50,
204
+ 75,
205
+ 100,
206
+ 250,
207
+ 500,
208
+ 750,
209
+ 1e3,
210
+ 2500,
211
+ 5e3,
212
+ 7500,
213
+ 1e4
214
+ ] }
215
+ });
190
216
  }
191
217
  recordDelivery(inbox, durationMs, success, activityType) {
192
218
  const deliveryAttributes = {
@@ -300,6 +326,13 @@ var FederationMetrics = class {
300
326
  if (attrs.remoteUrl != null) attributes["activitypub.remote.host"] = getRemoteHost(attrs.remoteUrl);
301
327
  this.documentCache.add(1, attributes);
302
328
  }
329
+ recordWebFingerHandle(attrs) {
330
+ const attributes = { "webfinger.handle.result": attrs.result };
331
+ if (attrs.scheme != null) attributes["webfinger.resource.scheme"] = attrs.scheme;
332
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
333
+ this.webFingerHandle.add(1, attributes);
334
+ this.webFingerHandleDuration.record(attrs.durationMs, attributes);
335
+ }
303
336
  };
304
337
  function buildActivityLifecycleAttributes(result, activityType) {
305
338
  const attributes = { "activitypub.processing.result": result };
@@ -424,6 +457,19 @@ function recordDocumentCache(meterProvider, attrs) {
424
457
  getFederationMetrics(meterProvider).recordDocumentCache(attrs);
425
458
  }
426
459
  /**
460
+ * Records one measurement on `webfinger.handle` (counter) and
461
+ * `webfinger.handle.duration` (histogram) for an incoming WebFinger
462
+ * request handled by Fedify. Counter and histogram are always recorded
463
+ * together, with `webfinger.handle.result` set to one of `resolved`,
464
+ * `invalid`, `not_found`, `tombstoned`, or `error`. The queried
465
+ * resource string is deliberately excluded; it remains on the
466
+ * `webfinger.handle` span for trace-level investigation.
467
+ * @since 2.3.0
468
+ */
469
+ function recordWebFingerHandle(meterProvider, attrs) {
470
+ getFederationMetrics(meterProvider).recordWebFingerHandle(attrs);
471
+ }
472
+ /**
427
473
  * Classifies a thrown value from a key or document fetch into the bounded
428
474
  * {@link LookupResult} taxonomy and, when an HTTP response was received,
429
475
  * surfaces its status code.
@@ -586,4 +632,4 @@ function getDurationMs(start) {
586
632
  return Math.max(0, performance.now() - start);
587
633
  }
588
634
  //#endregion
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 };
635
+ export { instrumentDocumentLoader as a, recordDocumentCache as c, recordInboxActivity as d, recordKeyLookup as f, recordWebFingerHandle as h, 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 };