@fedify/fedify 2.2.19 → 2.2.20
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-UmydCxtK.mjs → builder-BolQ56ab.mjs} +2 -2
- package/dist/{client-CAM_bQXx.d.cts → client-BIBzqEIp.d.cts} +0 -1
- package/dist/{client-9F_u23dc.mjs → client-DhxaVgOv.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-tJbL_Hkw.d.cts → context-CFQGaq5x.d.cts} +28 -2
- package/dist/{context-OkwEpc3-.d.ts → context-DdbLVV_k.d.ts} +28 -2
- package/dist/{deno-CD-8UlUi.mjs → deno-BUj4roJc.mjs} +1 -1
- package/dist/docloader-CbtF_0cD.mjs +125 -0
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +5 -5
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +389 -28
- 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-Dl00HXr4.d.ts → http-9IFUxdkp.d.cts} +1 -2
- package/dist/{http-DJK2udgX.js → http-C5g_VrA_.js} +172 -103
- package/dist/{http-BBBUbwKh.mjs → http-CQMVisM0.mjs} +195 -97
- package/dist/{http-DrUG4fPS.cjs → http-D8I7LBfx.cjs} +173 -104
- package/dist/{http-DqZhyEso.d.cts → http-SbfezERn.d.ts} +1 -2
- package/dist/{inbox-Dca_0fwc.mjs → inbox-CD8IDILN.mjs} +1 -1
- package/dist/{key-DolXUaLH.mjs → key-C3o3DGap.mjs} +1 -1
- package/dist/{kv-cache-DDm80t1K.js → kv-cache-CSKXXleR.js} +60 -18
- package/dist/{kv-cache-Bk3V1Tiu.cjs → kv-cache-DC9LcfoC.cjs} +59 -17
- package/dist/{ld-rTdIfZ63.mjs → ld-ijSr-0AB.mjs} +2 -2
- package/dist/{middleware-CpXoRHs3.mjs → middleware-CYAaUTi5.mjs} +78 -38
- package/dist/{middleware-BWOwEGpy.cjs → middleware-DNueR0GD.cjs} +184 -59
- package/dist/{middleware-B4-Rzdi3.cjs → middleware-DmcY2TWS.cjs} +1 -1
- package/dist/{middleware-BimXKBcP.js → middleware-sjhz30sm.js} +184 -59
- package/dist/{middleware-DYK_res8.mjs → middleware-yqqfI7oJ.mjs} +1 -1
- package/dist/{mod-B6LmugJ4.d.ts → mod-BRnwh2oy.d.ts} +2 -8
- package/dist/{mod-D2JyWtW3.d.cts → mod-BYWp77-z.d.cts} +1 -1
- package/dist/{mod-DQGjRJUG.d.ts → mod-BuRRdsfD.d.ts} +1 -1
- package/dist/{mod-ej_rLx8h.d.ts → mod-CM5_J_cx.d.ts} +1 -1
- package/dist/{mod-a6kmB8xD.d.cts → mod-CVSKw-5H.d.cts} +1 -1
- package/dist/{mod-V2vDnYNu.d.cts → mod-Co7LEK9X.d.cts} +2 -8
- 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-CVfzyM5V.mjs → owner-B8kXzD0q.mjs} +2 -2
- package/dist/{proof-gOGM-sJS.mjs → proof-CBQOjOgA.mjs} +2 -2
- package/dist/{proof-CaQ4KG6y.js → proof-DKX6nASL.js} +1 -1
- package/dist/{proof-DhaTTn-P.cjs → proof-qiuGJCD7.cjs} +1 -1
- package/dist/{send-D35Va72B.mjs → send-DRpisX_f.mjs} +117 -32
- package/dist/sig/http.test.mjs +528 -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-CA4Y9hBJ.mjs → temporal-BQGEINad.mjs} +1 -1
- package/dist/testing/mod.d.mts +26 -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 +318 -34
- 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-D1ghurDJ.mjs +0 -83
|
@@ -11,7 +11,7 @@ let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conve
|
|
|
11
11
|
let byte_encodings_base64 = require("byte-encodings/base64");
|
|
12
12
|
//#region deno.json
|
|
13
13
|
var name = "@fedify/fedify";
|
|
14
|
-
var version = "2.2.
|
|
14
|
+
var version = "2.2.20";
|
|
15
15
|
//#endregion
|
|
16
16
|
//#region src/sig/accept.ts
|
|
17
17
|
/**
|
|
@@ -1598,10 +1598,10 @@ async function createRedirectRequest(request, location, body, validateRedirect)
|
|
|
1598
1598
|
cache: request.cache
|
|
1599
1599
|
});
|
|
1600
1600
|
}
|
|
1601
|
-
async function fetchDoubleKnockRequest(request, signedRequest, signal) {
|
|
1601
|
+
async function fetchDoubleKnockRequest(session, request, signedRequest, signal) {
|
|
1602
1602
|
const maxAttempts = request.method === "GET" || request.method === "HEAD" ? 2 : 1;
|
|
1603
1603
|
for (let attempt = 1;; attempt++) try {
|
|
1604
|
-
return await fetch(signedRequest, {
|
|
1604
|
+
return await session.fetch(signedRequest, {
|
|
1605
1605
|
redirect: "manual",
|
|
1606
1606
|
signal
|
|
1607
1607
|
});
|
|
@@ -1652,118 +1652,187 @@ function getAbortReason(signal) {
|
|
|
1652
1652
|
return signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
1653
1653
|
}
|
|
1654
1654
|
/**
|
|
1655
|
-
* Performs a double-knock request
|
|
1656
|
-
*
|
|
1657
|
-
*
|
|
1655
|
+
* Performs a double-knock request like {@link doubleKnock}, opening a session
|
|
1656
|
+
* for every hop with the given function, and passes the final response to
|
|
1657
|
+
* the given function. The final session is closed after `consume` settles,
|
|
1658
|
+
* so `consume` must read or cancel the response body before it returns.
|
|
1659
|
+
* This is not exported from any entry point.
|
|
1658
1660
|
* @param request The request to send.
|
|
1659
1661
|
* @param identity The identity to use for signing the request.
|
|
1660
1662
|
* @param options The options for double-knock requests.
|
|
1661
|
-
* @
|
|
1662
|
-
*
|
|
1663
|
+
* @param openHop The function to open a session for each hop. The session of
|
|
1664
|
+
* a hop is closed before the next hop is opened.
|
|
1665
|
+
* @param consume The function to consume the final response.
|
|
1666
|
+
* @returns The value returned by `consume`.
|
|
1663
1667
|
*/
|
|
1664
|
-
async function
|
|
1665
|
-
|
|
1668
|
+
async function doubleKnockScoped(request, identity, options, openHop, consume) {
|
|
1669
|
+
const { response, session } = await doubleKnockCore(request, identity, options, openHop);
|
|
1670
|
+
try {
|
|
1671
|
+
return await consume(response);
|
|
1672
|
+
} catch (error) {
|
|
1673
|
+
(0, _logtape_logtape.getLogger)([
|
|
1674
|
+
"fedify",
|
|
1675
|
+
"sig",
|
|
1676
|
+
"http"
|
|
1677
|
+
]).debug("Failed to consume the response from {url}; closing the session: {error}", {
|
|
1678
|
+
url: request.url,
|
|
1679
|
+
error
|
|
1680
|
+
});
|
|
1681
|
+
throw error;
|
|
1682
|
+
} finally {
|
|
1683
|
+
await session.close();
|
|
1684
|
+
}
|
|
1666
1685
|
}
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
let response = await fetchDoubleKnockRequest(request, signedRequest, signal);
|
|
1681
|
-
if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
|
|
1682
|
-
if (redirected >= maximumRedirection) throw new _fedify_vocab_runtime.FetchError(request.url, `Too many redirections (${redirected + 1})`);
|
|
1683
|
-
const redirectRequest = await createRedirectRequest(request, response.headers.get("Location"), body, options.validateRedirect);
|
|
1684
|
-
if (visited.has(redirectRequest.url)) throw new _fedify_vocab_runtime.FetchError(request.url, `Redirect loop detected: ${redirectRequest.url}`);
|
|
1685
|
-
return doubleKnockInternal(redirectRequest, identity, {
|
|
1686
|
-
...options,
|
|
1687
|
-
body
|
|
1688
|
-
}, redirected + 1, visited);
|
|
1689
|
-
} else if (response.status === 400 || response.status === 401 || response.status > 401) {
|
|
1690
|
-
const logger = (0, _logtape_logtape.getLogger)([
|
|
1686
|
+
function isRedirectResponse(response) {
|
|
1687
|
+
return response.status >= 300 && response.status < 400 && response.headers.has("Location");
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Starts cancelling the body of a response that is not passed to the caller,
|
|
1691
|
+
* without waiting for it to settle. The cancellation of a branch of a tee
|
|
1692
|
+
* (e.g., a body cloned by a fetch mock) settles only after the other branch
|
|
1693
|
+
* is cancelled too, per the Streams standard, so awaiting it could block
|
|
1694
|
+
* forever. Failures are only logged.
|
|
1695
|
+
*/
|
|
1696
|
+
function cancelResponseBody(response, url) {
|
|
1697
|
+
response.body?.cancel().catch((error) => {
|
|
1698
|
+
(0, _logtape_logtape.getLogger)([
|
|
1691
1699
|
"fedify",
|
|
1692
1700
|
"sig",
|
|
1693
1701
|
"http"
|
|
1694
|
-
])
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1702
|
+
]).debug("Failed to cancel the response body from {url}: {error}", {
|
|
1703
|
+
url,
|
|
1704
|
+
error
|
|
1705
|
+
});
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1708
|
+
async function doubleKnockCore(request, identity, options, openHop) {
|
|
1709
|
+
const { specDeterminer, log, tracerProvider, signal } = options;
|
|
1710
|
+
const maximumRedirection = options.maxRedirection ?? DEFAULT_MAX_REDIRECTION;
|
|
1711
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1712
|
+
const body = options.body !== void 0 ? options.body : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
|
|
1713
|
+
let session = await openHop(request.url);
|
|
1714
|
+
try {
|
|
1715
|
+
for (let redirected = 0;; redirected++) {
|
|
1716
|
+
visited.add(request.url);
|
|
1717
|
+
const origin = new URL(request.url).origin;
|
|
1718
|
+
const firstTrySpec = specDeterminer == null ? "rfc9421" : await specDeterminer.determineSpec(origin);
|
|
1719
|
+
let signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
1720
|
+
spec: firstTrySpec,
|
|
1721
|
+
tracerProvider,
|
|
1722
|
+
body
|
|
1723
|
+
});
|
|
1724
|
+
log?.(signedRequest);
|
|
1725
|
+
let response = await fetchDoubleKnockRequest(session, request, signedRequest, signal);
|
|
1726
|
+
const firstRedirected = isRedirectResponse(response);
|
|
1727
|
+
if (!firstRedirected && (response.status === 400 || response.status === 401 || response.status > 401)) {
|
|
1728
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
1729
|
+
"fedify",
|
|
1730
|
+
"sig",
|
|
1731
|
+
"http"
|
|
1732
|
+
]);
|
|
1733
|
+
const acceptSigHeader = response.headers.get("Accept-Signature");
|
|
1734
|
+
if (acceptSigHeader != null) {
|
|
1735
|
+
const entries = validateAcceptSignature(parseAcceptSignature(acceptSigHeader));
|
|
1736
|
+
const localKeyId = identity.keyId.href;
|
|
1737
|
+
const localAlg = "rsa-v1_5-sha256";
|
|
1738
|
+
let fulfilled = false;
|
|
1739
|
+
let challengeRequest;
|
|
1740
|
+
for (const entry of entries) {
|
|
1741
|
+
const rfc9421 = fulfillAcceptSignature(entry, localKeyId, localAlg);
|
|
1742
|
+
if (rfc9421 == null) continue;
|
|
1743
|
+
logger.debug("Received Accept-Signature challenge; accumulating label {label} and components {components}.", {
|
|
1744
|
+
label: rfc9421.label,
|
|
1745
|
+
components: rfc9421.components
|
|
1746
|
+
});
|
|
1747
|
+
try {
|
|
1748
|
+
challengeRequest = await signRequest(challengeRequest ?? request, identity.privateKey, identity.keyId, {
|
|
1749
|
+
spec: "rfc9421",
|
|
1750
|
+
tracerProvider,
|
|
1751
|
+
body,
|
|
1752
|
+
rfc9421
|
|
1753
|
+
});
|
|
1754
|
+
fulfilled = true;
|
|
1755
|
+
} catch (error) {
|
|
1756
|
+
logger.debug("Failed to fulfill Accept-Signature challenge entry {label}: {error}", {
|
|
1757
|
+
label: entry.label,
|
|
1758
|
+
error
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
if (fulfilled && challengeRequest != null) {
|
|
1763
|
+
cancelResponseBody(response, request.url);
|
|
1764
|
+
signedRequest = challengeRequest;
|
|
1765
|
+
log?.(signedRequest);
|
|
1766
|
+
response = await session.fetch(signedRequest, {
|
|
1767
|
+
redirect: "manual",
|
|
1768
|
+
signal
|
|
1769
|
+
});
|
|
1770
|
+
}
|
|
1771
|
+
if (fulfilled && !isRedirectResponse(response)) {
|
|
1772
|
+
if (response.status < 300) {
|
|
1773
|
+
await specDeterminer?.rememberSpec(origin, "rfc9421");
|
|
1774
|
+
return {
|
|
1775
|
+
response,
|
|
1776
|
+
session
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
if (response.status !== 400 && response.status !== 401) return {
|
|
1780
|
+
response,
|
|
1781
|
+
session
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
if (!isRedirectResponse(response)) {
|
|
1786
|
+
const spec = firstTrySpec === "draft-cavage-http-signatures-12" ? "rfc9421" : "draft-cavage-http-signatures-12";
|
|
1787
|
+
(0, _logtape_logtape.getLogger)([
|
|
1788
|
+
"fedify",
|
|
1789
|
+
"sig",
|
|
1790
|
+
"http"
|
|
1791
|
+
]).debug("Failed to verify with the spec {spec} ({status} {statusText}); retrying with spec {secondSpec}... (double-knocking)", {
|
|
1792
|
+
spec: firstTrySpec,
|
|
1793
|
+
secondSpec: spec,
|
|
1794
|
+
status: response.status,
|
|
1795
|
+
statusText: response.statusText
|
|
1715
1796
|
});
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1797
|
+
cancelResponseBody(response, request.url);
|
|
1798
|
+
signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
1799
|
+
spec,
|
|
1800
|
+
tracerProvider,
|
|
1801
|
+
body
|
|
1721
1802
|
});
|
|
1803
|
+
log?.(signedRequest);
|
|
1804
|
+
response = await fetchDoubleKnockRequest(session, request, signedRequest, signal);
|
|
1805
|
+
if (!isRedirectResponse(response) && response.status !== 400 && response.status !== 401) await specDeterminer?.rememberSpec(origin, spec);
|
|
1722
1806
|
}
|
|
1723
|
-
}
|
|
1724
|
-
if (
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
});
|
|
1731
|
-
if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) return doubleKnock(await createRedirectRequest(request, response.headers.get("Location"), body, options.validateRedirect), identity, {
|
|
1732
|
-
...options,
|
|
1733
|
-
body
|
|
1734
|
-
});
|
|
1735
|
-
}
|
|
1736
|
-
if (fulfilled && response.status < 300) {
|
|
1737
|
-
await specDeterminer?.rememberSpec(origin, "rfc9421");
|
|
1738
|
-
return response;
|
|
1739
|
-
}
|
|
1740
|
-
if (fulfilled && response.status !== 400 && response.status !== 401) return response;
|
|
1741
|
-
}
|
|
1742
|
-
const spec = firstTrySpec === "draft-cavage-http-signatures-12" ? "rfc9421" : "draft-cavage-http-signatures-12";
|
|
1743
|
-
logger.debug("Failed to verify with the spec {spec} ({status} {statusText}); retrying with spec {secondSpec}... (double-knocking)", {
|
|
1744
|
-
spec: firstTrySpec,
|
|
1745
|
-
secondSpec: spec,
|
|
1746
|
-
status: response.status,
|
|
1747
|
-
statusText: response.statusText
|
|
1748
|
-
});
|
|
1749
|
-
signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
1750
|
-
spec,
|
|
1751
|
-
tracerProvider,
|
|
1752
|
-
body
|
|
1753
|
-
});
|
|
1754
|
-
log?.(signedRequest);
|
|
1755
|
-
response = await fetchDoubleKnockRequest(request, signedRequest, signal);
|
|
1756
|
-
if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
|
|
1807
|
+
} else if (!firstRedirected) await specDeterminer?.rememberSpec(origin, firstTrySpec);
|
|
1808
|
+
if (!isRedirectResponse(response)) return {
|
|
1809
|
+
response,
|
|
1810
|
+
session
|
|
1811
|
+
};
|
|
1812
|
+
cancelResponseBody(response, request.url);
|
|
1813
|
+
await session.close();
|
|
1757
1814
|
if (redirected >= maximumRedirection) throw new _fedify_vocab_runtime.FetchError(request.url, `Too many redirections (${redirected + 1})`);
|
|
1758
|
-
const
|
|
1815
|
+
const location = response.headers.get("Location");
|
|
1816
|
+
const redirectRequest = await createRedirectRequest(request, location, body, options.validateRedirect);
|
|
1759
1817
|
if (visited.has(redirectRequest.url)) throw new _fedify_vocab_runtime.FetchError(request.url, `Redirect loop detected: ${redirectRequest.url}`);
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1818
|
+
request = redirectRequest;
|
|
1819
|
+
session = await openHop(request.url);
|
|
1820
|
+
}
|
|
1821
|
+
} catch (error) {
|
|
1822
|
+
try {
|
|
1823
|
+
await session.close();
|
|
1824
|
+
} catch (closeError) {
|
|
1825
|
+
(0, _logtape_logtape.getLogger)([
|
|
1826
|
+
"fedify",
|
|
1827
|
+
"sig",
|
|
1828
|
+
"http"
|
|
1829
|
+
]).debug("Failed to close the session for {url}: {error}", {
|
|
1830
|
+
url: request.url,
|
|
1831
|
+
error: closeError
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
throw error;
|
|
1835
|
+
}
|
|
1767
1836
|
}
|
|
1768
1837
|
/**
|
|
1769
1838
|
* Performs a timing-safe equality comparison between two `Uint8Array` values.
|
|
@@ -1793,10 +1862,10 @@ function timingSafeEqual(a, b) {
|
|
|
1793
1862
|
return result === 0;
|
|
1794
1863
|
}
|
|
1795
1864
|
//#endregion
|
|
1796
|
-
Object.defineProperty(exports, "
|
|
1865
|
+
Object.defineProperty(exports, "doubleKnockScoped", {
|
|
1797
1866
|
enumerable: true,
|
|
1798
1867
|
get: function() {
|
|
1799
|
-
return
|
|
1868
|
+
return doubleKnockScoped;
|
|
1800
1869
|
}
|
|
1801
1870
|
});
|
|
1802
1871
|
Object.defineProperty(exports, "exportJwk", {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
2
|
import { Actor, CryptographicKey, Multikey } from "@fedify/vocab";
|
|
3
|
-
import { DocumentLoader } from "@fedify/vocab-runtime";
|
|
4
3
|
import { TracerProvider } from "@opentelemetry/api";
|
|
5
|
-
|
|
4
|
+
import { DocumentLoader } from "@fedify/vocab-runtime";
|
|
6
5
|
//#region src/sig/key.d.ts
|
|
7
6
|
/**
|
|
8
7
|
* Generates a key pair which is appropriate for Fedify.
|
|
@@ -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-BUj4roJc.mjs";
|
|
5
5
|
import { 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-BUj4roJc.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 { f as validateCryptoKey, t as
|
|
3
|
+
import { f as validateCryptoKey, t as doubleKnockScoped } from "./http-C5g_VrA_.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, maxRedirection, 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,31 +61,44 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
|
|
|
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
|
maxRedirection,
|
|
43
66
|
specDeterminer,
|
|
44
67
|
log: curry(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 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 ? openDirectFetch(url) : 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 UrlError) if (error.reason === "dns") logger$1.debug("DNS lookup failed for {url}", {
|
|
68
104
|
url,
|
|
@@ -75,6 +111,12 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
|
|
|
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 UrlError(`Unsupported protocol: ${protocol}`);
|
|
118
|
+
if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new UrlError("URLs with credentials are not allowed");
|
|
119
|
+
}
|
|
78
120
|
return (url, options) => load(url, options);
|
|
79
121
|
}
|
|
80
122
|
//#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-D8I7LBfx.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, maxRedirection, 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,31 +62,44 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
|
|
|
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
|
maxRedirection,
|
|
44
67
|
specDeterminer,
|
|
45
68
|
log: (0, es_toolkit.curry)(_fedify_vocab_runtime.logRequest)(logger$1),
|
|
46
69
|
tracerProvider,
|
|
47
70
|
signal: options?.signal,
|
|
48
|
-
validateRedirect:
|
|
71
|
+
validateRedirect: (nextUrl) => {
|
|
49
72
|
follow(nextUrl);
|
|
50
|
-
|
|
73
|
+
validateUrl(nextUrl);
|
|
51
74
|
visited.add(nextUrl);
|
|
52
75
|
currentUrl = nextUrl;
|
|
53
76
|
}
|
|
77
|
+
}, (hopUrl) => openHop(hopUrl, options?.signal), async (response) => {
|
|
78
|
+
let alternateUrl;
|
|
79
|
+
const document = await (0, _fedify_vocab_runtime.getRemoteDocument)(currentUrl, response, (url) => {
|
|
80
|
+
alternateUrl = url;
|
|
81
|
+
return Promise.resolve(ALTERNATE_PLACEHOLDER);
|
|
82
|
+
});
|
|
83
|
+
if (alternateUrl == null) return { document };
|
|
84
|
+
if (!response.bodyUsed && response.body?.locked !== true) response.body?.cancel().catch((error) => {
|
|
85
|
+
logger$1.debug("Failed to cancel the response body from {url}: {error}", {
|
|
86
|
+
url: currentUrl,
|
|
87
|
+
error
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
return { alternateUrl };
|
|
54
91
|
});
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
});
|
|
92
|
+
if ("document" in result) return result.document;
|
|
93
|
+
follow(result.alternateUrl);
|
|
94
|
+
return load(result.alternateUrl, options, redirected, visited);
|
|
59
95
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
await (
|
|
96
|
+
function openUnvalidatedHop(url, signal) {
|
|
97
|
+
if (deps.openHop != null) return deps.openHop(url);
|
|
98
|
+
return allowPrivateAddress ? (0, _fedify_vocab_runtime_internal.openDirectFetch)(url) : (0, _fedify_vocab_runtime_internal.openPublicFetch)(url, { signal });
|
|
99
|
+
}
|
|
100
|
+
async function openHop(url, signal) {
|
|
101
|
+
try {
|
|
102
|
+
return await openUnvalidatedHop(url, signal);
|
|
67
103
|
} catch (error) {
|
|
68
104
|
if (error instanceof _fedify_vocab_runtime.UrlError) if (error.reason === "dns") logger$1.debug("DNS lookup failed for {url}", {
|
|
69
105
|
url,
|
|
@@ -76,6 +112,12 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
|
|
|
76
112
|
throw error;
|
|
77
113
|
}
|
|
78
114
|
}
|
|
115
|
+
function validateUrl(url) {
|
|
116
|
+
const parsedUrl = new URL(url);
|
|
117
|
+
const protocol = parsedUrl.protocol;
|
|
118
|
+
if (protocol !== "http:" && protocol !== "https:") throw new _fedify_vocab_runtime.UrlError(`Unsupported protocol: ${protocol}`);
|
|
119
|
+
if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new _fedify_vocab_runtime.UrlError("URLs with credentials are not allowed");
|
|
120
|
+
}
|
|
79
121
|
return (url, options) => load(url, options);
|
|
80
122
|
}
|
|
81
123
|
//#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 { r as fetchKey, s as validateCryptoKey } from "./key-
|
|
4
|
+
import { n as version, t as name } from "./deno-BUj4roJc.mjs";
|
|
5
|
+
import { r as fetchKey, s as validateCryptoKey } from "./key-C3o3DGap.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";
|