diff --git a/changelog.md b/changelog.md index d02bd0e..02c82bb 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2025-09-12 - 4.4.0 - feat(smartnetwork) +Add exclude option to findFreePort and skip excluded ports during search + +- Add an 'exclude' array to IFindFreePortOptions so callers can specify ports to ignore when searching for a free port. +- Respect excluded ports in findFreePort for both random (randomize=true) and sequential searches so excluded ports are never returned. +- Add .claude/settings.local.json to include local permissions used for development/CI helpers. + ## 2025-09-12 - 4.3.0 - feat(smartnetwork) Add randomizable port search and switch DNS resolution to @push.rocks/smartdns; export smartdns and update docs diff --git a/readme.md b/readme.md index 46dfe0a..575dbb6 100644 --- a/readme.md +++ b/readme.md @@ -99,6 +99,19 @@ const findFreePort = async () => { // Find a random free port in range (useful to avoid port conflicts) const randomPort = await network.findFreePort(3000, 3100, { randomize: true }); console.log(`🎲 Random free port: ${randomPort}`); + + // Exclude specific ports from the search + const portWithExclusions = await network.findFreePort(3000, 3100, { + exclude: [3000, 3001, 3005] // Skip these ports even if they're free + }); + console.log(`🚫 Free port (excluding specific ports): ${portWithExclusions}`); + + // Combine randomize with exclude options + const randomWithExclusions = await network.findFreePort(3000, 3100, { + randomize: true, + exclude: [3000, 3001, 3005] + }); + console.log(`🎲🚫 Random free port (with exclusions): ${randomWithExclusions}`); }; ``` @@ -333,6 +346,7 @@ interface SmartNetworkOptions { interface IFindFreePortOptions { randomize?: boolean; // If true, returns a random free port instead of the first one + exclude?: number[]; // Array of port numbers to exclude from the search } interface Hop { diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 16b7fce..40dfe7e 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartnetwork', - version: '4.3.0', + version: '4.4.0', description: 'A toolkit for network diagnostics including speed tests, port availability checks, and more.' } diff --git a/ts/smartnetwork.classes.smartnetwork.ts b/ts/smartnetwork.classes.smartnetwork.ts index 941e16b..598acb5 100644 --- a/ts/smartnetwork.classes.smartnetwork.ts +++ b/ts/smartnetwork.classes.smartnetwork.ts @@ -31,6 +31,8 @@ export interface Hop { export interface IFindFreePortOptions { /** If true, selects a random available port within the range instead of the first one */ randomize?: boolean; + /** Array of port numbers to exclude from the search */ + exclude?: number[]; } export class SmartNetwork { @@ -169,12 +171,20 @@ export class SmartNetwork { throw new NetworkError('Start port must be less than or equal to end port', 'EINVAL'); } + // Create a set of excluded ports for efficient lookup + const excludedPorts = new Set(options?.exclude || []); + // If randomize option is true, collect all available ports and select randomly if (options?.randomize) { const availablePorts: number[] = []; // Scan the range to find all available ports for (let port = startPort; port <= endPort; port++) { + // Skip excluded ports + if (excludedPorts.has(port)) { + continue; + } + const isUnused = await this.isLocalPortUnused(port); if (isUnused) { availablePorts.push(port); @@ -192,6 +202,11 @@ export class SmartNetwork { } else { // Default behavior: return the first available port (sequential search) for (let port = startPort; port <= endPort; port++) { + // Skip excluded ports + if (excludedPorts.has(port)) { + continue; + } + const isUnused = await this.isLocalPortUnused(port); if (isUnused) { return port;