refactor(port-utils): drop Bun.serve in favor of node:net probe
isPortAvailable() previously bound a one-shot Bun.serve and stopped it. That call was reachable from the plugin bundle through src/shared/index.ts barrel re-export and crashed on Electron. Switch to node:net.createServer().listen(port, host), which Bun fully implements as well. Adds a 2s safety timeout and removes both "error" and "listening" handlers on resolution to prevent listener leaks. Behavior is bit-equivalent: returns true iff a server can bind to (host, port) right now. Test file is fully rewritten away from stale Bun.serve mocking. New tests exercise: free-port detection via port 0, EADDRINUSE handling via a real net.createServer blocker, findAvailablePort range exhaustion, getAvailableServerPort auto-selection, 127.0.0.1 default hostname binding, and probe-server resource cleanup.
This commit is contained in:
+45
-10
@@ -1,18 +1,53 @@
|
||||
import { createServer } from "node:net"
|
||||
|
||||
const DEFAULT_SERVER_PORT = 4096
|
||||
const MAX_PORT_ATTEMPTS = 20
|
||||
const PORT_CHECK_TIMEOUT_MS = 2000
|
||||
|
||||
export async function isPortAvailable(port: number, hostname: string = "127.0.0.1"): Promise<boolean> {
|
||||
try {
|
||||
const server = Bun.serve({
|
||||
port,
|
||||
hostname,
|
||||
fetch: () => new Response(),
|
||||
return new Promise<boolean>((resolve) => {
|
||||
const server = createServer()
|
||||
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
||||
let resolved = false
|
||||
|
||||
const finish = (isAvailable: boolean): void => {
|
||||
if (resolved) {
|
||||
return
|
||||
}
|
||||
resolved = true
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId)
|
||||
}
|
||||
server.removeAllListeners("error")
|
||||
server.removeAllListeners("listening")
|
||||
resolve(isAvailable)
|
||||
}
|
||||
|
||||
const closeThenFinish = (isAvailable: boolean): void => {
|
||||
try {
|
||||
server.close(() => finish(isAvailable))
|
||||
} catch {
|
||||
finish(isAvailable)
|
||||
}
|
||||
}
|
||||
|
||||
timeoutId = setTimeout(() => {
|
||||
closeThenFinish(false)
|
||||
}, PORT_CHECK_TIMEOUT_MS)
|
||||
|
||||
server.once("error", () => {
|
||||
finish(false)
|
||||
})
|
||||
server.stop(true)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
server.once("listening", () => {
|
||||
closeThenFinish(true)
|
||||
})
|
||||
|
||||
try {
|
||||
server.listen(port, hostname)
|
||||
} catch {
|
||||
finish(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function findAvailablePort(
|
||||
|
||||
Reference in New Issue
Block a user