mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-11-01 13:58:18 +00:00
Run the CLI subprocess suites and benchmark tools as native TypeScript with node:test, and move them under test/e2e-cli. Update CI and Compose runners, run local smoke tests on Linux, macOS, and Windows, and collect source-mapped CLI coverage with c8.
60 lines
2.2 KiB
TypeScript
60 lines
2.2 KiB
TypeScript
import { createConnection, createServer } from "node:net";
|
|
|
|
type WaitForPortOptions = { timeoutMs?: number; intervalMs?: number; connectTimeoutMs?: number };
|
|
|
|
async function connectWithTimeout(hostname: string, port: number, timeoutMs: number): Promise<void> {
|
|
await new Promise<void>((resolve, reject) => {
|
|
const socket = createConnection({ host: hostname, port });
|
|
socket.setTimeout(timeoutMs, () => socket.destroy(new Error(`connect timeout after ${timeoutMs}ms`)));
|
|
socket.once("error", reject);
|
|
socket.once("connect", () => {
|
|
socket.destroy();
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
export async function waitForPort(hostname: string, port: number, options: WaitForPortOptions = {}): Promise<void> {
|
|
const timeoutMs = options.timeoutMs ?? 15000;
|
|
const intervalMs = options.intervalMs ?? 250;
|
|
const started = Date.now();
|
|
let lastError: unknown;
|
|
while (Date.now() - started < timeoutMs) {
|
|
try {
|
|
await connectWithTimeout(hostname, port, options.connectTimeoutMs ?? 1000);
|
|
return;
|
|
} catch (error) {
|
|
lastError = error;
|
|
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
}
|
|
}
|
|
throw new Error(
|
|
`Port ${hostname}:${port} did not become ready within ${timeoutMs}ms` +
|
|
(lastError ? ` (last error: ${String(lastError)})` : "")
|
|
);
|
|
}
|
|
|
|
export async function getFreePort(hostname = "127.0.0.1"): Promise<number> {
|
|
const server = createServer();
|
|
await new Promise<void>((resolve, reject) => {
|
|
server.once("error", reject);
|
|
server.listen(0, hostname, resolve);
|
|
});
|
|
const address = server.address();
|
|
await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
|
|
if (!address || typeof address === "string") throw new Error("The listener did not bind a TCP port");
|
|
return address.port;
|
|
}
|
|
|
|
export async function getOptimalLoopbackIp(): Promise<string> {
|
|
for (const hostname of ["127.0.0.1", "::1"]) {
|
|
try {
|
|
await getFreePort(hostname);
|
|
return hostname;
|
|
} catch {
|
|
/* Try the other loopback family. */
|
|
}
|
|
}
|
|
return "127.0.0.1";
|
|
}
|