@fedify/vocab-runtime 2.4.5-dev.2453 → 2.4.5
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-7TFr1l-z.cjs → body-BO79kuv1.cjs} +1 -1
- package/dist/tests/{body-BgPyhKpw.mjs → body-CUzCsDZ4.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-D89fiHaF.cjs → docloader-B79_YgeY.cjs} +111 -39
- package/dist/tests/{docloader-BlukyWHf.mjs → docloader-C5ZHGEfO.mjs} +106 -40
- 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-4hfHamVH.mjs → portable-media-BpVpe1gj.mjs} +3 -3
- package/dist/tests/{portable-media-DH6pp6-g.cjs → portable-media-C3yY0W5E.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--AMbrM1o.mjs → request-3AP-HBJ6.mjs} +1 -1
- package/dist/tests/{request-RH-Q21hI.cjs → request-BhopJTZd.cjs} +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
|
@@ -3,7 +3,7 @@ let node_process = require("node:process");
|
|
|
3
3
|
node_process = require_rolldown_runtime.__toESM(node_process, 1);
|
|
4
4
|
//#region deno.json
|
|
5
5
|
var name = "@fedify/vocab-runtime";
|
|
6
|
-
var version = "2.4.5
|
|
6
|
+
var version = "2.4.5";
|
|
7
7
|
//#endregion
|
|
8
8
|
//#region src/request.ts
|
|
9
9
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-emK7D4bc.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-BhopJTZd.cjs");
|
|
3
3
|
let node_test = require("node:test");
|
|
4
4
|
let node_process = require("node:process");
|
|
5
5
|
node_process = require_rolldown_runtime.__toESM(node_process, 1);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createActivityPubRequest, o as version, r as getUserAgent } from "./request
|
|
1
|
+
import { n as createActivityPubRequest, o as version, r as getUserAgent } from "./request-3AP-HBJ6.mjs";
|
|
2
2
|
import { test } from "node:test";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { deepStrictEqual } from "node:assert";
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-emK7D4bc.cjs");
|
|
2
|
+
const require_url = require("./url-Bdj-rTcC.cjs");
|
|
3
|
+
const require_pinners = require("./pinners-CJi9ZkYs.cjs");
|
|
4
|
+
let node_process = require("node:process");
|
|
5
|
+
node_process = require_rolldown_runtime.__toESM(node_process, 1);
|
|
6
|
+
let node_net = require("node:net");
|
|
7
|
+
let node_http = require("node:http");
|
|
8
|
+
let node_fs = require("node:fs");
|
|
9
|
+
let node_child_process = require("node:child_process");
|
|
10
|
+
let node_path = require("node:path");
|
|
11
|
+
let node_url = require("node:url");
|
|
12
|
+
//#region src/pinners/testdata/support.ts
|
|
13
|
+
/**
|
|
14
|
+
* Helpers shared by the pinner tests in *../\*.test.ts*. They start local
|
|
15
|
+
* servers and proxies, and run child processes.
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
const isBun = require_pinners.detectRuntime() === "bun";
|
|
19
|
+
const isNode = require_pinners.detectRuntime() === "node";
|
|
20
|
+
/** Pinner options that allow connecting to any address, e.g., loopback. */
|
|
21
|
+
const allowAll = { verifyAddress() {} };
|
|
22
|
+
const ipv4 = (address) => ({
|
|
23
|
+
address,
|
|
24
|
+
family: 4
|
|
25
|
+
});
|
|
26
|
+
const ipv6 = (address) => ({
|
|
27
|
+
address,
|
|
28
|
+
family: 6
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* Whether the error is the rejection of an address by `verifyAddress`.
|
|
32
|
+
* The Bun pinner throws the `UrlError` itself, and the Node.js pinner
|
|
33
|
+
* rejects with the `TypeError` of `fetch()`, caused by the `UrlError` that
|
|
34
|
+
* its lookup failed with.
|
|
35
|
+
*/
|
|
36
|
+
function isAddressRejection(error) {
|
|
37
|
+
return error instanceof require_url.UrlError || error instanceof TypeError && error.cause instanceof require_url.UrlError;
|
|
38
|
+
}
|
|
39
|
+
function readBody(req) {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
let body = "";
|
|
42
|
+
req.setEncoding("utf8");
|
|
43
|
+
req.on("data", (chunk) => body += chunk);
|
|
44
|
+
req.on("end", () => resolve(body));
|
|
45
|
+
req.on("error", reject);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async function listen(server, host = "127.0.0.1") {
|
|
49
|
+
await new Promise((resolve, reject) => {
|
|
50
|
+
server.once("error", reject);
|
|
51
|
+
server.listen(0, host, () => {
|
|
52
|
+
server.off("error", reject);
|
|
53
|
+
resolve();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
return server.address().port;
|
|
57
|
+
}
|
|
58
|
+
async function closeServer(server) {
|
|
59
|
+
server.closeAllConnections?.();
|
|
60
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
61
|
+
}
|
|
62
|
+
function canConnect(host, port) {
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
const socket = (0, node_net.connect)({
|
|
65
|
+
host,
|
|
66
|
+
port
|
|
67
|
+
});
|
|
68
|
+
const done = (result) => {
|
|
69
|
+
socket.destroy();
|
|
70
|
+
resolve(result);
|
|
71
|
+
};
|
|
72
|
+
socket.setTimeout(2e3, () => done(false));
|
|
73
|
+
socket.once("connect", () => done(true));
|
|
74
|
+
socket.once("error", () => done(false));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
let loopbackSupport;
|
|
78
|
+
/**
|
|
79
|
+
* Checks which loopback addresses the tests can use. Only 127.0.0.1 is
|
|
80
|
+
* assumed: other addresses of 127.0.0.0/8 are not configured on macOS, and
|
|
81
|
+
* IPv6 can be disabled.
|
|
82
|
+
*/
|
|
83
|
+
function getLoopbackSupport() {
|
|
84
|
+
loopbackSupport ??= (async () => {
|
|
85
|
+
const server = (0, node_net.createServer)((socket) => socket.destroy());
|
|
86
|
+
let port;
|
|
87
|
+
try {
|
|
88
|
+
port = await listen(server, "::");
|
|
89
|
+
} catch {
|
|
90
|
+
return {
|
|
91
|
+
ipv6: false,
|
|
92
|
+
dualStack: false
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const [v4, v6] = await Promise.all([canConnect("127.0.0.1", port), canConnect("::1", port)]);
|
|
97
|
+
return {
|
|
98
|
+
ipv6: v6,
|
|
99
|
+
dualStack: v4 && v6
|
|
100
|
+
};
|
|
101
|
+
} finally {
|
|
102
|
+
await closeServer(server);
|
|
103
|
+
}
|
|
104
|
+
})();
|
|
105
|
+
return loopbackSupport;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Starts an HTTP server that records the requests it receives.
|
|
109
|
+
*/
|
|
110
|
+
async function startRecordingServer(host = "127.0.0.1", respond = (_, res) => res.end("pinned")) {
|
|
111
|
+
const requests = [];
|
|
112
|
+
const server = (0, node_http.createServer)(async (req, res) => {
|
|
113
|
+
const body = await readBody(req);
|
|
114
|
+
requests.push({
|
|
115
|
+
method: req.method ?? "",
|
|
116
|
+
url: req.url ?? "",
|
|
117
|
+
host: req.headers.host,
|
|
118
|
+
localAddress: req.socket.localAddress,
|
|
119
|
+
body
|
|
120
|
+
});
|
|
121
|
+
respond(req, res);
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
server,
|
|
125
|
+
port: await listen(server, host),
|
|
126
|
+
requests
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Starts a TCP server that only counts the connections it accepts, and drops
|
|
131
|
+
* them right away. It stands for an HTTPS server without a certificate.
|
|
132
|
+
*/
|
|
133
|
+
async function startCountingServer() {
|
|
134
|
+
const counter = { connections: 0 };
|
|
135
|
+
const server = (0, node_net.createServer)((socket) => {
|
|
136
|
+
counter.connections++;
|
|
137
|
+
socket.destroy();
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
server,
|
|
141
|
+
port: await listen(server),
|
|
142
|
+
counter
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Starts a proxy that records the request targets it is asked for, and never
|
|
147
|
+
* forwards them. It answers a request in absolute form with `via-proxy`,
|
|
148
|
+
* and drops tunnels (`CONNECT`). It parses requests by itself since
|
|
149
|
+
* the `connect` event of `node:http` servers is not available on older Bun
|
|
150
|
+
* versions.
|
|
151
|
+
* @param options.refuseTunnels Whether to refuse tunnels with a `403`
|
|
152
|
+
* response instead of dropping them. Some
|
|
153
|
+
* clients retry dropped tunnels without end
|
|
154
|
+
* (e.g., the `EnvHttpProxyAgent` of Node.js),
|
|
155
|
+
* and some take the response of a refused
|
|
156
|
+
* tunnel for the response of the request
|
|
157
|
+
* (e.g., Bun 1.3.14).
|
|
158
|
+
*/
|
|
159
|
+
async function startRecordingProxy({ refuseTunnels = false } = {}) {
|
|
160
|
+
const targets = [];
|
|
161
|
+
const server = (0, node_net.createServer)((socket) => {
|
|
162
|
+
let head = "";
|
|
163
|
+
socket.setEncoding("latin1");
|
|
164
|
+
socket.on("error", () => {});
|
|
165
|
+
socket.on("data", (chunk) => {
|
|
166
|
+
if (head.includes("\r\n\r\n")) return;
|
|
167
|
+
head += chunk;
|
|
168
|
+
if (!head.includes("\r\n\r\n")) return;
|
|
169
|
+
const [method, target] = head.slice(0, head.indexOf("\r\n")).split(" ");
|
|
170
|
+
targets.push(`${method} ${target}`);
|
|
171
|
+
if (method !== "CONNECT") socket.end("HTTP/1.1 200 OK\r\nContent-Length: 9\r\nConnection: close\r\n\r\nvia-proxy");
|
|
172
|
+
else if (refuseTunnels) socket.end("HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\nConnection: close\r\n\r\n");
|
|
173
|
+
else socket.destroy();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
return {
|
|
177
|
+
server,
|
|
178
|
+
port: await listen(server),
|
|
179
|
+
targets
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Returns the first of the given paths that exists.
|
|
184
|
+
*/
|
|
185
|
+
function findFile(name, candidates) {
|
|
186
|
+
const found = candidates.find((path) => (0, node_fs.existsSync)(path));
|
|
187
|
+
if (found == null) throw new Error(`Cannot find ${name} in ${candidates.join(", ")}`);
|
|
188
|
+
return found;
|
|
189
|
+
}
|
|
190
|
+
function getModuleDirectory(moduleUrl) {
|
|
191
|
+
return typeof moduleUrl === "string" && moduleUrl.startsWith("file:") ? (0, node_path.dirname)((0, node_url.fileURLToPath)(moduleUrl)) : null;
|
|
192
|
+
}
|
|
193
|
+
const TESTDATA = [
|
|
194
|
+
"src",
|
|
195
|
+
"pinners",
|
|
196
|
+
"testdata"
|
|
197
|
+
];
|
|
198
|
+
/**
|
|
199
|
+
* Finds a source file in *testdata/* (e.g., a child script that Bun runs
|
|
200
|
+
* directly). When a test runs from the compiled *dist/tests/pinners/*
|
|
201
|
+
* instead of *src/pinners/*, the test data is not compiled with it, so this
|
|
202
|
+
* falls back to the files in the package: first relative to the test module,
|
|
203
|
+
* then relative to the working directory (the package root, where the Bun
|
|
204
|
+
* test script runs), in case `import.meta.url` is unavailable in a CommonJS
|
|
205
|
+
* build.
|
|
206
|
+
* @param name The file name.
|
|
207
|
+
* @param moduleUrl The `import.meta.url` of the test module.
|
|
208
|
+
*/
|
|
209
|
+
function resolveTestdata(name, moduleUrl) {
|
|
210
|
+
const candidates = [];
|
|
211
|
+
const here = getModuleDirectory(moduleUrl);
|
|
212
|
+
if (here != null) candidates.push((0, node_path.join)(here, TESTDATA[2], name), (0, node_path.join)(here, "..", "..", "..", ...TESTDATA, name));
|
|
213
|
+
candidates.push((0, node_path.join)(node_process.default.cwd(), ...TESTDATA, name));
|
|
214
|
+
return findFile(name, candidates);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Finds a child script of *testdata/* compiled into
|
|
218
|
+
* *dist/tests/pinners/testdata/* by *tsdown.config.ts*, for Node.js, which
|
|
219
|
+
* cannot run TypeScript sources without flags before 22.6.0. Node.js tests
|
|
220
|
+
* run only from *dist/tests/* (`cd dist/tests && node --test`), so this looks
|
|
221
|
+
* relative to the test module, then relative to the working directory, in
|
|
222
|
+
* case `import.meta.url` is unavailable in a CommonJS build.
|
|
223
|
+
* @param name The compiled file name, e.g., `node-child.mjs`.
|
|
224
|
+
* @param moduleUrl The `import.meta.url` of the test module.
|
|
225
|
+
*/
|
|
226
|
+
function resolveCompiledTestdata(name, moduleUrl) {
|
|
227
|
+
const candidates = [];
|
|
228
|
+
const here = getModuleDirectory(moduleUrl);
|
|
229
|
+
if (here != null) candidates.push((0, node_path.join)(here, TESTDATA[2], name));
|
|
230
|
+
candidates.push((0, node_path.join)(node_process.default.cwd(), ...TESTDATA.slice(1), name), (0, node_path.join)(node_process.default.cwd(), "dist", "tests", ...TESTDATA.slice(1), name));
|
|
231
|
+
return findFile(name, candidates);
|
|
232
|
+
}
|
|
233
|
+
const PROXY_ENV_NAMES = [
|
|
234
|
+
"HTTP_PROXY",
|
|
235
|
+
"HTTPS_PROXY",
|
|
236
|
+
"ALL_PROXY",
|
|
237
|
+
"NO_PROXY"
|
|
238
|
+
];
|
|
239
|
+
/**
|
|
240
|
+
* Runs a child script in a new process of the current runtime, without proxy
|
|
241
|
+
* environment variables (and `NODE_USE_ENV_PROXY`) but with the given ones,
|
|
242
|
+
* and parses the last line of its output as JSON.
|
|
243
|
+
* @param script The path of the script.
|
|
244
|
+
* @param args The arguments to the script.
|
|
245
|
+
* @param extraEnv The environment variables to set.
|
|
246
|
+
*/
|
|
247
|
+
function runChild(script, args, extraEnv) {
|
|
248
|
+
const env = { ...node_process.default.env };
|
|
249
|
+
for (const name of PROXY_ENV_NAMES) {
|
|
250
|
+
delete env[name];
|
|
251
|
+
delete env[name.toLowerCase()];
|
|
252
|
+
}
|
|
253
|
+
delete env.NODE_USE_ENV_PROXY;
|
|
254
|
+
Object.assign(env, extraEnv);
|
|
255
|
+
return new Promise((resolve, reject) => {
|
|
256
|
+
(0, node_child_process.execFile)(node_process.default.execPath, [script, ...args], {
|
|
257
|
+
env,
|
|
258
|
+
timeout: 3e4
|
|
259
|
+
}, (error, stdout, stderr) => {
|
|
260
|
+
if (error != null) {
|
|
261
|
+
reject(new Error(`${script} failed: ${stderr}`, { cause: error }));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const line = stdout.trim().split("\n").at(-1) ?? "";
|
|
265
|
+
try {
|
|
266
|
+
resolve(JSON.parse(line));
|
|
267
|
+
} catch (cause) {
|
|
268
|
+
reject(new Error(`Unexpected output: ${stdout}${stderr}`, { cause }));
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
//#endregion
|
|
274
|
+
Object.defineProperty(exports, "allowAll", {
|
|
275
|
+
enumerable: true,
|
|
276
|
+
get: function() {
|
|
277
|
+
return allowAll;
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
Object.defineProperty(exports, "closeServer", {
|
|
281
|
+
enumerable: true,
|
|
282
|
+
get: function() {
|
|
283
|
+
return closeServer;
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
Object.defineProperty(exports, "getLoopbackSupport", {
|
|
287
|
+
enumerable: true,
|
|
288
|
+
get: function() {
|
|
289
|
+
return getLoopbackSupport;
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
Object.defineProperty(exports, "ipv4", {
|
|
293
|
+
enumerable: true,
|
|
294
|
+
get: function() {
|
|
295
|
+
return ipv4;
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
Object.defineProperty(exports, "ipv6", {
|
|
299
|
+
enumerable: true,
|
|
300
|
+
get: function() {
|
|
301
|
+
return ipv6;
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
Object.defineProperty(exports, "isAddressRejection", {
|
|
305
|
+
enumerable: true,
|
|
306
|
+
get: function() {
|
|
307
|
+
return isAddressRejection;
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
Object.defineProperty(exports, "isBun", {
|
|
311
|
+
enumerable: true,
|
|
312
|
+
get: function() {
|
|
313
|
+
return isBun;
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
Object.defineProperty(exports, "isNode", {
|
|
317
|
+
enumerable: true,
|
|
318
|
+
get: function() {
|
|
319
|
+
return isNode;
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
Object.defineProperty(exports, "listen", {
|
|
323
|
+
enumerable: true,
|
|
324
|
+
get: function() {
|
|
325
|
+
return listen;
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
Object.defineProperty(exports, "resolveCompiledTestdata", {
|
|
329
|
+
enumerable: true,
|
|
330
|
+
get: function() {
|
|
331
|
+
return resolveCompiledTestdata;
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
Object.defineProperty(exports, "resolveTestdata", {
|
|
335
|
+
enumerable: true,
|
|
336
|
+
get: function() {
|
|
337
|
+
return resolveTestdata;
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
Object.defineProperty(exports, "runChild", {
|
|
341
|
+
enumerable: true,
|
|
342
|
+
get: function() {
|
|
343
|
+
return runChild;
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
Object.defineProperty(exports, "startCountingServer", {
|
|
347
|
+
enumerable: true,
|
|
348
|
+
get: function() {
|
|
349
|
+
return startCountingServer;
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
Object.defineProperty(exports, "startRecordingProxy", {
|
|
353
|
+
enumerable: true,
|
|
354
|
+
get: function() {
|
|
355
|
+
return startRecordingProxy;
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
Object.defineProperty(exports, "startRecordingServer", {
|
|
359
|
+
enumerable: true,
|
|
360
|
+
get: function() {
|
|
361
|
+
return startRecordingServer;
|
|
362
|
+
}
|
|
363
|
+
});
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { n as UrlError } from "./url-COj5Q8mb.mjs";
|
|
2
|
+
import { t as detectRuntime } from "./pinners-zSofbiy2.mjs";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { connect, createServer } from "node:net";
|
|
5
|
+
import { createServer as createServer$1 } from "node:http";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { execFile } from "node:child_process";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
//#region src/pinners/testdata/support.ts
|
|
11
|
+
/**
|
|
12
|
+
* Helpers shared by the pinner tests in *../\*.test.ts*. They start local
|
|
13
|
+
* servers and proxies, and run child processes.
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
const isBun = detectRuntime() === "bun";
|
|
17
|
+
const isNode = detectRuntime() === "node";
|
|
18
|
+
/** Pinner options that allow connecting to any address, e.g., loopback. */
|
|
19
|
+
const allowAll = { verifyAddress() {} };
|
|
20
|
+
const ipv4 = (address) => ({
|
|
21
|
+
address,
|
|
22
|
+
family: 4
|
|
23
|
+
});
|
|
24
|
+
const ipv6 = (address) => ({
|
|
25
|
+
address,
|
|
26
|
+
family: 6
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Whether the error is the rejection of an address by `verifyAddress`.
|
|
30
|
+
* The Bun pinner throws the `UrlError` itself, and the Node.js pinner
|
|
31
|
+
* rejects with the `TypeError` of `fetch()`, caused by the `UrlError` that
|
|
32
|
+
* its lookup failed with.
|
|
33
|
+
*/
|
|
34
|
+
function isAddressRejection(error) {
|
|
35
|
+
return error instanceof UrlError || error instanceof TypeError && error.cause instanceof UrlError;
|
|
36
|
+
}
|
|
37
|
+
function readBody(req) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
let body = "";
|
|
40
|
+
req.setEncoding("utf8");
|
|
41
|
+
req.on("data", (chunk) => body += chunk);
|
|
42
|
+
req.on("end", () => resolve(body));
|
|
43
|
+
req.on("error", reject);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async function listen(server, host = "127.0.0.1") {
|
|
47
|
+
await new Promise((resolve, reject) => {
|
|
48
|
+
server.once("error", reject);
|
|
49
|
+
server.listen(0, host, () => {
|
|
50
|
+
server.off("error", reject);
|
|
51
|
+
resolve();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
return server.address().port;
|
|
55
|
+
}
|
|
56
|
+
async function closeServer(server) {
|
|
57
|
+
server.closeAllConnections?.();
|
|
58
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
59
|
+
}
|
|
60
|
+
function canConnect(host, port) {
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
const socket = connect({
|
|
63
|
+
host,
|
|
64
|
+
port
|
|
65
|
+
});
|
|
66
|
+
const done = (result) => {
|
|
67
|
+
socket.destroy();
|
|
68
|
+
resolve(result);
|
|
69
|
+
};
|
|
70
|
+
socket.setTimeout(2e3, () => done(false));
|
|
71
|
+
socket.once("connect", () => done(true));
|
|
72
|
+
socket.once("error", () => done(false));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
let loopbackSupport;
|
|
76
|
+
/**
|
|
77
|
+
* Checks which loopback addresses the tests can use. Only 127.0.0.1 is
|
|
78
|
+
* assumed: other addresses of 127.0.0.0/8 are not configured on macOS, and
|
|
79
|
+
* IPv6 can be disabled.
|
|
80
|
+
*/
|
|
81
|
+
function getLoopbackSupport() {
|
|
82
|
+
loopbackSupport ??= (async () => {
|
|
83
|
+
const server = createServer((socket) => socket.destroy());
|
|
84
|
+
let port;
|
|
85
|
+
try {
|
|
86
|
+
port = await listen(server, "::");
|
|
87
|
+
} catch {
|
|
88
|
+
return {
|
|
89
|
+
ipv6: false,
|
|
90
|
+
dualStack: false
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const [v4, v6] = await Promise.all([canConnect("127.0.0.1", port), canConnect("::1", port)]);
|
|
95
|
+
return {
|
|
96
|
+
ipv6: v6,
|
|
97
|
+
dualStack: v4 && v6
|
|
98
|
+
};
|
|
99
|
+
} finally {
|
|
100
|
+
await closeServer(server);
|
|
101
|
+
}
|
|
102
|
+
})();
|
|
103
|
+
return loopbackSupport;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Starts an HTTP server that records the requests it receives.
|
|
107
|
+
*/
|
|
108
|
+
async function startRecordingServer(host = "127.0.0.1", respond = (_, res) => res.end("pinned")) {
|
|
109
|
+
const requests = [];
|
|
110
|
+
const server = createServer$1(async (req, res) => {
|
|
111
|
+
const body = await readBody(req);
|
|
112
|
+
requests.push({
|
|
113
|
+
method: req.method ?? "",
|
|
114
|
+
url: req.url ?? "",
|
|
115
|
+
host: req.headers.host,
|
|
116
|
+
localAddress: req.socket.localAddress,
|
|
117
|
+
body
|
|
118
|
+
});
|
|
119
|
+
respond(req, res);
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
server,
|
|
123
|
+
port: await listen(server, host),
|
|
124
|
+
requests
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Starts a TCP server that only counts the connections it accepts, and drops
|
|
129
|
+
* them right away. It stands for an HTTPS server without a certificate.
|
|
130
|
+
*/
|
|
131
|
+
async function startCountingServer() {
|
|
132
|
+
const counter = { connections: 0 };
|
|
133
|
+
const server = createServer((socket) => {
|
|
134
|
+
counter.connections++;
|
|
135
|
+
socket.destroy();
|
|
136
|
+
});
|
|
137
|
+
return {
|
|
138
|
+
server,
|
|
139
|
+
port: await listen(server),
|
|
140
|
+
counter
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Starts a proxy that records the request targets it is asked for, and never
|
|
145
|
+
* forwards them. It answers a request in absolute form with `via-proxy`,
|
|
146
|
+
* and drops tunnels (`CONNECT`). It parses requests by itself since
|
|
147
|
+
* the `connect` event of `node:http` servers is not available on older Bun
|
|
148
|
+
* versions.
|
|
149
|
+
* @param options.refuseTunnels Whether to refuse tunnels with a `403`
|
|
150
|
+
* response instead of dropping them. Some
|
|
151
|
+
* clients retry dropped tunnels without end
|
|
152
|
+
* (e.g., the `EnvHttpProxyAgent` of Node.js),
|
|
153
|
+
* and some take the response of a refused
|
|
154
|
+
* tunnel for the response of the request
|
|
155
|
+
* (e.g., Bun 1.3.14).
|
|
156
|
+
*/
|
|
157
|
+
async function startRecordingProxy({ refuseTunnels = false } = {}) {
|
|
158
|
+
const targets = [];
|
|
159
|
+
const server = createServer((socket) => {
|
|
160
|
+
let head = "";
|
|
161
|
+
socket.setEncoding("latin1");
|
|
162
|
+
socket.on("error", () => {});
|
|
163
|
+
socket.on("data", (chunk) => {
|
|
164
|
+
if (head.includes("\r\n\r\n")) return;
|
|
165
|
+
head += chunk;
|
|
166
|
+
if (!head.includes("\r\n\r\n")) return;
|
|
167
|
+
const [method, target] = head.slice(0, head.indexOf("\r\n")).split(" ");
|
|
168
|
+
targets.push(`${method} ${target}`);
|
|
169
|
+
if (method !== "CONNECT") socket.end("HTTP/1.1 200 OK\r\nContent-Length: 9\r\nConnection: close\r\n\r\nvia-proxy");
|
|
170
|
+
else if (refuseTunnels) socket.end("HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\nConnection: close\r\n\r\n");
|
|
171
|
+
else socket.destroy();
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
return {
|
|
175
|
+
server,
|
|
176
|
+
port: await listen(server),
|
|
177
|
+
targets
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Returns the first of the given paths that exists.
|
|
182
|
+
*/
|
|
183
|
+
function findFile(name, candidates) {
|
|
184
|
+
const found = candidates.find((path) => existsSync(path));
|
|
185
|
+
if (found == null) throw new Error(`Cannot find ${name} in ${candidates.join(", ")}`);
|
|
186
|
+
return found;
|
|
187
|
+
}
|
|
188
|
+
function getModuleDirectory(moduleUrl) {
|
|
189
|
+
return typeof moduleUrl === "string" && moduleUrl.startsWith("file:") ? dirname(fileURLToPath(moduleUrl)) : null;
|
|
190
|
+
}
|
|
191
|
+
const TESTDATA = [
|
|
192
|
+
"src",
|
|
193
|
+
"pinners",
|
|
194
|
+
"testdata"
|
|
195
|
+
];
|
|
196
|
+
/**
|
|
197
|
+
* Finds a source file in *testdata/* (e.g., a child script that Bun runs
|
|
198
|
+
* directly). When a test runs from the compiled *dist/tests/pinners/*
|
|
199
|
+
* instead of *src/pinners/*, the test data is not compiled with it, so this
|
|
200
|
+
* falls back to the files in the package: first relative to the test module,
|
|
201
|
+
* then relative to the working directory (the package root, where the Bun
|
|
202
|
+
* test script runs), in case `import.meta.url` is unavailable in a CommonJS
|
|
203
|
+
* build.
|
|
204
|
+
* @param name The file name.
|
|
205
|
+
* @param moduleUrl The `import.meta.url` of the test module.
|
|
206
|
+
*/
|
|
207
|
+
function resolveTestdata(name, moduleUrl) {
|
|
208
|
+
const candidates = [];
|
|
209
|
+
const here = getModuleDirectory(moduleUrl);
|
|
210
|
+
if (here != null) candidates.push(join(here, TESTDATA[2], name), join(here, "..", "..", "..", ...TESTDATA, name));
|
|
211
|
+
candidates.push(join(process.cwd(), ...TESTDATA, name));
|
|
212
|
+
return findFile(name, candidates);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Finds a child script of *testdata/* compiled into
|
|
216
|
+
* *dist/tests/pinners/testdata/* by *tsdown.config.ts*, for Node.js, which
|
|
217
|
+
* cannot run TypeScript sources without flags before 22.6.0. Node.js tests
|
|
218
|
+
* run only from *dist/tests/* (`cd dist/tests && node --test`), so this looks
|
|
219
|
+
* relative to the test module, then relative to the working directory, in
|
|
220
|
+
* case `import.meta.url` is unavailable in a CommonJS build.
|
|
221
|
+
* @param name The compiled file name, e.g., `node-child.mjs`.
|
|
222
|
+
* @param moduleUrl The `import.meta.url` of the test module.
|
|
223
|
+
*/
|
|
224
|
+
function resolveCompiledTestdata(name, moduleUrl) {
|
|
225
|
+
const candidates = [];
|
|
226
|
+
const here = getModuleDirectory(moduleUrl);
|
|
227
|
+
if (here != null) candidates.push(join(here, TESTDATA[2], name));
|
|
228
|
+
candidates.push(join(process.cwd(), ...TESTDATA.slice(1), name), join(process.cwd(), "dist", "tests", ...TESTDATA.slice(1), name));
|
|
229
|
+
return findFile(name, candidates);
|
|
230
|
+
}
|
|
231
|
+
const PROXY_ENV_NAMES = [
|
|
232
|
+
"HTTP_PROXY",
|
|
233
|
+
"HTTPS_PROXY",
|
|
234
|
+
"ALL_PROXY",
|
|
235
|
+
"NO_PROXY"
|
|
236
|
+
];
|
|
237
|
+
/**
|
|
238
|
+
* Runs a child script in a new process of the current runtime, without proxy
|
|
239
|
+
* environment variables (and `NODE_USE_ENV_PROXY`) but with the given ones,
|
|
240
|
+
* and parses the last line of its output as JSON.
|
|
241
|
+
* @param script The path of the script.
|
|
242
|
+
* @param args The arguments to the script.
|
|
243
|
+
* @param extraEnv The environment variables to set.
|
|
244
|
+
*/
|
|
245
|
+
function runChild(script, args, extraEnv) {
|
|
246
|
+
const env = { ...process.env };
|
|
247
|
+
for (const name of PROXY_ENV_NAMES) {
|
|
248
|
+
delete env[name];
|
|
249
|
+
delete env[name.toLowerCase()];
|
|
250
|
+
}
|
|
251
|
+
delete env.NODE_USE_ENV_PROXY;
|
|
252
|
+
Object.assign(env, extraEnv);
|
|
253
|
+
return new Promise((resolve, reject) => {
|
|
254
|
+
execFile(process.execPath, [script, ...args], {
|
|
255
|
+
env,
|
|
256
|
+
timeout: 3e4
|
|
257
|
+
}, (error, stdout, stderr) => {
|
|
258
|
+
if (error != null) {
|
|
259
|
+
reject(new Error(`${script} failed: ${stderr}`, { cause: error }));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const line = stdout.trim().split("\n").at(-1) ?? "";
|
|
263
|
+
try {
|
|
264
|
+
resolve(JSON.parse(line));
|
|
265
|
+
} catch (cause) {
|
|
266
|
+
reject(new Error(`Unexpected output: ${stdout}${stderr}`, { cause }));
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
//#endregion
|
|
272
|
+
export { ipv6 as a, isNode as c, resolveTestdata as d, runChild as f, startRecordingServer as h, ipv4 as i, listen as l, startRecordingProxy as m, closeServer as n, isAddressRejection as o, startCountingServer as p, getLoopbackSupport as r, isBun as s, allowAll as t, resolveCompiledTestdata as u };
|