49 lines
1.4 KiB
TypeScript
49 lines
1.4 KiB
TypeScript
import { spawn } from "node:child_process";
|
|||
|
|
import net from "node:net";
|
||
|
|
|
||
|
|
export function isPortFree(host: string, port: number): Promise<boolean> {
|
||
|
|
return new Promise((resolve) => {
|
||
|
|
const socket = net.connect({ host, port, timeout: 500 });
|
||
|
|
socket.once("connect", () => {
|
||
|
|
socket.destroy();
|
||
|
|
resolve(false);
|
||
|
|
});
|
||
|
|
socket.once("timeout", () => {
|
||
|
|
socket.destroy();
|
||
|
|
resolve(true);
|
||
|
|
});
|
||
|
|
socket.once("error", () => {
|
||
|
|
resolve(true);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function waitUntilReady(
|
||
|
|
url: string,
|
||
|
|
timeoutMs: number,
|
||
|
|
intervalMs = 400,
|
||
|
|
): Promise<boolean> {
|
||
|
|
const deadline = Date.now() + timeoutMs;
|
||
|
|
while (Date.now() < deadline) {
|
||
|
|
try {
|
||
|
|
const response = await fetch(url, { signal: AbortSignal.timeout(1500) });
|
||
|
|
if (response.status === 200) return true;
|
||
|
|
} catch {
|
||
|
|
// not ready yet
|
||
|
|
}
|
||
|
|
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function openBrowser(url: string): void {
|
||
|
|
const command =
|
||
|
|
process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
||
|
|
const args = process.platform === "win32" ? ["", url] : [url];
|
||
|
|
try {
|
||
|
|
spawn(command, args, { detached: true, stdio: "ignore", shell: process.platform === "win32" }).unref();
|
||
|
|
} catch {
|
||
|
|
// best-effort - not fatal if no browser opener is available
|
||
|
|
}
|
||
|
|
}
|