@fedify/fedify 2.3.0-dev.994 → 2.3.0-pr.809.37

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 (138) hide show
  1. package/README.md +16 -7
  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-B-qJtC9Z.mjs → assert_rejects-DN60FHPX.mjs} +28 -3
  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-yAlpiIqP.mjs → builder-Dl5Sdmc9.mjs} +96 -42
  9. package/dist/{chunk-nlSIicah.js → chunk-CRNNMoPX.js} +2 -2
  10. package/dist/circuit-breaker-CSWsyoef.mjs +337 -0
  11. package/dist/{client-z-8dc-e1.d.cts → client-CAM_bQXx.d.cts} +1 -0
  12. package/dist/{client-AtlibPOU.d.ts → client-CSddvgWN.d.ts} +1 -2
  13. package/dist/compat/mod.d.cts +2 -1
  14. package/dist/compat/mod.d.ts +2 -3
  15. package/dist/compat/mod.js +3 -3
  16. package/dist/compat/outgoing-jsonld.test.mjs +4 -4
  17. package/dist/compat/public-audience.test.mjs +4 -4
  18. package/dist/compat/transformers.test.mjs +5 -5
  19. package/dist/{context-BzH2-ajs.d.ts → context-BBVLF7lx.d.cts} +342 -269
  20. package/dist/{context-DJGagtNd.d.cts → context-BU6jSQdo.d.ts} +344 -266
  21. package/dist/{context-Dk_tacqz.mjs → context-DVoTs_wM.mjs} +64 -5
  22. package/dist/{deno-XBVlKnOX.mjs → deno-BID9qTPk.mjs} +1 -1
  23. package/dist/{docloader-DPp-X6gk.mjs → docloader-BYFuVwwi.mjs} +4 -4
  24. package/dist/{esm-DVILvP5e.mjs → esm-vrlUxr60.mjs} +4 -7
  25. package/dist/federation/builder.test.mjs +163 -11
  26. package/dist/federation/circuit-breaker.test.d.mts +2 -0
  27. package/dist/federation/circuit-breaker.test.mjs +446 -0
  28. package/dist/federation/collection.test.mjs +3 -3
  29. package/dist/federation/handler.test.mjs +1770 -29
  30. package/dist/federation/idempotency.test.mjs +5 -5
  31. package/dist/federation/inbox.test.mjs +4 -4
  32. package/dist/federation/keycache.test.mjs +5 -5
  33. package/dist/federation/kv.test.mjs +2 -2
  34. package/dist/federation/metrics.test.d.mts +2 -0
  35. package/dist/federation/metrics.test.mjs +634 -0
  36. package/dist/federation/middleware.test.mjs +4036 -146
  37. package/dist/federation/mod.cjs +195 -14
  38. package/dist/federation/mod.d.cts +6 -4
  39. package/dist/federation/mod.d.ts +6 -6
  40. package/dist/federation/mod.js +192 -14
  41. package/dist/federation/mq.test.mjs +176 -15
  42. package/dist/federation/negotiation.test.mjs +4 -4
  43. package/dist/federation/retry.test.mjs +3 -3
  44. package/dist/federation/router.test.mjs +190 -9
  45. package/dist/federation/send.test.mjs +153 -15
  46. package/dist/federation/temporal.test.d.mts +2 -0
  47. package/dist/federation/temporal.test.mjs +71 -0
  48. package/dist/federation/webfinger.test.mjs +150 -5
  49. package/dist/{http-B4rrzxtg.js → http-B47Jg9iX.js} +994 -64
  50. package/dist/{http-CSlLA54v.mjs → http-CO59u1Yk.mjs} +146 -47
  51. package/dist/{http-BrGqJDXL.cjs → http-CjZwUPiT.cjs} +1099 -61
  52. package/dist/{http-aQzN9Ayi.d.ts → http-VyDTd4G3.d.cts} +15 -3
  53. package/dist/{http-CrGuipxe.d.cts → http-lf8Hsd91.d.ts} +15 -1
  54. package/dist/{key-Bzx--sHD.mjs → key-Dqdlo3mG.mjs} +43 -18
  55. package/dist/{kv-CbLNp3zQ.d.cts → kv-D6hNiMTK.d.ts} +1 -0
  56. package/dist/{kv-cache-DI-Sk7-I.cjs → kv-cache-BQ1-zDKX.cjs} +20 -3
  57. package/dist/{kv-cache-EMIqoIuB.js → kv-cache-Dh2ZH9Gr.js} +20 -3
  58. package/dist/{kv-cache-U__xU4qR.mjs → kv-cache-DhLkLv0P.mjs} +21 -3
  59. package/dist/{kv-GFYnFoOl.d.ts → kv-gJ8LYbxX.d.cts} +1 -3
  60. package/dist/ld-JbCByP5P.mjs +626 -0
  61. package/dist/metrics-ColY3QKj.mjs +815 -0
  62. package/dist/{middleware-zjZ6AFCW.mjs → middleware-Buqzuiaf.mjs} +1 -1
  63. package/dist/{middleware-An4DjES4.cjs → middleware-CWQ3Yi07.cjs} +2318 -623
  64. package/dist/{middleware-K1g6bEkV.mjs → middleware-Cg4EyWsq.mjs} +1639 -373
  65. package/dist/{middleware-nidH7VZH.js → middleware-iAJY2aU-.js} +2260 -556
  66. package/dist/{mod-CR8soWa9.d.ts → mod-B0hW12_O.d.cts} +26 -4
  67. package/dist/mod-C0F6kvgS.d.cts +182 -0
  68. package/dist/{mod-Cr3f-ACa.d.cts → mod-COIAjwRS.d.ts} +26 -2
  69. package/dist/{mod-CMEbIaNh.d.cts → mod-DFvNJcNb.d.ts} +56 -4
  70. package/dist/mod-vPYVoa5n.d.ts +182 -0
  71. package/dist/{mod-CLgIXe9w.d.ts → mod-yvIXFAEi.d.cts} +56 -6
  72. package/dist/mod.cjs +9 -6
  73. package/dist/mod.d.cts +12 -10
  74. package/dist/mod.d.ts +12 -12
  75. package/dist/mod.js +11 -11
  76. package/dist/mq-D-nlpY04.d.ts +208 -0
  77. package/dist/mq-D8uSFzxe.d.cts +208 -0
  78. package/dist/{negotiation-SQvQgUqe.mjs → negotiation-DDstyBvc.mjs} +29 -0
  79. package/dist/nodeinfo/client.test.mjs +4 -4
  80. package/dist/nodeinfo/handler.test.mjs +4 -4
  81. package/dist/nodeinfo/mod.d.cts +2 -1
  82. package/dist/nodeinfo/mod.d.ts +2 -3
  83. package/dist/nodeinfo/mod.js +3 -3
  84. package/dist/nodeinfo/types.test.mjs +3 -3
  85. package/dist/otel/exporter.test.mjs +28 -25
  86. package/dist/otel/mod.cjs +6 -5
  87. package/dist/otel/mod.d.cts +5 -3
  88. package/dist/otel/mod.d.ts +5 -5
  89. package/dist/otel/mod.js +8 -7
  90. package/dist/{outgoing-jsonld-CNmZLixq.mjs → outgoing-jsonld-L_DbOaFe.mjs} +2 -2
  91. package/dist/{owner-C-tqfvxn.mjs → owner-B4LgOmzV.mjs} +2 -2
  92. package/dist/{owner-CptqhsOy.d.cts → owner-CnngXDNJ.d.ts} +2 -1
  93. package/dist/{owner-74ARJ5TL.d.ts → owner-DEvZuyOE.d.cts} +2 -3
  94. package/dist/{proof-BlmRPzrK.mjs → proof-BoSGOq9q.mjs} +26 -8
  95. package/dist/{proof-Bbbwf8_x.js → proof-CzhZuawt.js} +378 -15
  96. package/dist/{proof-BW89QMVB.cjs → proof-DvMI-3vq.cjs} +432 -15
  97. package/dist/runtime/mod.d.cts +1 -0
  98. package/dist/runtime/mod.d.ts +1 -2
  99. package/dist/runtime/mod.js +3 -3
  100. package/dist/{send-05pJLcb6.mjs → send-ByK1Q7DK.mjs} +72 -26
  101. package/dist/sig/http.test.mjs +356 -33
  102. package/dist/sig/key.test.mjs +103 -6
  103. package/dist/sig/ld.test.mjs +696 -7
  104. package/dist/sig/mod.cjs +2 -2
  105. package/dist/sig/mod.d.cts +4 -3
  106. package/dist/sig/mod.d.ts +4 -5
  107. package/dist/sig/mod.js +4 -4
  108. package/dist/sig/owner.test.mjs +6 -6
  109. package/dist/sig/proof.test.mjs +169 -8
  110. package/dist/{std__assert-CRDpx_HF.mjs → std__assert-BBjXFNOb.mjs} +5 -30
  111. package/dist/temporal-LpjyAjKb.mjs +95 -0
  112. package/dist/testing/mod.d.mts +110 -10
  113. package/dist/testing/mod.mjs +1 -2
  114. package/dist/{transformers-ve6e2xcg.js → transformers-BGMIq1cs.js} +2 -2
  115. package/dist/{types-hvL8ElAs.js → types-CAY3OdLq.js} +2 -2
  116. package/dist/utils/docloader.test.mjs +6 -6
  117. package/dist/utils/kv-cache.test.mjs +67 -2
  118. package/dist/utils/mod.cjs +1 -1
  119. package/dist/utils/mod.d.cts +2 -1
  120. package/dist/utils/mod.d.ts +2 -3
  121. package/dist/utils/mod.js +3 -3
  122. package/dist/vocab/cjs.test.mjs +1 -1
  123. package/dist/vocab/mod.d.cts +1 -0
  124. package/dist/vocab/mod.d.ts +1 -2
  125. package/dist/vocab/mod.js +2 -2
  126. package/package.json +16 -16
  127. package/dist/ld-DR78beiv.mjs +0 -279
  128. package/dist/middleware-BFBaR0mF.cjs +0 -4
  129. package/dist/mod-2d12ffz3.d.ts +0 -64
  130. package/dist/mod-D35TRn09.d.cts +0 -62
  131. package/dist/router-CrMLXoOr.mjs +0 -114
  132. package/dist/{activity-listener-ell7W1s9.mjs → activity-listener-tztVvlNb.mjs} +0 -0
  133. package/dist/{assert_equals-Ew3jOFa3.mjs → assert_equals-C-ZRDbaf.mjs} +0 -0
  134. package/dist/{client-D_1QpnWt.mjs → client-ByXmQhYD.mjs} +1 -1
  135. package/dist/{collection-D-HqUuA2.mjs → collection-Cc3DVAhE.mjs} +0 -0
  136. package/dist/{keycache-EGATflN-.mjs → keycache-BeU0LCII.mjs} +0 -0
  137. package/dist/{public-audience-DYFHzm_c.mjs → public-audience-Cvbr2Gzt.mjs} +1 -1
  138. /package/dist/{retry-bMXBL97A.mjs → retry-CXg_MBI-.mjs} +0 -0
@@ -0,0 +1,815 @@
1
+ import "@js-temporal/polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { n as version, t as name } from "./deno-BID9qTPk.mjs";
5
+ import { metrics } from "@opentelemetry/api";
6
+ import { FetchError } from "@fedify/vocab-runtime";
7
+ //#region src/federation/metrics.ts
8
+ var FederationMetrics = class {
9
+ deliverySent;
10
+ deliveryPermanentFailure;
11
+ signatureVerificationFailure;
12
+ signatureVerificationDuration;
13
+ signatureKeyFetchDuration;
14
+ deliveryDuration;
15
+ inboxProcessingDuration;
16
+ httpServerRequestCount;
17
+ httpServerRequestDuration;
18
+ queueTaskEnqueued;
19
+ queueTaskStarted;
20
+ queueTaskCompleted;
21
+ queueTaskFailed;
22
+ queueTaskDuration;
23
+ queueTaskInFlight;
24
+ queueDepth;
25
+ fanoutRecipients;
26
+ inboxActivity;
27
+ outboxActivity;
28
+ circuitBreakerStateChange;
29
+ keyLookup;
30
+ keyLookupDuration;
31
+ documentFetch;
32
+ documentFetchDuration;
33
+ documentCache;
34
+ webFingerHandle;
35
+ webFingerHandleDuration;
36
+ collectionRequest;
37
+ collectionDispatchDuration;
38
+ collectionPageItems;
39
+ collectionTotalItems;
40
+ constructor(meterProvider) {
41
+ const meter = meterProvider.getMeter(name, version);
42
+ this.deliverySent = meter.createCounter("activitypub.delivery.sent", {
43
+ description: "ActivityPub delivery attempts.",
44
+ unit: "{attempt}"
45
+ });
46
+ this.deliveryPermanentFailure = meter.createCounter("activitypub.delivery.permanent_failure", {
47
+ description: "ActivityPub deliveries abandoned as permanent failures.",
48
+ unit: "{failure}"
49
+ });
50
+ this.signatureVerificationFailure = meter.createCounter("activitypub.signature.verification_failure", {
51
+ description: "ActivityPub signature verification failures.",
52
+ unit: "{failure}"
53
+ });
54
+ this.signatureVerificationDuration = meter.createHistogram("activitypub.signature.verification.duration", {
55
+ description: "Duration of ActivityPub signature verification, including local key lookup and remote key fetches.",
56
+ unit: "ms",
57
+ advice: { explicitBucketBoundaries: [
58
+ .1,
59
+ .25,
60
+ .5,
61
+ 1,
62
+ 2.5,
63
+ 5,
64
+ 10,
65
+ 25,
66
+ 50,
67
+ 100,
68
+ 250,
69
+ 500,
70
+ 1e3
71
+ ] }
72
+ });
73
+ this.signatureKeyFetchDuration = meter.createHistogram("activitypub.signature.key_fetch.duration", {
74
+ description: "Duration of public key lookup performed during ActivityPub signature verification.",
75
+ unit: "ms"
76
+ });
77
+ this.deliveryDuration = meter.createHistogram("activitypub.delivery.duration", {
78
+ description: "Duration of ActivityPub delivery attempts.",
79
+ unit: "ms"
80
+ });
81
+ this.inboxProcessingDuration = meter.createHistogram("activitypub.inbox.processing_duration", {
82
+ description: "Duration of ActivityPub inbox listener processing.",
83
+ unit: "ms"
84
+ });
85
+ this.httpServerRequestCount = meter.createCounter("fedify.http.server.request.count", {
86
+ description: "HTTP requests handled by Federation.fetch().",
87
+ unit: "{request}"
88
+ });
89
+ this.httpServerRequestDuration = meter.createHistogram("fedify.http.server.request.duration", {
90
+ description: "Duration of HTTP requests handled by Federation.fetch().",
91
+ unit: "ms",
92
+ advice: { explicitBucketBoundaries: [
93
+ 5,
94
+ 10,
95
+ 25,
96
+ 50,
97
+ 75,
98
+ 100,
99
+ 250,
100
+ 500,
101
+ 750,
102
+ 1e3,
103
+ 2500,
104
+ 5e3,
105
+ 7500,
106
+ 1e4
107
+ ] }
108
+ });
109
+ this.queueTaskEnqueued = meter.createCounter("fedify.queue.task.enqueued", {
110
+ description: "Tasks Fedify enqueued for inbox, outbox, or fanout work.",
111
+ unit: "{task}"
112
+ });
113
+ this.queueTaskStarted = meter.createCounter("fedify.queue.task.started", {
114
+ description: "Tasks Fedify began processing as a queue worker.",
115
+ unit: "{task}"
116
+ });
117
+ this.queueTaskCompleted = meter.createCounter("fedify.queue.task.completed", {
118
+ description: "Queue tasks Fedify finished processing without throwing.",
119
+ unit: "{task}"
120
+ });
121
+ this.queueTaskFailed = meter.createCounter("fedify.queue.task.failed", {
122
+ description: "Queue tasks Fedify abandoned because processing threw.",
123
+ unit: "{task}"
124
+ });
125
+ this.queueTaskDuration = meter.createHistogram("fedify.queue.task.duration", {
126
+ description: "Duration of queue task processing in Fedify workers.",
127
+ unit: "ms",
128
+ advice: { explicitBucketBoundaries: [
129
+ 5,
130
+ 10,
131
+ 25,
132
+ 50,
133
+ 75,
134
+ 100,
135
+ 250,
136
+ 500,
137
+ 750,
138
+ 1e3,
139
+ 2500,
140
+ 5e3,
141
+ 7500,
142
+ 1e4
143
+ ] }
144
+ });
145
+ this.queueTaskInFlight = meter.createUpDownCounter("fedify.queue.task.in_flight", {
146
+ description: "Queue tasks currently being processed in this Fedify process.",
147
+ unit: "{task}"
148
+ });
149
+ this.queueDepth = meter.createObservableGauge("fedify.queue.depth", {
150
+ description: "Messages waiting in configured Fedify queues, as reported by the queue backend.",
151
+ unit: "{message}"
152
+ });
153
+ this.fanoutRecipients = meter.createHistogram("activitypub.fanout.recipients", {
154
+ description: "Number of recipient inboxes produced by an ActivityPub fanout task.",
155
+ unit: "{recipient}"
156
+ });
157
+ this.inboxActivity = meter.createCounter("activitypub.inbox.activity", {
158
+ description: "ActivityPub activities observed at the inbox lifecycle level: queued, processed, retried, rejected, or abandoned.",
159
+ unit: "{activity}"
160
+ });
161
+ this.outboxActivity = meter.createCounter("activitypub.outbox.activity", {
162
+ description: "ActivityPub activities observed at the outbox lifecycle level: queued, retried, or abandoned. Per-recipient delivery counters live on `activitypub.delivery.*`.",
163
+ unit: "{activity}"
164
+ });
165
+ this.circuitBreakerStateChange = meter.createCounter("activitypub.circuit_breaker.state_change", {
166
+ description: "Outbound ActivityPub delivery circuit breaker changes.",
167
+ unit: "{change}"
168
+ });
169
+ this.keyLookup = meter.createCounter("activitypub.key.lookup", {
170
+ description: "Public-key lookup attempts performed by Fedify, including both cache hits and remote fetches.",
171
+ unit: "{lookup}"
172
+ });
173
+ this.keyLookupDuration = meter.createHistogram("activitypub.key.lookup.duration", {
174
+ description: "Duration of public-key lookups performed by Fedify, including any remote fetch.",
175
+ unit: "ms",
176
+ advice: { explicitBucketBoundaries: [
177
+ 5,
178
+ 10,
179
+ 25,
180
+ 50,
181
+ 75,
182
+ 100,
183
+ 250,
184
+ 500,
185
+ 750,
186
+ 1e3,
187
+ 2500,
188
+ 5e3,
189
+ 7500,
190
+ 1e4
191
+ ] }
192
+ });
193
+ this.documentFetch = meter.createCounter("activitypub.document.fetch", {
194
+ description: "Remote JSON-LD document loader invocations made by Fedify-wrapped loaders.",
195
+ unit: "{fetch}"
196
+ });
197
+ this.documentFetchDuration = meter.createHistogram("activitypub.document.fetch.duration", {
198
+ description: "Duration of remote JSON-LD document loader invocations made by Fedify-wrapped loaders.",
199
+ unit: "ms",
200
+ advice: { explicitBucketBoundaries: [
201
+ 5,
202
+ 10,
203
+ 25,
204
+ 50,
205
+ 75,
206
+ 100,
207
+ 250,
208
+ 500,
209
+ 750,
210
+ 1e3,
211
+ 2500,
212
+ 5e3,
213
+ 7500,
214
+ 1e4
215
+ ] }
216
+ });
217
+ this.documentCache = meter.createCounter("activitypub.document.cache", {
218
+ description: "KV-backed document loader cache lookups, with `hit` or `miss` classification.",
219
+ unit: "{lookup}"
220
+ });
221
+ this.webFingerHandle = meter.createCounter("webfinger.handle", {
222
+ description: "Incoming WebFinger requests handled by Fedify, classified by terminal outcome.",
223
+ unit: "{request}"
224
+ });
225
+ this.webFingerHandleDuration = meter.createHistogram("webfinger.handle.duration", {
226
+ description: "Duration of incoming WebFinger request handling in Fedify.",
227
+ unit: "ms",
228
+ advice: { explicitBucketBoundaries: [
229
+ 5,
230
+ 10,
231
+ 25,
232
+ 50,
233
+ 75,
234
+ 100,
235
+ 250,
236
+ 500,
237
+ 750,
238
+ 1e3,
239
+ 2500,
240
+ 5e3,
241
+ 7500,
242
+ 1e4
243
+ ] }
244
+ });
245
+ this.collectionRequest = meter.createCounter("activitypub.collection.request", {
246
+ description: "ActivityPub collection and collection-page requests handled by Fedify.",
247
+ unit: "{request}"
248
+ });
249
+ this.collectionDispatchDuration = meter.createHistogram("activitypub.collection.dispatch.duration", {
250
+ description: "Duration of ActivityPub collection dispatcher callbacks.",
251
+ unit: "ms",
252
+ advice: { explicitBucketBoundaries: [
253
+ 5,
254
+ 10,
255
+ 25,
256
+ 50,
257
+ 75,
258
+ 100,
259
+ 250,
260
+ 500,
261
+ 750,
262
+ 1e3,
263
+ 2500,
264
+ 5e3,
265
+ 7500,
266
+ 1e4
267
+ ] }
268
+ });
269
+ this.collectionPageItems = meter.createHistogram("activitypub.collection.page.items", {
270
+ description: "Number of items Fedify materialized for an ActivityPub collection response.",
271
+ unit: "{item}"
272
+ });
273
+ this.collectionTotalItems = meter.createHistogram("activitypub.collection.total_items", {
274
+ description: "Total item count reported by ActivityPub collection counters.",
275
+ unit: "{item}"
276
+ });
277
+ }
278
+ recordDelivery(inbox, durationMs, success, activityType) {
279
+ const deliveryAttributes = {
280
+ "activitypub.remote.host": getRemoteHost(inbox),
281
+ "activitypub.delivery.success": success
282
+ };
283
+ if (activityType != null) deliveryAttributes["activitypub.activity.type"] = activityType;
284
+ this.deliverySent.add(1, deliveryAttributes);
285
+ this.deliveryDuration.record(durationMs, deliveryAttributes);
286
+ }
287
+ recordPermanentFailure(inbox, statusCode) {
288
+ this.deliveryPermanentFailure.add(1, {
289
+ "activitypub.remote.host": getRemoteHost(inbox),
290
+ "http.response.status_code": statusCode
291
+ });
292
+ }
293
+ recordSignatureVerificationFailure(reason, remoteHost) {
294
+ const attributes = { "activitypub.verification.failure_reason": reason };
295
+ if (remoteHost != null) attributes["activitypub.remote.host"] = remoteHost;
296
+ this.signatureVerificationFailure.add(1, attributes);
297
+ }
298
+ recordSignatureVerificationDuration(durationMs, kind, result, extra = {}) {
299
+ const attributes = {
300
+ "activitypub.signature.kind": kind,
301
+ "activitypub.signature.result": result
302
+ };
303
+ if (extra.algorithm != null) attributes["http_signatures.algorithm"] = extra.algorithm;
304
+ if (extra.failureReason != null) attributes["http_signatures.failure_reason"] = extra.failureReason;
305
+ if (extra.ldType != null) attributes["ld_signatures.type"] = extra.ldType;
306
+ if (extra.cryptosuite != null) attributes["object_integrity_proofs.cryptosuite"] = extra.cryptosuite;
307
+ this.signatureVerificationDuration.record(durationMs, attributes);
308
+ }
309
+ recordSignatureKeyFetchDuration(durationMs, kind, result) {
310
+ this.signatureKeyFetchDuration.record(durationMs, {
311
+ "activitypub.signature.kind": kind,
312
+ "activitypub.signature.key_fetch.result": result
313
+ });
314
+ }
315
+ recordInboxProcessingDuration(activityType, durationMs) {
316
+ this.inboxProcessingDuration.record(durationMs, { "activitypub.activity.type": activityType });
317
+ }
318
+ recordHttpServerRequest(method, endpoint, durationMs, options = {}) {
319
+ const attributes = {
320
+ "http.request.method": normalizeHttpMethod(method),
321
+ "fedify.endpoint": endpoint
322
+ };
323
+ if (options.statusCode != null) attributes["http.response.status_code"] = options.statusCode;
324
+ if (options.routeTemplate != null) attributes["fedify.route.template"] = options.routeTemplate;
325
+ this.httpServerRequestCount.add(1, attributes);
326
+ this.httpServerRequestDuration.record(durationMs, attributes);
327
+ }
328
+ recordQueueTaskEnqueued(common, attempt) {
329
+ const attributes = buildQueueTaskAttributes(common);
330
+ attributes["fedify.queue.task.attempt"] = attempt;
331
+ this.queueTaskEnqueued.add(1, attributes);
332
+ }
333
+ recordQueueTaskStarted(common) {
334
+ this.queueTaskStarted.add(1, buildQueueTaskAttributes(common));
335
+ }
336
+ incrementQueueTaskInFlight(common) {
337
+ this.queueTaskInFlight.add(1, buildQueueTaskInFlightAttributes(common));
338
+ }
339
+ decrementQueueTaskInFlight(common) {
340
+ this.queueTaskInFlight.add(-1, buildQueueTaskInFlightAttributes(common));
341
+ }
342
+ recordQueueTaskOutcome(common, result, durationMs) {
343
+ const attributes = buildQueueTaskAttributes(common);
344
+ attributes["fedify.queue.task.result"] = result;
345
+ if (result === "completed") this.queueTaskCompleted.add(1, attributes);
346
+ else if (result === "failed") this.queueTaskFailed.add(1, attributes);
347
+ this.queueTaskDuration.record(durationMs, attributes);
348
+ }
349
+ recordFanoutRecipients(recipientCount, activityType) {
350
+ const attributes = {};
351
+ if (activityType != null) attributes["activitypub.activity.type"] = activityType;
352
+ this.fanoutRecipients.record(recipientCount, attributes);
353
+ }
354
+ recordInboxActivity(result, activityType) {
355
+ this.inboxActivity.add(1, buildActivityLifecycleAttributes(result, activityType));
356
+ }
357
+ recordOutboxActivity(result, activityType) {
358
+ this.outboxActivity.add(1, buildActivityLifecycleAttributes(result, activityType));
359
+ }
360
+ recordCircuitBreakerStateChange(remoteHost, state) {
361
+ this.circuitBreakerStateChange.add(1, {
362
+ "activitypub.remote.host": remoteHost,
363
+ "activitypub.circuit_breaker.state": state
364
+ });
365
+ }
366
+ recordKeyLookup(attrs) {
367
+ const attributes = {
368
+ "activitypub.lookup.kind": "public_key",
369
+ "activitypub.lookup.result": attrs.result,
370
+ "activitypub.cache.enabled": attrs.cacheEnabled
371
+ };
372
+ if (attrs.remoteUrl != null) attributes["activitypub.remote.host"] = getRemoteHost(attrs.remoteUrl);
373
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
374
+ this.keyLookup.add(1, attributes);
375
+ this.keyLookupDuration.record(attrs.durationMs, attributes);
376
+ }
377
+ recordDocumentFetch(attrs) {
378
+ const attributes = {
379
+ "activitypub.lookup.kind": attrs.kind,
380
+ "activitypub.lookup.result": attrs.result
381
+ };
382
+ if (attrs.remoteUrl != null) attributes["activitypub.remote.host"] = getRemoteHost(attrs.remoteUrl);
383
+ if (attrs.cacheEnabled != null) attributes["activitypub.cache.enabled"] = attrs.cacheEnabled;
384
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
385
+ this.documentFetch.add(1, attributes);
386
+ this.documentFetchDuration.record(attrs.durationMs, attributes);
387
+ }
388
+ recordDocumentCache(attrs) {
389
+ const attributes = {
390
+ "activitypub.lookup.kind": attrs.kind,
391
+ "activitypub.lookup.result": attrs.result
392
+ };
393
+ if (attrs.remoteUrl != null) attributes["activitypub.remote.host"] = getRemoteHost(attrs.remoteUrl);
394
+ this.documentCache.add(1, attributes);
395
+ }
396
+ recordWebFingerHandle(attrs) {
397
+ const attributes = { "webfinger.handle.result": attrs.result };
398
+ if (attrs.scheme != null) attributes["webfinger.resource.scheme"] = attrs.scheme;
399
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
400
+ this.webFingerHandle.add(1, attributes);
401
+ this.webFingerHandleDuration.record(attrs.durationMs, attributes);
402
+ }
403
+ recordCollectionRequest(attrs) {
404
+ this.collectionRequest.add(1, buildCollectionAttributes(attrs));
405
+ }
406
+ recordCollectionDispatchDuration(durationMs, attrs) {
407
+ this.collectionDispatchDuration.record(durationMs, buildCollectionAttributes(attrs));
408
+ }
409
+ recordCollectionPageItems(itemCount, attrs) {
410
+ this.collectionPageItems.record(itemCount, buildCollectionAttributes(attrs));
411
+ }
412
+ recordCollectionTotalItems(totalItems, attrs) {
413
+ this.collectionTotalItems.record(totalItems, buildCollectionAttributes(attrs));
414
+ }
415
+ };
416
+ function buildCollectionAttributes(attrs) {
417
+ const attributes = {
418
+ "activitypub.collection.kind": attrs.kind,
419
+ "activitypub.collection.page": attrs.page,
420
+ "activitypub.collection.result": attrs.result,
421
+ "fedify.collection.dispatcher": attrs.dispatcher
422
+ };
423
+ if (attrs.statusCode != null) attributes["http.response.status_code"] = attrs.statusCode;
424
+ return attributes;
425
+ }
426
+ function buildActivityLifecycleAttributes(result, activityType) {
427
+ const attributes = { "activitypub.processing.result": result };
428
+ if (activityType != null) attributes["activitypub.activity.type"] = activityType;
429
+ return attributes;
430
+ }
431
+ function buildQueueTaskAttributes(common) {
432
+ const attributes = { "fedify.queue.role": common.role };
433
+ const backend = getQueueBackend(common.queue);
434
+ if (backend != null) attributes["fedify.queue.backend"] = backend;
435
+ const nativeRetrial = common.queue?.nativeRetrial;
436
+ if (typeof nativeRetrial === "boolean") attributes["fedify.queue.native_retrial"] = nativeRetrial;
437
+ if (common.activityType != null) attributes["activitypub.activity.type"] = common.activityType;
438
+ return attributes;
439
+ }
440
+ function buildQueueTaskInFlightAttributes(common) {
441
+ return buildQueueTaskAttributes({
442
+ role: common.role,
443
+ queue: common.queue
444
+ });
445
+ }
446
+ /**
447
+ * Returns the constructor name of the given message queue, when it is a
448
+ * meaningful identifier. Used as a best-effort `fedify.queue.backend`
449
+ * attribute on queue task metrics; returns `undefined` for plain object
450
+ * literals (whose constructor is `Object`) so the attribute does not appear
451
+ * with a non-informative value.
452
+ * @since 2.3.0
453
+ */
454
+ function getQueueBackend(queue) {
455
+ const name = queue?.constructor?.name;
456
+ if (name == null || name === "" || name === "Object") return void 0;
457
+ return name;
458
+ }
459
+ /**
460
+ * Registers a callback for observing queue backend depth.
461
+ * @since 2.3.0
462
+ */
463
+ function registerQueueDepthGauge(meterProvider, entries, options = {}) {
464
+ const uniqueQueues = /* @__PURE__ */ new Map();
465
+ for (const { role, queue } of entries) {
466
+ if (queue?.getDepth == null) continue;
467
+ const roles = uniqueQueues.get(queue);
468
+ if (roles == null) uniqueQueues.set(queue, [role]);
469
+ else if (!roles.includes(role)) roles.push(role);
470
+ }
471
+ if (uniqueQueues.size < 1) return;
472
+ const queueEntries = Array.from(uniqueQueues.entries());
473
+ getFederationMetrics(meterProvider).queueDepth.addCallback(async (observableResult) => {
474
+ await Promise.all(queueEntries.map(async ([queue, roles]) => {
475
+ let depth;
476
+ try {
477
+ depth = await queue.getDepth();
478
+ } catch {
479
+ return;
480
+ }
481
+ if (depth == null) return;
482
+ const attributes = buildQueueDepthAttributes(queue, roles, options);
483
+ observableResult.observe(depth.queued, {
484
+ ...attributes,
485
+ "fedify.queue.depth.state": "queued"
486
+ });
487
+ if (depth.ready != null) observableResult.observe(depth.ready, {
488
+ ...attributes,
489
+ "fedify.queue.depth.state": "ready"
490
+ });
491
+ if (depth.delayed != null) observableResult.observe(depth.delayed, {
492
+ ...attributes,
493
+ "fedify.queue.depth.state": "delayed"
494
+ });
495
+ }));
496
+ });
497
+ }
498
+ function buildQueueDepthAttributes(queue, roles, options) {
499
+ const sortedRoles = roles.toSorted();
500
+ const role = sortedRoles.length === 1 ? sortedRoles[0] : "shared";
501
+ const attributes = { "fedify.queue.role": role };
502
+ if (options.sourceId != null) attributes["fedify.federation.instance_id"] = options.sourceId;
503
+ if (role === "shared") attributes["fedify.queue.roles"] = sortedRoles.join(",");
504
+ const backend = getQueueBackend(queue);
505
+ if (backend != null) attributes["fedify.queue.backend"] = backend;
506
+ const nativeRetrial = queue.nativeRetrial;
507
+ if (typeof nativeRetrial === "boolean") attributes["fedify.queue.native_retrial"] = nativeRetrial;
508
+ return attributes;
509
+ }
510
+ /**
511
+ * Records `fedify.queue.task.enqueued` for an outgoing outbox enqueue and,
512
+ * for the initial attempt, also records
513
+ * `activitypub.outbox.activity{queued}`.
514
+ *
515
+ * Both `Context.sendActivity()` and `OutboxContext.forwardActivity()` enqueue
516
+ * outbox messages with the same metric attributes (role, queue, activity
517
+ * type, attempt), so they share this helper rather than each defining a local
518
+ * closure. Retry enqueues (attempt > 0) intentionally do not record a
519
+ * second `activitypub.outbox.activity{queued}`; retries are reported as
520
+ * `result=retried` from the retry-scheduling site, which has the failure
521
+ * context.
522
+ * @since 2.3.0
523
+ */
524
+ function recordOutboxEnqueue(meterProvider, outboxQueue, message) {
525
+ const metrics = getFederationMetrics(meterProvider);
526
+ metrics.recordQueueTaskEnqueued({
527
+ role: "outbox",
528
+ queue: outboxQueue,
529
+ activityType: message.activityType
530
+ }, message.attempt);
531
+ if (message.attempt === 0) metrics.recordOutboxActivity("queued", message.activityType);
532
+ }
533
+ /**
534
+ * Records `activitypub.fanout.recipients` with the number of recipient
535
+ * inboxes a single fanout produced. The histogram is unitless count
536
+ * (one measurement per fanout enqueue). Recipient URLs are deliberately
537
+ * not recorded; only the activity type, when known.
538
+ * @since 2.3.0
539
+ */
540
+ function recordFanoutRecipients(meterProvider, recipientCount, activityType) {
541
+ getFederationMetrics(meterProvider).recordFanoutRecipients(recipientCount, activityType);
542
+ }
543
+ /**
544
+ * Records one `activitypub.inbox.activity` measurement. The
545
+ * `activitypub.processing.result` attribute is always present;
546
+ * `activitypub.activity.type` is recorded only when Fedify already knows
547
+ * the activity type.
548
+ * @since 2.3.0
549
+ */
550
+ function recordInboxActivity(meterProvider, result, activityType) {
551
+ getFederationMetrics(meterProvider).recordInboxActivity(result, activityType);
552
+ }
553
+ /**
554
+ * Records one `activitypub.outbox.activity` measurement. The
555
+ * `activitypub.processing.result` attribute is always present;
556
+ * `activitypub.activity.type` is recorded only when Fedify already knows
557
+ * the activity type (it is always known for outbox lifecycle events).
558
+ * @since 2.3.0
559
+ */
560
+ function recordOutboxActivity(meterProvider, result, activityType) {
561
+ getFederationMetrics(meterProvider).recordOutboxActivity(result, activityType);
562
+ }
563
+ /**
564
+ * Records one outbound delivery circuit breaker state transition.
565
+ * @since 2.3.0
566
+ */
567
+ function recordCircuitBreakerStateChange(meterProvider, remoteHost, state) {
568
+ getFederationMetrics(meterProvider).recordCircuitBreakerStateChange(remoteHost, state);
569
+ }
570
+ /**
571
+ * Records one measurement on `activitypub.key.lookup` (counter) and
572
+ * `activitypub.key.lookup.duration` (histogram) for a public-key lookup.
573
+ *
574
+ * `activitypub.lookup.kind` is always recorded as `public_key`; the result
575
+ * classification, remote host, HTTP status code (when an HTTP response was
576
+ * received), and `activitypub.cache.enabled` are recorded as attributes on
577
+ * both measurements. Full key URLs and key IDs are deliberately omitted to
578
+ * keep cardinality bounded.
579
+ * @since 2.3.0
580
+ */
581
+ function recordKeyLookup(meterProvider, attrs) {
582
+ getFederationMetrics(meterProvider).recordKeyLookup(attrs);
583
+ }
584
+ /**
585
+ * Records one measurement each on `activitypub.document.fetch` (counter)
586
+ * and `activitypub.document.fetch.duration` (histogram) for one remote
587
+ * JSON-LD document loader invocation, with bounded
588
+ * `activitypub.lookup.kind` and `activitypub.lookup.result` attributes
589
+ * plus the optional remote-host, cache-enabled, and HTTP status-code
590
+ * attributes. Counter and histogram are always recorded together so
591
+ * aggregate rate and latency views stay in sync.
592
+ * @since 2.3.0
593
+ */
594
+ function recordDocumentFetch(meterProvider, attrs) {
595
+ getFederationMetrics(meterProvider).recordDocumentFetch(attrs);
596
+ }
597
+ /**
598
+ * Records one `activitypub.document.cache` measurement, classifying the
599
+ * lookup as `hit` (the cache returned an entry) or `miss` (the cache was
600
+ * consulted and returned nothing, prompting a delegate fetch).
601
+ * @since 2.3.0
602
+ */
603
+ function recordDocumentCache(meterProvider, attrs) {
604
+ getFederationMetrics(meterProvider).recordDocumentCache(attrs);
605
+ }
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
+ /**
620
+ * Records one `activitypub.collection.request` measurement for a
621
+ * collection or collection-page request handled by Fedify.
622
+ * @since 2.3.0
623
+ */
624
+ function recordCollectionRequest(meterProvider, attrs) {
625
+ getFederationMetrics(meterProvider).recordCollectionRequest(attrs);
626
+ }
627
+ /**
628
+ * Records one `activitypub.collection.dispatch.duration` measurement for a
629
+ * collection dispatcher callback invocation.
630
+ * @since 2.3.0
631
+ */
632
+ function recordCollectionDispatchDuration(meterProvider, durationMs, attrs) {
633
+ getFederationMetrics(meterProvider).recordCollectionDispatchDuration(durationMs, attrs);
634
+ }
635
+ /**
636
+ * Records one `activitypub.collection.page.items` measurement when Fedify
637
+ * has materialized collection items in memory.
638
+ * @since 2.3.0
639
+ */
640
+ function recordCollectionPageItems(meterProvider, itemCount, attrs) {
641
+ getFederationMetrics(meterProvider).recordCollectionPageItems(itemCount, attrs);
642
+ }
643
+ /**
644
+ * Records one `activitypub.collection.total_items` measurement when a
645
+ * collection counter has already reported a total item count.
646
+ * @since 2.3.0
647
+ */
648
+ function recordCollectionTotalItems(meterProvider, totalItems, attrs) {
649
+ getFederationMetrics(meterProvider).recordCollectionTotalItems(totalItems, attrs);
650
+ }
651
+ /**
652
+ * Classifies a thrown value from a key or document fetch into the bounded
653
+ * {@link LookupResult} taxonomy and, when an HTTP response was received,
654
+ * surfaces its status code.
655
+ *
656
+ * - `FetchError` with a `Response` whose status is `404` or `410`:
657
+ * `result=not_found` and the response status code.
658
+ * - `FetchError` with any other `Response`: `result=error` and the
659
+ * response status code.
660
+ * - `FetchError` without a `Response`: `result=network_error`.
661
+ * - An `AbortError` (typically from a cancelled fetch): `result=network_error`.
662
+ * - A bare `TypeError` (the shape native `fetch()` raises on DNS, connect,
663
+ * and TLS failures before any response is observed):
664
+ * `result=network_error`.
665
+ * - Any other value: `result=error`.
666
+ * @since 2.3.0
667
+ */
668
+ function classifyFetchError(error) {
669
+ if (error instanceof FetchError) {
670
+ if (error.response != null) {
671
+ const status = error.response.status;
672
+ return {
673
+ result: status === 404 || status === 410 ? "not_found" : "error",
674
+ statusCode: status
675
+ };
676
+ }
677
+ return { result: "network_error" };
678
+ }
679
+ if (isAbortError(error)) return { result: "network_error" };
680
+ if (error instanceof TypeError) return { result: "network_error" };
681
+ return { result: "error" };
682
+ }
683
+ /**
684
+ * Wraps a {@link DocumentLoader} so each invocation records one
685
+ * measurement on `activitypub.document.fetch` (counter) and one on
686
+ * `activitypub.document.fetch.duration` (histogram), classifying the
687
+ * outcome via {@link classifyFetchError} when the wrapped loader throws
688
+ * and as `fetched` on success. The wrapper rethrows whatever the
689
+ * wrapped loader throws so caller behavior is unchanged.
690
+ *
691
+ * The wrapper records the host of the requested URL, including any
692
+ * non-default port, on `activitypub.remote.host` when the URL parses; full
693
+ * URLs, paths, and query strings are deliberately excluded to keep
694
+ * cardinality bounded.
695
+ * HTTP status codes are recorded only when the failure carries a
696
+ * `Response` (currently, when the wrapped loader throws a
697
+ * {@link FetchError} with a non-`null` `response`).
698
+ * @since 2.3.0
699
+ */
700
+ function instrumentDocumentLoader(loader, options) {
701
+ const meterProvider = options.meterProvider;
702
+ if (meterProvider == null) return loader;
703
+ return async (url, opts) => {
704
+ const start = performance.now();
705
+ let remoteUrl;
706
+ try {
707
+ remoteUrl = new URL(url);
708
+ } catch {
709
+ remoteUrl = void 0;
710
+ }
711
+ try {
712
+ const result = await loader(url, opts);
713
+ recordDocumentFetch(meterProvider, {
714
+ durationMs: getDurationMs(start),
715
+ kind: options.kind,
716
+ result: "fetched",
717
+ remoteUrl,
718
+ cacheEnabled: options.cacheEnabled
719
+ });
720
+ return result;
721
+ } catch (error) {
722
+ const classified = classifyFetchError(error);
723
+ recordDocumentFetch(meterProvider, {
724
+ durationMs: getDurationMs(start),
725
+ kind: options.kind,
726
+ result: classified.result,
727
+ remoteUrl,
728
+ cacheEnabled: options.cacheEnabled,
729
+ statusCode: classified.statusCode
730
+ });
731
+ throw error;
732
+ }
733
+ };
734
+ }
735
+ /**
736
+ * Times an awaited public key fetch and records exactly one
737
+ * `activitypub.signature.key_fetch.duration` measurement, classifying the
738
+ * outcome as `hit`, `fetched`, or `error` based on the `cached` flag and
739
+ * whether the returned key is non-null. Errors thrown by the fetch are
740
+ * reported as `error` and rethrown, so verifier behavior is unchanged.
741
+ *
742
+ * Shared by the three signature verifiers (HTTP, Linked Data, Object
743
+ * Integrity Proofs); the only per-call variation is the
744
+ * `activitypub.signature.kind` attribute value.
745
+ * @since 2.3.0
746
+ */
747
+ async function measureSignatureKeyFetch(meterProvider, kind, fetch) {
748
+ const start = performance.now();
749
+ try {
750
+ const result = await fetch();
751
+ getFederationMetrics(meterProvider).recordSignatureKeyFetchDuration(getDurationMs(start), kind, result.key != null ? result.cached ? "hit" : "fetched" : "error");
752
+ return result;
753
+ } catch (error) {
754
+ getFederationMetrics(meterProvider).recordSignatureKeyFetchDuration(getDurationMs(start), kind, "error");
755
+ throw error;
756
+ }
757
+ }
758
+ /**
759
+ * Whether the given thrown value is an `AbortError`.
760
+ *
761
+ * `processQueuedTask` distinguishes aborted tasks (recorded as
762
+ * `fedify.queue.task.result=aborted`) from other failures so that backend
763
+ * shutdown signals do not inflate the `fedify.queue.task.failed` counter.
764
+ * @since 2.3.0
765
+ */
766
+ function isAbortError(error) {
767
+ if (error == null || typeof error !== "object") return false;
768
+ const name = error.name;
769
+ return typeof name === "string" && name === "AbortError";
770
+ }
771
+ const KNOWN_HTTP_METHODS = new Set([
772
+ "CONNECT",
773
+ "DELETE",
774
+ "GET",
775
+ "HEAD",
776
+ "OPTIONS",
777
+ "PATCH",
778
+ "POST",
779
+ "PUT",
780
+ "QUERY",
781
+ "TRACE"
782
+ ]);
783
+ function normalizeHttpMethod(method) {
784
+ const upper = method.toUpperCase();
785
+ return KNOWN_HTTP_METHODS.has(upper) ? upper : "_OTHER";
786
+ }
787
+ const federationMetrics = /* @__PURE__ */ new WeakMap();
788
+ /**
789
+ * Gets the cached Fedify metric instruments for a meter provider.
790
+ * @since 2.3.0
791
+ */
792
+ function getFederationMetrics(meterProvider = metrics.getMeterProvider()) {
793
+ let instruments = federationMetrics.get(meterProvider);
794
+ if (instruments == null) {
795
+ instruments = new FederationMetrics(meterProvider);
796
+ federationMetrics.set(meterProvider, instruments);
797
+ }
798
+ return instruments;
799
+ }
800
+ /**
801
+ * Gets the bounded remote host attribute value for a URL.
802
+ * @since 2.3.0
803
+ */
804
+ function getRemoteHost(url) {
805
+ return url.host;
806
+ }
807
+ /**
808
+ * Gets an elapsed duration in milliseconds from a `performance.now()` value.
809
+ * @since 2.3.0
810
+ */
811
+ function getDurationMs(start) {
812
+ return Math.max(0, performance.now() - start);
813
+ }
814
+ //#endregion
815
+ export { recordKeyLookup as _, instrumentDocumentLoader as a, recordWebFingerHandle as b, recordCircuitBreakerStateChange as c, recordCollectionRequest as d, recordCollectionTotalItems as f, recordInboxActivity as g, recordFanoutRecipients as h, getRemoteHost as i, recordCollectionDispatchDuration as l, recordDocumentFetch as m, getDurationMs as n, isAbortError as o, recordDocumentCache as p, getFederationMetrics as r, measureSignatureKeyFetch as s, classifyFetchError as t, recordCollectionPageItems as u, recordOutboxActivity as v, registerQueueDepthGauge as x, recordOutboxEnqueue as y };