import * as net from 'net' ; const MAX_PORT = 65535 ; const MIN_PORT = 1024 ; export class PortProvider { private reservedPorts = new Set < number > ( ) ; addReservedPort ( port ) { if ( this . reservedPorts . has ( port ) ) { throw new Error ( `Port ${ port } is already reserved` ) ; } this . reservedPorts . add ( port ) ; } async reserveFirstFreePort ( initialPort ) { for ( let port = initialPort ; port < 65536 ; port ++ ) { if ( ! this . reservedPorts . has ( port ) && ! ( await isPortUsed ( port ) ) ) { this . reservedPorts . add ( port ) ; return port ; } } throw new Error ( 'port not found' ) ; } async reserveNewPort ( ) { while ( true ) { const port = getRandomPortOver1023 ( ) ; if ( this . reservedPorts . has ( port ) ) { continue ; } if ( await isPortUsed ( port ) ) { continue ; } this . reservedPorts . add ( port ) ; return port ; } } } function getRandomPortOver1023 ( ) { return Math . floor ( Math . random ( ) * ( MAX_PORT + 1 - MIN_PORT ) + MIN_PORT ) ; } interface ServerError extends Error { code : string ; } export function isPortUsed ( port ) { return new Promise ( ( resolve , reject ) => { let isUsed = false ; const server = new net . Server ( ) ; server . on ( 'error' , ( error ) => { if ( error . code === 'EADDRINUSE' ) { isUsed = true ; } else { reject ( error ) ; } server . close ( ) ; } ) ; server . listen ( { host : 'localhost' , port , exclusive : true } , ( ) => { isUsed = false ; server . close ( ) ; } ) ; server . on ( 'close' , ( ) => { resolve ( isUsed ) ; } ) ; } ) ; }