forked from ProverCoderAI/docker-git
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathports.ts
More file actions
86 lines (71 loc) · 2.32 KB
/
Copy pathports.ts
File metadata and controls
86 lines (71 loc) · 2.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import { createConnection, createServer } from "node:net"
const PORT_CHECK_TIMEOUT_MS = 500
const PORT_READY_TIMEOUT_MS = 30_000
const PORT_READY_POLL_MS = 200
const MAX_PORT_ATTEMPTS = 20
const delay = (ms: number): Promise<void> =>
new Promise((resolve) => {
setTimeout(resolve, ms)
})
export const isPortAvailable = (port: number): Promise<boolean> =>
new Promise((resolve, reject) => {
const server = createServer()
server.unref()
server.once("error", (error: NodeJS.ErrnoException) => {
if (error.code === "EADDRINUSE" || error.code === "EACCES") {
resolve(false)
return
}
reject(error)
})
server.listen(port, () => {
server.close(() => resolve(true))
})
})
export const findAvailablePort = async (preferredPort: number): Promise<number> => {
for (let offset = 0; offset < MAX_PORT_ATTEMPTS; offset += 1) {
const candidate = preferredPort + offset
if (await isPortAvailable(candidate)) {
return candidate
}
}
throw new Error(
`Could not find a free local port starting from ${preferredPort} after ${MAX_PORT_ATTEMPTS} attempts`,
)
}
const isPortReachable = (port: number): Promise<boolean> =>
new Promise((resolve) => {
const socket = createConnection({ host: "127.0.0.1", port })
socket.setTimeout(PORT_CHECK_TIMEOUT_MS)
const finalize = (result: boolean): void => {
socket.removeAllListeners()
socket.destroy()
resolve(result)
}
socket.once("connect", () => finalize(true))
socket.once("timeout", () => finalize(false))
socket.once("error", () => finalize(false))
})
export const waitForPort = async (
port: number,
options: {
readonly timeoutMs?: number
readonly pollMs?: number
readonly isCancelled?: () => boolean
} = {},
): Promise<void> => {
const timeoutMs = options.timeoutMs ?? PORT_READY_TIMEOUT_MS
const pollMs = options.pollMs ?? PORT_READY_POLL_MS
const isCancelled = options.isCancelled ?? (() => false)
const startedAt = Date.now()
while (Date.now() - startedAt < timeoutMs) {
if (isCancelled()) {
throw new Error(`Local dev server exited before port ${port} became ready`)
}
if (await isPortReachable(port)) {
return
}
await delay(pollMs)
}
throw new Error(`Timed out waiting for local dev server on port ${port}`)
}