@fedify/fedify 2.3.0-dev.1114 → 2.3.0-dev.1131
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{builder-YlEusQth.mjs → builder-DckAhD27.mjs} +2 -2
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{context-cSUMk2da.d.ts → context-Cq18Gplu.d.ts} +3 -208
- package/dist/{context-Ch-ZLyTQ.d.cts → context-tc6VOOOL.d.cts} +3 -208
- package/dist/{deno-CF3jMgip.mjs → deno--CS-SBS9.mjs} +1 -1
- package/dist/{docloader-BENj6vQ4.mjs → docloader-k6huZLQL.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.d.mts +2 -0
- package/dist/federation/metrics.test.mjs +335 -0
- package/dist/federation/middleware.test.mjs +444 -6
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -2
- package/dist/federation/mod.d.ts +3 -2
- package/dist/federation/mod.js +1 -1
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{http-CKCgOPkX.cjs → http-CJfvRL7D.cjs} +352 -22
- package/dist/{http-BmOZYc-8.mjs → http-IywnQdiX.mjs} +7 -5
- package/dist/{http-D6LP89UO.d.ts → http-VyDTd4G3.d.cts} +8 -1
- package/dist/{http-CpzZ9zsb.js → http-cqujdCRz.js} +323 -23
- package/dist/{http-D6aw3j2U.d.cts → http-lf8Hsd91.d.ts} +8 -1
- package/dist/{key-B4I8H5Lc.mjs → key-Df3tMleh.mjs} +42 -17
- package/dist/{kv-cache-DY-XWOqM.cjs → kv-cache-L0SMQkcd.cjs} +19 -2
- package/dist/{kv-cache-Wc5ezcVW.js → kv-cache-pEejzYq4.js} +19 -2
- package/dist/{kv-cache-DihufyAQ.mjs → kv-cache-q9Ec2ryS.mjs} +19 -1
- package/dist/{ld-B5D5THhl.mjs → ld-BGwiJpl3.mjs} +3 -3
- package/dist/{metrics-ek3ilf6c.mjs → metrics-BTOMkW8C.mjs} +280 -5
- package/dist/{middleware-EqTYPG4F.cjs → middleware-B2rtdpFV.cjs} +75 -28
- package/dist/{middleware-DlcecZMq.mjs → middleware-BB0IbDow.mjs} +84 -37
- package/dist/{middleware-EI7OU6BR.mjs → middleware-Dnql59Y8.mjs} +1 -1
- package/dist/{middleware-CuZbBw-N.js → middleware-DtOddSVg.js} +75 -28
- package/dist/{mod-BDhgfjP7.d.cts → mod-B0hW12_O.d.cts} +1 -1
- package/dist/{mod-B-Lin9Sy.d.ts → mod-COIAjwRS.d.ts} +1 -1
- package/dist/{mod-C6E8rkcz.d.ts → mod-CajNYYkt.d.ts} +1 -1
- package/dist/{mod-DLrRb0dx.d.ts → mod-DFvNJcNb.d.ts} +54 -3
- package/dist/{mod-P9tE2WmM.d.cts → mod-DnzgcPcy.d.cts} +1 -1
- package/dist/{mod-BR_BB0bh.d.cts → mod-yvIXFAEi.d.cts} +54 -3
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +6 -5
- package/dist/mod.d.ts +6 -5
- package/dist/mod.js +4 -4
- package/dist/mq-D-nlpY04.d.ts +208 -0
- package/dist/mq-D8uSFzxe.d.cts +208 -0
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-DO810N24.mjs → owner-CIt4hvmM.mjs} +2 -2
- package/dist/{proof-DIoqrKnX.cjs → proof-B1_u25UV.cjs} +1 -1
- package/dist/{proof-BgfyWv7b.mjs → proof-BYlrRSmZ.mjs} +3 -3
- package/dist/{proof-Vd8-1EWh.js → proof-DMGIjHYH.js} +1 -1
- package/dist/{send-CAYXdUTk.mjs → send-DJFpze7B.mjs} +3 -3
- package/dist/sig/http.test.mjs +6 -2
- package/dist/sig/key.test.mjs +99 -2
- package/dist/sig/ld.test.mjs +2 -2
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/proof.test.mjs +1 -1
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/kv-cache.test.mjs +67 -2
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- 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-
|
|
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;
|
|
@@ -20,6 +21,14 @@ var FederationMetrics = class {
|
|
|
20
21
|
queueTaskFailed;
|
|
21
22
|
queueTaskDuration;
|
|
22
23
|
queueTaskInFlight;
|
|
24
|
+
fanoutRecipients;
|
|
25
|
+
inboxActivity;
|
|
26
|
+
outboxActivity;
|
|
27
|
+
keyLookup;
|
|
28
|
+
keyLookupDuration;
|
|
29
|
+
documentFetch;
|
|
30
|
+
documentFetchDuration;
|
|
31
|
+
documentCache;
|
|
23
32
|
constructor(meterProvider) {
|
|
24
33
|
const meter = meterProvider.getMeter(name, version);
|
|
25
34
|
this.deliverySent = meter.createCounter("activitypub.delivery.sent", {
|
|
@@ -114,6 +123,70 @@ var FederationMetrics = class {
|
|
|
114
123
|
description: "Queue tasks currently being processed in this Fedify process.",
|
|
115
124
|
unit: "{task}"
|
|
116
125
|
});
|
|
126
|
+
this.fanoutRecipients = meter.createHistogram("activitypub.fanout.recipients", {
|
|
127
|
+
description: "Number of recipient inboxes produced by an ActivityPub fanout task.",
|
|
128
|
+
unit: "{recipient}"
|
|
129
|
+
});
|
|
130
|
+
this.inboxActivity = meter.createCounter("activitypub.inbox.activity", {
|
|
131
|
+
description: "ActivityPub activities observed at the inbox lifecycle level: queued, processed, retried, rejected, or abandoned.",
|
|
132
|
+
unit: "{activity}"
|
|
133
|
+
});
|
|
134
|
+
this.outboxActivity = meter.createCounter("activitypub.outbox.activity", {
|
|
135
|
+
description: "ActivityPub activities observed at the outbox lifecycle level: queued, retried, or abandoned. Per-recipient delivery counters live on `activitypub.delivery.*`.",
|
|
136
|
+
unit: "{activity}"
|
|
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
|
+
});
|
|
117
190
|
}
|
|
118
191
|
recordDelivery(inbox, durationMs, success, activityType) {
|
|
119
192
|
const deliveryAttributes = {
|
|
@@ -186,7 +259,53 @@ var FederationMetrics = class {
|
|
|
186
259
|
else if (result === "failed") this.queueTaskFailed.add(1, attributes);
|
|
187
260
|
this.queueTaskDuration.record(durationMs, attributes);
|
|
188
261
|
}
|
|
262
|
+
recordFanoutRecipients(recipientCount, activityType) {
|
|
263
|
+
const attributes = {};
|
|
264
|
+
if (activityType != null) attributes["activitypub.activity.type"] = activityType;
|
|
265
|
+
this.fanoutRecipients.record(recipientCount, attributes);
|
|
266
|
+
}
|
|
267
|
+
recordInboxActivity(result, activityType) {
|
|
268
|
+
this.inboxActivity.add(1, buildActivityLifecycleAttributes(result, activityType));
|
|
269
|
+
}
|
|
270
|
+
recordOutboxActivity(result, activityType) {
|
|
271
|
+
this.outboxActivity.add(1, buildActivityLifecycleAttributes(result, activityType));
|
|
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
|
+
}
|
|
189
303
|
};
|
|
304
|
+
function buildActivityLifecycleAttributes(result, activityType) {
|
|
305
|
+
const attributes = { "activitypub.processing.result": result };
|
|
306
|
+
if (activityType != null) attributes["activitypub.activity.type"] = activityType;
|
|
307
|
+
return attributes;
|
|
308
|
+
}
|
|
190
309
|
function buildQueueTaskAttributes(common) {
|
|
191
310
|
const attributes = { "fedify.queue.role": common.role };
|
|
192
311
|
const backend = getQueueBackend(common.queue);
|
|
@@ -216,20 +335,176 @@ function getQueueBackend(queue) {
|
|
|
216
335
|
return name;
|
|
217
336
|
}
|
|
218
337
|
/**
|
|
219
|
-
* Records `fedify.queue.task.enqueued` for an outgoing outbox enqueue
|
|
338
|
+
* Records `fedify.queue.task.enqueued` for an outgoing outbox enqueue and,
|
|
339
|
+
* for the initial attempt, also records
|
|
340
|
+
* `activitypub.outbox.activity{queued}`.
|
|
220
341
|
*
|
|
221
342
|
* Both `Context.sendActivity()` and `OutboxContext.forwardActivity()` enqueue
|
|
222
343
|
* outbox messages with the same metric attributes (role, queue, activity
|
|
223
344
|
* type, attempt), so they share this helper rather than each defining a local
|
|
224
|
-
* closure.
|
|
345
|
+
* closure. Retry enqueues (attempt > 0) intentionally do not record a
|
|
346
|
+
* second `activitypub.outbox.activity{queued}`; retries are reported as
|
|
347
|
+
* `result=retried` from the retry-scheduling site, which has the failure
|
|
348
|
+
* context.
|
|
225
349
|
* @since 2.3.0
|
|
226
350
|
*/
|
|
227
351
|
function recordOutboxEnqueue(meterProvider, outboxQueue, message) {
|
|
228
|
-
getFederationMetrics(meterProvider)
|
|
352
|
+
const metrics = getFederationMetrics(meterProvider);
|
|
353
|
+
metrics.recordQueueTaskEnqueued({
|
|
229
354
|
role: "outbox",
|
|
230
355
|
queue: outboxQueue,
|
|
231
356
|
activityType: message.activityType
|
|
232
357
|
}, message.attempt);
|
|
358
|
+
if (message.attempt === 0) metrics.recordOutboxActivity("queued", message.activityType);
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Records `activitypub.fanout.recipients` with the number of recipient
|
|
362
|
+
* inboxes a single fanout produced. The histogram is unitless count
|
|
363
|
+
* (one measurement per fanout enqueue). Recipient URLs are deliberately
|
|
364
|
+
* not recorded; only the activity type, when known.
|
|
365
|
+
* @since 2.3.0
|
|
366
|
+
*/
|
|
367
|
+
function recordFanoutRecipients(meterProvider, recipientCount, activityType) {
|
|
368
|
+
getFederationMetrics(meterProvider).recordFanoutRecipients(recipientCount, activityType);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Records one `activitypub.inbox.activity` measurement. The
|
|
372
|
+
* `activitypub.processing.result` attribute is always present;
|
|
373
|
+
* `activitypub.activity.type` is recorded only when Fedify already knows
|
|
374
|
+
* the activity type.
|
|
375
|
+
* @since 2.3.0
|
|
376
|
+
*/
|
|
377
|
+
function recordInboxActivity(meterProvider, result, activityType) {
|
|
378
|
+
getFederationMetrics(meterProvider).recordInboxActivity(result, activityType);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Records one `activitypub.outbox.activity` measurement. The
|
|
382
|
+
* `activitypub.processing.result` attribute is always present;
|
|
383
|
+
* `activitypub.activity.type` is recorded only when Fedify already knows
|
|
384
|
+
* the activity type (it is always known for outbox lifecycle events).
|
|
385
|
+
* @since 2.3.0
|
|
386
|
+
*/
|
|
387
|
+
function recordOutboxActivity(meterProvider, result, activityType) {
|
|
388
|
+
getFederationMetrics(meterProvider).recordOutboxActivity(result, activityType);
|
|
389
|
+
}
|
|
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
|
+
};
|
|
233
508
|
}
|
|
234
509
|
/**
|
|
235
510
|
* Times an awaited public key fetch and records exactly one
|
|
@@ -311,4 +586,4 @@ function getDurationMs(start) {
|
|
|
311
586
|
return Math.max(0, performance.now() - start);
|
|
312
587
|
}
|
|
313
588
|
//#endregion
|
|
314
|
-
export {
|
|
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-
|
|
6
|
-
const require_proof = require("./proof-
|
|
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-
|
|
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");
|
|
@@ -817,6 +817,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
|
|
|
817
817
|
code: _opentelemetry_api.SpanStatusCode.UNSET,
|
|
818
818
|
message: `Activity ${activity.id?.href} has already been processed.`
|
|
819
819
|
});
|
|
820
|
+
require_http.recordInboxActivity(meterProvider, "rejected", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
820
821
|
return "alreadyProcessed";
|
|
821
822
|
}
|
|
822
823
|
}
|
|
@@ -826,6 +827,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
|
|
|
826
827
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
827
828
|
message: "Missing actor."
|
|
828
829
|
});
|
|
830
|
+
require_http.recordInboxActivity(meterProvider, "rejected", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
829
831
|
return "missingActor";
|
|
830
832
|
}
|
|
831
833
|
span.setAttribute("activitypub.actor.id", activity.actorId.href);
|
|
@@ -861,6 +863,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
|
|
|
861
863
|
queue,
|
|
862
864
|
activityType: (0, _fedify_vocab.getTypeId)(activity).href
|
|
863
865
|
}, 0);
|
|
866
|
+
require_http.recordInboxActivity(meterProvider, "queued", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
864
867
|
logger.info("Activity {activityId} is enqueued.", {
|
|
865
868
|
activityId: activity.id?.href,
|
|
866
869
|
activity: json,
|
|
@@ -880,6 +883,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
|
|
|
880
883
|
code: _opentelemetry_api.SpanStatusCode.UNSET,
|
|
881
884
|
message: `Unsupported activity type: ${(0, _fedify_vocab.getTypeId)(activity).href}`
|
|
882
885
|
});
|
|
886
|
+
require_http.recordInboxActivity(meterProvider, "rejected", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
883
887
|
span.end();
|
|
884
888
|
return "unsupportedActivity";
|
|
885
889
|
}
|
|
@@ -889,7 +893,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
|
|
|
889
893
|
const activityType = (0, _fedify_vocab.getTypeId)(activity).href;
|
|
890
894
|
const started = performance.now();
|
|
891
895
|
try {
|
|
892
|
-
await listener(inboxContextFactory(recipient, json, activity
|
|
896
|
+
await listener(inboxContextFactory(recipient, json, activity.id?.href, activityType), activity);
|
|
893
897
|
} finally {
|
|
894
898
|
require_http.getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, require_http.getDurationMs(started));
|
|
895
899
|
}
|
|
@@ -914,9 +918,11 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
|
|
|
914
918
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
915
919
|
message: String(error)
|
|
916
920
|
});
|
|
921
|
+
require_http.recordInboxActivity(meterProvider, "rejected", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
917
922
|
span.end();
|
|
918
923
|
return "error";
|
|
919
924
|
}
|
|
925
|
+
require_http.recordInboxActivity(meterProvider, "processed", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
920
926
|
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
|
|
921
927
|
logger.info("Activity {activityId} has been processed.", {
|
|
922
928
|
activityId: activity.id?.href,
|
|
@@ -2907,23 +2913,50 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
2907
2913
|
}
|
|
2908
2914
|
const { allowPrivateAddress, userAgent } = options;
|
|
2909
2915
|
this.allowPrivateAddress = allowPrivateAddress ?? false;
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
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"
|
|
2938
|
+
});
|
|
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
|
|
2919
2949
|
});
|
|
2920
|
-
this.
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
userAgent,
|
|
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,
|
|
2924
2953
|
specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, options.firstKnock),
|
|
2925
2954
|
tracerProvider: this.tracerProvider
|
|
2926
|
-
})
|
|
2955
|
+
}), {
|
|
2956
|
+
meterProvider: this._meterProvider,
|
|
2957
|
+
kind: "object",
|
|
2958
|
+
cacheEnabled: userAuthFactory == null ? false : void 0
|
|
2959
|
+
});
|
|
2927
2960
|
this.userAgent = userAgent;
|
|
2928
2961
|
this.onOutboxError = options.onOutboxError;
|
|
2929
2962
|
this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
|
|
@@ -3233,6 +3266,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3233
3266
|
});
|
|
3234
3267
|
}
|
|
3235
3268
|
}
|
|
3269
|
+
require_http.recordOutboxActivity(this.meterProvider, "abandoned", message.activityType);
|
|
3236
3270
|
return;
|
|
3237
3271
|
}
|
|
3238
3272
|
if (this.outboxQueue?.nativeRetrial) {
|
|
@@ -3263,11 +3297,15 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3263
3297
|
queue: outboxQueue,
|
|
3264
3298
|
activityType: retryMessage.activityType
|
|
3265
3299
|
}, retryMessage.attempt);
|
|
3300
|
+
require_http.recordOutboxActivity(this.meterProvider, "retried", retryMessage.activityType);
|
|
3266
3301
|
}
|
|
3267
|
-
} else
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3302
|
+
} else {
|
|
3303
|
+
logger.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
|
|
3304
|
+
...logData,
|
|
3305
|
+
error
|
|
3306
|
+
});
|
|
3307
|
+
require_http.recordOutboxActivity(this.meterProvider, "abandoned", message.activityType);
|
|
3308
|
+
}
|
|
3271
3309
|
return;
|
|
3272
3310
|
}
|
|
3273
3311
|
logger.info("Successfully sent activity {activityId} to {inbox}.", { ...logData });
|
|
@@ -3302,6 +3340,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3302
3340
|
activity: message.activity,
|
|
3303
3341
|
recipient: message.identifier
|
|
3304
3342
|
});
|
|
3343
|
+
require_http.recordInboxActivity(this.meterProvider, "rejected", activityType);
|
|
3305
3344
|
return;
|
|
3306
3345
|
}
|
|
3307
3346
|
}
|
|
@@ -3318,6 +3357,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3318
3357
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
3319
3358
|
message: `Unsupported activity type: ${activityType}`
|
|
3320
3359
|
});
|
|
3360
|
+
require_http.recordInboxActivity(this.meterProvider, "rejected", activityType);
|
|
3321
3361
|
span.end();
|
|
3322
3362
|
return;
|
|
3323
3363
|
}
|
|
@@ -3330,6 +3370,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3330
3370
|
} finally {
|
|
3331
3371
|
require_http.getFederationMetrics(this.meterProvider).recordInboxProcessingDuration(activityType, require_http.getDurationMs(started));
|
|
3332
3372
|
}
|
|
3373
|
+
require_http.recordInboxActivity(this.meterProvider, "processed", activityType);
|
|
3333
3374
|
} catch (error) {
|
|
3334
3375
|
try {
|
|
3335
3376
|
await this.inboxErrorHandler?.(context, error);
|
|
@@ -3380,13 +3421,17 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3380
3421
|
queue: inboxQueue,
|
|
3381
3422
|
activityType
|
|
3382
3423
|
}, retryMessage.attempt);
|
|
3424
|
+
require_http.recordInboxActivity(this.meterProvider, "retried", activityType);
|
|
3383
3425
|
}
|
|
3384
|
-
} else
|
|
3385
|
-
error,
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3426
|
+
} else {
|
|
3427
|
+
logger.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
|
|
3428
|
+
error,
|
|
3429
|
+
activityId: activity.id?.href,
|
|
3430
|
+
activity: message.activity,
|
|
3431
|
+
recipient: message.identifier
|
|
3432
|
+
});
|
|
3433
|
+
require_http.recordInboxActivity(this.meterProvider, "abandoned", activityType);
|
|
3434
|
+
}
|
|
3390
3435
|
span.setStatus({
|
|
3391
3436
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
3392
3437
|
message: String(error)
|
|
@@ -4226,6 +4271,7 @@ var ContextImpl = class ContextImpl {
|
|
|
4226
4271
|
contextLoader: options.contextLoader ?? this.contextLoader,
|
|
4227
4272
|
userAgent: options.userAgent ?? this.federation.userAgent,
|
|
4228
4273
|
tracerProvider: options.tracerProvider ?? this.tracerProvider,
|
|
4274
|
+
meterProvider: options.meterProvider ?? this.meterProvider,
|
|
4229
4275
|
allowPrivateAddress: this.federation.allowPrivateAddress
|
|
4230
4276
|
});
|
|
4231
4277
|
}
|
|
@@ -4413,6 +4459,7 @@ var ContextImpl = class ContextImpl {
|
|
|
4413
4459
|
queue: this.federation.fanoutQueue,
|
|
4414
4460
|
activityType: message.activityType
|
|
4415
4461
|
}, 0);
|
|
4462
|
+
require_http.recordFanoutRecipients(this.federation.meterProvider, globalThis.Object.keys(message.inboxes).length, message.activityType);
|
|
4416
4463
|
return true;
|
|
4417
4464
|
}
|
|
4418
4465
|
async *getFollowers(identifier) {
|