@fedify/fedify 2.3.9-dev.2048 → 2.3.9
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-BfY2wZ4V.mjs → builder-Dm72xfCy.mjs} +2 -2
- package/dist/{client-DYDoDpMa.mjs → client-Ccbsek6u.mjs} +7 -3
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{context-BepjCFHN.mjs → context-CxbCIptr.mjs} +2 -1
- package/dist/{deno-ayal9buz.mjs → deno-Deo5dZf_.mjs} +1 -1
- package/dist/docloader-BAVVKXPl.mjs +79 -0
- package/dist/federation/builder.test.mjs +2 -2
- package/dist/federation/circuit-breaker.test.mjs +1 -1
- package/dist/federation/collection.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +7 -7
- package/dist/federation/idempotency.test.mjs +3 -3
- package/dist/federation/kv.test.mjs +1 -1
- package/dist/federation/metrics.test.mjs +2 -2
- package/dist/federation/middleware.test.mjs +187 -8
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +98 -43
- package/dist/federation/negotiation.test.mjs +1 -1
- package/dist/federation/router.test.mjs +1 -1
- package/dist/federation/send.test.mjs +85 -4
- package/dist/federation/temporal.test.mjs +2 -2
- package/dist/federation/webfinger.test.mjs +3 -3
- package/dist/{http-B2u6PvNX.mjs → http-BqObaL32.mjs} +8 -4
- package/dist/{http-BGP2QCbe.cjs → http-CjvY7wBM.cjs} +6 -2
- package/dist/{http-BPPCh0xN.js → http-DuWPL6ic.js} +6 -2
- package/dist/{key-C9DkrWwb.mjs → key-D1TEfV4d.mjs} +2 -2
- package/dist/{kv-cache-C1TC_e9y.cjs → kv-cache-C_4ufNbl.cjs} +34 -8
- package/dist/{kv-cache-qJSjfLGm.mjs → kv-cache-DGnB9ZX_.mjs} +1 -1
- package/dist/{kv-cache-Cl1q1aA9.js → kv-cache-w0De7ODp.js} +35 -9
- package/dist/{ld-DNl7KwLY.mjs → ld-DnGqAKWi.mjs} +4 -4
- package/dist/{metrics-Ce55disp.mjs → metrics-jUJmtE2u.mjs} +1 -1
- package/dist/{middleware-C_utyVa_.js → middleware-A-SjayRO.js} +19 -5
- package/dist/{middleware-BZojifnF.cjs → middleware-BW5W0CTK.cjs} +19 -5
- package/dist/{middleware-C96wm_7x.mjs → middleware-C70IPcBw.mjs} +1 -1
- package/dist/{middleware-BCG2BX1n.mjs → middleware-XCr66VRe.mjs} +13 -13
- package/dist/{mod-DHgc4ygG.d.ts → mod-CQ8EjnN5.d.ts} +2 -0
- package/dist/{mod-1TuoXkGs.d.cts → mod-EbRFjVlY.d.cts} +2 -0
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +1 -1
- package/dist/mod.d.ts +1 -1
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/client.test.mjs +50 -2
- package/dist/nodeinfo/handler.test.mjs +3 -3
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/nodeinfo/types.test.mjs +1 -1
- package/dist/otel/exporter.test.mjs +1 -1
- package/dist/{owner-DqqDBw-l.mjs → owner-C4iDnghj.mjs} +2 -2
- package/dist/{proof-D5vP7VIN.mjs → proof-CpU6r25U.mjs} +3 -3
- package/dist/{proof-Dz9ObvUq.js → proof-DqO9zYcc.js} +2 -2
- package/dist/{proof-I6YcKRRq.cjs → proof-DvVA2Bmk.cjs} +2 -2
- package/dist/{send-CBtR8DQK.mjs → send-YVatAj-a.mjs} +18 -4
- package/dist/sig/http.test.mjs +43 -3
- package/dist/sig/key.test.mjs +2 -2
- package/dist/sig/ld.test.mjs +5 -2
- 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 +2 -2
- package/dist/{std__assert-BBjXFNOb.mjs → std__assert-BvGuO4xO.mjs} +2 -27
- package/dist/{temporal-B9t2tY9A.mjs → temporal-BJ-5wxWU.mjs} +1 -1
- package/dist/testing/mod.mjs +1 -1
- package/dist/{types-DWs-qZaY.cjs → types-DRPjmHVV.cjs} +7 -3
- package/dist/{types-sXITXcWa.js → types-miAYfiGO.js} +7 -3
- package/dist/utils/docloader.test.mjs +299 -3
- package/dist/utils/kv-cache.test.mjs +1 -1
- 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/dist/vocab/cjs.test.mjs +1 -1
- package/package.json +8 -8
- package/dist/docloader-C-JrNL14.mjs +0 -53
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
4
|
+
import { n as version, t as name } from "./deno-Deo5dZf_.mjs";
|
|
5
5
|
import { t as ActivityListenerSet } from "./activity-listener-ell7W1s9.mjs";
|
|
6
6
|
import { getLogger } from "@logtape/logtape";
|
|
7
7
|
import { Router, RouterError, assertPath } from "@fedify/uri-template";
|
|
@@ -80,7 +80,7 @@ var FederationBuilderImpl = class {
|
|
|
80
80
|
* specified.
|
|
81
81
|
*/
|
|
82
82
|
async build(options) {
|
|
83
|
-
const { FederationImpl } = await import("./middleware-
|
|
83
|
+
const { FederationImpl } = await import("./middleware-C70IPcBw.mjs");
|
|
84
84
|
const f = new FederationImpl(options);
|
|
85
85
|
const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
|
|
86
86
|
f.router = this.router.clone();
|
|
@@ -93,8 +93,8 @@ const DEFAULT_MAX_REDIRECTION = 5;
|
|
|
93
93
|
* bounce the request to an internal address.
|
|
94
94
|
* @param url The URL to fetch.
|
|
95
95
|
* @param headers The request headers.
|
|
96
|
-
* @returns The final response, or `null` if the URL is disallowed
|
|
97
|
-
* are too many redirects.
|
|
96
|
+
* @returns The final response, or `null` if the URL is disallowed, its
|
|
97
|
+
* hostname cannot be resolved, or there are too many redirects.
|
|
98
98
|
*/
|
|
99
99
|
async function fetchPublicUrl(url, headers) {
|
|
100
100
|
let current = url instanceof URL ? url : new URL(url);
|
|
@@ -104,7 +104,11 @@ async function fetchPublicUrl(url, headers) {
|
|
|
104
104
|
await validatePublicUrl(current.href);
|
|
105
105
|
} catch (e) {
|
|
106
106
|
if (e instanceof UrlError) {
|
|
107
|
-
logger.error("
|
|
107
|
+
if (e.reason === "dns") logger.error("DNS lookup failed for {url}: {error}", {
|
|
108
|
+
url: current.href,
|
|
109
|
+
error: e
|
|
110
|
+
});
|
|
111
|
+
else logger.error("Refused to fetch a private or invalid URL {url}: {error}", {
|
|
108
112
|
url: current.href,
|
|
109
113
|
error: e
|
|
110
114
|
});
|
|
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
5
5
|
import { n as assertInstanceOf, t as assert } from "../assert-jSdZMYam.mjs";
|
|
6
6
|
import { t as MemoryKvStore } from "../kv-CX8qfaad.mjs";
|
|
7
|
-
import { b as autoIdAssigner, n as FederationImpl, y as actorDehydrator } from "../middleware-
|
|
7
|
+
import { b as autoIdAssigner, n as FederationImpl, y as actorDehydrator } from "../middleware-XCr66VRe.mjs";
|
|
8
8
|
import { Follow, Person } from "@fedify/vocab";
|
|
9
9
|
import { test } from "@fedify/fixture";
|
|
10
10
|
//#region src/compat/transformers.test.ts
|
|
@@ -10,6 +10,7 @@ import { lookupObject, traverseCollection } from "@fedify/vocab";
|
|
|
10
10
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
11
11
|
import { metrics, trace } from "@opentelemetry/api";
|
|
12
12
|
//#region ../fixture/dist/mod.js
|
|
13
|
+
const fixtureFetch = globalThis.fetch.bind(globalThis);
|
|
13
14
|
const logger = getLogger(["fixture", "docloader"]);
|
|
14
15
|
/**
|
|
15
16
|
* A mock of the document loader. This does not make any actual HTTP requests
|
|
@@ -24,7 +25,7 @@ async function mockDocumentLoader(resource, _options) {
|
|
|
24
25
|
if ("navigator" in globalThis && navigator.userAgent === "Cloudflare-Workers") {
|
|
25
26
|
const testUrl = new URL(url);
|
|
26
27
|
testUrl.hostname += ".test";
|
|
27
|
-
const resp = await
|
|
28
|
+
const resp = await fixtureFetch(testUrl);
|
|
28
29
|
if (resp.ok) {
|
|
29
30
|
const document = await resp.json();
|
|
30
31
|
logger.debug("Successfully fetched fixture {resource}: {status} {statusText}\n{body}", {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import "@js-temporal/polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { s as validateCryptoKey } from "./key-D1TEfV4d.mjs";
|
|
5
|
+
import { n as doubleKnock } from "./http-BqObaL32.mjs";
|
|
6
|
+
import { getLogger } from "@logtape/logtape";
|
|
7
|
+
import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
|
|
8
|
+
import { curry } from "es-toolkit";
|
|
9
|
+
//#region src/utils/docloader.ts
|
|
10
|
+
const logger = getLogger([
|
|
11
|
+
"fedify",
|
|
12
|
+
"utils",
|
|
13
|
+
"docloader"
|
|
14
|
+
]);
|
|
15
|
+
const DEFAULT_MAX_REDIRECTION = 20;
|
|
16
|
+
/**
|
|
17
|
+
* Gets an authenticated {@link DocumentLoader} for the given identity.
|
|
18
|
+
* Note that an authenticated document loader intentionally does not cache
|
|
19
|
+
* the fetched documents.
|
|
20
|
+
* At most 20 HTTP redirects and alternate document links are followed in total
|
|
21
|
+
* per call. Revisiting a URL within that chain throws a {@link FetchError}.
|
|
22
|
+
* @param identity The identity to get the document loader for.
|
|
23
|
+
* The actor's key pair.
|
|
24
|
+
* @param options The options for the document loader.
|
|
25
|
+
* @returns The authenticated document loader.
|
|
26
|
+
* @throws {TypeError} If the key is invalid or unsupported.
|
|
27
|
+
* @since 0.4.0
|
|
28
|
+
*/
|
|
29
|
+
function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedirection, userAgent, specDeterminer, tracerProvider } = {}) {
|
|
30
|
+
validateCryptoKey(identity.privateKey);
|
|
31
|
+
async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
|
|
32
|
+
options?.signal?.throwIfAborted();
|
|
33
|
+
let currentUrl = new URL(url).href;
|
|
34
|
+
await validateUrl(currentUrl);
|
|
35
|
+
visited.add(currentUrl);
|
|
36
|
+
const originalRequest = createActivityPubRequest(currentUrl, { userAgent });
|
|
37
|
+
function follow(nextUrl) {
|
|
38
|
+
options?.signal?.throwIfAborted();
|
|
39
|
+
if (redirected >= DEFAULT_MAX_REDIRECTION) throw new FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
|
|
40
|
+
if (visited.has(nextUrl)) throw new FetchError(currentUrl, `Redirect loop detected: ${nextUrl}`);
|
|
41
|
+
redirected++;
|
|
42
|
+
}
|
|
43
|
+
const response = await doubleKnock(originalRequest, identity, {
|
|
44
|
+
maxRedirection,
|
|
45
|
+
specDeterminer,
|
|
46
|
+
log: curry(logRequest)(logger),
|
|
47
|
+
tracerProvider,
|
|
48
|
+
signal: options?.signal,
|
|
49
|
+
validateRedirect: async (nextUrl) => {
|
|
50
|
+
follow(nextUrl);
|
|
51
|
+
await validateUrl(nextUrl);
|
|
52
|
+
visited.add(nextUrl);
|
|
53
|
+
currentUrl = nextUrl;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return getRemoteDocument(currentUrl, response, (alternateUrl) => {
|
|
57
|
+
follow(alternateUrl);
|
|
58
|
+
return load(alternateUrl, options, redirected, visited);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async function validateUrl(url) {
|
|
62
|
+
if (!allowPrivateAddress) try {
|
|
63
|
+
await validatePublicUrl(url);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
if (error instanceof UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
|
|
66
|
+
url,
|
|
67
|
+
error
|
|
68
|
+
});
|
|
69
|
+
else logger.error("Disallowed private URL: {url}", {
|
|
70
|
+
url,
|
|
71
|
+
error
|
|
72
|
+
});
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return (url, options) => load(url, options);
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
export { getAuthenticatedDocumentLoader as t };
|
|
@@ -2,10 +2,10 @@ import "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { n as assertExists } from "../std__assert-BvGuO4xO.mjs";
|
|
6
6
|
import { t as assertThrows } from "../assert_throws-BOkhLGYc.mjs";
|
|
7
7
|
import { t as MemoryKvStore } from "../kv-CX8qfaad.mjs";
|
|
8
|
-
import { r as createFederationBuilder } from "../builder-
|
|
8
|
+
import { r as createFederationBuilder } from "../builder-Dm72xfCy.mjs";
|
|
9
9
|
import { DisallowedOperatorError, DisallowedVarSpecModifierError, DuplicateRouteVariableError, RouteTemplateOptionsNotMatchedError, RouterError } from "@fedify/uri-template";
|
|
10
10
|
import { Activity, Note, Person } from "@fedify/vocab";
|
|
11
11
|
import { test } from "@fedify/fixture";
|
|
@@ -2,7 +2,7 @@ import { Temporal } from "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
5
|
-
import "../std__assert-
|
|
5
|
+
import "../std__assert-BvGuO4xO.mjs";
|
|
6
6
|
import { t as assertThrows } from "../assert_throws-BOkhLGYc.mjs";
|
|
7
7
|
import { t as MemoryKvStore } from "../kv-CX8qfaad.mjs";
|
|
8
8
|
import { n as normalizeCircuitBreakerOptions, r as parseCircuitBreakerKvState, t as CircuitBreaker } from "../circuit-breaker-CDABzqVf.mjs";
|
|
@@ -2,7 +2,7 @@ import "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
5
|
-
import "../std__assert-
|
|
5
|
+
import "../std__assert-BvGuO4xO.mjs";
|
|
6
6
|
import { n as digest, t as buildCollectionSynchronizationHeader } from "../collection-CA3V5zyK.mjs";
|
|
7
7
|
import { test } from "@fedify/fixture";
|
|
8
8
|
import { decodeHex } from "byte-encodings/hex";
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-
|
|
4
|
+
import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-CxbCIptr.mjs";
|
|
5
5
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
6
|
-
import "../std__assert-
|
|
6
|
+
import "../std__assert-BvGuO4xO.mjs";
|
|
7
7
|
import { n as assertGreaterOrEqual, t as assertRejects } from "../assert_rejects-DN60FHPX.mjs";
|
|
8
8
|
import { n as assertInstanceOf, t as assert } from "../assert-jSdZMYam.mjs";
|
|
9
9
|
import { r as parseAcceptSignature } from "../accept-Dd__NiUL.mjs";
|
|
10
|
-
import { a as generateCryptoKeyPair } from "../key-
|
|
11
|
-
import { s as signRequest } from "../http-
|
|
10
|
+
import { a as generateCryptoKeyPair } from "../key-D1TEfV4d.mjs";
|
|
11
|
+
import { s as signRequest } from "../http-BqObaL32.mjs";
|
|
12
12
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-BAK-tUlf.mjs";
|
|
13
|
-
import { a as compactJsonLd, p as signJsonLd } from "../ld-
|
|
14
|
-
import { r as signObject } from "../proof-
|
|
13
|
+
import { a as compactJsonLd, p as signJsonLd } from "../ld-DnGqAKWi.mjs";
|
|
14
|
+
import { r as signObject } from "../proof-CpU6r25U.mjs";
|
|
15
15
|
import { t as MemoryKvStore } from "../kv-CX8qfaad.mjs";
|
|
16
|
-
import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-
|
|
16
|
+
import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-XCr66VRe.mjs";
|
|
17
17
|
import { t as ActivityListenerSet } from "../activity-listener-ell7W1s9.mjs";
|
|
18
18
|
import { Activity, Create, CryptographicKey, Multikey, Note, Person, Tombstone } from "@fedify/vocab";
|
|
19
19
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
@@ -2,11 +2,11 @@ import "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
5
|
-
import "../std__assert-
|
|
5
|
+
import "../std__assert-BvGuO4xO.mjs";
|
|
6
6
|
import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
|
|
7
|
-
import { r as signObject } from "../proof-
|
|
7
|
+
import { r as signObject } from "../proof-CpU6r25U.mjs";
|
|
8
8
|
import { t as MemoryKvStore } from "../kv-CX8qfaad.mjs";
|
|
9
|
-
import { o as createFederation } from "../middleware-
|
|
9
|
+
import { o as createFederation } from "../middleware-XCr66VRe.mjs";
|
|
10
10
|
import { Create, Follow, Person } from "@fedify/vocab";
|
|
11
11
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
12
12
|
//#region src/federation/idempotency.test.ts
|
|
@@ -2,7 +2,7 @@ import { Temporal } from "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
5
|
-
import "../std__assert-
|
|
5
|
+
import "../std__assert-BvGuO4xO.mjs";
|
|
6
6
|
import { t as MemoryKvStore } from "../kv-CX8qfaad.mjs";
|
|
7
7
|
import { test } from "@fedify/fixture";
|
|
8
8
|
//#region src/federation/kv.test.ts
|
|
@@ -2,9 +2,9 @@ import "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
5
|
-
import "../std__assert-
|
|
5
|
+
import "../std__assert-BvGuO4xO.mjs";
|
|
6
6
|
import { t as assertRejects } from "../assert_rejects-DN60FHPX.mjs";
|
|
7
|
-
import { _ as recordKeyLookup, 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, m as recordDocumentFetch, p as recordDocumentCache, r as getFederationMetrics, t as classifyFetchError, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "../metrics-
|
|
7
|
+
import { _ as recordKeyLookup, 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, m as recordDocumentFetch, p as recordDocumentCache, r as getFederationMetrics, t as classifyFetchError, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "../metrics-jUJmtE2u.mjs";
|
|
8
8
|
import { createTestMeterProvider, test } from "@fedify/fixture";
|
|
9
9
|
import { FetchError } from "@fedify/vocab-runtime";
|
|
10
10
|
import { DataPointType, MeterProvider, MetricReader } from "@opentelemetry/sdk-metrics";
|
|
@@ -3,21 +3,21 @@ import "urlpattern-polyfill";
|
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as esm_default } from "../esm-vrlUxr60.mjs";
|
|
5
5
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { n as assertExists } from "../std__assert-BvGuO4xO.mjs";
|
|
7
7
|
import { r as assertFalse, t as assertRejects } from "../assert_rejects-DN60FHPX.mjs";
|
|
8
8
|
import { n as assertInstanceOf, t as assert } from "../assert-jSdZMYam.mjs";
|
|
9
9
|
import { t as assertThrows } from "../assert_throws-BOkhLGYc.mjs";
|
|
10
10
|
import { t as assertNotEquals } from "../assert_not_equals-DkVK8oqV.mjs";
|
|
11
11
|
import { t as assertStrictEquals } from "../assert_strict_equals-XEgZAlrj.mjs";
|
|
12
|
-
import { g as recordInboxActivity } from "../metrics-
|
|
13
|
-
import { l as verifyRequest, s as signRequest } from "../http-
|
|
12
|
+
import { g as recordInboxActivity } from "../metrics-jUJmtE2u.mjs";
|
|
13
|
+
import { l as verifyRequest, s as signRequest } from "../http-BqObaL32.mjs";
|
|
14
14
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
|
|
15
|
-
import { t as getAuthenticatedDocumentLoader } from "../docloader-
|
|
16
|
-
import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-
|
|
17
|
-
import { t as doesActorOwnKey } from "../owner-
|
|
18
|
-
import { i as verifyObject, r as signObject } from "../proof-
|
|
15
|
+
import { t as getAuthenticatedDocumentLoader } from "../docloader-BAVVKXPl.mjs";
|
|
16
|
+
import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-DnGqAKWi.mjs";
|
|
17
|
+
import { t as doesActorOwnKey } from "../owner-C4iDnghj.mjs";
|
|
18
|
+
import { i as verifyObject, r as signObject } from "../proof-CpU6r25U.mjs";
|
|
19
19
|
import { t as MemoryKvStore } from "../kv-CX8qfaad.mjs";
|
|
20
|
-
import { g as handleBenchmarkTrigger, i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-
|
|
20
|
+
import { g as handleBenchmarkTrigger, i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-XCr66VRe.mjs";
|
|
21
21
|
import { t as CircuitBreaker } from "../circuit-breaker-CDABzqVf.mjs";
|
|
22
22
|
import { t as markCircuitBreakerLegacySweepDone } from "../circuit-breaker-test-utils-Dm5MM6l9.mjs";
|
|
23
23
|
import { configure, reset } from "@logtape/logtape";
|
|
@@ -27,6 +27,7 @@ import { Create, Offer, Person, getTypeId, lookupObject } from "@fedify/vocab";
|
|
|
27
27
|
import { SpanStatusCode, metrics } from "@opentelemetry/api";
|
|
28
28
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
29
29
|
import { FetchError, UrlError, getDocumentLoader } from "@fedify/vocab-runtime";
|
|
30
|
+
import dns from "node:dns/promises";
|
|
30
31
|
import serialize from "json-canon";
|
|
31
32
|
import { DataPointType, MeterProvider as MeterProvider$1, MetricReader } from "@opentelemetry/sdk-metrics";
|
|
32
33
|
//#region ../fixture/src/fixtures/example.com/create.json
|
|
@@ -1168,6 +1169,31 @@ test({
|
|
|
1168
1169
|
esm_default.hardReset();
|
|
1169
1170
|
}
|
|
1170
1171
|
});
|
|
1172
|
+
test("Federation.fetch() [rfc9421] multiple POST signatures", async (t) => {
|
|
1173
|
+
for (const validSecond of [true, false]) await t.step(validSecond ? "valid second signature" : "both signatures invalid", async () => {
|
|
1174
|
+
const inbox = [];
|
|
1175
|
+
const federation = createFederation({
|
|
1176
|
+
kv: new MemoryKvStore(),
|
|
1177
|
+
documentLoaderFactory: () => mockDocumentLoader,
|
|
1178
|
+
contextLoaderFactory: () => mockDocumentLoader
|
|
1179
|
+
});
|
|
1180
|
+
federation.setActorDispatcher("/users/{identifier}", (ctx, identifier) => new vocab.Person({ id: ctx.getActorUri(identifier) }));
|
|
1181
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(vocab.Create, (_ctx, activity) => {
|
|
1182
|
+
inbox.push(activity.id.href);
|
|
1183
|
+
});
|
|
1184
|
+
const signed = await signRequest(new Request("https://example.com/inbox", {
|
|
1185
|
+
method: "POST",
|
|
1186
|
+
headers: { "Content-Type": "application/activity+json" },
|
|
1187
|
+
body: JSON.stringify(create_default)
|
|
1188
|
+
}), rsaPrivateKey2, rsaPublicKey2.id, { spec: "rfc9421" });
|
|
1189
|
+
const input = signed.headers.get("Signature-Input");
|
|
1190
|
+
const signature = signed.headers.get("Signature");
|
|
1191
|
+
signed.headers.set("Signature-Input", `${input}, ${input.replace(/^sig1=/, "sig2=")}`);
|
|
1192
|
+
signed.headers.set("Signature", `sig1=:AAAAAA==:, ${validSecond ? signature.replace(/^sig1=/, "sig2=") : "sig2=:AAAAAA==:"}`);
|
|
1193
|
+
assertEquals((await federation.fetch(signed, { contextData: void 0 })).status, validSecond ? 202 : 401);
|
|
1194
|
+
assertEquals(inbox, validSecond ? [id$2] : []);
|
|
1195
|
+
});
|
|
1196
|
+
});
|
|
1171
1197
|
test("Federation.fetch()", async (t) => {
|
|
1172
1198
|
esm_default.spyGlobal();
|
|
1173
1199
|
esm_default.get("https://example.com/key2", {
|
|
@@ -2655,6 +2681,7 @@ test("FederationImpl.processQueuedTask()", async (t) => {
|
|
|
2655
2681
|
const kv = new MemoryKvStore();
|
|
2656
2682
|
const queuedMessages = [];
|
|
2657
2683
|
const federation = new FederationImpl({
|
|
2684
|
+
allowPrivateAddress: true,
|
|
2658
2685
|
kv,
|
|
2659
2686
|
queue: {
|
|
2660
2687
|
nativeRetrial: true,
|
|
@@ -2717,6 +2744,7 @@ test("FederationImpl.processQueuedTask()", async (t) => {
|
|
|
2717
2744
|
const kv = new MemoryKvStore();
|
|
2718
2745
|
let queuedMessages = [];
|
|
2719
2746
|
const federation = new FederationImpl({
|
|
2747
|
+
allowPrivateAddress: true,
|
|
2720
2748
|
kv,
|
|
2721
2749
|
queue: {
|
|
2722
2750
|
enqueue(message, _options) {
|
|
@@ -4484,6 +4512,157 @@ test("FederationImpl.processQueuedTask()", async (t) => {
|
|
|
4484
4512
|
assertEquals(queuedMessages, []);
|
|
4485
4513
|
});
|
|
4486
4514
|
});
|
|
4515
|
+
test({
|
|
4516
|
+
name: "FederationImpl.processQueuedTask() accounts for destination validation failures",
|
|
4517
|
+
ignore: "Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted",
|
|
4518
|
+
async fn(t) {
|
|
4519
|
+
for (const result of [
|
|
4520
|
+
"throws",
|
|
4521
|
+
"empty",
|
|
4522
|
+
"private"
|
|
4523
|
+
]) for (const failureThreshold of [1, 5]) for (const redirected of [false, true]) await t.step(`${result}, threshold: ${failureThreshold}, redirected: ${redirected}`, async () => {
|
|
4524
|
+
const destination = "https://delivery.invalid/inbox";
|
|
4525
|
+
const publicInbox = "https://8.8.8.8/inbox";
|
|
4526
|
+
const queuedMessages = [];
|
|
4527
|
+
const delays = [];
|
|
4528
|
+
const errors = [];
|
|
4529
|
+
const activities = [];
|
|
4530
|
+
const attempts = [];
|
|
4531
|
+
const delay = Temporal.Duration.from({ seconds: 5 });
|
|
4532
|
+
const kv = new MemoryKvStore();
|
|
4533
|
+
await markCircuitBreakerLegacySweepDone(kv);
|
|
4534
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
4535
|
+
const recoveryDelay = Temporal.Duration.from({ minutes: 30 });
|
|
4536
|
+
const federation = new FederationImpl({
|
|
4537
|
+
kv,
|
|
4538
|
+
meterProvider,
|
|
4539
|
+
circuitBreaker: {
|
|
4540
|
+
failureThreshold,
|
|
4541
|
+
recoveryDelay
|
|
4542
|
+
},
|
|
4543
|
+
queue: {
|
|
4544
|
+
enqueue(message, options) {
|
|
4545
|
+
queuedMessages.push(message);
|
|
4546
|
+
delays.push(options?.delay);
|
|
4547
|
+
return Promise.resolve();
|
|
4548
|
+
},
|
|
4549
|
+
listen() {
|
|
4550
|
+
return Promise.resolve();
|
|
4551
|
+
}
|
|
4552
|
+
},
|
|
4553
|
+
documentLoaderFactory: () => mockDocumentLoader,
|
|
4554
|
+
contextLoaderFactory: () => mockDocumentLoader,
|
|
4555
|
+
onOutboxError(error, activity) {
|
|
4556
|
+
errors.push(error);
|
|
4557
|
+
activities.push(activity);
|
|
4558
|
+
},
|
|
4559
|
+
outboxRetryPolicy(options) {
|
|
4560
|
+
attempts.push(options.attempts);
|
|
4561
|
+
return delay;
|
|
4562
|
+
}
|
|
4563
|
+
});
|
|
4564
|
+
const message = {
|
|
4565
|
+
type: "outbox",
|
|
4566
|
+
id: crypto.randomUUID(),
|
|
4567
|
+
baseUrl: "https://example.com",
|
|
4568
|
+
keys: [],
|
|
4569
|
+
activity: {
|
|
4570
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
4571
|
+
type: "Create",
|
|
4572
|
+
id: "https://example.com/activities/dns-failure",
|
|
4573
|
+
actor: "https://example.com/users/alice",
|
|
4574
|
+
object: {
|
|
4575
|
+
type: "Note",
|
|
4576
|
+
content: "test"
|
|
4577
|
+
}
|
|
4578
|
+
},
|
|
4579
|
+
activityType: "https://www.w3.org/ns/activitystreams#Create",
|
|
4580
|
+
inbox: redirected ? publicInbox : destination,
|
|
4581
|
+
sharedInbox: false,
|
|
4582
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4583
|
+
attempt: 0,
|
|
4584
|
+
headers: {},
|
|
4585
|
+
traceContext: {}
|
|
4586
|
+
};
|
|
4587
|
+
const originalLookup = dns.lookup;
|
|
4588
|
+
const resolverError = /* @__PURE__ */ new Error("Resolver unavailable");
|
|
4589
|
+
const lookups = [];
|
|
4590
|
+
dns.lookup = ((hostname) => {
|
|
4591
|
+
lookups.push(hostname);
|
|
4592
|
+
assertEquals(hostname, "delivery.invalid");
|
|
4593
|
+
if (result === "throws") return Promise.reject(resolverError);
|
|
4594
|
+
return Promise.resolve(result === "empty" ? [] : [{
|
|
4595
|
+
address: "127.0.0.1",
|
|
4596
|
+
family: 4
|
|
4597
|
+
}]);
|
|
4598
|
+
});
|
|
4599
|
+
try {
|
|
4600
|
+
esm_default.mockGlobal().catch(202);
|
|
4601
|
+
if (redirected) esm_default.route(publicInbox, {
|
|
4602
|
+
status: 307,
|
|
4603
|
+
headers: { Location: destination }
|
|
4604
|
+
});
|
|
4605
|
+
await federation.processQueuedTask(void 0, message);
|
|
4606
|
+
assertEquals(errors.length, 1);
|
|
4607
|
+
const error = errors[0];
|
|
4608
|
+
if (result === "private") {
|
|
4609
|
+
assertInstanceOf(error, UrlError);
|
|
4610
|
+
assertEquals(error.reason, "disallowed");
|
|
4611
|
+
} else {
|
|
4612
|
+
assertInstanceOf(error, FetchError);
|
|
4613
|
+
assertEquals(error.url.href, destination);
|
|
4614
|
+
assertInstanceOf(error.cause, UrlError);
|
|
4615
|
+
assertEquals(error.cause.reason, "dns");
|
|
4616
|
+
assertStrictEquals(error.cause.cause, result === "throws" ? resolverError : void 0);
|
|
4617
|
+
}
|
|
4618
|
+
assertEquals(activities.length, 1);
|
|
4619
|
+
assertInstanceOf(activities[0], Create);
|
|
4620
|
+
assertEquals(activities[0].id?.href, "https://example.com/activities/dns-failure");
|
|
4621
|
+
assertEquals(attempts, [0]);
|
|
4622
|
+
const host = new URL(message.inbox).host;
|
|
4623
|
+
const state = await kv.get([
|
|
4624
|
+
"_fedify",
|
|
4625
|
+
"circuit",
|
|
4626
|
+
host
|
|
4627
|
+
]);
|
|
4628
|
+
if (result === "private") assertEquals(state, void 0);
|
|
4629
|
+
else {
|
|
4630
|
+
assertExists(state);
|
|
4631
|
+
assertEquals(state.failures.length, 1);
|
|
4632
|
+
assertEquals(state.state, failureThreshold === 1 ? "open" : "closed");
|
|
4633
|
+
}
|
|
4634
|
+
if (result !== "private" && failureThreshold === 1) {
|
|
4635
|
+
assertEquals(queuedMessages.length, 1);
|
|
4636
|
+
const held = queuedMessages[0];
|
|
4637
|
+
assertEquals(held.attempt, 0);
|
|
4638
|
+
assertEquals(held.circuitHeld, true);
|
|
4639
|
+
assertExists(held.circuitHeldSince);
|
|
4640
|
+
assertEquals(delays, [recoveryDelay]);
|
|
4641
|
+
} else {
|
|
4642
|
+
assertEquals(queuedMessages, [{
|
|
4643
|
+
...message,
|
|
4644
|
+
attempt: 1
|
|
4645
|
+
}]);
|
|
4646
|
+
assertEquals(delays, [delay]);
|
|
4647
|
+
}
|
|
4648
|
+
const expectedCount = result === "private" && !redirected ? 0 : 1;
|
|
4649
|
+
const sent = recorder.getMeasurements("activitypub.delivery.sent");
|
|
4650
|
+
assertEquals(sent.length, expectedCount);
|
|
4651
|
+
if (expectedCount === 1) {
|
|
4652
|
+
assertEquals(sent[0].value, 1);
|
|
4653
|
+
assertEquals(sent[0].attributes["activitypub.delivery.success"], false);
|
|
4654
|
+
assertEquals(sent[0].attributes["activitypub.remote.host"], host);
|
|
4655
|
+
}
|
|
4656
|
+
assertEquals(lookups, ["delivery.invalid"]);
|
|
4657
|
+
assertEquals(esm_default.callHistory.calls(destination).length, 0);
|
|
4658
|
+
assertEquals(esm_default.callHistory.calls().length, redirected ? 1 : 0);
|
|
4659
|
+
} finally {
|
|
4660
|
+
dns.lookup = originalLookup;
|
|
4661
|
+
esm_default.hardReset();
|
|
4662
|
+
}
|
|
4663
|
+
});
|
|
4664
|
+
}
|
|
4665
|
+
});
|
|
4487
4666
|
test("FederationImpl.processQueuedTask() permanent failure", async (t) => {
|
|
4488
4667
|
esm_default.spyGlobal();
|
|
4489
4668
|
esm_default.post("https://gone.example/inbox", {
|
package/dist/federation/mod.cjs
CHANGED
|
@@ -2,7 +2,7 @@ const { Temporal } = require("@js-temporal/polyfill");
|
|
|
2
2
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
4
|
require("../chunk-DDcVe30Y.cjs");
|
|
5
|
-
const require_middleware = require("../middleware-
|
|
5
|
+
const require_middleware = require("../middleware-BW5W0CTK.cjs");
|
|
6
6
|
let _logtape_logtape = require("@logtape/logtape");
|
|
7
7
|
let _fedify_uri_template = require("@fedify/uri-template");
|
|
8
8
|
let es_toolkit = require("es-toolkit");
|
package/dist/federation/mod.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { a as SendActivityError, c as buildCollectionSynchronizationHeader, d as normalizeCircuitBreakerOptions, f as parseCircuitBreakerKvState, i as createExponentialBackoffPolicy, l as digest, o as respondWithObject, p as createFederationBuilder, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as CircuitBreaker } from "../middleware-
|
|
3
|
+
import { a as SendActivityError, c as buildCollectionSynchronizationHeader, d as normalizeCircuitBreakerOptions, f as parseCircuitBreakerKvState, i as createExponentialBackoffPolicy, l as digest, o as respondWithObject, p as createFederationBuilder, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as CircuitBreaker } from "../middleware-A-SjayRO.js";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
5
|
import { Router as Router$1, RouterError as RouterError$1, assertPath, isPath } from "@fedify/uri-template";
|
|
6
6
|
import { isEqual } from "es-toolkit";
|