@fedify/vocab-runtime 2.4.5-dev.2462 → 2.4.6-dev.2628
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/deno.json +5 -2
- package/dist/{docloader-BMHljyYb.d.cts → docloader-DOWjnNCz.d.cts} +4 -8
- package/dist/{docloader-BMHljyYb.d.ts → docloader-DOWjnNCz.d.ts} +4 -8
- package/dist/internal/jsonld-cache.cjs +1 -1
- package/dist/internal/jsonld-cache.d.cts +1 -1
- package/dist/internal/jsonld-cache.d.ts +1 -1
- package/dist/internal/jsonld-cache.js +1 -1
- package/dist/internal/portable-dereference.cjs +1 -1
- package/dist/internal/portable-dereference.d.cts +2 -2
- package/dist/internal/portable-dereference.d.ts +2 -2
- package/dist/internal/portable-dereference.js +1 -1
- package/dist/internal.cjs +6 -0
- package/dist/internal.d.cts +108 -0
- package/dist/internal.d.ts +108 -0
- package/dist/internal.js +3 -0
- package/dist/mod.cjs +104 -38
- package/dist/mod.d.cts +3 -336
- package/dist/mod.d.ts +4 -336
- package/dist/mod.js +104 -38
- package/dist/{portable-6Aj-7dZJ.d.cts → portable-CTnZWX7J.d.cts} +1 -1
- package/dist/{portable-nkTiqH0w.d.ts → portable-Cq-rhQgY.d.ts} +1 -1
- package/dist/public-fetch-Dvfvawei.cjs +527 -0
- package/dist/public-fetch-ziZ39L5I.js +510 -0
- package/dist/tests/{body-Dlnsb-QX.cjs → body-1uyjqhY5.cjs} +1 -1
- package/dist/tests/{body-BXrFIcFa.mjs → body-BIuoyur4.mjs} +1 -1
- package/dist/tests/body.test.cjs +1 -1
- package/dist/tests/body.test.mjs +1 -1
- package/dist/tests/decimal.test.cjs +3 -3
- package/dist/tests/decimal.test.mjs +3 -3
- package/dist/tests/{docloader-DsuF8YDf.mjs → docloader-CmylAvrm.mjs} +106 -40
- package/dist/tests/{docloader-c9BQWaF1.cjs → docloader-DGDSOOhc.cjs} +111 -39
- package/dist/tests/docloader.test.cjs +531 -1293
- package/dist/tests/docloader.test.mjs +472 -1252
- package/dist/tests/esm-Ds-eHTfY.mjs +1229 -0
- package/dist/tests/esm-Q2tdh5f3.cjs +1215 -0
- package/dist/tests/internal/portable-dereference.test.cjs +3 -3
- package/dist/tests/internal/portable-dereference.test.mjs +3 -3
- package/dist/tests/{jsonld-cache-BEXiiM-V.cjs → jsonld-cache-DKIWKxEX.cjs} +1 -1
- package/dist/tests/{jsonld-cache-DFnWsLuZ.mjs → jsonld-cache-KH40z9_7.mjs} +1 -1
- package/dist/tests/jsonld-cache.test.cjs +2 -2
- package/dist/tests/jsonld-cache.test.mjs +2 -2
- package/dist/tests/node-BvS3AhZ2.cjs +89 -0
- package/dist/tests/node-Cg4pRuYy.mjs +84 -0
- package/dist/tests/pin-grant-CPrpk1l4.cjs +70 -0
- package/dist/tests/pin-grant-H5XhM1Xs.mjs +53 -0
- package/dist/tests/pin-grant.test.cjs +87 -0
- package/dist/tests/pin-grant.test.d.cts +1 -0
- package/dist/tests/pin-grant.test.d.mts +1 -0
- package/dist/tests/pin-grant.test.mjs +88 -0
- package/dist/tests/pinners/bun.test.cjs +166 -0
- package/dist/tests/pinners/bun.test.d.cts +1 -0
- package/dist/tests/pinners/bun.test.d.mts +1 -0
- package/dist/tests/pinners/bun.test.mjs +167 -0
- package/dist/tests/pinners/node.test.cjs +269 -0
- package/dist/tests/pinners/node.test.d.cts +1 -0
- package/dist/tests/pinners/node.test.d.mts +1 -0
- package/dist/tests/pinners/node.test.mjs +268 -0
- package/dist/tests/pinners/pinners.test.cjs +196 -0
- package/dist/tests/pinners/pinners.test.d.cts +1 -0
- package/dist/tests/pinners/pinners.test.d.mts +1 -0
- package/dist/tests/pinners/pinners.test.mjs +197 -0
- package/dist/tests/pinners/testdata/node-child.cjs +135 -0
- package/dist/tests/pinners/testdata/node-child.d.cts +1 -0
- package/dist/tests/pinners/testdata/node-child.d.mts +1 -0
- package/dist/tests/pinners/testdata/node-child.mjs +134 -0
- package/dist/tests/pinners-CJi9ZkYs.cjs +220 -0
- package/dist/tests/pinners-zSofbiy2.mjs +203 -0
- package/dist/tests/{portable-dereference-DmTYDy2p.mjs → portable-dereference-BHRE5Pkm.mjs} +2 -2
- package/dist/tests/{portable-dereference-qTLMqe2_.cjs → portable-dereference-mRHrC7PI.cjs} +2 -2
- package/dist/tests/{portable-media-M7idvaUq.mjs → portable-media-B83gylai.mjs} +3 -3
- package/dist/tests/{portable-media-CS4czG6X.cjs → portable-media-DyuL0sR_.cjs} +3 -3
- package/dist/tests/portable-media.test.cjs +1 -1
- package/dist/tests/portable-media.test.mjs +1 -1
- package/dist/tests/portable-workers.test.cjs +2 -2
- package/dist/tests/portable-workers.test.mjs +2 -2
- package/dist/tests/public-fetch-DNkPoks1.mjs +170 -0
- package/dist/tests/public-fetch-U822jbKB.cjs +186 -0
- package/dist/tests/public-fetch.test.cjs +1073 -0
- package/dist/tests/public-fetch.test.d.cts +1 -0
- package/dist/tests/public-fetch.test.d.mts +1 -0
- package/dist/tests/public-fetch.test.mjs +1074 -0
- package/dist/tests/{request-CgTgsBUA.cjs → request-BDe7VfMX.cjs} +1 -1
- package/dist/tests/{request-C336eT6A.mjs → request-Bj4ZeRfh.mjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/dist/tests/support-BmzI3dRJ.cjs +363 -0
- package/dist/tests/support-CiHDVJgh.mjs +272 -0
- package/dist/tests/{url-BNakuZ8k.cjs → url-Bdj-rTcC.cjs} +88 -12
- package/dist/tests/{url-DMxmp7ZG.mjs → url-COj5Q8mb.mjs} +65 -13
- package/dist/tests/url.test.cjs +4 -1
- package/dist/tests/url.test.mjs +4 -1
- package/dist/url-4EE56sNP.d.cts +352 -0
- package/dist/url-4EE56sNP.d.ts +352 -0
- package/dist/{url-DrGTR8yv.cjs → url-DIKZnaJv.cjs} +88 -12
- package/dist/{url-Dzyp-NsC.js → url-mWveR5GA.js} +65 -13
- package/package.json +14 -3
- package/src/docloader.test.ts +696 -48
- package/src/docloader.ts +186 -75
- package/src/internal.ts +13 -0
- package/src/pin-grant.test.ts +118 -0
- package/src/pin-grant.ts +99 -0
- package/src/pinners/bun.test.ts +278 -0
- package/src/pinners/bun.ts +223 -0
- package/src/pinners/mod.ts +59 -0
- package/src/pinners/node.test.ts +415 -0
- package/src/pinners/node.ts +144 -0
- package/src/pinners/pinners.test.ts +316 -0
- package/src/pinners/testdata/node-child.ts +147 -0
- package/src/pinners/testdata/proxy-child.ts +82 -0
- package/src/pinners/testdata/support.ts +343 -0
- package/src/pinners/testdata/tls-cert.pem +12 -0
- package/src/pinners/testdata/tls-child.ts +88 -0
- package/src/pinners/testdata/tls-key.pem +5 -0
- package/src/pinners/types.ts +48 -0
- package/src/public-fetch.test.ts +1286 -0
- package/src/public-fetch.ts +338 -0
- package/src/testdata/public-fetch-deno-worker.ts +74 -0
- package/src/url.test.ts +11 -0
- package/src/url.ts +113 -22
- package/tsdown.config.ts +7 -2
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const require_rolldown_runtime = require("../../rolldown-runtime-emK7D4bc.cjs");
|
|
2
|
+
const require_pin_grant = require("../../pin-grant-CPrpk1l4.cjs");
|
|
3
|
+
const require_node = require("../../node-BvS3AhZ2.cjs");
|
|
4
|
+
let node_process = require("node:process");
|
|
5
|
+
node_process = require_rolldown_runtime.__toESM(node_process, 1);
|
|
6
|
+
//#region src/pinners/testdata/node-child.ts
|
|
7
|
+
/**
|
|
8
|
+
* A child process for the regression tests of the Node.js pinner in
|
|
9
|
+
* *../node.test.ts* that depend on the environment at startup: Node.js reads
|
|
10
|
+
* proxy environment variables (with `NODE_USE_ENV_PROXY=1`) and
|
|
11
|
+
* `NODE_EXTRA_CA_CERTS` only at startup.
|
|
12
|
+
*
|
|
13
|
+
* Node.js cannot run TypeScript sources without flags before 22.6.0, so
|
|
14
|
+
* *../../../tsdown.config.ts* compiles this script into
|
|
15
|
+
* *dist/tests/pinners/testdata/node-child.mjs*, which the tests run. It has
|
|
16
|
+
* no top-level `await`, so that it can be compiled to CommonJS as well.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
*
|
|
20
|
+
* - `node node-child.mjs proxy <http-port> <https-port>`: requests
|
|
21
|
+
* `http://pinned-host.invalid:<http-port>/pinned` and
|
|
22
|
+
* `https://pinned-host.invalid:<https-port>/pinned` through a pinner
|
|
23
|
+
* that allows loopback addresses, with both pinned to 127.0.0.1, and then
|
|
24
|
+
* `http://127.0.0.1:<http-port>/control` with the global `fetch()` as
|
|
25
|
+
* a control.
|
|
26
|
+
* - `node node-child.mjs tls <https-port>`: requests
|
|
27
|
+
* `https://<hostname>:<https-port>/<case>` through a pinner that allows
|
|
28
|
+
* loopback addresses, with every hostname pinned to 127.0.0.1, in the same
|
|
29
|
+
* order as *tls-child.ts* does for Bun.
|
|
30
|
+
*
|
|
31
|
+
* It prints the results as a JSON line.
|
|
32
|
+
* @module
|
|
33
|
+
*/
|
|
34
|
+
const loopback = [{
|
|
35
|
+
address: "127.0.0.1",
|
|
36
|
+
family: 4
|
|
37
|
+
}];
|
|
38
|
+
/**
|
|
39
|
+
* Describes an error by its code, or by the code of its cause, since
|
|
40
|
+
* the `fetch()` of Node.js rejects with a `TypeError` caused by the actual
|
|
41
|
+
* error.
|
|
42
|
+
*/
|
|
43
|
+
function describeError(error) {
|
|
44
|
+
for (let e = error; typeof e === "object" && e !== null;) {
|
|
45
|
+
const { code, cause } = e;
|
|
46
|
+
if (typeof code === "string") return code;
|
|
47
|
+
e = cause;
|
|
48
|
+
}
|
|
49
|
+
return String(error);
|
|
50
|
+
}
|
|
51
|
+
async function attempt(fn) {
|
|
52
|
+
try {
|
|
53
|
+
const response = await fn();
|
|
54
|
+
return {
|
|
55
|
+
status: response.status,
|
|
56
|
+
body: await response.text()
|
|
57
|
+
};
|
|
58
|
+
} catch (error) {
|
|
59
|
+
return { error: describeError(error) };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function fetchPinned(pinner, url, withSignals = false) {
|
|
63
|
+
const grant = require_pin_grant.issuePinGrant(url, loopback);
|
|
64
|
+
return attempt(() => withSignals ? pinner.fetch(grant, new Request(url, { signal: new AbortController().signal }), new AbortController().signal) : pinner.fetch(grant, new Request(url)));
|
|
65
|
+
}
|
|
66
|
+
async function proxy(pinner, httpPort, httpsPort) {
|
|
67
|
+
const http = await fetchPinned(pinner, new URL(`http://pinned-host.invalid:${httpPort}/pinned`));
|
|
68
|
+
const https = await fetchPinned(pinner, new URL(`https://pinned-host.invalid:${httpsPort}/pinned`));
|
|
69
|
+
const control = await attempt(() => fetch(`http://127.0.0.1:${httpPort}/control`, {
|
|
70
|
+
redirect: "manual",
|
|
71
|
+
signal: AbortSignal.timeout(1e4)
|
|
72
|
+
}));
|
|
73
|
+
return {
|
|
74
|
+
version: node_process.default.version,
|
|
75
|
+
http,
|
|
76
|
+
https,
|
|
77
|
+
control
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function tls(pinner, port) {
|
|
81
|
+
const results = [];
|
|
82
|
+
for (const [name, hostname, withSignals] of [
|
|
83
|
+
[
|
|
84
|
+
"pinned",
|
|
85
|
+
"pinned-host.invalid",
|
|
86
|
+
false
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
"unlisted",
|
|
90
|
+
"unlisted-host.invalid",
|
|
91
|
+
false
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
"unlisted-again",
|
|
95
|
+
"unlisted-host.invalid",
|
|
96
|
+
false
|
|
97
|
+
],
|
|
98
|
+
[
|
|
99
|
+
"alternate",
|
|
100
|
+
"alternate-host.invalid",
|
|
101
|
+
false
|
|
102
|
+
],
|
|
103
|
+
[
|
|
104
|
+
"signals",
|
|
105
|
+
"pinned-host.invalid",
|
|
106
|
+
true
|
|
107
|
+
]
|
|
108
|
+
]) {
|
|
109
|
+
const url = new URL(`https://${hostname}:${port}/${name}`);
|
|
110
|
+
results.push({
|
|
111
|
+
case: name,
|
|
112
|
+
...await fetchPinned(pinner, url, withSignals)
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
version: node_process.default.version,
|
|
117
|
+
results
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
async function main() {
|
|
121
|
+
const [mode, ...ports] = node_process.default.argv.slice(2);
|
|
122
|
+
const [first, second] = ports.map(Number);
|
|
123
|
+
const pinner = require_node.createNodePinner({
|
|
124
|
+
verifyAddress() {},
|
|
125
|
+
undici: await import("undici")
|
|
126
|
+
});
|
|
127
|
+
if (mode === "proxy" && Number.isInteger(first) && Number.isInteger(second)) return await proxy(pinner, first, second);
|
|
128
|
+
else if (mode === "tls" && Number.isInteger(first)) return await tls(pinner, first);
|
|
129
|
+
throw new TypeError("Usage: node-child.mjs proxy <http-port> <https-port> | node-child.mjs tls <https-port>");
|
|
130
|
+
}
|
|
131
|
+
main().then((output) => node_process.default.stdout.write(`${JSON.stringify(output)}\n`, () => node_process.default.exit(0)), (error) => {
|
|
132
|
+
console.error(error);
|
|
133
|
+
node_process.default.exit(1);
|
|
134
|
+
});
|
|
135
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { t as issuePinGrant } from "../../pin-grant-H5XhM1Xs.mjs";
|
|
2
|
+
import { t as createNodePinner } from "../../node-Cg4pRuYy.mjs";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
//#region src/pinners/testdata/node-child.ts
|
|
5
|
+
/**
|
|
6
|
+
* A child process for the regression tests of the Node.js pinner in
|
|
7
|
+
* *../node.test.ts* that depend on the environment at startup: Node.js reads
|
|
8
|
+
* proxy environment variables (with `NODE_USE_ENV_PROXY=1`) and
|
|
9
|
+
* `NODE_EXTRA_CA_CERTS` only at startup.
|
|
10
|
+
*
|
|
11
|
+
* Node.js cannot run TypeScript sources without flags before 22.6.0, so
|
|
12
|
+
* *../../../tsdown.config.ts* compiles this script into
|
|
13
|
+
* *dist/tests/pinners/testdata/node-child.mjs*, which the tests run. It has
|
|
14
|
+
* no top-level `await`, so that it can be compiled to CommonJS as well.
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
*
|
|
18
|
+
* - `node node-child.mjs proxy <http-port> <https-port>`: requests
|
|
19
|
+
* `http://pinned-host.invalid:<http-port>/pinned` and
|
|
20
|
+
* `https://pinned-host.invalid:<https-port>/pinned` through a pinner
|
|
21
|
+
* that allows loopback addresses, with both pinned to 127.0.0.1, and then
|
|
22
|
+
* `http://127.0.0.1:<http-port>/control` with the global `fetch()` as
|
|
23
|
+
* a control.
|
|
24
|
+
* - `node node-child.mjs tls <https-port>`: requests
|
|
25
|
+
* `https://<hostname>:<https-port>/<case>` through a pinner that allows
|
|
26
|
+
* loopback addresses, with every hostname pinned to 127.0.0.1, in the same
|
|
27
|
+
* order as *tls-child.ts* does for Bun.
|
|
28
|
+
*
|
|
29
|
+
* It prints the results as a JSON line.
|
|
30
|
+
* @module
|
|
31
|
+
*/
|
|
32
|
+
const loopback = [{
|
|
33
|
+
address: "127.0.0.1",
|
|
34
|
+
family: 4
|
|
35
|
+
}];
|
|
36
|
+
/**
|
|
37
|
+
* Describes an error by its code, or by the code of its cause, since
|
|
38
|
+
* the `fetch()` of Node.js rejects with a `TypeError` caused by the actual
|
|
39
|
+
* error.
|
|
40
|
+
*/
|
|
41
|
+
function describeError(error) {
|
|
42
|
+
for (let e = error; typeof e === "object" && e !== null;) {
|
|
43
|
+
const { code, cause } = e;
|
|
44
|
+
if (typeof code === "string") return code;
|
|
45
|
+
e = cause;
|
|
46
|
+
}
|
|
47
|
+
return String(error);
|
|
48
|
+
}
|
|
49
|
+
async function attempt(fn) {
|
|
50
|
+
try {
|
|
51
|
+
const response = await fn();
|
|
52
|
+
return {
|
|
53
|
+
status: response.status,
|
|
54
|
+
body: await response.text()
|
|
55
|
+
};
|
|
56
|
+
} catch (error) {
|
|
57
|
+
return { error: describeError(error) };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function fetchPinned(pinner, url, withSignals = false) {
|
|
61
|
+
const grant = issuePinGrant(url, loopback);
|
|
62
|
+
return attempt(() => withSignals ? pinner.fetch(grant, new Request(url, { signal: new AbortController().signal }), new AbortController().signal) : pinner.fetch(grant, new Request(url)));
|
|
63
|
+
}
|
|
64
|
+
async function proxy(pinner, httpPort, httpsPort) {
|
|
65
|
+
const http = await fetchPinned(pinner, new URL(`http://pinned-host.invalid:${httpPort}/pinned`));
|
|
66
|
+
const https = await fetchPinned(pinner, new URL(`https://pinned-host.invalid:${httpsPort}/pinned`));
|
|
67
|
+
const control = await attempt(() => fetch(`http://127.0.0.1:${httpPort}/control`, {
|
|
68
|
+
redirect: "manual",
|
|
69
|
+
signal: AbortSignal.timeout(1e4)
|
|
70
|
+
}));
|
|
71
|
+
return {
|
|
72
|
+
version: process.version,
|
|
73
|
+
http,
|
|
74
|
+
https,
|
|
75
|
+
control
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function tls(pinner, port) {
|
|
79
|
+
const results = [];
|
|
80
|
+
for (const [name, hostname, withSignals] of [
|
|
81
|
+
[
|
|
82
|
+
"pinned",
|
|
83
|
+
"pinned-host.invalid",
|
|
84
|
+
false
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
"unlisted",
|
|
88
|
+
"unlisted-host.invalid",
|
|
89
|
+
false
|
|
90
|
+
],
|
|
91
|
+
[
|
|
92
|
+
"unlisted-again",
|
|
93
|
+
"unlisted-host.invalid",
|
|
94
|
+
false
|
|
95
|
+
],
|
|
96
|
+
[
|
|
97
|
+
"alternate",
|
|
98
|
+
"alternate-host.invalid",
|
|
99
|
+
false
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
"signals",
|
|
103
|
+
"pinned-host.invalid",
|
|
104
|
+
true
|
|
105
|
+
]
|
|
106
|
+
]) {
|
|
107
|
+
const url = new URL(`https://${hostname}:${port}/${name}`);
|
|
108
|
+
results.push({
|
|
109
|
+
case: name,
|
|
110
|
+
...await fetchPinned(pinner, url, withSignals)
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
version: process.version,
|
|
115
|
+
results
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
async function main() {
|
|
119
|
+
const [mode, ...ports] = process.argv.slice(2);
|
|
120
|
+
const [first, second] = ports.map(Number);
|
|
121
|
+
const pinner = createNodePinner({
|
|
122
|
+
verifyAddress() {},
|
|
123
|
+
undici: await import("undici")
|
|
124
|
+
});
|
|
125
|
+
if (mode === "proxy" && Number.isInteger(first) && Number.isInteger(second)) return await proxy(pinner, first, second);
|
|
126
|
+
else if (mode === "tls" && Number.isInteger(first)) return await tls(pinner, first);
|
|
127
|
+
throw new TypeError("Usage: node-child.mjs proxy <http-port> <https-port> | node-child.mjs tls <https-port>");
|
|
128
|
+
}
|
|
129
|
+
main().then((output) => process.stdout.write(`${JSON.stringify(output)}\n`, () => process.exit(0)), (error) => {
|
|
130
|
+
console.error(error);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
});
|
|
133
|
+
//#endregion
|
|
134
|
+
export {};
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
const require_pin_grant = require("./pin-grant-CPrpk1l4.cjs");
|
|
2
|
+
const require_url = require("./url-Bdj-rTcC.cjs");
|
|
3
|
+
const require_node = require("./node-BvS3AhZ2.cjs");
|
|
4
|
+
let _logtape_logtape = require("@logtape/logtape");
|
|
5
|
+
let node_net = require("node:net");
|
|
6
|
+
//#region src/pinners/bun.ts
|
|
7
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
8
|
+
"fedify",
|
|
9
|
+
"runtime",
|
|
10
|
+
"fetch"
|
|
11
|
+
]);
|
|
12
|
+
/**
|
|
13
|
+
* The first Bun version that honors `proxy: false` when proxy environment
|
|
14
|
+
* variables are set at startup. Earlier versions send pinned requests
|
|
15
|
+
* through the proxy, with the validated IP address as the target.
|
|
16
|
+
*/
|
|
17
|
+
const PROXY_FALSE_MIN_VERSION = "1.4.3";
|
|
18
|
+
const PROXY_VARIABLES = [
|
|
19
|
+
"HTTP_PROXY",
|
|
20
|
+
"http_proxy",
|
|
21
|
+
"HTTPS_PROXY",
|
|
22
|
+
"https_proxy",
|
|
23
|
+
"ALL_PROXY",
|
|
24
|
+
"all_proxy"
|
|
25
|
+
];
|
|
26
|
+
const FALLBACK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
27
|
+
"ConnectionRefused",
|
|
28
|
+
"ECONNREFUSED",
|
|
29
|
+
"EHOSTUNREACH",
|
|
30
|
+
"ENETUNREACH",
|
|
31
|
+
"ETIMEDOUT"
|
|
32
|
+
]);
|
|
33
|
+
/**
|
|
34
|
+
* Bun versions before 1.3.11 verify the server certificate against the `Host`
|
|
35
|
+
* header, including a non-default port, instead of `tls.serverName`, so
|
|
36
|
+
* pinned requests to HTTPS servers on non-default ports always fail the check
|
|
37
|
+
* there. A custom `checkServerIdentity` cannot stand in for it: these
|
|
38
|
+
* versions send the whole request before a failing custom check aborts it
|
|
39
|
+
* (and 1.3.10 even reuses the connection for the next request), so such
|
|
40
|
+
* requests are refused instead.
|
|
41
|
+
*/
|
|
42
|
+
const HOST_HEADER_IDENTITY_RANGE = "<1.3.11";
|
|
43
|
+
/**
|
|
44
|
+
* Creates a pinner for Bun. Every request is sent to an IP literal URL with
|
|
45
|
+
* the original `Host` header, `tls.serverName` set to the original hostname
|
|
46
|
+
* (so that the certificate is still verified against it), and `proxy: false`.
|
|
47
|
+
*
|
|
48
|
+
* GET and HEAD requests fall back to the next address on transport errors
|
|
49
|
+
* raised before a response arrives. Other methods use only the first
|
|
50
|
+
* address, since a transport error does not guarantee that the request did
|
|
51
|
+
* not reach the server, and retrying it could deliver an activity twice.
|
|
52
|
+
* Together with the transport retries of `doubleKnock()`, a GET request is
|
|
53
|
+
* attempted at most twice per address. Note that Bun 1.4 also reports
|
|
54
|
+
* a connection reset after the request was sent as a `TypeError`, so GET and
|
|
55
|
+
* HEAD requests may be repeated against the next address in that case, which
|
|
56
|
+
* is acceptable for these idempotent methods.
|
|
57
|
+
*
|
|
58
|
+
* Bun versions before 1.3.11 verify certificates against the `Host` header,
|
|
59
|
+
* which includes a non-default port, and cannot safely check them against
|
|
60
|
+
* the original hostname in any other way, so requests to HTTPS servers on
|
|
61
|
+
* non-default ports fail there with a `TypeError` before connecting.
|
|
62
|
+
*
|
|
63
|
+
* Bun versions before 1.4.3 ignore `proxy: false` when proxy environment
|
|
64
|
+
* variables are set, so requests go through the proxy with the validated IP
|
|
65
|
+
* address as the target. In that case a warning is logged once.
|
|
66
|
+
* @param options The pinner options.
|
|
67
|
+
* @returns The pinner.
|
|
68
|
+
*/
|
|
69
|
+
function createBunPinner({ verifyAddress }) {
|
|
70
|
+
let proxyWarningPending = ignoresProxyFalse() && hasProxyVariables();
|
|
71
|
+
return {
|
|
72
|
+
supports(url) {
|
|
73
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
74
|
+
},
|
|
75
|
+
async fetch(grant, request, signal) {
|
|
76
|
+
const url = new URL(request.url);
|
|
77
|
+
const { hostname, addresses } = require_pin_grant.redeemPinGrant(grant, url);
|
|
78
|
+
if (proxyWarningPending) {
|
|
79
|
+
proxyWarningPending = false;
|
|
80
|
+
logger.warn("Bun {version} ignores proxy: false while proxy environment variables are set, so outbound requests go through the proxy instead of connecting directly to the validated addresses. Upgrade to Bun {minVersion} or later, or unset the proxy environment variables; see {url}.", {
|
|
81
|
+
version: getBun()?.version,
|
|
82
|
+
minVersion: PROXY_FALSE_MIN_VERSION,
|
|
83
|
+
url: "https://fedify.dev/manual/deploy#bun-behind-an-http-proxy"
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const fallback = request.method === "GET" || request.method === "HEAD";
|
|
87
|
+
const candidates = fallback ? addresses : addresses.slice(0, 1);
|
|
88
|
+
const body = request.body == null ? null : await request.arrayBuffer();
|
|
89
|
+
let lastError = /* @__PURE__ */ new TypeError(`No address to connect to for ${url.host}.`);
|
|
90
|
+
for (const address of candidates) {
|
|
91
|
+
throwIfAborted(signal, request.signal);
|
|
92
|
+
verifyAddress(address);
|
|
93
|
+
const headers = new Headers(request.headers);
|
|
94
|
+
headers.set("Host", url.host);
|
|
95
|
+
const init = {
|
|
96
|
+
method: request.method,
|
|
97
|
+
headers,
|
|
98
|
+
body,
|
|
99
|
+
redirect: "manual",
|
|
100
|
+
signal: signal == null || signal === request.signal ? request.signal : anySignal(signal, request.signal),
|
|
101
|
+
proxy: false
|
|
102
|
+
};
|
|
103
|
+
if (url.protocol === "https:") init.tls = getTlsOptions(url, hostname);
|
|
104
|
+
try {
|
|
105
|
+
return await fetch(toAddressUrl(url, address), init);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throwIfAborted(signal, request.signal);
|
|
108
|
+
if (!fallback || !isFallbackError(error)) throw error;
|
|
109
|
+
lastError = error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
throw lastError;
|
|
113
|
+
},
|
|
114
|
+
release() {}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function getTlsOptions(url, hostname) {
|
|
118
|
+
const bun = getBun();
|
|
119
|
+
if (url.port !== "" && bun != null && bun.semver.satisfies(bun.version, HOST_HEADER_IDENTITY_RANGE)) throw new TypeError(`Bun ${bun.version} cannot verify the certificate of ${url.host} when connecting to a validated address; upgrade to Bun 1.3.11 or later to fetch from HTTPS servers on non-default ports.`);
|
|
120
|
+
return (0, node_net.isIP)(hostname) === 0 ? { serverName: hostname } : {};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Combines two signals without `AbortSignal.any()`, which Bun 1.1.0 lacks.
|
|
124
|
+
*/
|
|
125
|
+
function anySignal(a, b) {
|
|
126
|
+
if (a.aborted) return a;
|
|
127
|
+
if (b.aborted) return b;
|
|
128
|
+
const controller = new AbortController();
|
|
129
|
+
const abort = (event) => {
|
|
130
|
+
controller.abort(event.target.reason);
|
|
131
|
+
a.removeEventListener("abort", abort);
|
|
132
|
+
b.removeEventListener("abort", abort);
|
|
133
|
+
};
|
|
134
|
+
a.addEventListener("abort", abort);
|
|
135
|
+
b.addEventListener("abort", abort);
|
|
136
|
+
return controller.signal;
|
|
137
|
+
}
|
|
138
|
+
function toAddressUrl(url, address) {
|
|
139
|
+
const result = new URL(url);
|
|
140
|
+
result.hostname = address.family === 6 ? `[${address.address}]` : address.address;
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
function isFallbackError(error) {
|
|
144
|
+
if (error instanceof Error && error.name === "AbortError") return false;
|
|
145
|
+
if (error instanceof TypeError) return true;
|
|
146
|
+
const code = error?.code;
|
|
147
|
+
return typeof code === "string" && FALLBACK_ERROR_CODES.has(code);
|
|
148
|
+
}
|
|
149
|
+
function throwIfAborted(...signals) {
|
|
150
|
+
for (const signal of signals) signal?.throwIfAborted();
|
|
151
|
+
}
|
|
152
|
+
function getBun() {
|
|
153
|
+
return globalThis.Bun;
|
|
154
|
+
}
|
|
155
|
+
function ignoresProxyFalse() {
|
|
156
|
+
const bun = getBun();
|
|
157
|
+
if (bun == null) return false;
|
|
158
|
+
return !bun.semver.satisfies(bun.version, `>=${PROXY_FALSE_MIN_VERSION}`);
|
|
159
|
+
}
|
|
160
|
+
function hasProxyVariables() {
|
|
161
|
+
const env = globalThis.process?.env;
|
|
162
|
+
if (env == null) return false;
|
|
163
|
+
return PROXY_VARIABLES.some((name) => (env[name] ?? "") !== "");
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/pinners/mod.ts
|
|
167
|
+
/**
|
|
168
|
+
* Detects the current runtime. Cloudflare Workers is checked first, since
|
|
169
|
+
* `process` can exist there with `nodejs_compat`, and Deno and Bun before
|
|
170
|
+
* Node.js, since both provide `process.versions.node`.
|
|
171
|
+
* @returns The current runtime.
|
|
172
|
+
*/
|
|
173
|
+
function detectRuntime() {
|
|
174
|
+
const g = globalThis;
|
|
175
|
+
if (g.navigator?.userAgent === "Cloudflare-Workers") return "cfworkers";
|
|
176
|
+
if ("Deno" in globalThis) return "deno";
|
|
177
|
+
if ("Bun" in globalThis) return "bun";
|
|
178
|
+
if (g.process?.versions?.node != null) return "node";
|
|
179
|
+
return "other";
|
|
180
|
+
}
|
|
181
|
+
let runtimePinner;
|
|
182
|
+
/**
|
|
183
|
+
* Gets the production pinner of the current runtime, which rejects
|
|
184
|
+
* non-public addresses right before connecting. Deno and Cloudflare Workers
|
|
185
|
+
* have no pinner.
|
|
186
|
+
* @returns The pinner, or `null` if the runtime has none.
|
|
187
|
+
*/
|
|
188
|
+
function getRuntimePinner() {
|
|
189
|
+
runtimePinner ??= createRuntimePinner(detectRuntime());
|
|
190
|
+
return runtimePinner;
|
|
191
|
+
}
|
|
192
|
+
async function createRuntimePinner(runtime) {
|
|
193
|
+
switch (runtime) {
|
|
194
|
+
case "bun": return createBunPinner({ verifyAddress: require_url.assertPublicAddress });
|
|
195
|
+
case "node": return require_node.createNodePinner({
|
|
196
|
+
verifyAddress: require_url.assertPublicAddress,
|
|
197
|
+
undici: await import("undici")
|
|
198
|
+
});
|
|
199
|
+
default: return null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
Object.defineProperty(exports, "createBunPinner", {
|
|
204
|
+
enumerable: true,
|
|
205
|
+
get: function() {
|
|
206
|
+
return createBunPinner;
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
Object.defineProperty(exports, "detectRuntime", {
|
|
210
|
+
enumerable: true,
|
|
211
|
+
get: function() {
|
|
212
|
+
return detectRuntime;
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
Object.defineProperty(exports, "getRuntimePinner", {
|
|
216
|
+
enumerable: true,
|
|
217
|
+
get: function() {
|
|
218
|
+
return getRuntimePinner;
|
|
219
|
+
}
|
|
220
|
+
});
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { n as redeemPinGrant } from "./pin-grant-H5XhM1Xs.mjs";
|
|
2
|
+
import { i as assertPublicAddress } from "./url-COj5Q8mb.mjs";
|
|
3
|
+
import { t as createNodePinner } from "./node-Cg4pRuYy.mjs";
|
|
4
|
+
import { getLogger } from "@logtape/logtape";
|
|
5
|
+
import { isIP } from "node:net";
|
|
6
|
+
//#region src/pinners/bun.ts
|
|
7
|
+
const logger = getLogger([
|
|
8
|
+
"fedify",
|
|
9
|
+
"runtime",
|
|
10
|
+
"fetch"
|
|
11
|
+
]);
|
|
12
|
+
/**
|
|
13
|
+
* The first Bun version that honors `proxy: false` when proxy environment
|
|
14
|
+
* variables are set at startup. Earlier versions send pinned requests
|
|
15
|
+
* through the proxy, with the validated IP address as the target.
|
|
16
|
+
*/
|
|
17
|
+
const PROXY_FALSE_MIN_VERSION = "1.4.3";
|
|
18
|
+
const PROXY_VARIABLES = [
|
|
19
|
+
"HTTP_PROXY",
|
|
20
|
+
"http_proxy",
|
|
21
|
+
"HTTPS_PROXY",
|
|
22
|
+
"https_proxy",
|
|
23
|
+
"ALL_PROXY",
|
|
24
|
+
"all_proxy"
|
|
25
|
+
];
|
|
26
|
+
const FALLBACK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
27
|
+
"ConnectionRefused",
|
|
28
|
+
"ECONNREFUSED",
|
|
29
|
+
"EHOSTUNREACH",
|
|
30
|
+
"ENETUNREACH",
|
|
31
|
+
"ETIMEDOUT"
|
|
32
|
+
]);
|
|
33
|
+
/**
|
|
34
|
+
* Bun versions before 1.3.11 verify the server certificate against the `Host`
|
|
35
|
+
* header, including a non-default port, instead of `tls.serverName`, so
|
|
36
|
+
* pinned requests to HTTPS servers on non-default ports always fail the check
|
|
37
|
+
* there. A custom `checkServerIdentity` cannot stand in for it: these
|
|
38
|
+
* versions send the whole request before a failing custom check aborts it
|
|
39
|
+
* (and 1.3.10 even reuses the connection for the next request), so such
|
|
40
|
+
* requests are refused instead.
|
|
41
|
+
*/
|
|
42
|
+
const HOST_HEADER_IDENTITY_RANGE = "<1.3.11";
|
|
43
|
+
/**
|
|
44
|
+
* Creates a pinner for Bun. Every request is sent to an IP literal URL with
|
|
45
|
+
* the original `Host` header, `tls.serverName` set to the original hostname
|
|
46
|
+
* (so that the certificate is still verified against it), and `proxy: false`.
|
|
47
|
+
*
|
|
48
|
+
* GET and HEAD requests fall back to the next address on transport errors
|
|
49
|
+
* raised before a response arrives. Other methods use only the first
|
|
50
|
+
* address, since a transport error does not guarantee that the request did
|
|
51
|
+
* not reach the server, and retrying it could deliver an activity twice.
|
|
52
|
+
* Together with the transport retries of `doubleKnock()`, a GET request is
|
|
53
|
+
* attempted at most twice per address. Note that Bun 1.4 also reports
|
|
54
|
+
* a connection reset after the request was sent as a `TypeError`, so GET and
|
|
55
|
+
* HEAD requests may be repeated against the next address in that case, which
|
|
56
|
+
* is acceptable for these idempotent methods.
|
|
57
|
+
*
|
|
58
|
+
* Bun versions before 1.3.11 verify certificates against the `Host` header,
|
|
59
|
+
* which includes a non-default port, and cannot safely check them against
|
|
60
|
+
* the original hostname in any other way, so requests to HTTPS servers on
|
|
61
|
+
* non-default ports fail there with a `TypeError` before connecting.
|
|
62
|
+
*
|
|
63
|
+
* Bun versions before 1.4.3 ignore `proxy: false` when proxy environment
|
|
64
|
+
* variables are set, so requests go through the proxy with the validated IP
|
|
65
|
+
* address as the target. In that case a warning is logged once.
|
|
66
|
+
* @param options The pinner options.
|
|
67
|
+
* @returns The pinner.
|
|
68
|
+
*/
|
|
69
|
+
function createBunPinner({ verifyAddress }) {
|
|
70
|
+
let proxyWarningPending = ignoresProxyFalse() && hasProxyVariables();
|
|
71
|
+
return {
|
|
72
|
+
supports(url) {
|
|
73
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
74
|
+
},
|
|
75
|
+
async fetch(grant, request, signal) {
|
|
76
|
+
const url = new URL(request.url);
|
|
77
|
+
const { hostname, addresses } = redeemPinGrant(grant, url);
|
|
78
|
+
if (proxyWarningPending) {
|
|
79
|
+
proxyWarningPending = false;
|
|
80
|
+
logger.warn("Bun {version} ignores proxy: false while proxy environment variables are set, so outbound requests go through the proxy instead of connecting directly to the validated addresses. Upgrade to Bun {minVersion} or later, or unset the proxy environment variables; see {url}.", {
|
|
81
|
+
version: getBun()?.version,
|
|
82
|
+
minVersion: PROXY_FALSE_MIN_VERSION,
|
|
83
|
+
url: "https://fedify.dev/manual/deploy#bun-behind-an-http-proxy"
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const fallback = request.method === "GET" || request.method === "HEAD";
|
|
87
|
+
const candidates = fallback ? addresses : addresses.slice(0, 1);
|
|
88
|
+
const body = request.body == null ? null : await request.arrayBuffer();
|
|
89
|
+
let lastError = /* @__PURE__ */ new TypeError(`No address to connect to for ${url.host}.`);
|
|
90
|
+
for (const address of candidates) {
|
|
91
|
+
throwIfAborted(signal, request.signal);
|
|
92
|
+
verifyAddress(address);
|
|
93
|
+
const headers = new Headers(request.headers);
|
|
94
|
+
headers.set("Host", url.host);
|
|
95
|
+
const init = {
|
|
96
|
+
method: request.method,
|
|
97
|
+
headers,
|
|
98
|
+
body,
|
|
99
|
+
redirect: "manual",
|
|
100
|
+
signal: signal == null || signal === request.signal ? request.signal : anySignal(signal, request.signal),
|
|
101
|
+
proxy: false
|
|
102
|
+
};
|
|
103
|
+
if (url.protocol === "https:") init.tls = getTlsOptions(url, hostname);
|
|
104
|
+
try {
|
|
105
|
+
return await fetch(toAddressUrl(url, address), init);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throwIfAborted(signal, request.signal);
|
|
108
|
+
if (!fallback || !isFallbackError(error)) throw error;
|
|
109
|
+
lastError = error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
throw lastError;
|
|
113
|
+
},
|
|
114
|
+
release() {}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function getTlsOptions(url, hostname) {
|
|
118
|
+
const bun = getBun();
|
|
119
|
+
if (url.port !== "" && bun != null && bun.semver.satisfies(bun.version, HOST_HEADER_IDENTITY_RANGE)) throw new TypeError(`Bun ${bun.version} cannot verify the certificate of ${url.host} when connecting to a validated address; upgrade to Bun 1.3.11 or later to fetch from HTTPS servers on non-default ports.`);
|
|
120
|
+
return isIP(hostname) === 0 ? { serverName: hostname } : {};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Combines two signals without `AbortSignal.any()`, which Bun 1.1.0 lacks.
|
|
124
|
+
*/
|
|
125
|
+
function anySignal(a, b) {
|
|
126
|
+
if (a.aborted) return a;
|
|
127
|
+
if (b.aborted) return b;
|
|
128
|
+
const controller = new AbortController();
|
|
129
|
+
const abort = (event) => {
|
|
130
|
+
controller.abort(event.target.reason);
|
|
131
|
+
a.removeEventListener("abort", abort);
|
|
132
|
+
b.removeEventListener("abort", abort);
|
|
133
|
+
};
|
|
134
|
+
a.addEventListener("abort", abort);
|
|
135
|
+
b.addEventListener("abort", abort);
|
|
136
|
+
return controller.signal;
|
|
137
|
+
}
|
|
138
|
+
function toAddressUrl(url, address) {
|
|
139
|
+
const result = new URL(url);
|
|
140
|
+
result.hostname = address.family === 6 ? `[${address.address}]` : address.address;
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
function isFallbackError(error) {
|
|
144
|
+
if (error instanceof Error && error.name === "AbortError") return false;
|
|
145
|
+
if (error instanceof TypeError) return true;
|
|
146
|
+
const code = error?.code;
|
|
147
|
+
return typeof code === "string" && FALLBACK_ERROR_CODES.has(code);
|
|
148
|
+
}
|
|
149
|
+
function throwIfAborted(...signals) {
|
|
150
|
+
for (const signal of signals) signal?.throwIfAborted();
|
|
151
|
+
}
|
|
152
|
+
function getBun() {
|
|
153
|
+
return globalThis.Bun;
|
|
154
|
+
}
|
|
155
|
+
function ignoresProxyFalse() {
|
|
156
|
+
const bun = getBun();
|
|
157
|
+
if (bun == null) return false;
|
|
158
|
+
return !bun.semver.satisfies(bun.version, `>=${PROXY_FALSE_MIN_VERSION}`);
|
|
159
|
+
}
|
|
160
|
+
function hasProxyVariables() {
|
|
161
|
+
const env = globalThis.process?.env;
|
|
162
|
+
if (env == null) return false;
|
|
163
|
+
return PROXY_VARIABLES.some((name) => (env[name] ?? "") !== "");
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/pinners/mod.ts
|
|
167
|
+
/**
|
|
168
|
+
* Detects the current runtime. Cloudflare Workers is checked first, since
|
|
169
|
+
* `process` can exist there with `nodejs_compat`, and Deno and Bun before
|
|
170
|
+
* Node.js, since both provide `process.versions.node`.
|
|
171
|
+
* @returns The current runtime.
|
|
172
|
+
*/
|
|
173
|
+
function detectRuntime() {
|
|
174
|
+
const g = globalThis;
|
|
175
|
+
if (g.navigator?.userAgent === "Cloudflare-Workers") return "cfworkers";
|
|
176
|
+
if ("Deno" in globalThis) return "deno";
|
|
177
|
+
if ("Bun" in globalThis) return "bun";
|
|
178
|
+
if (g.process?.versions?.node != null) return "node";
|
|
179
|
+
return "other";
|
|
180
|
+
}
|
|
181
|
+
let runtimePinner;
|
|
182
|
+
/**
|
|
183
|
+
* Gets the production pinner of the current runtime, which rejects
|
|
184
|
+
* non-public addresses right before connecting. Deno and Cloudflare Workers
|
|
185
|
+
* have no pinner.
|
|
186
|
+
* @returns The pinner, or `null` if the runtime has none.
|
|
187
|
+
*/
|
|
188
|
+
function getRuntimePinner() {
|
|
189
|
+
runtimePinner ??= createRuntimePinner(detectRuntime());
|
|
190
|
+
return runtimePinner;
|
|
191
|
+
}
|
|
192
|
+
async function createRuntimePinner(runtime) {
|
|
193
|
+
switch (runtime) {
|
|
194
|
+
case "bun": return createBunPinner({ verifyAddress: assertPublicAddress });
|
|
195
|
+
case "node": return createNodePinner({
|
|
196
|
+
verifyAddress: assertPublicAddress,
|
|
197
|
+
undici: await import("undici")
|
|
198
|
+
});
|
|
199
|
+
default: return null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
export { getRuntimePinner as n, createBunPinner as r, detectRuntime as t };
|