@fedify/fedify 2.0.35-dev.2470 → 2.0.35-dev.2485
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-CusH9K0G.mjs → builder-CKbQrOH1.mjs} +3 -3
- package/dist/{client-CAM_bQXx.d.cts → client-BIBzqEIp.d.cts} +0 -1
- package/dist/{client-BUqbea1d.mjs → client-DGNacEx_.mjs} +113 -64
- package/dist/{client-CSddvgWN.d.ts → client-jE7oGvRh.d.ts} +0 -1
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{context-r8sjhltN.d.cts → context-CnoWxuoB.d.cts} +2 -2
- package/dist/{context-B2iX3heK.d.ts → context-lxjaUTAI.d.ts} +2 -2
- package/dist/{deno-BtRoR9in.mjs → deno-DL_ifPJk.mjs} +1 -1
- package/dist/docloader-B0_0Fjqo.mjs +124 -0
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +6 -6
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +42 -23
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +2 -2
- package/dist/federation/mod.d.ts +2 -2
- package/dist/federation/mod.js +1 -1
- package/dist/federation/send.test.mjs +445 -70
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{http-Bs6thuCC.d.cts → http-Cd8UD1ym.d.ts} +1 -2
- package/dist/{http-C7d5tddX.cjs → http-CjkqnlR2.cjs} +114 -56
- package/dist/{http-CDupyfOc.d.ts → http-DFqAb11b.d.cts} +1 -2
- package/dist/{http-BTQ5wllw.js → http-DVEBwq6e.js} +113 -55
- package/dist/{http-CMduEDST.mjs → http-Dvsq4uCu.mjs} +136 -49
- package/dist/{inbox-BkvNt_Sb.mjs → inbox-D6YbZFrZ.mjs} +1 -1
- package/dist/{key-CDAqlyxF.mjs → key-CgcWGw6W.mjs} +1 -1
- package/dist/{kv-cache-Cm0DSKFo.js → kv-cache-DC_F2WSH.js} +60 -18
- package/dist/{kv-cache-D-xcxGwU.cjs → kv-cache-nN9RaiYp.cjs} +59 -17
- package/dist/{ld-Ceiqwon2.mjs → ld-CfOIwHup.mjs} +2 -2
- package/dist/{middleware-NVjGfqSC.js → middleware-B0eqDUmD.js} +118 -33
- package/dist/{middleware-Bj0R0_D7.mjs → middleware-CA7egMDR.mjs} +12 -12
- package/dist/{middleware-Bx0vwC0P.cjs → middleware-Cu_yKroA.cjs} +1 -1
- package/dist/{middleware-bv6duFIF.mjs → middleware-DEwj7jGj.mjs} +1 -1
- package/dist/{middleware-BWtJCObq.cjs → middleware-DZvWX5af.cjs} +118 -33
- package/dist/{mod-BkPB4hPe.d.cts → mod-BPDfu4Ey.d.cts} +2 -7
- package/dist/{mod-E7YM72JZ.d.ts → mod-BS-jg3Wx.d.ts} +1 -1
- package/dist/{mod-BKJyD7nk.d.ts → mod-DGP89q0u.d.ts} +2 -7
- package/dist/{mod-IOWUF5Yr.d.cts → mod-DVPS4yam.d.cts} +1 -1
- package/dist/{mod-CM_eR8hQ.d.ts → mod-I-34OMN3.d.ts} +1 -1
- package/dist/{mod-DDTLbfce.d.cts → mod-w1oyHseQ.d.cts} +1 -1
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +6 -6
- package/dist/mod.d.ts +6 -6
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/client.test.mjs +337 -19
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.d.cts +1 -1
- package/dist/nodeinfo/mod.d.ts +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/otel/exporter.test.mjs +292 -30
- package/dist/otel/mod.cjs +36 -30
- package/dist/otel/mod.d.cts +13 -0
- package/dist/otel/mod.d.ts +13 -0
- package/dist/otel/mod.js +36 -30
- package/dist/{owner-Cmn2WKBi.mjs → owner-BvVwCoj2.mjs} +2 -2
- package/dist/{proof-DllVQdfR.cjs → proof-BssSZpMi.cjs} +1 -1
- package/dist/{proof-VZ9Ryp3F.mjs → proof-C9TBSmUR.mjs} +2 -2
- package/dist/{proof-ClDA_fq4.js → proof-Cp62GFJ3.js} +1 -1
- package/dist/{send-BiC5gxMS.mjs → send-ZWmjk2Zf.mjs} +117 -32
- package/dist/sig/http.test.mjs +411 -2
- package/dist/sig/key.test.mjs +2 -2
- package/dist/sig/ld.test.mjs +2 -2
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/proof.test.mjs +1 -1
- package/dist/{temporal-DXm8zGR8.mjs → temporal-BNtFi_ut.mjs} +1 -1
- package/dist/testing/mod.d.mts +0 -1
- package/dist/{types-miAYfiGO.js → types-B1_pRFE_.js} +112 -63
- package/dist/{types-DRPjmHVV.cjs → types-DOvyymM4.cjs} +111 -62
- package/dist/utils/docloader.test.mjs +316 -32
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
- package/dist/docloader-BF0FJHJ1.mjs +0 -82
|
@@ -1,7 +1,7 @@
|
|
|
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-
|
|
4
|
+
import { n as version, t as name } from "./deno-DL_ifPJk.mjs";
|
|
5
5
|
import { Activity, getTypeId } from "@fedify/vocab";
|
|
6
6
|
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
7
7
|
import { getLogger } from "@logtape/logtape";
|
|
@@ -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-DL_ifPJk.mjs";
|
|
5
5
|
import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
|
|
6
6
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
7
7
|
import { FetchError, UrlError, getDocumentLoader } from "@fedify/vocab-runtime";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { l as validateCryptoKey, t as
|
|
3
|
+
import { l as validateCryptoKey, t as doubleKnockScoped } from "./http-DVEBwq6e.js";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
5
|
import { curry } from "es-toolkit";
|
|
6
|
-
import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts
|
|
6
|
+
import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts } from "@fedify/vocab-runtime";
|
|
7
|
+
import { openDirectFetch, openPublicFetch } from "@fedify/vocab-runtime/internal";
|
|
7
8
|
//#region src/utils/docloader.ts
|
|
8
9
|
const logger$1 = getLogger([
|
|
9
10
|
"fedify",
|
|
@@ -24,12 +25,34 @@ const DEFAULT_MAX_REDIRECTION = 20;
|
|
|
24
25
|
* @throws {TypeError} If the key is invalid or unsupported.
|
|
25
26
|
* @since 0.4.0
|
|
26
27
|
*/
|
|
27
|
-
function getAuthenticatedDocumentLoader(identity,
|
|
28
|
+
function getAuthenticatedDocumentLoader(identity, options = {}) {
|
|
29
|
+
return createAuthenticatedDocumentLoader(identity, options);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The placeholder that the alternate document callback of
|
|
33
|
+
* `getRemoteDocument()` returns, so that the alternate document is loaded
|
|
34
|
+
* after the current hop is closed.
|
|
35
|
+
*/
|
|
36
|
+
const ALTERNATE_PLACEHOLDER = Object.freeze({
|
|
37
|
+
contextUrl: null,
|
|
38
|
+
document: null,
|
|
39
|
+
documentUrl: "about:blank"
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* The implementation of {@link getAuthenticatedDocumentLoader}. This is not
|
|
43
|
+
* exported from any entry point.
|
|
44
|
+
* @param identity The identity to get the document loader for.
|
|
45
|
+
* @param options The options for the document loader.
|
|
46
|
+
* @param deps The dependencies, for tests.
|
|
47
|
+
* @returns The authenticated document loader.
|
|
48
|
+
* @throws {TypeError} If the key is invalid or unsupported.
|
|
49
|
+
*/
|
|
50
|
+
function createAuthenticatedDocumentLoader(identity, { allowPrivateAddress, userAgent, specDeterminer, tracerProvider } = {}, deps = {}) {
|
|
28
51
|
validateCryptoKey(identity.privateKey);
|
|
29
52
|
async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
|
|
30
53
|
options?.signal?.throwIfAborted();
|
|
31
54
|
let currentUrl = new URL(url).href;
|
|
32
|
-
|
|
55
|
+
validateUrl(currentUrl);
|
|
33
56
|
visited.add(currentUrl);
|
|
34
57
|
const originalRequest = createActivityPubRequest(currentUrl, { userAgent });
|
|
35
58
|
function follow(nextUrl) {
|
|
@@ -38,30 +61,43 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, userAge
|
|
|
38
61
|
if (visited.has(nextUrl)) throw new FetchError(currentUrl, `Redirect loop detected: ${nextUrl}`);
|
|
39
62
|
redirected++;
|
|
40
63
|
}
|
|
41
|
-
const
|
|
64
|
+
const result = await doubleKnockScoped(originalRequest, identity, {
|
|
42
65
|
specDeterminer,
|
|
43
66
|
log: curry(logRequest)(logger$1),
|
|
44
67
|
tracerProvider,
|
|
45
68
|
signal: options?.signal,
|
|
46
|
-
validateRedirect:
|
|
69
|
+
validateRedirect: (nextUrl) => {
|
|
47
70
|
follow(nextUrl);
|
|
48
|
-
|
|
71
|
+
validateUrl(nextUrl);
|
|
49
72
|
visited.add(nextUrl);
|
|
50
73
|
currentUrl = nextUrl;
|
|
51
74
|
}
|
|
75
|
+
}, (hopUrl) => openHop(hopUrl, options?.signal), async (response) => {
|
|
76
|
+
let alternateUrl;
|
|
77
|
+
const document = await getRemoteDocument(currentUrl, response, (url) => {
|
|
78
|
+
alternateUrl = url;
|
|
79
|
+
return Promise.resolve(ALTERNATE_PLACEHOLDER);
|
|
80
|
+
});
|
|
81
|
+
if (alternateUrl == null) return { document };
|
|
82
|
+
if (!response.bodyUsed && response.body?.locked !== true) response.body?.cancel().catch((error) => {
|
|
83
|
+
logger$1.debug("Failed to cancel the response body from {url}: {error}", {
|
|
84
|
+
url: currentUrl,
|
|
85
|
+
error
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
return { alternateUrl };
|
|
52
89
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
90
|
+
if ("document" in result) return result.document;
|
|
91
|
+
follow(result.alternateUrl);
|
|
92
|
+
return load(result.alternateUrl, options, redirected, visited);
|
|
57
93
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
await
|
|
94
|
+
function openUnvalidatedHop(url, signal) {
|
|
95
|
+
if (deps.openHop != null) return deps.openHop(url);
|
|
96
|
+
return allowPrivateAddress ? openDirectFetch(url) : openPublicFetch(url, { signal });
|
|
97
|
+
}
|
|
98
|
+
async function openHop(url, signal) {
|
|
99
|
+
try {
|
|
100
|
+
return await openUnvalidatedHop(url, signal);
|
|
65
101
|
} catch (error) {
|
|
66
102
|
if (error instanceof UrlError) if (error.reason === "dns") logger$1.debug("DNS lookup failed for {url}", {
|
|
67
103
|
url,
|
|
@@ -74,6 +110,12 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, userAge
|
|
|
74
110
|
throw error;
|
|
75
111
|
}
|
|
76
112
|
}
|
|
113
|
+
function validateUrl(url) {
|
|
114
|
+
const parsedUrl = new URL(url);
|
|
115
|
+
const protocol = parsedUrl.protocol;
|
|
116
|
+
if (protocol !== "http:" && protocol !== "https:") throw new UrlError(`Unsupported protocol: ${protocol}`);
|
|
117
|
+
if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new UrlError("URLs with credentials are not allowed");
|
|
118
|
+
}
|
|
77
119
|
return (url, options) => load(url, options);
|
|
78
120
|
}
|
|
79
121
|
//#endregion
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
const { Temporal } = require("@js-temporal/polyfill");
|
|
2
2
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
3
3
|
require("./chunk-DDcVe30Y.cjs");
|
|
4
|
-
const require_http = require("./http-
|
|
4
|
+
const require_http = require("./http-CjkqnlR2.cjs");
|
|
5
5
|
let _logtape_logtape = require("@logtape/logtape");
|
|
6
6
|
let es_toolkit = require("es-toolkit");
|
|
7
7
|
let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
|
|
8
|
+
let _fedify_vocab_runtime_internal = require("@fedify/vocab-runtime/internal");
|
|
8
9
|
//#region src/utils/docloader.ts
|
|
9
10
|
const logger$1 = (0, _logtape_logtape.getLogger)([
|
|
10
11
|
"fedify",
|
|
@@ -25,12 +26,34 @@ const DEFAULT_MAX_REDIRECTION = 20;
|
|
|
25
26
|
* @throws {TypeError} If the key is invalid or unsupported.
|
|
26
27
|
* @since 0.4.0
|
|
27
28
|
*/
|
|
28
|
-
function getAuthenticatedDocumentLoader(identity,
|
|
29
|
+
function getAuthenticatedDocumentLoader(identity, options = {}) {
|
|
30
|
+
return createAuthenticatedDocumentLoader(identity, options);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The placeholder that the alternate document callback of
|
|
34
|
+
* `getRemoteDocument()` returns, so that the alternate document is loaded
|
|
35
|
+
* after the current hop is closed.
|
|
36
|
+
*/
|
|
37
|
+
const ALTERNATE_PLACEHOLDER = Object.freeze({
|
|
38
|
+
contextUrl: null,
|
|
39
|
+
document: null,
|
|
40
|
+
documentUrl: "about:blank"
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* The implementation of {@link getAuthenticatedDocumentLoader}. This is not
|
|
44
|
+
* exported from any entry point.
|
|
45
|
+
* @param identity The identity to get the document loader for.
|
|
46
|
+
* @param options The options for the document loader.
|
|
47
|
+
* @param deps The dependencies, for tests.
|
|
48
|
+
* @returns The authenticated document loader.
|
|
49
|
+
* @throws {TypeError} If the key is invalid or unsupported.
|
|
50
|
+
*/
|
|
51
|
+
function createAuthenticatedDocumentLoader(identity, { allowPrivateAddress, userAgent, specDeterminer, tracerProvider } = {}, deps = {}) {
|
|
29
52
|
require_http.validateCryptoKey(identity.privateKey);
|
|
30
53
|
async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
|
|
31
54
|
options?.signal?.throwIfAborted();
|
|
32
55
|
let currentUrl = new URL(url).href;
|
|
33
|
-
|
|
56
|
+
validateUrl(currentUrl);
|
|
34
57
|
visited.add(currentUrl);
|
|
35
58
|
const originalRequest = (0, _fedify_vocab_runtime.createActivityPubRequest)(currentUrl, { userAgent });
|
|
36
59
|
function follow(nextUrl) {
|
|
@@ -39,30 +62,43 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, userAge
|
|
|
39
62
|
if (visited.has(nextUrl)) throw new _fedify_vocab_runtime.FetchError(currentUrl, `Redirect loop detected: ${nextUrl}`);
|
|
40
63
|
redirected++;
|
|
41
64
|
}
|
|
42
|
-
const
|
|
65
|
+
const result = await require_http.doubleKnockScoped(originalRequest, identity, {
|
|
43
66
|
specDeterminer,
|
|
44
67
|
log: (0, es_toolkit.curry)(_fedify_vocab_runtime.logRequest)(logger$1),
|
|
45
68
|
tracerProvider,
|
|
46
69
|
signal: options?.signal,
|
|
47
|
-
validateRedirect:
|
|
70
|
+
validateRedirect: (nextUrl) => {
|
|
48
71
|
follow(nextUrl);
|
|
49
|
-
|
|
72
|
+
validateUrl(nextUrl);
|
|
50
73
|
visited.add(nextUrl);
|
|
51
74
|
currentUrl = nextUrl;
|
|
52
75
|
}
|
|
76
|
+
}, (hopUrl) => openHop(hopUrl, options?.signal), async (response) => {
|
|
77
|
+
let alternateUrl;
|
|
78
|
+
const document = await (0, _fedify_vocab_runtime.getRemoteDocument)(currentUrl, response, (url) => {
|
|
79
|
+
alternateUrl = url;
|
|
80
|
+
return Promise.resolve(ALTERNATE_PLACEHOLDER);
|
|
81
|
+
});
|
|
82
|
+
if (alternateUrl == null) return { document };
|
|
83
|
+
if (!response.bodyUsed && response.body?.locked !== true) response.body?.cancel().catch((error) => {
|
|
84
|
+
logger$1.debug("Failed to cancel the response body from {url}: {error}", {
|
|
85
|
+
url: currentUrl,
|
|
86
|
+
error
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
return { alternateUrl };
|
|
53
90
|
});
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
});
|
|
91
|
+
if ("document" in result) return result.document;
|
|
92
|
+
follow(result.alternateUrl);
|
|
93
|
+
return load(result.alternateUrl, options, redirected, visited);
|
|
58
94
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
await (
|
|
95
|
+
function openUnvalidatedHop(url, signal) {
|
|
96
|
+
if (deps.openHop != null) return deps.openHop(url);
|
|
97
|
+
return allowPrivateAddress ? (0, _fedify_vocab_runtime_internal.openDirectFetch)(url) : (0, _fedify_vocab_runtime_internal.openPublicFetch)(url, { signal });
|
|
98
|
+
}
|
|
99
|
+
async function openHop(url, signal) {
|
|
100
|
+
try {
|
|
101
|
+
return await openUnvalidatedHop(url, signal);
|
|
66
102
|
} catch (error) {
|
|
67
103
|
if (error instanceof _fedify_vocab_runtime.UrlError) if (error.reason === "dns") logger$1.debug("DNS lookup failed for {url}", {
|
|
68
104
|
url,
|
|
@@ -75,6 +111,12 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, userAge
|
|
|
75
111
|
throw error;
|
|
76
112
|
}
|
|
77
113
|
}
|
|
114
|
+
function validateUrl(url) {
|
|
115
|
+
const parsedUrl = new URL(url);
|
|
116
|
+
const protocol = parsedUrl.protocol;
|
|
117
|
+
if (protocol !== "http:" && protocol !== "https:") throw new _fedify_vocab_runtime.UrlError(`Unsupported protocol: ${protocol}`);
|
|
118
|
+
if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new _fedify_vocab_runtime.UrlError("URLs with credentials are not allowed");
|
|
119
|
+
}
|
|
78
120
|
return (url, options) => load(url, options);
|
|
79
121
|
}
|
|
80
122
|
//#endregion
|
|
@@ -1,8 +1,8 @@
|
|
|
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-
|
|
5
|
-
import { o as validateCryptoKey, r as fetchKey } from "./key-
|
|
4
|
+
import { n as version, t as name } from "./deno-DL_ifPJk.mjs";
|
|
5
|
+
import { o as validateCryptoKey, r as fetchKey } from "./key-CgcWGw6W.mjs";
|
|
6
6
|
import { Activity, CryptographicKey, Object as Object$1, getTypeId } from "@fedify/vocab";
|
|
7
7
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
8
8
|
import { getDocumentLoader } from "@fedify/vocab-runtime";
|
|
@@ -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 { c as importJwk, d as name, f as version, i as exportJwk, l as validateCryptoKey, r as verifyRequest, t as
|
|
6
|
-
import { a as MAX_BODY_SIZE, i as BodyTooLargeError, n as getNodeInfo, o as readBoundedText, t as nodeInfoToJson } from "./types-
|
|
7
|
-
import { _ as signJsonLd, a as doesActorOwnKey, c as assertSafeJsonLd, f as detachSignature, g as isInvalidUrlTypeError, h as isClearlyMalformedContextReference, m as hasSignature, n as signObject, o as getKeyOwner, p as getNormalizationContextLoader, r as verifyObject, s as InvalidContextReferenceError, u as compactJsonLd, v as verifyCompactJsonLd, x as wrapContextLoaderForJsonLd } from "./proof-
|
|
8
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-
|
|
5
|
+
import { c as importJwk, d as name, f as version, i as exportJwk, l as validateCryptoKey, r as verifyRequest, t as doubleKnockScoped } from "./http-DVEBwq6e.js";
|
|
6
|
+
import { a as MAX_BODY_SIZE, i as BodyTooLargeError, n as getNodeInfo, o as readBoundedText, t as nodeInfoToJson } from "./types-B1_pRFE_.js";
|
|
7
|
+
import { _ as signJsonLd, a as doesActorOwnKey, c as assertSafeJsonLd, f as detachSignature, g as isInvalidUrlTypeError, h as isClearlyMalformedContextReference, m as hasSignature, n as signObject, o as getKeyOwner, p as getNormalizationContextLoader, r as verifyObject, s as InvalidContextReferenceError, u as compactJsonLd, v as verifyCompactJsonLd, x as wrapContextLoaderForJsonLd } from "./proof-Cp62GFJ3.js";
|
|
8
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-DC_F2WSH.js";
|
|
9
9
|
import { getLogger, withContext } from "@logtape/logtape";
|
|
10
10
|
import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
11
11
|
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
@@ -13,10 +13,11 @@ import { cloneDeep } from "es-toolkit";
|
|
|
13
13
|
import { Router } from "uri-template-router";
|
|
14
14
|
import { parseTemplate } from "url-template";
|
|
15
15
|
import { encodeHex } from "byte-encodings/hex";
|
|
16
|
-
import { FetchError, UrlError, getDocumentLoader
|
|
16
|
+
import { FetchError, UrlError, getDocumentLoader } from "@fedify/vocab-runtime";
|
|
17
17
|
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";
|
|
18
18
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
19
19
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
20
|
+
import { openDirectFetch, openPublicFetch } from "@fedify/vocab-runtime/internal";
|
|
20
21
|
import { domainToASCII } from "node:url";
|
|
21
22
|
//#region src/federation/inbox.ts
|
|
22
23
|
var InboxListenerSet = class InboxListenerSet {
|
|
@@ -2262,9 +2263,10 @@ function extractInboxes({ recipients, preferSharedInbox, excludeBaseUris }) {
|
|
|
2262
2263
|
*
|
|
2263
2264
|
* @param parameters The parameters for sending the activity.
|
|
2264
2265
|
* See also {@link SendActivityParameters}.
|
|
2266
|
+
* @param deps The dependencies, for tests.
|
|
2265
2267
|
* @throws {Error} If the activity fails to send.
|
|
2266
2268
|
*/
|
|
2267
|
-
function sendActivity(options) {
|
|
2269
|
+
function sendActivity(options, deps = {}) {
|
|
2268
2270
|
const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
|
|
2269
2271
|
return tracerProvider.getTracer(name, version).startActiveSpan("activitypub.send_activity", {
|
|
2270
2272
|
kind: SpanKind.CLIENT,
|
|
@@ -2276,7 +2278,7 @@ function sendActivity(options) {
|
|
|
2276
2278
|
await sendActivityInternal({
|
|
2277
2279
|
...options,
|
|
2278
2280
|
tracerProvider
|
|
2279
|
-
}, span);
|
|
2281
|
+
}, span, deps);
|
|
2280
2282
|
} catch (e) {
|
|
2281
2283
|
span.setStatus({
|
|
2282
2284
|
code: SpanStatusCode.ERROR,
|
|
@@ -2310,21 +2312,25 @@ async function readLimitedResponseBody(response, maxBytes) {
|
|
|
2310
2312
|
totalBytes += value.length;
|
|
2311
2313
|
}
|
|
2312
2314
|
} finally {
|
|
2315
|
+
if (truncated) reader.cancel().catch(() => {});
|
|
2313
2316
|
reader.releaseLock();
|
|
2314
2317
|
}
|
|
2315
2318
|
let result = chunks.join("");
|
|
2316
2319
|
if (truncated) result += "… (truncated)";
|
|
2317
2320
|
return result;
|
|
2318
2321
|
}
|
|
2319
|
-
async function sendActivityInternal({ activity, activityId, keys, inbox, allowPrivateAddress, headers, specDeterminer, tracerProvider }, span) {
|
|
2322
|
+
async function sendActivityInternal({ activity, activityId, keys, inbox, allowPrivateAddress, headers, specDeterminer, tracerProvider }, span, deps) {
|
|
2320
2323
|
const logger = getLogger([
|
|
2321
2324
|
"fedify",
|
|
2322
2325
|
"federation",
|
|
2323
2326
|
"outbox"
|
|
2324
2327
|
]);
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
+
const openUnvalidatedHop = deps.openHop ?? (allowPrivateAddress ? openDirectFetch : (url) => openPublicFetch(url));
|
|
2329
|
+
let opened = false;
|
|
2330
|
+
async function openHop(url) {
|
|
2331
|
+
let session;
|
|
2332
|
+
try {
|
|
2333
|
+
session = await openUnvalidatedHop(url);
|
|
2328
2334
|
} catch (error) {
|
|
2329
2335
|
if (error instanceof UrlError && error.reason === "dns") {
|
|
2330
2336
|
logger.error("DNS resolution failed for URL: {url}", {
|
|
@@ -2341,8 +2347,9 @@ async function sendActivityInternal({ activity, activityId, keys, inbox, allowPr
|
|
|
2341
2347
|
});
|
|
2342
2348
|
throw error;
|
|
2343
2349
|
}
|
|
2350
|
+
opened = true;
|
|
2351
|
+
return session;
|
|
2344
2352
|
}
|
|
2345
|
-
await validateUrl(inbox.href);
|
|
2346
2353
|
headers = new Headers(headers);
|
|
2347
2354
|
headers.set("Content-Type", "application/activity+json");
|
|
2348
2355
|
const request = new Request(inbox, {
|
|
@@ -2363,36 +2370,30 @@ async function sendActivityInternal({ activity, activityId, keys, inbox, allowPr
|
|
|
2363
2370
|
privateKey: pair.privateKey
|
|
2364
2371
|
}))
|
|
2365
2372
|
});
|
|
2366
|
-
let
|
|
2373
|
+
let outcome;
|
|
2367
2374
|
try {
|
|
2368
|
-
|
|
2375
|
+
outcome = rsaKey == null ? await fetchWithValidatedRedirects(request, openHop, (response) => consumeSendActivityResponse(response, inbox)) : await doubleKnockScoped(request, rsaKey, {
|
|
2369
2376
|
tracerProvider,
|
|
2370
|
-
specDeterminer
|
|
2371
|
-
|
|
2372
|
-
});
|
|
2377
|
+
specDeterminer
|
|
2378
|
+
}, openHop, (response) => consumeSendActivityResponse(response, inbox));
|
|
2373
2379
|
} catch (error) {
|
|
2374
|
-
logger.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
|
|
2380
|
+
if (opened) logger.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
|
|
2375
2381
|
activityId,
|
|
2376
2382
|
inbox: inbox.href,
|
|
2377
2383
|
error
|
|
2378
2384
|
});
|
|
2379
2385
|
throw error;
|
|
2380
2386
|
}
|
|
2381
|
-
if (!
|
|
2382
|
-
|
|
2383
|
-
try {
|
|
2384
|
-
error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
|
|
2385
|
-
} catch (_) {
|
|
2386
|
-
error = "";
|
|
2387
|
-
}
|
|
2387
|
+
if (!outcome.ok) {
|
|
2388
|
+
const { status, statusText, error } = outcome;
|
|
2388
2389
|
logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
|
|
2389
2390
|
activityId,
|
|
2390
2391
|
inbox: inbox.href,
|
|
2391
|
-
status
|
|
2392
|
-
statusText
|
|
2392
|
+
status,
|
|
2393
|
+
statusText,
|
|
2393
2394
|
error
|
|
2394
2395
|
});
|
|
2395
|
-
throw new SendActivityError(inbox,
|
|
2396
|
+
throw new SendActivityError(inbox, status, `Failed to send activity ${activityId} to ${inbox.href} (${status} ${statusText}):\n${error}`, error);
|
|
2396
2397
|
}
|
|
2397
2398
|
span.addEvent("activitypub.activity.sent", {
|
|
2398
2399
|
"activitypub.activity.json": JSON.stringify(activity),
|
|
@@ -2400,10 +2401,74 @@ async function sendActivityInternal({ activity, activityId, keys, inbox, allowPr
|
|
|
2400
2401
|
"activitypub.activity.id": activityId ?? ""
|
|
2401
2402
|
});
|
|
2402
2403
|
}
|
|
2403
|
-
|
|
2404
|
+
/**
|
|
2405
|
+
* Starts cancelling the body of a response without waiting for it to settle.
|
|
2406
|
+
* The cancellation of a branch of a tee (e.g., a body cloned by a fetch mock)
|
|
2407
|
+
* settles only after the other branch is cancelled too, per the Streams
|
|
2408
|
+
* standard, so awaiting it could block forever. Failures are only logged.
|
|
2409
|
+
*/
|
|
2410
|
+
function cancelResponseBody(response, url) {
|
|
2411
|
+
response.body?.cancel().catch((error) => {
|
|
2412
|
+
getLogger([
|
|
2413
|
+
"fedify",
|
|
2414
|
+
"federation",
|
|
2415
|
+
"outbox"
|
|
2416
|
+
]).debug("Failed to cancel the response body from {url}: {error}", {
|
|
2417
|
+
url,
|
|
2418
|
+
error
|
|
2419
|
+
});
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Reads what is needed from the final response of a delivery while its
|
|
2424
|
+
* session is open: the body of a successful response is cancelled, and at
|
|
2425
|
+
* most 1 KiB of an error response body is read.
|
|
2426
|
+
*/
|
|
2427
|
+
async function consumeSendActivityResponse(response, inbox) {
|
|
2428
|
+
if (response.ok) {
|
|
2429
|
+
cancelResponseBody(response, response.url || inbox.href);
|
|
2430
|
+
return { ok: true };
|
|
2431
|
+
}
|
|
2432
|
+
let error;
|
|
2433
|
+
try {
|
|
2434
|
+
error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
|
|
2435
|
+
} catch (_) {
|
|
2436
|
+
error = "";
|
|
2437
|
+
}
|
|
2438
|
+
return {
|
|
2439
|
+
ok: false,
|
|
2440
|
+
status: response.status,
|
|
2441
|
+
statusText: response.statusText,
|
|
2442
|
+
error
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
/**
|
|
2446
|
+
* Sends a request without signing it, following redirects manually like the
|
|
2447
|
+
* Fetch standard does. Every hop is opened with `openHop()`, which validates
|
|
2448
|
+
* the destination, after the previous hop is closed. The final response is
|
|
2449
|
+
* passed to `consume()`, and its session is closed after that.
|
|
2450
|
+
* This is exported only for tests; it is not exported from any entry point.
|
|
2451
|
+
* @param request The request to send.
|
|
2452
|
+
* @param openHop The function to open a session for each hop.
|
|
2453
|
+
* @param consume The function to consume the final response.
|
|
2454
|
+
* @returns The value returned by `consume`.
|
|
2455
|
+
*/
|
|
2456
|
+
async function fetchWithValidatedRedirects(request, openHop, consume) {
|
|
2457
|
+
const logger = getLogger([
|
|
2458
|
+
"fedify",
|
|
2459
|
+
"federation",
|
|
2460
|
+
"outbox"
|
|
2461
|
+
]);
|
|
2404
2462
|
let body = await request.clone().text();
|
|
2405
2463
|
for (let redirects = 0;; redirects++) {
|
|
2406
|
-
const
|
|
2464
|
+
const session = await openHop(request.url);
|
|
2465
|
+
let response;
|
|
2466
|
+
try {
|
|
2467
|
+
response = await session.fetch(request, { redirect: "manual" });
|
|
2468
|
+
} catch (error) {
|
|
2469
|
+
await closeAfterError(session, logger);
|
|
2470
|
+
throw error;
|
|
2471
|
+
}
|
|
2407
2472
|
const location = response.headers.get("Location");
|
|
2408
2473
|
if (![
|
|
2409
2474
|
301,
|
|
@@ -2411,11 +2476,21 @@ async function fetchWithValidatedRedirects(request, validateUrl) {
|
|
|
2411
2476
|
303,
|
|
2412
2477
|
307,
|
|
2413
2478
|
308
|
|
2414
|
-
].includes(response.status) || location == null)
|
|
2415
|
-
|
|
2479
|
+
].includes(response.status) || location == null) try {
|
|
2480
|
+
return await consume(response);
|
|
2481
|
+
} catch (error) {
|
|
2482
|
+
logger.debug("Failed to consume the response from {url}; closing the session: {error}", {
|
|
2483
|
+
url: request.url,
|
|
2484
|
+
error
|
|
2485
|
+
});
|
|
2486
|
+
throw error;
|
|
2487
|
+
} finally {
|
|
2488
|
+
await session.close();
|
|
2489
|
+
}
|
|
2490
|
+
cancelResponseBody(response, request.url);
|
|
2491
|
+
await session.close();
|
|
2416
2492
|
if (redirects >= 20) throw new TypeError("Too many redirects");
|
|
2417
2493
|
const url = new URL(location, request.url);
|
|
2418
|
-
await validateUrl(url.href);
|
|
2419
2494
|
const headers = new Headers(request.headers);
|
|
2420
2495
|
if (url.origin !== new URL(request.url).origin) {
|
|
2421
2496
|
headers.delete("Authorization");
|
|
@@ -2443,6 +2518,16 @@ async function fetchWithValidatedRedirects(request, validateUrl) {
|
|
|
2443
2518
|
});
|
|
2444
2519
|
}
|
|
2445
2520
|
}
|
|
2521
|
+
async function closeAfterError(session, logger) {
|
|
2522
|
+
try {
|
|
2523
|
+
await session.close();
|
|
2524
|
+
} catch (error) {
|
|
2525
|
+
logger.debug("Failed to close the session for {url}: {error}", {
|
|
2526
|
+
url: session.url.href,
|
|
2527
|
+
error
|
|
2528
|
+
});
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2446
2531
|
/**
|
|
2447
2532
|
* An error that is thrown when an activity fails to send to a remote inbox.
|
|
2448
2533
|
* It contains structured information about the failure, including the HTTP
|
|
@@ -2,24 +2,24 @@ import { Temporal } from "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { n as RouterError } from "./router-CrMLXoOr.mjs";
|
|
5
|
-
import { n as version, t as name } from "./deno-
|
|
6
|
-
import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { n as version, t as name } from "./deno-DL_ifPJk.mjs";
|
|
6
|
+
import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-CgcWGw6W.mjs";
|
|
7
|
+
import { u as verifyRequest } from "./http-Dvsq4uCu.mjs";
|
|
8
|
+
import { n as getAuthenticatedDocumentLoader } from "./docloader-B0_0Fjqo.mjs";
|
|
9
9
|
import { n as kvCache } from "./kv-cache-BrjuJue8.mjs";
|
|
10
|
-
import { a as compactJsonLd, c as getNormalizationContextLoader, d as isInvalidUrlTypeError, f as signJsonLd, g as wrapContextLoaderForJsonLd, l as hasSignature, p as verifyCompactJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as isClearlyMalformedContextReference } from "./ld-
|
|
11
|
-
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-
|
|
12
|
-
import { n as signObject, r as verifyObject } from "./proof-
|
|
13
|
-
import { d as
|
|
10
|
+
import { a as compactJsonLd, c as getNormalizationContextLoader, d as isInvalidUrlTypeError, f as signJsonLd, g as wrapContextLoaderForJsonLd, l as hasSignature, p as verifyCompactJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as isClearlyMalformedContextReference } from "./ld-CfOIwHup.mjs";
|
|
11
|
+
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-BvVwCoj2.mjs";
|
|
12
|
+
import { n as signObject, r as verifyObject } from "./proof-C9TBSmUR.mjs";
|
|
13
|
+
import { d as MAX_BODY_SIZE, f as readBoundedText, t as getNodeInfo, u as BodyTooLargeError } from "./client-DGNacEx_.mjs";
|
|
14
14
|
import { t as nodeInfoToJson } from "./types-MNlFfxjv.mjs";
|
|
15
|
-
import { n as actorsHaveSameOrigin, r as routeActivity } from "./inbox-
|
|
16
|
-
import { t as FederationBuilderImpl } from "./builder-
|
|
15
|
+
import { n as actorsHaveSameOrigin, r as routeActivity } from "./inbox-D6YbZFrZ.mjs";
|
|
16
|
+
import { t as FederationBuilderImpl } from "./builder-CKbQrOH1.mjs";
|
|
17
17
|
import { t as buildCollectionSynchronizationHeader } from "./collection-DI6YzYpd.mjs";
|
|
18
18
|
import { t as KvKeyCache } from "./keycache-DBD6fzl1.mjs";
|
|
19
19
|
import { t as acceptsJsonLd } from "./negotiation-TWEs6Cl8.mjs";
|
|
20
|
-
import { t as hasMalformedKnownTemporalLiteral } from "./temporal-
|
|
20
|
+
import { t as hasMalformedKnownTemporalLiteral } from "./temporal-BNtFi_ut.mjs";
|
|
21
21
|
import { t as createExponentialBackoffPolicy } from "./retry-Ddbq3AcK.mjs";
|
|
22
|
-
import {
|
|
22
|
+
import { i as sendActivity, n as extractInboxes, t as SendActivityError } from "./send-ZWmjk2Zf.mjs";
|
|
23
23
|
import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
24
24
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
25
25
|
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
const { Temporal } = require("@js-temporal/polyfill");
|
|
2
2
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
3
|
-
const require_middleware = require("./middleware-
|
|
3
|
+
const require_middleware = require("./middleware-DZvWX5af.cjs");
|
|
4
4
|
exports.FederationImpl = require_middleware.FederationImpl;
|