@fedify/fedify 2.3.0-dev.1212 → 2.3.0-dev.1213

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 (83) hide show
  1. package/dist/{builder-DdbtvTFp.mjs → builder-Bj-7Sl7u.mjs} +9 -2
  2. package/dist/compat/mod.d.cts +1 -1
  3. package/dist/compat/mod.d.ts +1 -1
  4. package/dist/compat/outgoing-jsonld.test.mjs +1 -1
  5. package/dist/compat/public-audience.test.mjs +1 -1
  6. package/dist/compat/transformers.test.mjs +2 -2
  7. package/dist/{context-DMHK7jqX.d.cts → context-BBVLF7lx.d.cts} +41 -2
  8. package/dist/{context-K9cg8oGx.d.ts → context-BU6jSQdo.d.ts} +42 -2
  9. package/dist/{deno-DTaoLXHr.mjs → deno-BUzynMVz.mjs} +1 -1
  10. package/dist/{docloader-CdNiXmNg.mjs → docloader-jQPthO4U.mjs} +2 -2
  11. package/dist/{esm-BQRw925N.mjs → esm-vrlUxr60.mjs} +23 -1
  12. package/dist/federation/builder.test.mjs +21 -2
  13. package/dist/federation/circuit-breaker.test.mjs +1 -1
  14. package/dist/federation/handler.test.mjs +6 -6
  15. package/dist/federation/idempotency.test.mjs +4 -4
  16. package/dist/federation/keycache.test.mjs +1 -1
  17. package/dist/federation/kv.test.mjs +1 -1
  18. package/dist/federation/metrics.test.mjs +147 -1
  19. package/dist/federation/middleware.test.mjs +446 -18
  20. package/dist/federation/mod.cjs +1 -1
  21. package/dist/federation/mod.d.cts +3 -3
  22. package/dist/federation/mod.d.ts +3 -3
  23. package/dist/federation/mod.js +1 -1
  24. package/dist/federation/send.test.mjs +8 -4426
  25. package/dist/federation/temporal.test.mjs +1 -1
  26. package/dist/federation/webfinger.test.mjs +2 -2
  27. package/dist/{http-Czeyq7if.cjs → http-B1zlPuh3.cjs} +79 -2
  28. package/dist/{http-BEG9kx13.js → http-B_WbYMnB.js} +74 -3
  29. package/dist/{http-ByCfCX5K.mjs → http-CDaMGwCP.mjs} +4 -4
  30. package/dist/{key-Bhsx9PrC.mjs → key-DdP4HxTK.mjs} +2 -2
  31. package/dist/{kv-cache-qRBN2G2Z.cjs → kv-cache-CPIfTWt5.cjs} +1 -1
  32. package/dist/{kv-cache-D9U1AnXH.js → kv-cache-CXo8QM4m.js} +1 -1
  33. package/dist/{kv-cache-D4jzgeYW.mjs → kv-cache-DSjv5Aeh.mjs} +1 -1
  34. package/dist/{ld-CHtLb_Uh.mjs → ld-CuOEh5aB.mjs} +3 -3
  35. package/dist/{metrics-uwSF8DLC.mjs → metrics-B5vvJYMV.mjs} +74 -3
  36. package/dist/{middleware-BmSzD5U9.mjs → middleware-Cq9S8A5O.mjs} +328 -24
  37. package/dist/{middleware-CyiBzIwY.mjs → middleware-DPE-IRlD.mjs} +1 -1
  38. package/dist/{middleware-DrKDd2JT.js → middleware-Dft_sYeS.js} +352 -41
  39. package/dist/{middleware-CRORNnSU.cjs → middleware-DlqW4IRW.cjs} +351 -40
  40. package/dist/{mod-YLnSsEHY.d.cts → mod-C0F6kvgS.d.cts} +1 -1
  41. package/dist/{mod-CfOFqS0w.d.ts → mod-vPYVoa5n.d.ts} +1 -1
  42. package/dist/mod.cjs +4 -4
  43. package/dist/mod.d.cts +4 -4
  44. package/dist/mod.d.ts +4 -4
  45. package/dist/mod.js +4 -4
  46. package/dist/nodeinfo/client.test.mjs +2 -2
  47. package/dist/nodeinfo/handler.test.mjs +2 -2
  48. package/dist/nodeinfo/types.test.mjs +1 -1
  49. package/dist/otel/exporter.test.mjs +1 -1
  50. package/dist/{outgoing-jsonld-BgFLCJQ_.mjs → outgoing-jsonld-L_DbOaFe.mjs} +1 -1
  51. package/dist/{owner-B0Zrhs0w.mjs → owner--n8rmG51.mjs} +2 -2
  52. package/dist/{proof-frzCtYji.cjs → proof-BKpJ_p_d.cjs} +1 -1
  53. package/dist/{proof-CZhAX94C.js → proof-BsvB1vGI.js} +1 -1
  54. package/dist/{proof-DbJFxpzD.mjs → proof-dhtwaP4z.mjs} +5 -5
  55. package/dist/{send-kst2L0Df.mjs → send-CTQ30Wbe.mjs} +3 -3
  56. package/dist/sig/accept.test.mjs +1 -1
  57. package/dist/sig/http.test.mjs +4 -4
  58. package/dist/sig/key.test.mjs +2 -2
  59. package/dist/sig/ld.test.mjs +3 -3
  60. package/dist/sig/mod.cjs +2 -2
  61. package/dist/sig/mod.js +2 -2
  62. package/dist/sig/owner.test.mjs +2 -2
  63. package/dist/sig/proof.test.mjs +3 -3
  64. package/dist/{temporal-CcGypkzd.mjs → temporal-gfUaZjGU.mjs} +1 -1
  65. package/dist/testing/mod.d.mts +1 -0
  66. package/dist/utils/docloader.test.mjs +4 -4
  67. package/dist/utils/kv-cache.test.mjs +1 -1
  68. package/dist/utils/mod.cjs +1 -1
  69. package/dist/utils/mod.js +1 -1
  70. package/package.json +7 -7
  71. package/dist/chunk-DNRtMIoB.mjs +0 -29
  72. package/dist/execAsync-Dmet7-28.mjs +0 -13
  73. package/dist/getMachineId-bsd-Bn0le7-J.mjs +0 -29
  74. package/dist/getMachineId-darwin-CVjKuDgj.mjs +0 -26
  75. package/dist/getMachineId-linux-DbG4BXa-.mjs +0 -22
  76. package/dist/getMachineId-unsupported-lC8T9hPE.mjs +0 -17
  77. package/dist/getMachineId-win-c5zxTSS1.mjs +0 -28
  78. /package/dist/{accept-CceiKpCy.mjs → accept-CPkZzmGN.mjs} +0 -0
  79. /package/dist/{client-B_A6mfn3.mjs → client-ByXmQhYD.mjs} +0 -0
  80. /package/dist/{keys-C3kae-6B.mjs → keys-DGu1NFwu.mjs} +0 -0
  81. /package/dist/{kv-x2IvBUyq.mjs → kv-rV3vodCc.mjs} +0 -0
  82. /package/dist/{public-audience-N3pyOx2p.mjs → public-audience-Cvbr2Gzt.mjs} +0 -0
  83. /package/dist/{types-BFowWFTT.mjs → types-J53Kw7so.mjs} +0 -0
@@ -1,27 +1,27 @@
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-DTaoLXHr.mjs";
5
- import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, y as recordOutboxEnqueue } from "./metrics-uwSF8DLC.mjs";
6
- import { t as formatAcceptSignature } from "./accept-CceiKpCy.mjs";
7
- import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-Bhsx9PrC.mjs";
8
- import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-ByCfCX5K.mjs";
9
- import { t as getAuthenticatedDocumentLoader } from "./docloader-CdNiXmNg.mjs";
10
- import { n as kvCache } from "./kv-cache-D4jzgeYW.mjs";
11
- import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-CHtLb_Uh.mjs";
12
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-B0Zrhs0w.mjs";
13
- import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-BgFLCJQ_.mjs";
14
- import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-DbJFxpzD.mjs";
15
- import { t as getNodeInfo } from "./client-B_A6mfn3.mjs";
16
- import { t as nodeInfoToJson } from "./types-BFowWFTT.mjs";
17
- import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-DdbtvTFp.mjs";
4
+ import { n as version, t as name } from "./deno-BUzynMVz.mjs";
5
+ import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-B5vvJYMV.mjs";
6
+ import { t as formatAcceptSignature } from "./accept-CPkZzmGN.mjs";
7
+ import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-DdP4HxTK.mjs";
8
+ import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-CDaMGwCP.mjs";
9
+ import { t as getAuthenticatedDocumentLoader } from "./docloader-jQPthO4U.mjs";
10
+ import { n as kvCache } from "./kv-cache-DSjv5Aeh.mjs";
11
+ import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-CuOEh5aB.mjs";
12
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner--n8rmG51.mjs";
13
+ import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-L_DbOaFe.mjs";
14
+ import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-dhtwaP4z.mjs";
15
+ import { t as getNodeInfo } from "./client-ByXmQhYD.mjs";
16
+ import { t as nodeInfoToJson } from "./types-J53Kw7so.mjs";
17
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-CTQ30Wbe.mjs";
18
+ import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-Bj-7Sl7u.mjs";
18
19
  import { t as CircuitBreaker } from "./circuit-breaker-CSWsyoef.mjs";
19
20
  import { t as buildCollectionSynchronizationHeader } from "./collection-Cc3DVAhE.mjs";
20
21
  import { t as KvKeyCache } from "./keycache-BeU0LCII.mjs";
21
22
  import { t as acceptsJsonLd } from "./negotiation-DDstyBvc.mjs";
22
- import { t as hasMalformedKnownTemporalLiteral } from "./temporal-CcGypkzd.mjs";
23
+ import { t as hasMalformedKnownTemporalLiteral } from "./temporal-gfUaZjGU.mjs";
23
24
  import { t as createExponentialBackoffPolicy } from "./retry-CXg_MBI-.mjs";
24
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-kst2L0Df.mjs";
25
25
  import { getLogger, withContext } from "@logtape/logtape";
26
26
  import { RouterError } from "@fedify/uri-template";
27
27
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
@@ -30,6 +30,7 @@ import { SpanKind, SpanStatusCode, context, metrics, propagation, trace } from "
30
30
  import { FetchError, getDocumentLoader } from "@fedify/vocab-runtime";
31
31
  import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_HEADER, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
32
32
  import { uniq } from "es-toolkit";
33
+ import { DataPointType, MeterProvider as MeterProvider$1, MetricReader } from "@opentelemetry/sdk-metrics";
33
34
  import { domainToASCII } from "node:url";
34
35
  //#region src/compat/transformers.ts
35
36
  const logger$1 = getLogger([
@@ -156,6 +157,201 @@ function handleNodeInfoJrd(_request, context) {
156
157
  return Promise.resolve(response);
157
158
  }
158
159
  //#endregion
160
+ //#region src/federation/bench.ts
161
+ /**
162
+ * Metric reader owned by `benchmarkMode`.
163
+ * @since 2.3.0
164
+ */
165
+ var BenchmarkMetricReader = class extends MetricReader {
166
+ onShutdown() {
167
+ return Promise.resolve();
168
+ }
169
+ onForceFlush() {
170
+ return Promise.resolve();
171
+ }
172
+ };
173
+ /**
174
+ * Creates the in-process OpenTelemetry meter provider used by benchmark mode.
175
+ * @returns The meter provider and the metric reader attached to it.
176
+ * @since 2.3.0
177
+ */
178
+ function createBenchmarkMeterProvider() {
179
+ const reader = new BenchmarkMetricReader();
180
+ return {
181
+ meterProvider: new MeterProvider$1({ readers: [reader] }),
182
+ reader
183
+ };
184
+ }
185
+ /**
186
+ * Collects and serializes benchmark-mode metrics from a benchmark reader.
187
+ * @param reader The benchmark metric reader to collect from.
188
+ * @returns A server metric snapshot with any collection errors stringified.
189
+ * @since 2.3.0
190
+ */
191
+ async function collectBenchmarkMetrics(reader) {
192
+ const result = await reader.collect();
193
+ return {
194
+ version: 1,
195
+ source: "server",
196
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
197
+ scopeMetrics: serializeScopeMetrics(result.resourceMetrics),
198
+ errors: result.errors.map((error) => String(error))
199
+ };
200
+ }
201
+ /**
202
+ * Handles `GET /.well-known/fedify/bench/stats`.
203
+ * @param request The HTTP request to handle.
204
+ * @param reader The benchmark metric reader to collect from.
205
+ * @returns A JSON metric snapshot response, or `405 Method Not Allowed`.
206
+ * @since 2.3.0
207
+ */
208
+ async function handleBenchmarkStats(request, reader) {
209
+ if (request.method !== "GET") return new Response("Method not allowed", {
210
+ status: 405,
211
+ headers: { "Allow": "GET" }
212
+ });
213
+ return jsonResponse(await collectBenchmarkMetrics(reader));
214
+ }
215
+ /**
216
+ * Handles `POST /.well-known/fedify/bench/trigger`.
217
+ *
218
+ * The handler validates a benchmark trigger request, checks recipients against
219
+ * server-controlled trigger options, and calls `Context.sendActivity()` to use
220
+ * the target's normal outbox path.
221
+ * @param request The HTTP request to handle.
222
+ * @param context The Fedify context used to resolve actors and send activity.
223
+ * @param options Server-controlled benchmark trigger delivery options.
224
+ * @returns A JSON response describing the sent activity, or a validation error.
225
+ * @since 2.3.0
226
+ */
227
+ async function handleBenchmarkTrigger(request, context, options = {}) {
228
+ if (request.method !== "POST") return new Response("Method not allowed", {
229
+ status: 405,
230
+ headers: { "Allow": "POST" }
231
+ });
232
+ let json;
233
+ try {
234
+ json = await request.json();
235
+ } catch {
236
+ return jsonResponse({ error: "Invalid JSON request body." }, 400);
237
+ }
238
+ try {
239
+ const body = asRecord(json, "request body");
240
+ const sender = parseSender(body.sender);
241
+ const recipients = await parseRecipients(body.recipients, context);
242
+ const activity = await parseActivity(body.activity, context);
243
+ if (activity.id == null) throw new BenchmarkTriggerError("activity must have an id.");
244
+ const activityId = activity.id.href;
245
+ const inboxes = extractInboxes({ recipients });
246
+ const inboxUrls = Object.keys(inboxes);
247
+ if (inboxUrls.length < 1) throw new BenchmarkTriggerError("No valid recipient inboxes found. The recipients list must not be empty.");
248
+ const unsafeInboxes = options.allowUnsafeRecipients ? [] : inboxUrls.filter((inbox) => !options.sinks?.has(inbox));
249
+ if (unsafeInboxes.length > 0) return jsonResponse({
250
+ error: "unsafe_recipient",
251
+ unsafeInboxes
252
+ }, 403);
253
+ await context.sendActivity(sender, recipients, activity);
254
+ return jsonResponse({
255
+ version: 1,
256
+ activityId,
257
+ queueCorrelationId: activityId,
258
+ recipientCount: recipients.length,
259
+ inboxCount: inboxUrls.length
260
+ }, 202);
261
+ } catch (error) {
262
+ if (error instanceof BenchmarkTriggerError) return jsonResponse({ error: error.message }, error.status);
263
+ throw error;
264
+ }
265
+ }
266
+ var BenchmarkTriggerError = class extends Error {
267
+ status;
268
+ constructor(message, status = 400) {
269
+ super(message);
270
+ this.status = status;
271
+ }
272
+ };
273
+ function parseSender(value) {
274
+ const sender = asRecord(value, "sender");
275
+ if (typeof sender.identifier === "string") return { identifier: sender.identifier };
276
+ if (typeof sender.username === "string") return { username: sender.username };
277
+ throw new BenchmarkTriggerError("sender must be { identifier } or { username }.");
278
+ }
279
+ async function parseRecipients(value, context) {
280
+ if (!Array.isArray(value)) throw new BenchmarkTriggerError("recipients must be an array.");
281
+ return await Promise.all(value.map(async (item) => {
282
+ let object;
283
+ try {
284
+ object = await Object$1.fromJsonLd(item, {
285
+ documentLoader: context.documentLoader,
286
+ contextLoader: context.contextLoader
287
+ });
288
+ } catch (error) {
289
+ throw new BenchmarkTriggerError(`Invalid ActivityPub recipient: ${error}`);
290
+ }
291
+ if (!isRecipient(object)) throw new BenchmarkTriggerError("each recipient must be an ActivityPub actor.");
292
+ const recipient = object;
293
+ if (recipient.id == null || recipient.inboxId == null) throw new BenchmarkTriggerError("each recipient must have id and inbox properties.");
294
+ return recipient;
295
+ }));
296
+ }
297
+ function isRecipient(value) {
298
+ return value != null && typeof value === "object" && "id" in value && "inboxId" in value;
299
+ }
300
+ async function parseActivity(value, context) {
301
+ try {
302
+ return await Activity.fromJsonLd(value, {
303
+ documentLoader: context.documentLoader,
304
+ contextLoader: context.contextLoader
305
+ });
306
+ } catch (error) {
307
+ throw new BenchmarkTriggerError(`Invalid ActivityPub activity: ${error}`);
308
+ }
309
+ }
310
+ function asRecord(value, name) {
311
+ if (value == null || typeof value !== "object" || Array.isArray(value)) throw new BenchmarkTriggerError(`${name} must be an object.`);
312
+ return value;
313
+ }
314
+ function jsonResponse(body, status = 200) {
315
+ return new Response(JSON.stringify(body), {
316
+ status,
317
+ headers: { "Content-Type": "application/json" }
318
+ });
319
+ }
320
+ function serializeScopeMetrics(resourceMetrics) {
321
+ return resourceMetrics.scopeMetrics.map(serializeScope);
322
+ }
323
+ function serializeScope(scopeMetrics) {
324
+ return {
325
+ scope: {
326
+ name: scopeMetrics.scope.name,
327
+ version: scopeMetrics.scope.version
328
+ },
329
+ metrics: scopeMetrics.metrics.map(serializeMetric)
330
+ };
331
+ }
332
+ function serializeMetric(metric) {
333
+ return {
334
+ name: metric.descriptor.name,
335
+ description: metric.descriptor.description,
336
+ unit: metric.descriptor.unit,
337
+ dataPointType: serializeDataPointType(metric.dataPointType),
338
+ dataPoints: metric.dataPoints.map((point) => ({
339
+ attributes: { ...point.attributes },
340
+ startTime: point.startTime,
341
+ endTime: point.endTime,
342
+ value: point.value
343
+ }))
344
+ };
345
+ }
346
+ function serializeDataPointType(dataPointType) {
347
+ switch (dataPointType) {
348
+ case DataPointType.HISTOGRAM: return "histogram";
349
+ case DataPointType.EXPONENTIAL_HISTOGRAM: return "exponential_histogram";
350
+ case DataPointType.GAUGE: return "gauge";
351
+ case DataPointType.SUM: return "sum";
352
+ }
353
+ }
354
+ //#endregion
159
355
  //#region src/federation/inbox.ts
160
356
  async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
161
357
  const logger = getLogger([
@@ -2085,6 +2281,7 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
2085
2281
  //#endregion
2086
2282
  //#region src/federation/middleware.ts
2087
2283
  const circuitBreakerCasWarningKvStores = /* @__PURE__ */ new WeakSet();
2284
+ let nextQueueDepthGaugeSourceId = 0;
2088
2285
  const retryAfterHttpDate = /* @__PURE__ */ new RegExp("^(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \\d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \\d{4} \\d{2}:\\d{2}:\\d{2} GMT|(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), \\d{2}-(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-\\d{2} \\d{2}:\\d{2}:\\d{2} GMT|(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (?: \\d|\\d{2}) \\d{2}:\\d{2}:\\d{2} \\d{4})$");
2089
2286
  function parseRetryAfter(headers, now = Temporal.Now.instant()) {
2090
2287
  const value = headers.get("Retry-After");
@@ -2113,6 +2310,54 @@ function parseRetryAfterDuration(durationLike) {
2113
2310
  function clampNegativeDelay(delay) {
2114
2311
  return delay.sign < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay;
2115
2312
  }
2313
+ function getBenchmarkRelaxations(allowPrivateAddress, signatureTimeWindow) {
2314
+ const relaxations = [];
2315
+ if (allowPrivateAddress) relaxations.push({
2316
+ protection: "private_address_checks",
2317
+ effect: "disabled",
2318
+ effectiveValue: true
2319
+ });
2320
+ if (signatureTimeWindow === false) relaxations.push({
2321
+ protection: "http_signature_time_window",
2322
+ effect: "disabled",
2323
+ effectiveValue: false,
2324
+ secureDefaultSeconds: 3600
2325
+ });
2326
+ else try {
2327
+ const seconds = Temporal.Duration.from(signatureTimeWindow).total({ unit: "seconds" });
2328
+ if (seconds !== 3600) relaxations.push({
2329
+ protection: "http_signature_time_window",
2330
+ effect: "changed",
2331
+ effectiveSeconds: seconds,
2332
+ secureDefaultSeconds: 3600
2333
+ });
2334
+ } catch {}
2335
+ return relaxations;
2336
+ }
2337
+ function formatBenchmarkRelaxations(relaxations) {
2338
+ if (relaxations.length < 1) return "no benchmark-only protections relaxed";
2339
+ return relaxations.map((relaxation) => {
2340
+ switch (relaxation.protection) {
2341
+ case "private_address_checks": return "private address checks disabled (allowPrivateAddress=true)";
2342
+ case "http_signature_time_window":
2343
+ if (relaxation.effect === "disabled") return `HTTP Signature time window disabled (signatureTimeWindow=false)`;
2344
+ return `HTTP Signature time window set to ${relaxation.effectiveSeconds}s (secure default: ${relaxation.secureDefaultSeconds}s)`;
2345
+ }
2346
+ }).join("; ");
2347
+ }
2348
+ function getBenchmarkTriggerOptions(benchmarkOptions) {
2349
+ const sinks = benchmarkOptions.triggerSinks?.map((sink) => {
2350
+ try {
2351
+ return new URL(sink).href;
2352
+ } catch {
2353
+ throw new TypeError("benchmarkMode.triggerSinks must contain only URLs.");
2354
+ }
2355
+ });
2356
+ return {
2357
+ sinks: sinks == null ? void 0 : new Set(sinks),
2358
+ allowUnsafeRecipients: benchmarkOptions.allowUnsafeTriggerRecipients === true
2359
+ };
2360
+ }
2116
2361
  function maxDelay(first, second) {
2117
2362
  return Temporal.Duration.compare(first, second) >= 0 ? first : second;
2118
2363
  }
@@ -2149,8 +2394,10 @@ function isPermanentInboxParseError(error) {
2149
2394
  }
2150
2395
  /**
2151
2396
  * Create a new {@link Federation} instance.
2152
- * @param parameters Parameters for initializing the instance.
2397
+ * @param options Parameters for initializing the instance.
2153
2398
  * @returns A new {@link Federation} instance.
2399
+ * @throws {TypeError} If benchmark mode and `meterProvider` are both
2400
+ * specified.
2154
2401
  * @since 0.10.0
2155
2402
  */
2156
2403
  function createFederation(options) {
@@ -2184,8 +2431,31 @@ var FederationImpl = class extends FederationBuilderImpl {
2184
2431
  _meterProvider;
2185
2432
  firstKnock;
2186
2433
  inboxChallengePolicy;
2434
+ benchmarkMode;
2435
+ benchmarkMetricReader;
2436
+ benchmarkTriggerOptions;
2437
+ #queueDepthGaugeSourceId = `fedify-${(++nextQueueDepthGaugeSourceId).toString(36)}`;
2438
+ #queueDepthGaugeEntries = [];
2439
+ #queueDepthGaugeMeterProvider;
2187
2440
  constructor(options) {
2188
2441
  super();
2442
+ const benchmarkMode = options.benchmarkMode != null && options.benchmarkMode !== false;
2443
+ const benchmarkOptions = typeof options.benchmarkMode === "object" ? options.benchmarkMode : {};
2444
+ const hasCustomLoaderFactory = options.documentLoaderFactory != null || options.contextLoaderFactory != null;
2445
+ const allowPrivateAddress = options.allowPrivateAddress ?? (benchmarkMode && !hasCustomLoaderFactory ? true : false);
2446
+ const signatureTimeWindow = options.signatureTimeWindow ?? (benchmarkMode ? false : { hours: 1 });
2447
+ if (benchmarkMode && options.meterProvider != null) throw new TypeError("benchmarkMode requires Fedify to own the meterProvider; OpenTelemetry metric readers cannot be added after a MeterProvider is constructed.");
2448
+ if (benchmarkMode) {
2449
+ const relaxations = getBenchmarkRelaxations(allowPrivateAddress, signatureTimeWindow);
2450
+ const relaxationSummary = formatBenchmarkRelaxations(relaxations);
2451
+ getLogger([
2452
+ "fedify",
2453
+ "federation",
2454
+ "benchmark"
2455
+ ]).warn(`Fedify benchmarkMode is enabled; ${relaxationSummary}. Benchmark endpoints are active and must not be used in production.`, { relaxations });
2456
+ }
2457
+ this.benchmarkMode = benchmarkMode;
2458
+ this.benchmarkTriggerOptions = benchmarkMode ? getBenchmarkTriggerOptions(benchmarkOptions) : {};
2189
2459
  this.kv = options.kv;
2190
2460
  this.kvPrefixes = {
2191
2461
  activityIdempotence: ["_fedify", "activityIdempotence"],
@@ -2253,13 +2523,13 @@ var FederationImpl = class extends FederationBuilderImpl {
2253
2523
  }
2254
2524
  this.router.trailingSlashInsensitive = options.trailingSlashInsensitive ?? false;
2255
2525
  this._initializeRouter();
2256
- if (options.allowPrivateAddress || options.userAgent != null) {
2526
+ if (options.allowPrivateAddress === true || options.userAgent != null) {
2257
2527
  if (options.documentLoaderFactory != null) throw new TypeError("Cannot set documentLoaderFactory with allowPrivateAddress or userAgent options.");
2258
2528
  if (options.contextLoaderFactory != null) throw new TypeError("Cannot set contextLoaderFactory with allowPrivateAddress or userAgent options.");
2259
2529
  if (options.authenticatedDocumentLoaderFactory != null) throw new TypeError("Cannot set authenticatedDocumentLoaderFactory with allowPrivateAddress or userAgent options.");
2260
2530
  }
2261
- const { allowPrivateAddress, userAgent } = options;
2262
- this.allowPrivateAddress = allowPrivateAddress ?? false;
2531
+ const { userAgent } = options;
2532
+ this.allowPrivateAddress = allowPrivateAddress;
2263
2533
  const userDocumentLoaderFactory = options.documentLoaderFactory;
2264
2534
  const userContextLoaderFactory = options.contextLoaderFactory;
2265
2535
  const userAuthFactory = options.authenticatedDocumentLoaderFactory;
@@ -2307,25 +2577,55 @@ var FederationImpl = class extends FederationBuilderImpl {
2307
2577
  this.userAgent = userAgent;
2308
2578
  this.onOutboxError = options.onOutboxError;
2309
2579
  this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
2310
- this.signatureTimeWindow = options.signatureTimeWindow ?? { hours: 1 };
2580
+ this.signatureTimeWindow = signatureTimeWindow;
2311
2581
  this.skipSignatureVerification = options.skipSignatureVerification ?? false;
2312
2582
  this.inboxChallengePolicy = options.inboxChallengePolicy;
2313
2583
  this.outboxRetryPolicy = options.outboxRetryPolicy ?? createExponentialBackoffPolicy();
2314
2584
  this.inboxRetryPolicy = options.inboxRetryPolicy ?? createExponentialBackoffPolicy();
2315
2585
  this.activityTransformers = options.activityTransformers ?? getDefaultActivityTransformers();
2316
2586
  this._tracerProvider = options.tracerProvider;
2317
- this._meterProvider = options.meterProvider;
2587
+ if (benchmarkMode) {
2588
+ const benchmarkMetrics = createBenchmarkMeterProvider();
2589
+ this._meterProvider = benchmarkMetrics.meterProvider;
2590
+ this.benchmarkMetricReader = benchmarkMetrics.reader;
2591
+ } else this._meterProvider = options.meterProvider;
2592
+ this.#queueDepthGaugeEntries = [
2593
+ {
2594
+ role: "inbox",
2595
+ queue: this.inboxQueue
2596
+ },
2597
+ {
2598
+ role: "outbox",
2599
+ queue: this.outboxQueue
2600
+ },
2601
+ {
2602
+ role: "fanout",
2603
+ queue: this.fanoutQueue
2604
+ }
2605
+ ];
2606
+ this.#registerQueueDepthGauge(this._meterProvider ?? metrics.getMeterProvider());
2318
2607
  this.firstKnock = options.firstKnock;
2319
2608
  }
2320
2609
  get tracerProvider() {
2321
2610
  return this._tracerProvider ?? trace.getTracerProvider();
2322
2611
  }
2323
2612
  get meterProvider() {
2324
- return this._meterProvider ?? metrics.getMeterProvider();
2613
+ const meterProvider = this._meterProvider ?? metrics.getMeterProvider();
2614
+ this.#registerQueueDepthGauge(meterProvider);
2615
+ return meterProvider;
2616
+ }
2617
+ #registerQueueDepthGauge(meterProvider) {
2618
+ if (meterProvider === this.#queueDepthGaugeMeterProvider) return;
2619
+ registerQueueDepthGauge(meterProvider, this.#queueDepthGaugeEntries, { sourceId: this.#queueDepthGaugeSourceId });
2620
+ this.#queueDepthGaugeMeterProvider = meterProvider;
2325
2621
  }
2326
2622
  _initializeRouter() {
2327
2623
  this.router.add("/.well-known/webfinger", "webfinger");
2328
2624
  this.router.add("/.well-known/nodeinfo", "nodeInfoJrd");
2625
+ if (this.benchmarkMode) {
2626
+ this.router.add("/.well-known/fedify/bench/stats", "benchmarkStats");
2627
+ this.router.add("/.well-known/fedify/bench/trigger", "benchmarkTrigger");
2628
+ }
2329
2629
  }
2330
2630
  _getTracer() {
2331
2631
  return this.tracerProvider.getTracer(name, version);
@@ -3337,6 +3637,8 @@ var FederationImpl = class extends FederationBuilderImpl {
3337
3637
  context,
3338
3638
  nodeInfoDispatcher: this.nodeInfoDispatcher
3339
3639
  });
3640
+ case "benchmarkStats": return await handleBenchmarkStats(request, this.benchmarkMetricReader);
3641
+ case "benchmarkTrigger": return await handleBenchmarkTrigger(request, context, this.benchmarkTriggerOptions);
3340
3642
  }
3341
3643
  if (request.method !== "POST" && !acceptsJsonLd(request)) {
3342
3644
  metricState.endpoint = "not_acceptable";
@@ -3576,6 +3878,8 @@ function getEndpointCategory(routeName) {
3576
3878
  case "liked": return "liked";
3577
3879
  case "featured": return "featured";
3578
3880
  case "featuredTags": return "featured_tags";
3881
+ case "benchmarkStats":
3882
+ case "benchmarkTrigger": return "benchmark";
3579
3883
  default: return "not_found";
3580
3884
  }
3581
3885
  }
@@ -4646,4 +4950,4 @@ function getRequestId(request) {
4646
4950
  return `req_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
4647
4951
  }
4648
4952
  //#endregion
4649
- export { handleNodeInfoJrd as _, OutboxContextImpl as a, handleActor as c, handleInbox as d, handleObject as f, handleNodeInfo as g, respondWithObjectIfAcceptable as h, KvSpecDeterminer as i, handleCollection as l, respondWithObject as m, FederationImpl as n, createFederation as o, handleOutbox as p, InboxContextImpl as r, handleWebFinger as s, ContextImpl as t, handleCustomCollection as u, actorDehydrator as v, autoIdAssigner as y };
4953
+ export { handleNodeInfo as _, OutboxContextImpl as a, autoIdAssigner as b, handleActor as c, handleInbox as d, handleObject as f, handleBenchmarkTrigger as g, respondWithObjectIfAcceptable as h, KvSpecDeterminer as i, handleCollection as l, respondWithObject as m, FederationImpl as n, createFederation as o, handleOutbox as p, InboxContextImpl as r, handleWebFinger as s, ContextImpl as t, handleCustomCollection as u, handleNodeInfoJrd as v, actorDehydrator as y };
@@ -1,5 +1,5 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as FederationImpl } from "./middleware-BmSzD5U9.mjs";
4
+ import { n as FederationImpl } from "./middleware-Cq9S8A5O.mjs";
5
5
  export { FederationImpl };