@fedify/fedify 2.3.0-dev.1212 → 2.3.0-dev.1214
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/README.md +2 -2
- package/dist/{builder-DdbtvTFp.mjs → builder-TXkv6PZ5.mjs} +9 -2
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/outgoing-jsonld.test.mjs +1 -1
- package/dist/compat/public-audience.test.mjs +1 -1
- package/dist/compat/transformers.test.mjs +2 -2
- package/dist/{context-DMHK7jqX.d.cts → context-BBVLF7lx.d.cts} +41 -2
- package/dist/{context-K9cg8oGx.d.ts → context-BU6jSQdo.d.ts} +42 -2
- package/dist/{deno-DTaoLXHr.mjs → deno-CO_m34_l.mjs} +1 -1
- package/dist/{docloader-CdNiXmNg.mjs → docloader-CEoZEOwZ.mjs} +2 -2
- package/dist/{esm-BQRw925N.mjs → esm-vrlUxr60.mjs} +23 -1
- package/dist/federation/builder.test.mjs +21 -2
- package/dist/federation/circuit-breaker.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +6 -6
- package/dist/federation/idempotency.test.mjs +4 -4
- package/dist/federation/keycache.test.mjs +1 -1
- package/dist/federation/kv.test.mjs +1 -1
- package/dist/federation/metrics.test.mjs +147 -1
- package/dist/federation/middleware.test.mjs +446 -18
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/send.test.mjs +8 -4426
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +2 -2
- package/dist/{http-Czeyq7if.cjs → http-BBhBmWpD.cjs} +79 -2
- package/dist/{http-ByCfCX5K.mjs → http-BqtmoQ9o.mjs} +4 -4
- package/dist/{http-BEG9kx13.js → http-TC1SuqYB.js} +74 -3
- package/dist/{key-Bhsx9PrC.mjs → key-BquF6Qul.mjs} +2 -2
- package/dist/{kv-cache-qRBN2G2Z.cjs → kv-cache-CabxhXOA.cjs} +1 -1
- package/dist/{kv-cache-D9U1AnXH.js → kv-cache-DM4INGLw.js} +1 -1
- package/dist/{kv-cache-D4jzgeYW.mjs → kv-cache-Dc3bvP1V.mjs} +1 -1
- package/dist/{ld-CHtLb_Uh.mjs → ld-D_HyqMtM.mjs} +3 -3
- package/dist/{metrics-uwSF8DLC.mjs → metrics-GDOei3t4.mjs} +74 -3
- package/dist/{middleware-BmSzD5U9.mjs → middleware-CX-Rghhp.mjs} +328 -24
- package/dist/{middleware-CyiBzIwY.mjs → middleware-CvVbThhb.mjs} +1 -1
- package/dist/{middleware-DrKDd2JT.js → middleware-DNT_qJDO.js} +352 -41
- package/dist/{middleware-CRORNnSU.cjs → middleware-DOWOfX4Y.cjs} +351 -40
- package/dist/{mod-YLnSsEHY.d.cts → mod-C0F6kvgS.d.cts} +1 -1
- package/dist/{mod-CfOFqS0w.d.ts → mod-vPYVoa5n.d.ts} +1 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +4 -4
- package/dist/mod.d.ts +4 -4
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/client.test.mjs +2 -2
- package/dist/nodeinfo/handler.test.mjs +2 -2
- package/dist/nodeinfo/types.test.mjs +1 -1
- package/dist/otel/exporter.test.mjs +1 -1
- package/dist/{outgoing-jsonld-BgFLCJQ_.mjs → outgoing-jsonld-L_DbOaFe.mjs} +1 -1
- package/dist/{owner-B0Zrhs0w.mjs → owner-UiG90UB-.mjs} +2 -2
- package/dist/{proof-frzCtYji.cjs → proof-BVyIPgSw.cjs} +1 -1
- package/dist/{proof-DbJFxpzD.mjs → proof-CTdVp3oP.mjs} +5 -5
- package/dist/{proof-CZhAX94C.js → proof-DEsjO1_l.js} +1 -1
- package/dist/{send-kst2L0Df.mjs → send-BZ0SNcwz.mjs} +3 -3
- package/dist/sig/accept.test.mjs +1 -1
- package/dist/sig/http.test.mjs +4 -4
- package/dist/sig/key.test.mjs +2 -2
- package/dist/sig/ld.test.mjs +3 -3
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +2 -2
- package/dist/sig/proof.test.mjs +3 -3
- package/dist/{temporal-CcGypkzd.mjs → temporal-DvpcXhSy.mjs} +1 -1
- package/dist/testing/mod.d.mts +1 -0
- package/dist/utils/docloader.test.mjs +4 -4
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +7 -7
- package/dist/chunk-DNRtMIoB.mjs +0 -29
- package/dist/execAsync-Dmet7-28.mjs +0 -13
- package/dist/getMachineId-bsd-Bn0le7-J.mjs +0 -29
- package/dist/getMachineId-darwin-CVjKuDgj.mjs +0 -26
- package/dist/getMachineId-linux-DbG4BXa-.mjs +0 -22
- package/dist/getMachineId-unsupported-lC8T9hPE.mjs +0 -17
- package/dist/getMachineId-win-c5zxTSS1.mjs +0 -28
- /package/dist/{accept-CceiKpCy.mjs → accept-CPkZzmGN.mjs} +0 -0
- /package/dist/{client-B_A6mfn3.mjs → client-ByXmQhYD.mjs} +0 -0
- /package/dist/{keys-C3kae-6B.mjs → keys-DGu1NFwu.mjs} +0 -0
- /package/dist/{kv-x2IvBUyq.mjs → kv-rV3vodCc.mjs} +0 -0
- /package/dist/{public-audience-N3pyOx2p.mjs → public-audience-Cvbr2Gzt.mjs} +0 -0
- /package/dist/{types-BFowWFTT.mjs → types-J53Kw7so.mjs} +0 -0
|
@@ -2,10 +2,10 @@ import { Temporal } from "@js-temporal/polyfill";
|
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
import { t as __exportAll } from "./chunk-CRNNMoPX.js";
|
|
4
4
|
import { r as getDefaultActivityTransformers } from "./transformers-BGMIq1cs.js";
|
|
5
|
-
import { D as recordOutboxEnqueue, E as recordOutboxActivity,
|
|
6
|
-
import { _ as hasSignatureLike, b as signJsonLd, c as getKeyOwner, f as compactJsonLd, g as hasSignature, h as getNormalizationContextLoader, i as verifyObject, l as InvalidContextReferenceError, m as detachSignature, n as hasProofLike, o as normalizeOutgoingActivityJsonLd, r as signObject, s as doesActorOwnKey, u as assertSafeJsonLd, v as isClearlyMalformedContextReference, w as wrapContextLoaderForJsonLd, x as verifyCompactJsonLd, y as isInvalidUrlTypeError } from "./proof-
|
|
5
|
+
import { A as formatAcceptSignature, D as recordOutboxEnqueue, E as recordOutboxActivity, F as version, O as recordWebFingerHandle, P as name, S as recordCollectionTotalItems, T as recordInboxActivity, a as verifyRequestDetailed, b as recordCollectionPageItems, d as validateCryptoKey, f as getDurationMs, g as isAbortError, h as instrumentDocumentLoader, i as verifyRequest, k as registerQueueDepthGauge, m as getRemoteHost, n as parseRfc9421SignatureInput, o as exportJwk, p as getFederationMetrics, t as doubleKnock, u as importJwk, v as recordCircuitBreakerStateChange, w as recordFanoutRecipients, x as recordCollectionRequest, y as recordCollectionDispatchDuration } from "./http-TC1SuqYB.js";
|
|
6
|
+
import { _ as hasSignatureLike, b as signJsonLd, c as getKeyOwner, f as compactJsonLd, g as hasSignature, h as getNormalizationContextLoader, i as verifyObject, l as InvalidContextReferenceError, m as detachSignature, n as hasProofLike, o as normalizeOutgoingActivityJsonLd, r as signObject, s as doesActorOwnKey, u as assertSafeJsonLd, v as isClearlyMalformedContextReference, w as wrapContextLoaderForJsonLd, x as verifyCompactJsonLd, y as isInvalidUrlTypeError } from "./proof-DEsjO1_l.js";
|
|
7
7
|
import { n as getNodeInfo, t as nodeInfoToJson } from "./types-CAY3OdLq.js";
|
|
8
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-
|
|
8
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-DM4INGLw.js";
|
|
9
9
|
import { getLogger, withContext } from "@logtape/logtape";
|
|
10
10
|
import { Router, RouterError, assertPath } from "@fedify/uri-template";
|
|
11
11
|
import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
@@ -16,6 +16,7 @@ import { FetchError, getDocumentLoader } from "@fedify/vocab-runtime";
|
|
|
16
16
|
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";
|
|
17
17
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
18
18
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
19
|
+
import { DataPointType, MeterProvider as MeterProvider$1, MetricReader } from "@opentelemetry/sdk-metrics";
|
|
19
20
|
import { domainToASCII } from "node:url";
|
|
20
21
|
//#region src/federation/activity-listener.ts
|
|
21
22
|
var ActivityListenerSet = class {
|
|
@@ -117,6 +118,13 @@ var FederationBuilderImpl = class {
|
|
|
117
118
|
this.collectionCallbacks = {};
|
|
118
119
|
this.collectionTypeIds = {};
|
|
119
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Builds the federation object.
|
|
123
|
+
* @param options Parameters for initializing the federation object.
|
|
124
|
+
* @returns The federation object.
|
|
125
|
+
* @throws {TypeError} If benchmark mode and `meterProvider` are both
|
|
126
|
+
* specified.
|
|
127
|
+
*/
|
|
120
128
|
async build(options) {
|
|
121
129
|
const { FederationImpl } = await Promise.resolve().then(() => middleware_exports);
|
|
122
130
|
const f = new FederationImpl(options);
|
|
@@ -3072,36 +3080,6 @@ function handleNodeInfoJrd(_request, context) {
|
|
|
3072
3080
|
return Promise.resolve(response);
|
|
3073
3081
|
}
|
|
3074
3082
|
//#endregion
|
|
3075
|
-
//#region src/federation/retry.ts
|
|
3076
|
-
/**
|
|
3077
|
-
* Creates an exponential backoff retry policy. The delay between retries
|
|
3078
|
-
* starts at the `initialDelay` and is multiplied by the `factor` for each
|
|
3079
|
-
* subsequent retry, up to the `maxDelay`. The policy will give up after
|
|
3080
|
-
* `maxAttempts` attempts. The actual delay is randomized to avoid
|
|
3081
|
-
* synchronization (jitter).
|
|
3082
|
-
* @param options The options for the policy.
|
|
3083
|
-
* @returns The retry policy.
|
|
3084
|
-
* @since 0.12.0
|
|
3085
|
-
*/
|
|
3086
|
-
function createExponentialBackoffPolicy(options = {}) {
|
|
3087
|
-
const initialDelay = Temporal.Duration.from(options.initialDelay ?? { seconds: 1 });
|
|
3088
|
-
const maxDelay = Temporal.Duration.from(options.maxDelay ?? { hours: 12 });
|
|
3089
|
-
const maxAttempts = options.maxAttempts ?? 10;
|
|
3090
|
-
const factor = options.factor ?? 2;
|
|
3091
|
-
const jitter = options.jitter ?? true;
|
|
3092
|
-
return ({ attempts }) => {
|
|
3093
|
-
if (attempts >= maxAttempts) return null;
|
|
3094
|
-
let milliseconds = initialDelay.total("millisecond");
|
|
3095
|
-
milliseconds *= factor ** attempts;
|
|
3096
|
-
if (jitter) {
|
|
3097
|
-
milliseconds *= 1 + Math.random();
|
|
3098
|
-
milliseconds = Math.round(milliseconds);
|
|
3099
|
-
}
|
|
3100
|
-
const delay = Temporal.Duration.from({ milliseconds });
|
|
3101
|
-
return Temporal.Duration.compare(delay, maxDelay) > 0 ? maxDelay : delay;
|
|
3102
|
-
};
|
|
3103
|
-
}
|
|
3104
|
-
//#endregion
|
|
3105
3083
|
//#region src/federation/send.ts
|
|
3106
3084
|
/**
|
|
3107
3085
|
* Extracts the inbox URLs from recipients.
|
|
@@ -3331,6 +3309,231 @@ var SendActivityError = class extends Error {
|
|
|
3331
3309
|
}
|
|
3332
3310
|
};
|
|
3333
3311
|
//#endregion
|
|
3312
|
+
//#region src/federation/bench.ts
|
|
3313
|
+
/**
|
|
3314
|
+
* Metric reader owned by `benchmarkMode`.
|
|
3315
|
+
* @since 2.3.0
|
|
3316
|
+
*/
|
|
3317
|
+
var BenchmarkMetricReader = class extends MetricReader {
|
|
3318
|
+
onShutdown() {
|
|
3319
|
+
return Promise.resolve();
|
|
3320
|
+
}
|
|
3321
|
+
onForceFlush() {
|
|
3322
|
+
return Promise.resolve();
|
|
3323
|
+
}
|
|
3324
|
+
};
|
|
3325
|
+
/**
|
|
3326
|
+
* Creates the in-process OpenTelemetry meter provider used by benchmark mode.
|
|
3327
|
+
* @returns The meter provider and the metric reader attached to it.
|
|
3328
|
+
* @since 2.3.0
|
|
3329
|
+
*/
|
|
3330
|
+
function createBenchmarkMeterProvider() {
|
|
3331
|
+
const reader = new BenchmarkMetricReader();
|
|
3332
|
+
return {
|
|
3333
|
+
meterProvider: new MeterProvider$1({ readers: [reader] }),
|
|
3334
|
+
reader
|
|
3335
|
+
};
|
|
3336
|
+
}
|
|
3337
|
+
/**
|
|
3338
|
+
* Collects and serializes benchmark-mode metrics from a benchmark reader.
|
|
3339
|
+
* @param reader The benchmark metric reader to collect from.
|
|
3340
|
+
* @returns A server metric snapshot with any collection errors stringified.
|
|
3341
|
+
* @since 2.3.0
|
|
3342
|
+
*/
|
|
3343
|
+
async function collectBenchmarkMetrics(reader) {
|
|
3344
|
+
const result = await reader.collect();
|
|
3345
|
+
return {
|
|
3346
|
+
version: 1,
|
|
3347
|
+
source: "server",
|
|
3348
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3349
|
+
scopeMetrics: serializeScopeMetrics(result.resourceMetrics),
|
|
3350
|
+
errors: result.errors.map((error) => String(error))
|
|
3351
|
+
};
|
|
3352
|
+
}
|
|
3353
|
+
/**
|
|
3354
|
+
* Handles `GET /.well-known/fedify/bench/stats`.
|
|
3355
|
+
* @param request The HTTP request to handle.
|
|
3356
|
+
* @param reader The benchmark metric reader to collect from.
|
|
3357
|
+
* @returns A JSON metric snapshot response, or `405 Method Not Allowed`.
|
|
3358
|
+
* @since 2.3.0
|
|
3359
|
+
*/
|
|
3360
|
+
async function handleBenchmarkStats(request, reader) {
|
|
3361
|
+
if (request.method !== "GET") return new Response("Method not allowed", {
|
|
3362
|
+
status: 405,
|
|
3363
|
+
headers: { "Allow": "GET" }
|
|
3364
|
+
});
|
|
3365
|
+
return jsonResponse(await collectBenchmarkMetrics(reader));
|
|
3366
|
+
}
|
|
3367
|
+
/**
|
|
3368
|
+
* Handles `POST /.well-known/fedify/bench/trigger`.
|
|
3369
|
+
*
|
|
3370
|
+
* The handler validates a benchmark trigger request, checks recipients against
|
|
3371
|
+
* server-controlled trigger options, and calls `Context.sendActivity()` to use
|
|
3372
|
+
* the target's normal outbox path.
|
|
3373
|
+
* @param request The HTTP request to handle.
|
|
3374
|
+
* @param context The Fedify context used to resolve actors and send activity.
|
|
3375
|
+
* @param options Server-controlled benchmark trigger delivery options.
|
|
3376
|
+
* @returns A JSON response describing the sent activity, or a validation error.
|
|
3377
|
+
* @since 2.3.0
|
|
3378
|
+
*/
|
|
3379
|
+
async function handleBenchmarkTrigger(request, context, options = {}) {
|
|
3380
|
+
if (request.method !== "POST") return new Response("Method not allowed", {
|
|
3381
|
+
status: 405,
|
|
3382
|
+
headers: { "Allow": "POST" }
|
|
3383
|
+
});
|
|
3384
|
+
let json;
|
|
3385
|
+
try {
|
|
3386
|
+
json = await request.json();
|
|
3387
|
+
} catch {
|
|
3388
|
+
return jsonResponse({ error: "Invalid JSON request body." }, 400);
|
|
3389
|
+
}
|
|
3390
|
+
try {
|
|
3391
|
+
const body = asRecord(json, "request body");
|
|
3392
|
+
const sender = parseSender(body.sender);
|
|
3393
|
+
const recipients = await parseRecipients(body.recipients, context);
|
|
3394
|
+
const activity = await parseActivity(body.activity, context);
|
|
3395
|
+
if (activity.id == null) throw new BenchmarkTriggerError("activity must have an id.");
|
|
3396
|
+
const activityId = activity.id.href;
|
|
3397
|
+
const inboxes = extractInboxes({ recipients });
|
|
3398
|
+
const inboxUrls = Object.keys(inboxes);
|
|
3399
|
+
if (inboxUrls.length < 1) throw new BenchmarkTriggerError("No valid recipient inboxes found. The recipients list must not be empty.");
|
|
3400
|
+
const unsafeInboxes = options.allowUnsafeRecipients ? [] : inboxUrls.filter((inbox) => !options.sinks?.has(inbox));
|
|
3401
|
+
if (unsafeInboxes.length > 0) return jsonResponse({
|
|
3402
|
+
error: "unsafe_recipient",
|
|
3403
|
+
unsafeInboxes
|
|
3404
|
+
}, 403);
|
|
3405
|
+
await context.sendActivity(sender, recipients, activity);
|
|
3406
|
+
return jsonResponse({
|
|
3407
|
+
version: 1,
|
|
3408
|
+
activityId,
|
|
3409
|
+
queueCorrelationId: activityId,
|
|
3410
|
+
recipientCount: recipients.length,
|
|
3411
|
+
inboxCount: inboxUrls.length
|
|
3412
|
+
}, 202);
|
|
3413
|
+
} catch (error) {
|
|
3414
|
+
if (error instanceof BenchmarkTriggerError) return jsonResponse({ error: error.message }, error.status);
|
|
3415
|
+
throw error;
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
var BenchmarkTriggerError = class extends Error {
|
|
3419
|
+
status;
|
|
3420
|
+
constructor(message, status = 400) {
|
|
3421
|
+
super(message);
|
|
3422
|
+
this.status = status;
|
|
3423
|
+
}
|
|
3424
|
+
};
|
|
3425
|
+
function parseSender(value) {
|
|
3426
|
+
const sender = asRecord(value, "sender");
|
|
3427
|
+
if (typeof sender.identifier === "string") return { identifier: sender.identifier };
|
|
3428
|
+
if (typeof sender.username === "string") return { username: sender.username };
|
|
3429
|
+
throw new BenchmarkTriggerError("sender must be { identifier } or { username }.");
|
|
3430
|
+
}
|
|
3431
|
+
async function parseRecipients(value, context) {
|
|
3432
|
+
if (!Array.isArray(value)) throw new BenchmarkTriggerError("recipients must be an array.");
|
|
3433
|
+
return await Promise.all(value.map(async (item) => {
|
|
3434
|
+
let object;
|
|
3435
|
+
try {
|
|
3436
|
+
object = await Object$1.fromJsonLd(item, {
|
|
3437
|
+
documentLoader: context.documentLoader,
|
|
3438
|
+
contextLoader: context.contextLoader
|
|
3439
|
+
});
|
|
3440
|
+
} catch (error) {
|
|
3441
|
+
throw new BenchmarkTriggerError(`Invalid ActivityPub recipient: ${error}`);
|
|
3442
|
+
}
|
|
3443
|
+
if (!isRecipient(object)) throw new BenchmarkTriggerError("each recipient must be an ActivityPub actor.");
|
|
3444
|
+
const recipient = object;
|
|
3445
|
+
if (recipient.id == null || recipient.inboxId == null) throw new BenchmarkTriggerError("each recipient must have id and inbox properties.");
|
|
3446
|
+
return recipient;
|
|
3447
|
+
}));
|
|
3448
|
+
}
|
|
3449
|
+
function isRecipient(value) {
|
|
3450
|
+
return value != null && typeof value === "object" && "id" in value && "inboxId" in value;
|
|
3451
|
+
}
|
|
3452
|
+
async function parseActivity(value, context) {
|
|
3453
|
+
try {
|
|
3454
|
+
return await Activity.fromJsonLd(value, {
|
|
3455
|
+
documentLoader: context.documentLoader,
|
|
3456
|
+
contextLoader: context.contextLoader
|
|
3457
|
+
});
|
|
3458
|
+
} catch (error) {
|
|
3459
|
+
throw new BenchmarkTriggerError(`Invalid ActivityPub activity: ${error}`);
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
function asRecord(value, name) {
|
|
3463
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) throw new BenchmarkTriggerError(`${name} must be an object.`);
|
|
3464
|
+
return value;
|
|
3465
|
+
}
|
|
3466
|
+
function jsonResponse(body, status = 200) {
|
|
3467
|
+
return new Response(JSON.stringify(body), {
|
|
3468
|
+
status,
|
|
3469
|
+
headers: { "Content-Type": "application/json" }
|
|
3470
|
+
});
|
|
3471
|
+
}
|
|
3472
|
+
function serializeScopeMetrics(resourceMetrics) {
|
|
3473
|
+
return resourceMetrics.scopeMetrics.map(serializeScope);
|
|
3474
|
+
}
|
|
3475
|
+
function serializeScope(scopeMetrics) {
|
|
3476
|
+
return {
|
|
3477
|
+
scope: {
|
|
3478
|
+
name: scopeMetrics.scope.name,
|
|
3479
|
+
version: scopeMetrics.scope.version
|
|
3480
|
+
},
|
|
3481
|
+
metrics: scopeMetrics.metrics.map(serializeMetric)
|
|
3482
|
+
};
|
|
3483
|
+
}
|
|
3484
|
+
function serializeMetric(metric) {
|
|
3485
|
+
return {
|
|
3486
|
+
name: metric.descriptor.name,
|
|
3487
|
+
description: metric.descriptor.description,
|
|
3488
|
+
unit: metric.descriptor.unit,
|
|
3489
|
+
dataPointType: serializeDataPointType(metric.dataPointType),
|
|
3490
|
+
dataPoints: metric.dataPoints.map((point) => ({
|
|
3491
|
+
attributes: { ...point.attributes },
|
|
3492
|
+
startTime: point.startTime,
|
|
3493
|
+
endTime: point.endTime,
|
|
3494
|
+
value: point.value
|
|
3495
|
+
}))
|
|
3496
|
+
};
|
|
3497
|
+
}
|
|
3498
|
+
function serializeDataPointType(dataPointType) {
|
|
3499
|
+
switch (dataPointType) {
|
|
3500
|
+
case DataPointType.HISTOGRAM: return "histogram";
|
|
3501
|
+
case DataPointType.EXPONENTIAL_HISTOGRAM: return "exponential_histogram";
|
|
3502
|
+
case DataPointType.GAUGE: return "gauge";
|
|
3503
|
+
case DataPointType.SUM: return "sum";
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
//#endregion
|
|
3507
|
+
//#region src/federation/retry.ts
|
|
3508
|
+
/**
|
|
3509
|
+
* Creates an exponential backoff retry policy. The delay between retries
|
|
3510
|
+
* starts at the `initialDelay` and is multiplied by the `factor` for each
|
|
3511
|
+
* subsequent retry, up to the `maxDelay`. The policy will give up after
|
|
3512
|
+
* `maxAttempts` attempts. The actual delay is randomized to avoid
|
|
3513
|
+
* synchronization (jitter).
|
|
3514
|
+
* @param options The options for the policy.
|
|
3515
|
+
* @returns The retry policy.
|
|
3516
|
+
* @since 0.12.0
|
|
3517
|
+
*/
|
|
3518
|
+
function createExponentialBackoffPolicy(options = {}) {
|
|
3519
|
+
const initialDelay = Temporal.Duration.from(options.initialDelay ?? { seconds: 1 });
|
|
3520
|
+
const maxDelay = Temporal.Duration.from(options.maxDelay ?? { hours: 12 });
|
|
3521
|
+
const maxAttempts = options.maxAttempts ?? 10;
|
|
3522
|
+
const factor = options.factor ?? 2;
|
|
3523
|
+
const jitter = options.jitter ?? true;
|
|
3524
|
+
return ({ attempts }) => {
|
|
3525
|
+
if (attempts >= maxAttempts) return null;
|
|
3526
|
+
let milliseconds = initialDelay.total("millisecond");
|
|
3527
|
+
milliseconds *= factor ** attempts;
|
|
3528
|
+
if (jitter) {
|
|
3529
|
+
milliseconds *= 1 + Math.random();
|
|
3530
|
+
milliseconds = Math.round(milliseconds);
|
|
3531
|
+
}
|
|
3532
|
+
const delay = Temporal.Duration.from({ milliseconds });
|
|
3533
|
+
return Temporal.Duration.compare(delay, maxDelay) > 0 ? maxDelay : delay;
|
|
3534
|
+
};
|
|
3535
|
+
}
|
|
3536
|
+
//#endregion
|
|
3334
3537
|
//#region src/federation/webfinger.ts
|
|
3335
3538
|
const logger = getLogger([
|
|
3336
3539
|
"fedify",
|
|
@@ -3527,6 +3730,7 @@ var middleware_exports = /* @__PURE__ */ __exportAll({
|
|
|
3527
3730
|
createFederation: () => createFederation
|
|
3528
3731
|
});
|
|
3529
3732
|
const circuitBreakerCasWarningKvStores = /* @__PURE__ */ new WeakSet();
|
|
3733
|
+
let nextQueueDepthGaugeSourceId = 0;
|
|
3530
3734
|
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})$");
|
|
3531
3735
|
function parseRetryAfter(headers, now = Temporal.Now.instant()) {
|
|
3532
3736
|
const value = headers.get("Retry-After");
|
|
@@ -3555,6 +3759,54 @@ function parseRetryAfterDuration(durationLike) {
|
|
|
3555
3759
|
function clampNegativeDelay(delay) {
|
|
3556
3760
|
return delay.sign < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay;
|
|
3557
3761
|
}
|
|
3762
|
+
function getBenchmarkRelaxations(allowPrivateAddress, signatureTimeWindow) {
|
|
3763
|
+
const relaxations = [];
|
|
3764
|
+
if (allowPrivateAddress) relaxations.push({
|
|
3765
|
+
protection: "private_address_checks",
|
|
3766
|
+
effect: "disabled",
|
|
3767
|
+
effectiveValue: true
|
|
3768
|
+
});
|
|
3769
|
+
if (signatureTimeWindow === false) relaxations.push({
|
|
3770
|
+
protection: "http_signature_time_window",
|
|
3771
|
+
effect: "disabled",
|
|
3772
|
+
effectiveValue: false,
|
|
3773
|
+
secureDefaultSeconds: 3600
|
|
3774
|
+
});
|
|
3775
|
+
else try {
|
|
3776
|
+
const seconds = Temporal.Duration.from(signatureTimeWindow).total({ unit: "seconds" });
|
|
3777
|
+
if (seconds !== 3600) relaxations.push({
|
|
3778
|
+
protection: "http_signature_time_window",
|
|
3779
|
+
effect: "changed",
|
|
3780
|
+
effectiveSeconds: seconds,
|
|
3781
|
+
secureDefaultSeconds: 3600
|
|
3782
|
+
});
|
|
3783
|
+
} catch {}
|
|
3784
|
+
return relaxations;
|
|
3785
|
+
}
|
|
3786
|
+
function formatBenchmarkRelaxations(relaxations) {
|
|
3787
|
+
if (relaxations.length < 1) return "no benchmark-only protections relaxed";
|
|
3788
|
+
return relaxations.map((relaxation) => {
|
|
3789
|
+
switch (relaxation.protection) {
|
|
3790
|
+
case "private_address_checks": return "private address checks disabled (allowPrivateAddress=true)";
|
|
3791
|
+
case "http_signature_time_window":
|
|
3792
|
+
if (relaxation.effect === "disabled") return `HTTP Signature time window disabled (signatureTimeWindow=false)`;
|
|
3793
|
+
return `HTTP Signature time window set to ${relaxation.effectiveSeconds}s (secure default: ${relaxation.secureDefaultSeconds}s)`;
|
|
3794
|
+
}
|
|
3795
|
+
}).join("; ");
|
|
3796
|
+
}
|
|
3797
|
+
function getBenchmarkTriggerOptions(benchmarkOptions) {
|
|
3798
|
+
const sinks = benchmarkOptions.triggerSinks?.map((sink) => {
|
|
3799
|
+
try {
|
|
3800
|
+
return new URL(sink).href;
|
|
3801
|
+
} catch {
|
|
3802
|
+
throw new TypeError("benchmarkMode.triggerSinks must contain only URLs.");
|
|
3803
|
+
}
|
|
3804
|
+
});
|
|
3805
|
+
return {
|
|
3806
|
+
sinks: sinks == null ? void 0 : new Set(sinks),
|
|
3807
|
+
allowUnsafeRecipients: benchmarkOptions.allowUnsafeTriggerRecipients === true
|
|
3808
|
+
};
|
|
3809
|
+
}
|
|
3558
3810
|
function maxDelay(first, second) {
|
|
3559
3811
|
return Temporal.Duration.compare(first, second) >= 0 ? first : second;
|
|
3560
3812
|
}
|
|
@@ -3591,8 +3843,10 @@ function isPermanentInboxParseError(error) {
|
|
|
3591
3843
|
}
|
|
3592
3844
|
/**
|
|
3593
3845
|
* Create a new {@link Federation} instance.
|
|
3594
|
-
* @param
|
|
3846
|
+
* @param options Parameters for initializing the instance.
|
|
3595
3847
|
* @returns A new {@link Federation} instance.
|
|
3848
|
+
* @throws {TypeError} If benchmark mode and `meterProvider` are both
|
|
3849
|
+
* specified.
|
|
3596
3850
|
* @since 0.10.0
|
|
3597
3851
|
*/
|
|
3598
3852
|
function createFederation(options) {
|
|
@@ -3626,8 +3880,31 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3626
3880
|
_meterProvider;
|
|
3627
3881
|
firstKnock;
|
|
3628
3882
|
inboxChallengePolicy;
|
|
3883
|
+
benchmarkMode;
|
|
3884
|
+
benchmarkMetricReader;
|
|
3885
|
+
benchmarkTriggerOptions;
|
|
3886
|
+
#queueDepthGaugeSourceId = `fedify-${(++nextQueueDepthGaugeSourceId).toString(36)}`;
|
|
3887
|
+
#queueDepthGaugeEntries = [];
|
|
3888
|
+
#queueDepthGaugeMeterProvider;
|
|
3629
3889
|
constructor(options) {
|
|
3630
3890
|
super();
|
|
3891
|
+
const benchmarkMode = options.benchmarkMode != null && options.benchmarkMode !== false;
|
|
3892
|
+
const benchmarkOptions = typeof options.benchmarkMode === "object" ? options.benchmarkMode : {};
|
|
3893
|
+
const hasCustomLoaderFactory = options.documentLoaderFactory != null || options.contextLoaderFactory != null;
|
|
3894
|
+
const allowPrivateAddress = options.allowPrivateAddress ?? (benchmarkMode && !hasCustomLoaderFactory ? true : false);
|
|
3895
|
+
const signatureTimeWindow = options.signatureTimeWindow ?? (benchmarkMode ? false : { hours: 1 });
|
|
3896
|
+
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.");
|
|
3897
|
+
if (benchmarkMode) {
|
|
3898
|
+
const relaxations = getBenchmarkRelaxations(allowPrivateAddress, signatureTimeWindow);
|
|
3899
|
+
const relaxationSummary = formatBenchmarkRelaxations(relaxations);
|
|
3900
|
+
getLogger([
|
|
3901
|
+
"fedify",
|
|
3902
|
+
"federation",
|
|
3903
|
+
"benchmark"
|
|
3904
|
+
]).warn(`Fedify benchmarkMode is enabled; ${relaxationSummary}. Benchmark endpoints are active and must not be used in production.`, { relaxations });
|
|
3905
|
+
}
|
|
3906
|
+
this.benchmarkMode = benchmarkMode;
|
|
3907
|
+
this.benchmarkTriggerOptions = benchmarkMode ? getBenchmarkTriggerOptions(benchmarkOptions) : {};
|
|
3631
3908
|
this.kv = options.kv;
|
|
3632
3909
|
this.kvPrefixes = {
|
|
3633
3910
|
activityIdempotence: ["_fedify", "activityIdempotence"],
|
|
@@ -3695,13 +3972,13 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3695
3972
|
}
|
|
3696
3973
|
this.router.trailingSlashInsensitive = options.trailingSlashInsensitive ?? false;
|
|
3697
3974
|
this._initializeRouter();
|
|
3698
|
-
if (options.allowPrivateAddress || options.userAgent != null) {
|
|
3975
|
+
if (options.allowPrivateAddress === true || options.userAgent != null) {
|
|
3699
3976
|
if (options.documentLoaderFactory != null) throw new TypeError("Cannot set documentLoaderFactory with allowPrivateAddress or userAgent options.");
|
|
3700
3977
|
if (options.contextLoaderFactory != null) throw new TypeError("Cannot set contextLoaderFactory with allowPrivateAddress or userAgent options.");
|
|
3701
3978
|
if (options.authenticatedDocumentLoaderFactory != null) throw new TypeError("Cannot set authenticatedDocumentLoaderFactory with allowPrivateAddress or userAgent options.");
|
|
3702
3979
|
}
|
|
3703
|
-
const {
|
|
3704
|
-
this.allowPrivateAddress = allowPrivateAddress
|
|
3980
|
+
const { userAgent } = options;
|
|
3981
|
+
this.allowPrivateAddress = allowPrivateAddress;
|
|
3705
3982
|
const userDocumentLoaderFactory = options.documentLoaderFactory;
|
|
3706
3983
|
const userContextLoaderFactory = options.contextLoaderFactory;
|
|
3707
3984
|
const userAuthFactory = options.authenticatedDocumentLoaderFactory;
|
|
@@ -3749,25 +4026,55 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3749
4026
|
this.userAgent = userAgent;
|
|
3750
4027
|
this.onOutboxError = options.onOutboxError;
|
|
3751
4028
|
this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
|
|
3752
|
-
this.signatureTimeWindow =
|
|
4029
|
+
this.signatureTimeWindow = signatureTimeWindow;
|
|
3753
4030
|
this.skipSignatureVerification = options.skipSignatureVerification ?? false;
|
|
3754
4031
|
this.inboxChallengePolicy = options.inboxChallengePolicy;
|
|
3755
4032
|
this.outboxRetryPolicy = options.outboxRetryPolicy ?? createExponentialBackoffPolicy();
|
|
3756
4033
|
this.inboxRetryPolicy = options.inboxRetryPolicy ?? createExponentialBackoffPolicy();
|
|
3757
4034
|
this.activityTransformers = options.activityTransformers ?? getDefaultActivityTransformers();
|
|
3758
4035
|
this._tracerProvider = options.tracerProvider;
|
|
3759
|
-
|
|
4036
|
+
if (benchmarkMode) {
|
|
4037
|
+
const benchmarkMetrics = createBenchmarkMeterProvider();
|
|
4038
|
+
this._meterProvider = benchmarkMetrics.meterProvider;
|
|
4039
|
+
this.benchmarkMetricReader = benchmarkMetrics.reader;
|
|
4040
|
+
} else this._meterProvider = options.meterProvider;
|
|
4041
|
+
this.#queueDepthGaugeEntries = [
|
|
4042
|
+
{
|
|
4043
|
+
role: "inbox",
|
|
4044
|
+
queue: this.inboxQueue
|
|
4045
|
+
},
|
|
4046
|
+
{
|
|
4047
|
+
role: "outbox",
|
|
4048
|
+
queue: this.outboxQueue
|
|
4049
|
+
},
|
|
4050
|
+
{
|
|
4051
|
+
role: "fanout",
|
|
4052
|
+
queue: this.fanoutQueue
|
|
4053
|
+
}
|
|
4054
|
+
];
|
|
4055
|
+
this.#registerQueueDepthGauge(this._meterProvider ?? metrics.getMeterProvider());
|
|
3760
4056
|
this.firstKnock = options.firstKnock;
|
|
3761
4057
|
}
|
|
3762
4058
|
get tracerProvider() {
|
|
3763
4059
|
return this._tracerProvider ?? trace.getTracerProvider();
|
|
3764
4060
|
}
|
|
3765
4061
|
get meterProvider() {
|
|
3766
|
-
|
|
4062
|
+
const meterProvider = this._meterProvider ?? metrics.getMeterProvider();
|
|
4063
|
+
this.#registerQueueDepthGauge(meterProvider);
|
|
4064
|
+
return meterProvider;
|
|
4065
|
+
}
|
|
4066
|
+
#registerQueueDepthGauge(meterProvider) {
|
|
4067
|
+
if (meterProvider === this.#queueDepthGaugeMeterProvider) return;
|
|
4068
|
+
registerQueueDepthGauge(meterProvider, this.#queueDepthGaugeEntries, { sourceId: this.#queueDepthGaugeSourceId });
|
|
4069
|
+
this.#queueDepthGaugeMeterProvider = meterProvider;
|
|
3767
4070
|
}
|
|
3768
4071
|
_initializeRouter() {
|
|
3769
4072
|
this.router.add("/.well-known/webfinger", "webfinger");
|
|
3770
4073
|
this.router.add("/.well-known/nodeinfo", "nodeInfoJrd");
|
|
4074
|
+
if (this.benchmarkMode) {
|
|
4075
|
+
this.router.add("/.well-known/fedify/bench/stats", "benchmarkStats");
|
|
4076
|
+
this.router.add("/.well-known/fedify/bench/trigger", "benchmarkTrigger");
|
|
4077
|
+
}
|
|
3771
4078
|
}
|
|
3772
4079
|
_getTracer() {
|
|
3773
4080
|
return this.tracerProvider.getTracer(name, version);
|
|
@@ -4779,6 +5086,8 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
4779
5086
|
context,
|
|
4780
5087
|
nodeInfoDispatcher: this.nodeInfoDispatcher
|
|
4781
5088
|
});
|
|
5089
|
+
case "benchmarkStats": return await handleBenchmarkStats(request, this.benchmarkMetricReader);
|
|
5090
|
+
case "benchmarkTrigger": return await handleBenchmarkTrigger(request, context, this.benchmarkTriggerOptions);
|
|
4782
5091
|
}
|
|
4783
5092
|
if (request.method !== "POST" && !acceptsJsonLd(request)) {
|
|
4784
5093
|
metricState.endpoint = "not_acceptable";
|
|
@@ -5018,6 +5327,8 @@ function getEndpointCategory(routeName) {
|
|
|
5018
5327
|
case "liked": return "liked";
|
|
5019
5328
|
case "featured": return "featured";
|
|
5020
5329
|
case "featuredTags": return "featured_tags";
|
|
5330
|
+
case "benchmarkStats":
|
|
5331
|
+
case "benchmarkTrigger": return "benchmark";
|
|
5021
5332
|
default: return "not_found";
|
|
5022
5333
|
}
|
|
5023
5334
|
}
|
|
@@ -6088,4 +6399,4 @@ function getRequestId(request) {
|
|
|
6088
6399
|
return `req_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
|
|
6089
6400
|
}
|
|
6090
6401
|
//#endregion
|
|
6091
|
-
export {
|
|
6402
|
+
export { SendActivityError as a, buildCollectionSynchronizationHeader as c, normalizeCircuitBreakerOptions as d, parseCircuitBreakerKvState as f, createExponentialBackoffPolicy as i, digest as l, middleware_exports as n, respondWithObject as o, createFederationBuilder as p, handleWebFinger as r, respondWithObjectIfAcceptable as s, createFederation as t, CircuitBreaker as u };
|