Files
wehub-resource-sync 070959e133
landing-page-staging / Deploy landing page to staging (push) Has been skipped
landing-page-ci / Validate landing page (push) Failing after 4s
visual-baseline / Capture visual baselines (push) Has been cancelled
bake-plugin-previews / Bake plugin previews (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:00:47 +08:00

48 lines
1.5 KiB
TypeScript

import { createServer } from 'node:net';
import type { ToolsDevPortAllocation } from './types.ts';
export async function allocateToolsDevPorts(): Promise<ToolsDevPortAllocation> {
const [daemonPort, webPort] = await Promise.all([reserveFreePort(), reserveFreePort()]);
if (daemonPort.port === webPort.port) {
await Promise.all([daemonPort.release(), webPort.release()]);
return await allocateToolsDevPorts();
}
let released = false;
return {
daemonPort: daemonPort.port,
webPort: webPort.port,
async release() {
if (released) return;
released = true;
await Promise.all([daemonPort.release(), webPort.release()]);
},
};
}
async function reserveFreePort(): Promise<{ port: number; release: () => Promise<void> }> {
const server = createServer();
await new Promise<void>((resolveListen, rejectListen) => {
server.once('error', rejectListen);
server.listen(0, '127.0.0.1', () => resolveListen());
});
const address = server.address();
if (address == null || typeof address === 'string') {
await new Promise<void>((resolveClose, rejectClose) => {
server.close((error) => (error == null ? resolveClose() : rejectClose(error)));
});
throw new Error('failed to allocate a local TCP port');
}
let released = false;
return {
port: address.port,
async release() {
if (released) return;
released = true;
await new Promise<void>((resolveClose, rejectClose) => {
server.close((error) => (error == null ? resolveClose() : rejectClose(error)));
});
},
};
}