Repository: unjs/get-port-please
Branch: main
Commit: 32a2f925b3f3
Files: 19
Total size: 35.4 KB
Directory structure:
gitextract_9ta45hy0/
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .prettierrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── eslint.config.mjs
├── package.json
├── renovate.json
├── src/
│ ├── _internal.ts
│ ├── get-port.ts
│ ├── index.ts
│ ├── socket.ts
│ ├── types.ts
│ └── unsafe-ports.ts
├── test/
│ ├── index.test.ts
│ ├── socket.test.ts
│ └── utils.ts
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/ci.yml
================================================
name: ci
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
ci:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- run: npm i -g -f corepack && corepack enable
- uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
- run: pnpm install
- run: pnpm lint
if: ${{ matrix.os != 'windows-latest' }}
- run: pnpm build
if: ${{ matrix.os != 'windows-latest' }}
- run: pnpm vitest --coverage
- uses: codecov/codecov-action@v5
================================================
FILE: .gitignore
================================================
.vscode
node_modules
*.log*
dist
coverage
================================================
FILE: .prettierrc
================================================
{}
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## v3.2.0
[compare changes](https://github.com/unjs/get-port-please/compare/v3.1.2...v3.2.0)
### 🚀 Enhancements
- Add unix domain socket utils ([#110](https://github.com/unjs/get-port-please/pull/110))
### 🩹 Fixes
- **validateHostname:** Add `169.254.0.0/16` range to not allowed hostnames ([#101](https://github.com/unjs/get-port-please/pull/101))
### 🏡 Chore
- Update repo ([17df943](https://github.com/unjs/get-port-please/commit/17df943))
- Update deps ([20d20ec](https://github.com/unjs/get-port-please/commit/20d20ec))
### ✅ Tests
- Mock `console.log` implementation in error tests ([#104](https://github.com/unjs/get-port-please/pull/104))
### 🤖 CI
- Fix corepack ([9590839](https://github.com/unjs/get-port-please/commit/9590839))
### ❤️ Contributors
- Pooya Parsa ([@pi0](https://github.com/pi0))
- João Carmona ([@jpsc](https://github.com/jpsc))
- Sam Bostock ([@sambostock](https://github.com/sambostock))
## v3.1.2
[compare changes](https://github.com/unjs/get-port-please/compare/v3.1.1...v3.1.2)
### 🩹 Fixes
- Accept ipv6 as valid hostname ([d537a51](https://github.com/unjs/get-port-please/commit/d537a51))
- Use closed ranges as port ranges ([#66](https://github.com/unjs/get-port-please/pull/66))
- Ranges now work even if port is unset in options ([#72](https://github.com/unjs/get-port-please/pull/72))
- Don't use random port by default if user specified any port ([#65](https://github.com/unjs/get-port-please/pull/65))
### 💅 Refactors
- Correct typos in `GetPortError` message ([#77](https://github.com/unjs/get-port-please/pull/77))
- Fix typo in error message ([#73](https://github.com/unjs/get-port-please/pull/73))
- Improve log when using alternative port ([#78](https://github.com/unjs/get-port-please/pull/78))
### 🏡 Chore
- Update fallback message ([1f45050](https://github.com/unjs/get-port-please/commit/1f45050))
- Update dependencies and lockfile ([62735f6](https://github.com/unjs/get-port-please/commit/62735f6))
### ✅ Tests
- Simplify ([9dccfd9](https://github.com/unjs/get-port-please/commit/9dccfd9))
- Update snapshot ([59cdd8c](https://github.com/unjs/get-port-please/commit/59cdd8c))
### ❤️ Contributors
- Pooya Parsa ([@pi0](http://github.com/pi0))
- Filip Joelsson <filip.gberg@gmail.com>
- Daniel Roe <daniel@roe.dev>
- Vladimir Vagaytsev ([@vvagaytsev](http://github.com/vvagaytsev))
- Yu Le <is.yuler@gmail.com>
## v3.1.1
[compare changes](https://github.com/unjs/get-port-please/compare/v3.1.0...v3.1.1)
### 🩹 Fixes
- Ignore ipv6 link-local addresses from hosts ([9e76e76](https://github.com/unjs/get-port-please/commit/9e76e76))
### ❤️ Contributors
- Pooya Parsa ([@pi0](http://github.com/pi0))
## v3.1.0
[compare changes](https://github.com/unjs/get-port-please/compare/v3.0.2...v3.1.0)
### 🚀 Enhancements
- Automatically fallback hostname for invalid hosts ([#62](https://github.com/unjs/get-port-please/pull/62))
### 🩹 Fixes
- Validate hostname and improve errors ([#59](https://github.com/unjs/get-port-please/pull/59))
- Skip all listen errors ([#61](https://github.com/unjs/get-port-please/pull/61))
### 💅 Refactors
- Split internals and types ([cf4317c](https://github.com/unjs/get-port-please/commit/cf4317c))
### 🏡 Chore
- Run tests on windows ([#60](https://github.com/unjs/get-port-please/pull/60))
- Use changelogen for releases ([7d4d6bb](https://github.com/unjs/get-port-please/commit/7d4d6bb))
- **release:** V3.0.3 ([86672c4](https://github.com/unjs/get-port-please/commit/86672c4))
### ✅ Tests
- Block host on all interfaces ([5a95184](https://github.com/unjs/get-port-please/commit/5a95184))
- Skip invalid host tests on windows ([e8b92ac](https://github.com/unjs/get-port-please/commit/e8b92ac))
### 🎨 Styles
- Lint ([1501204](https://github.com/unjs/get-port-please/commit/1501204))
### ❤️ Contributors
- Pooya Parsa ([@pi0](http://github.com/pi0))
## v3.0.3
[compare changes](https://github.com/unjs/get-port-please/compare/v3.0.2...v3.0.3)
### 🩹 Fixes
- Validate hostname and improve errors ([#59](https://github.com/unjs/get-port-please/pull/59))
- Skip all listen errors ([#61](https://github.com/unjs/get-port-please/pull/61))
### 💅 Refactors
- Split internals and types ([cf4317c](https://github.com/unjs/get-port-please/commit/cf4317c))
### 🏡 Chore
- Run tests on windows ([#60](https://github.com/unjs/get-port-please/pull/60))
- Use changelogen for releases ([7d4d6bb](https://github.com/unjs/get-port-please/commit/7d4d6bb))
### ✅ Tests
- Block host on all interfaces ([5a95184](https://github.com/unjs/get-port-please/commit/5a95184))
- Skip invalid host tests on windows ([e8b92ac](https://github.com/unjs/get-port-please/commit/e8b92ac))
### 🎨 Styles
- Lint ([1501204](https://github.com/unjs/get-port-please/commit/1501204))
### ❤️ Contributors
- Pooya Parsa ([@pi0](http://github.com/pi0))
### [3.0.2](https://github.com/unjs/get-port-please/compare/v3.0.1...v3.0.2) (2023-08-27)
### Bug Fixes
* use random port when `port: 0` is set ([081ed99](https://github.com/unjs/get-port-please/commit/081ed99661bb63e7910194e1c802ec0d33d75953))
### [3.0.1](https://github.com/unjs/get-port-please/compare/v3.0.0...v3.0.1) (2023-01-03)
### Bug Fixes
* use `process.env.PORT` as default value if `port` option is missing ([#27](https://github.com/unjs/get-port-please/issues/27)) ([57e1232](https://github.com/unjs/get-port-please/commit/57e1232274fe703d3b3039362d57c9897dae0e38))
## [3.0.0](https://github.com/unjs/get-port-please/compare/v2.6.1...v3.0.0) (2022-11-14)
### ⚠ BREAKING CHANGES
* no default value for `alternativePortRange` if explicit `port` provided
* drop memo support
### Bug Fixes
* no default value for `alternativePortRange` if explicit `port` provided ([00366ae](https://github.com/unjs/get-port-please/commit/00366aeae4226de8dd47532137dce39be6297050)), closes [#15](https://github.com/unjs/get-port-please/issues/15)
* drop memo support ([bc6f5c5](https://github.com/unjs/get-port-please/commit/bc6f5c5baa99b72fe3136230ba995e6df06b27e9))
### [2.6.1](https://github.com/unjs/get-port-please/compare/v2.6.0...v2.6.1) (2022-08-08)
### Bug Fixes
* ignore falsy `config.port` option ([ca34365](https://github.com/unjs/get-port-please/commit/ca343657a02fbfa97fbc0822a18b6cd2f3766032))
## [2.6.0](https://github.com/unjs/get-port-please/compare/v2.5.0...v2.6.0) (2022-08-08)
### Features
* alternative port and verbose logging ([49200e6](https://github.com/unjs/get-port-please/commit/49200e6df6ca6ba9283cc3e7162b44b5ef0906e6))
* verbose logging (opt-in) ([779f3b7](https://github.com/unjs/get-port-please/commit/779f3b763c2d9ff44bba5594f944b4019d95b2b5))
### Bug Fixes
* generate range before passing to findPort ([848a8c2](https://github.com/unjs/get-port-please/commit/848a8c2bb588431861a0d29b243aad6c024f48cf))
## [2.5.0](https://github.com/unjs/get-port-please/compare/v2.4.3...v2.5.0) (2022-04-11)
### Features
* `portRange` support ([44c8acd](https://github.com/unjs/get-port-please/commit/44c8acdc7a26de5b7308ee409543a7f9e8fdb275))
* ignore unsafe ports ([#8](https://github.com/unjs/get-port-please/issues/8)) ([dd29cbd](https://github.com/unjs/get-port-please/commit/dd29cbd174c6676869cfb114c33671126c48ce86))
### [2.4.3](https://github.com/unjs/get-port-please/compare/v2.4.2...v2.4.3) (2022-02-28)
### Bug Fixes
* **waitForPort:** check for all available hosts by default ([ba5f3b0](https://github.com/unjs/get-port-please/commit/ba5f3b0dfe80b016719be2349bbf62914f9905c8))
### [2.4.2](https://github.com/unjs/get-port-please/compare/v2.4.1...v2.4.2) (2022-02-25)
### Bug Fixes
* **waitForPort:** wait for delay ([01497a8](https://github.com/unjs/get-port-please/commit/01497a80943116ff9a82aa516509044ae2a7d979))
### [2.4.1](https://github.com/unjs/get-port-please/compare/v2.4.0...v2.4.1) (2022-02-25)
### Bug Fixes
* **getRandomPort:** host argument is optional ([7081383](https://github.com/unjs/get-port-please/commit/70813834ba289c631df379b0ddd34eccbe54b0d4))
## [2.4.0](https://github.com/unjs/get-port-please/compare/v2.3.0...v2.4.0) (2022-02-25)
### Features
* check port against all available hosts ([bac49cc](https://github.com/unjs/get-port-please/commit/bac49cc4de6aea681314c794284cca684c25e0fa))
* export `waitForPort` ([1c42832](https://github.com/unjs/get-port-please/commit/1c4283292353e908fa3049e0b3f60f23338c6cff))
## [2.3.0](https://github.com/unjs/get-port-please/compare/v2.2.0...v2.3.0) (2022-02-10)
### Features
* export `checkPort` utils ([#6](https://github.com/unjs/get-port-please/issues/6)) ([c248dff](https://github.com/unjs/get-port-please/commit/c248dff81a7eb3acc20f0277d1eae00130e43d0d))
## [2.2.0](https://github.com/unjs/get-port-please/compare/v2.1.0...v2.2.0) (2021-04-12)
### Features
* add `host` option ([#4](https://github.com/unjs/get-port-please/issues/4)) ([f85d941](https://github.com/unjs/get-port-please/commit/f85d94154e3832e3cf854c2d631329c29e71df92))
### Bug Fixes
* resolve defaults at `getPort` ([dcab479](https://github.com/unjs/get-port-please/commit/dcab4795d49184c7e4df115372f43e4eec52fbe3))
## [2.1.0](https://github.com/unjs/get-port-please/compare/v2.0.0...v2.1.0) (2020-12-04)
### Features
* allow config to be string or number ([13f4275](https://github.com/unjs/get-port-please/commit/13f4275e021fe1cd69c3ac775c284d92cd6c601d))
## [2.0.0](https://github.com/unjs/get-port-please/compare/v1.1.0...v2.0.0) (2020-12-04)
### ⚠ BREAKING CHANGES
* use named exports
### Features
* use named exports ([37e5aa2](https://github.com/unjs/get-port-please/commit/37e5aa2a485165c325f674951cef324889318304))
## [1.1.0](https://github.com/unjs/get-port-please/compare/v1.0.0...v1.1.0) (2020-11-16)
### Features
* update fs-memo and improve config handling ([5e4acee](https://github.com/unjs/get-port-please/commit/5e4acee1d7aa47c100815a25a43a508eafbacd6b))
## [1.0.0](https://github.com/unjs/get-port-please/compare/v0.0.6...v1.0.0) (2020-06-16)
### [0.0.6](https://github.com/unjs/get-port-please/compare/v0.0.5...v0.0.6) (2020-06-01)
### [0.0.5](https://github.com/unjs/get-port-please/compare/v0.0.4...v0.0.5) (2020-06-01)
### Bug Fixes
* name is not defined ([c1829f1](https://github.com/unjs/get-port-please/commit/c1829f12cfaf5304661ef16d744bbc66a2610a2d))
### [0.0.4](https://github.com/unjs/get-port-please/compare/v0.0.3...v0.0.4) (2020-06-01)
### Features
* name and random options ([ccea688](https://github.com/unjs/get-port-please/commit/ccea68889f440d0760412caff696dccfeac3144f))
### [0.0.3](https://github.com/unjs/get-port-please/compare/v0.0.2...v0.0.3) (2020-06-01)
### Bug Fixes
* **types:** use interface instead of type infering ([09135eb](https://github.com/unjs/get-port-please/commit/09135ebf0b7c96533b68cabdf8a9c512415e00b8))
### [0.0.2](https://github.com/unjs/get-port-please/compare/v0.0.1...v0.0.2) (2020-06-01)
### 0.0.1 (2020-05-31)
### Bug Fixes
* set version to 0 ([79c01ef](https://github.com/unjs/get-port-please/commit/79c01ef53e9425345bc0ec2cf58287b1fc940a7c))
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) Pooya Parsa <pooya@pi0.io>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# 🔌 get-port-please
Get an available TCP port to listen
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![License][license-src]][license-href]
[![JSDocs][jsdocs-src]][jsdocs-href]
## Usage
Install package:
```bash
npm i get-port-please
```
```js
// ESM
import {
getPort,
checkPort,
getRandomPort,
waitForPort,
getSocketAddress,
isSocketSupported,
cleanSocket
} from "get-port-please";
// CommonJS
const {
getPort,
checkPort,
getRandomPort,
waitForPort,
getSocketAddress,
isSocketSupported,
cleanSocket
} = require("get-port-please");
```
```
getPort(options: GetPortOptions): Promise<number>;
checkPort(port: number, host?: string): Promise<number | false>
waitForPort(port: number, options): Promise<number | false>
```
Try sequence is: port > ports > random
## Options
```ts
interface GetPortOptions {
name?: string;
random?: boolean;
port?: number;
portRange?: [fromInclusive: number, toInclusive: number];
ports?: number[];
host?: string;
memoDir?: string;
memoName?: string;
}
```
### `name`
Unique name for port memorizing. Default is `default`.
### `random`
If enabled, `port` and `ports` will be ignored. Default is `false`.
### `port`
First port to check. Default is `process.env.PORT || 3000`
### `ports`
Extended ports to check.
### `portRange`
Extended port range to check.
The range's start and end are **inclusive**, i.e. it is `[start, end]` in the mathematical notion.
Reversed port ranges are not supported. If `start > end`, then an empty range will be returned.
### `alternativePortRange`
Alternative port range to check as fallback when none of the ports are available.
The range's start and end are **inclusive**, i.e. it is `[start, end]` in the mathematical notion.
Reversed port ranges are not supported. If `start > end`, then an empty range will be returned.
The default range is `[3000, 3100]` (only when `port` is unspecified).
### `host`
The host to check. Default is `process.env.HOST` otherwise all available hosts will be checked.
## License
MIT
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/get-port-please?style=flat&colorA=18181B&colorB=F0DB4F
[npm-version-href]: https://npmjs.com/package/get-port-please
[npm-downloads-src]: https://img.shields.io/npm/dm/get-port-please?style=flat&colorA=18181B&colorB=F0DB4F
[npm-downloads-href]: https://npmjs.com/package/get-port-please
[codecov-src]: https://img.shields.io/codecov/c/gh/unjs/get-port-please/main?style=flat&colorA=18181B&colorB=F0DB4F
[codecov-href]: https://codecov.io/gh/unjs/get-port-please
[license-src]: https://img.shields.io/github/license/unjs/get-port-please.svg?style=flat&colorA=18181B&colorB=F0DB4F
[license-href]: https://github.com/unjs/get-port-please/blob/main/LICENSE
[jsdocs-src]: https://img.shields.io/badge/jsDocs.io-reference-18181B?style=flat&colorA=18181B&colorB=F0DB4F
[jsdocs-href]: https://www.jsdocs.io/package/get-port-please
================================================
FILE: eslint.config.mjs
================================================
import unjs from "eslint-config-unjs";
// https://github.com/unjs/eslint-config
export default unjs({
ignores: [],
rules: {},
});
================================================
FILE: package.json
================================================
{
"name": "get-port-please",
"version": "3.2.0",
"description": "Get an available TCP port to listen",
"repository": "unjs/get-port-please",
"license": "MIT",
"exports": {
".": {
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.cjs",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "unbuild",
"dev": "vitest",
"lint": "eslint . && prettier -c src test",
"lint:fix": "eslint --fix . && prettier -w src test",
"prepack": "unbuild",
"release": "pnpm test && pnpm build && changelogen --release --push && pnpm publish",
"test": "pnpm lint && vitest run"
},
"devDependencies": {
"@types/node": "^24.3.0",
"@vitest/coverage-v8": "^3.2.4",
"changelogen": "^0.6.2",
"eslint": "^9.34.0",
"eslint-config-unjs": "^0.5.0",
"jiti": "^2.5.1",
"prettier": "^3.6.2",
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"vitest": "^3.2.4"
},
"packageManager": "pnpm@10.15.1"
}
================================================
FILE: renovate.json
================================================
{
"extends": ["github>unjs/renovate-config"]
}
================================================
FILE: src/_internal.ts
================================================
import { createServer, AddressInfo } from "node:net";
import { networkInterfaces } from "node:os";
import { isSafePort } from "./unsafe-ports";
import type { PortNumber, HostAddress } from "./types";
export class GetPortError extends Error {
name = "GetPortError";
constructor(
public message: string,
opts?: any,
) {
super(message, opts);
}
}
export function _log(verbose: boolean, message: string) {
if (verbose) {
console.log(`[get-port] ${message}`);
}
}
export function _generateRange(from: number, to: number): number[] {
if (to < from) {
return [];
}
const r = [];
for (let index = from; index <= to; index++) {
r.push(index);
}
return r;
}
export function _tryPort(
port: PortNumber,
host: HostAddress,
): Promise<PortNumber | false> {
return new Promise((resolve) => {
const server = createServer();
server.unref();
server.on("error", () => {
resolve(false);
});
server.listen({ port, host }, () => {
const { port } = server.address() as AddressInfo;
server.close(() => {
resolve(isSafePort(port) && port);
});
});
});
}
export function _getLocalHosts(additional: HostAddress[]): HostAddress[] {
const hosts = new Set<HostAddress>(additional);
for (const _interface of Object.values(networkInterfaces())) {
for (const config of _interface || []) {
if (
config.address &&
!config.internal &&
!config.address.startsWith("fe80::") && // Link-Local
!config.address.startsWith("169.254") // reserved for Automatic Private IP Addressing
) {
hosts.add(config.address);
}
}
}
return [...hosts];
}
export async function _findPort(
ports: number[],
host: HostAddress,
): Promise<PortNumber> {
for (const port of ports) {
const r = await _tryPort(port, host);
if (r) {
return r;
}
}
}
export function _fmtOnHost(hostname: string | undefined) {
return hostname ? `on host ${JSON.stringify(hostname)}` : "on any host";
}
const HOSTNAME_RE = /^(?!-)[\d.:A-Za-z-]{1,63}(?<!-)$/;
export function _validateHostname(
hostname: string | undefined,
_public: boolean,
verbose: boolean,
) {
if (hostname && !HOSTNAME_RE.test(hostname)) {
const fallbackHost = _public ? "0.0.0.0" : "127.0.0.1";
_log(
verbose,
`Invalid hostname: ${JSON.stringify(hostname)}. Using ${JSON.stringify(
fallbackHost,
)} as fallback.`,
);
return fallbackHost;
}
return hostname;
}
================================================
FILE: src/get-port.ts
================================================
import { isSafePort } from "./unsafe-ports";
import type {
GetPortInput,
PortNumber,
GetPortOptions,
HostAddress,
WaitForPortOptions,
} from "./types";
import {
GetPortError,
_findPort,
_fmtOnHost,
_generateRange,
_getLocalHosts,
_tryPort,
_log,
_validateHostname,
} from "./_internal";
export async function getPort(
_userOptions: GetPortInput = {},
): Promise<PortNumber> {
if (typeof _userOptions === "number" || typeof _userOptions === "string") {
_userOptions = { port: Number.parseInt(_userOptions + "") || 0 };
}
const _port = Number(_userOptions.port ?? process.env.PORT);
const _userSpecifiedAnyPort = Boolean(
_userOptions.port ||
_userOptions.ports?.length ||
_userOptions.portRange?.length,
);
const options = {
name: "default",
random: _port === 0,
ports: [],
portRange: [],
alternativePortRange: _userSpecifiedAnyPort ? [] : [3000, 3100],
verbose: false,
..._userOptions,
port: _port,
host: _validateHostname(
_userOptions.host ?? process.env.HOST,
_userOptions.public,
_userOptions.verbose,
),
} as GetPortOptions;
if (options.random && !_userSpecifiedAnyPort) {
return getRandomPort(options.host);
}
// Generate list of ports to check
const portsToCheck: PortNumber[] = [
options.port,
...options.ports,
..._generateRange(...options.portRange),
].filter((port) => {
if (!port) {
return false;
}
if (!isSafePort(port)) {
_log(options.verbose, `Ignoring unsafe port: ${port}`);
return false;
}
return true;
});
if (portsToCheck.length === 0) {
portsToCheck.push(3000);
}
// Try to find a port
let availablePort = await _findPort(portsToCheck, options.host);
// Try fallback port range
if (!availablePort && options.alternativePortRange.length > 0) {
availablePort = await _findPort(
_generateRange(...options.alternativePortRange),
options.host,
);
if (portsToCheck.length > 0) {
let message = `Unable to find an available port (tried ${portsToCheck.join(
"-",
)} ${_fmtOnHost(options.host)}).`;
if (availablePort) {
message += ` Using alternative port ${availablePort}.`;
}
_log(options.verbose, message);
}
}
// Try random port
if (!availablePort && _userOptions.random !== false) {
availablePort = await getRandomPort(options.host);
if (availablePort) {
_log(options.verbose, `Using random port ${availablePort}`);
}
}
// Throw error if no port is available
if (!availablePort) {
const triedRanges = [
options.port,
options.portRange.join("-"),
options.alternativePortRange.join("-"),
]
.filter(Boolean)
.join(", ");
throw new GetPortError(
`Unable to find an available port ${_fmtOnHost(
options.host,
)} (tried ${triedRanges})`,
);
}
return availablePort;
}
export async function getRandomPort(host?: HostAddress) {
const port = await checkPort(0, host);
if (port === false) {
throw new GetPortError(`Unable to find a random port ${_fmtOnHost(host)}`);
}
return port;
}
export async function waitForPort(
port: PortNumber,
options: WaitForPortOptions = {},
) {
const delay = options.delay || 500;
const retries = options.retries || 4;
for (let index = retries; index > 0; index--) {
if ((await _tryPort(port, options.host)) === false) {
return;
}
await new Promise((resolve) => setTimeout(resolve, delay));
}
throw new GetPortError(
`Timeout waiting for port ${port} after ${retries} retries with ${delay}ms interval.`,
);
}
export async function checkPort(
port: PortNumber,
host: HostAddress | HostAddress[] = process.env.HOST,
verbose?: boolean,
): Promise<PortNumber | false> {
if (!host) {
host = _getLocalHosts([undefined /* default */, "0.0.0.0"]);
}
if (!Array.isArray(host)) {
return _tryPort(port, host);
}
for (const _host of host) {
const _port = await _tryPort(port, _host);
if (_port === false) {
if (port < 1024 && verbose) {
_log(
verbose,
`Unable to listen to the privileged port ${port} ${_fmtOnHost(
_host,
)}`,
);
}
return false;
}
if (port === 0 && _port !== 0) {
port = _port;
}
}
return port;
}
================================================
FILE: src/index.ts
================================================
export * from "./types";
export * from "./get-port";
export * from "./unsafe-ports";
export * from "./socket";
================================================
FILE: src/socket.ts
================================================
import { tmpdir } from "node:os";
import { join } from "node:path";
import { Server } from "node:net";
import { rm } from "node:fs/promises";
export interface GetSocketOptions {
/* Human readable prefix for the socket name */
name: string;
/**
* Use process ID in the socket name.
*/
pid?: boolean;
/**
* Use a random number in the socket name.
*
*/
random?: boolean;
}
let _nodeMajorVersion: number | undefined;
let _isSocketSupported: boolean | undefined;
/**
* Generates a socket address based on the provided options.
*/
export function getSocketAddress(opts: GetSocketOptions): string {
const parts = [
opts.name,
opts.pid ? process.pid : undefined,
opts.random ? Math.round(Math.random() * 10_000) : undefined,
].filter(Boolean);
const socketName = `${parts.join("-")}.sock`;
// Windows: pipe
if (process.platform === "win32") {
return join(String.raw`\\.\pipe`, socketName);
}
// Linux: abstract namespace
// Fallback to a regular file socket on older Node.js versions to avoid issues
if (process.platform === "linux") {
if (_nodeMajorVersion === undefined) {
_nodeMajorVersion = +process.versions.node.split(".")[0];
}
if (_nodeMajorVersion >= 20) {
return `\0${socketName}`;
}
}
// Unix socket
return join(tmpdir(), socketName);
}
/**
* Test if the current environment supports sockets.
*/
export async function isSocketSupported(): Promise<boolean> {
if (_isSocketSupported !== undefined) {
return _isSocketSupported;
}
if (globalThis.process?.versions?.webcontainer) {
// Seems broken: https://stackblitz.com/edit/stackblitz-starters-1y68uhvu
return false;
}
const socketAddress = getSocketAddress({ name: "get-port", random: true });
const server = new Server();
try {
await new Promise<void>((resolve, reject) => {
server.on("error", reject);
server.listen(socketAddress, resolve);
});
_isSocketSupported = true;
return true;
} catch {
_isSocketSupported = false;
return false;
} finally {
if (server.listening) {
server.close();
await cleanSocket(socketAddress);
}
}
}
export async function cleanSocket(path: string): Promise<void> {
if (!path || path[0] === "\0" || path.startsWith(String.raw`\\.\pipe`)) {
// Abstract namespace sockets or invalid paths
return;
}
return rm(path, { force: true, recursive: true }).catch(console.error);
}
================================================
FILE: src/types.ts
================================================
export interface GetPortOptions {
name: string;
random: boolean;
port: number;
ports: number[];
portRange: [fromInclusive: number, toInclusive: number];
alternativePortRange: [fromInclusive: number, toInclusive: number];
host: string;
verbose?: boolean;
public?: boolean;
}
export interface WaitForPortOptions {
host?: HostAddress;
delay?: number;
retries?: number;
}
export type GetPortInput = Partial<GetPortOptions> | number | string;
export type HostAddress = undefined | string;
export type PortNumber = number;
================================================
FILE: src/unsafe-ports.ts
================================================
// https://chromium.googlesource.com/chromium/src.git/+/refs/heads/master/net/base/port_util.cc
const unsafePorts = new Set([
1, // tcpmux
7, // echo
9, // discard
11, // systat
13, // daytime
15, // netstat
17, // qotd
19, // chargen
20, // ftp data
21, // ftp access
22, // ssh
23, // telnet
25, // smtp
37, // time
42, // name
43, // nicname
53, // domain
69, // tftp
77, // priv-rjs
79, // finger
87, // ttylink
95, // supdup
101, // hostriame
102, // iso-tsap
103, // gppitnp
104, // acr-nema
109, // pop2
110, // pop3
111, // sunrpc
113, // auth
115, // sftp
117, // uucp-path
119, // nntp
123, // NTP
135, // loc-srv /epmap
137, // netbios
139, // netbios
143, // imap2
161, // snmp
179, // BGP
389, // ldap
427, // SLP (Also used by Apple Filing Protocol)
465, // smtp+ssl
512, // print / exec
513, // login
514, // shell
515, // printer
526, // tempo
530, // courier
531, // chat
532, // netnews
540, // uucp
548, // AFP (Apple Filing Protocol)
554, // rtsp
556, // remotefs
563, // nntp+ssl
587, // smtp (rfc6409)
601, // syslog-conn (rfc3195)
636, // ldap+ssl
989, // ftps-data
990, // ftps
993, // ldap+ssl
995, // pop3+ssl
1719, // h323gatestat
1720, // h323hostcall
1723, // pptp
2049, // nfs
3659, // apple-sasl / PasswordServer
4045, // lockd
5060, // sip
5061, // sips
6000, // X11
6566, // sane-port
6665, // Alternate IRC [Apple addition]
6666, // Alternate IRC [Apple addition]
6667, // Standard IRC [Apple addition]
6668, // Alternate IRC [Apple addition]
6669, // Alternate IRC [Apple addition]
6697, // IRC + TLS
10_080, // Amanda
]);
export function isUnsafePort(port: number) {
return unsafePorts.has(port);
}
export function isSafePort(port: number) {
return !isUnsafePort(port);
}
================================================
FILE: test/index.test.ts
================================================
import { Server } from "node:net";
import { networkInterfaces } from "node:os";
import { describe, test, expect, beforeEach, afterEach, vi } from "vitest";
import { getPort, getRandomPort } from "../src";
import { _generateRange, _getLocalHosts } from "../src/_internal";
import { blockPort } from "./utils";
const isWindows = process.platform === "win32";
describe("getPort", () => {
let portBlocker: Server;
afterEach(() => {
portBlocker?.close();
});
describe("default host", () => {
test("default port is not in use", async () => {
const port = await getPort();
expect(port).toEqual(3000);
});
test("default port is in use", async () => {
portBlocker = await blockPort(3000);
const port = await getPort();
expect(port).toEqual(3001);
});
});
describe("order", () => {
test("ports is preferred", async () => {
const port = await getPort({ ports: [8080] });
expect(port).toEqual(8080);
});
test("portRange is preferred over random", async () => {
const port = await getPort({ random: true, portRange: [8081, 8085] });
expect(port).toEqual(8081);
});
});
describe("localhost", () => {
test("default port is not in use", async () => {
const port = await getPort({ host: "localhost" });
expect(port).toEqual(3000);
});
test("default port is in use", async () => {
process.env.HOST = "localhost";
portBlocker = await blockPort(3000, "localhost");
const port1 = await getPort({ port: 3000, portRange: [3000, 3100] });
expect(port1).toEqual(3001);
const port2 = await getPort();
expect(port2).toEqual(3001);
const port3 = await getPort(3000);
expect(port3).not.toEqual(3001);
});
});
describe("ipv6", () => {
test("get port on ::1", async () => {
await blockPort(3000, "::1");
const port = await getPort({ host: "::1" });
expect(port).not.toBe(3000);
});
});
});
describe("random port", () => {
test("{ random: true }", async () => {
const port = await getPort({ random: true });
expect(typeof port).toBe("number");
expect(port).not.toBe(3000);
});
test("getRandomPort", async () => {
const port = await getRandomPort();
expect(typeof port).toBe("number");
expect(port).not.toBe(3000);
});
test("{ port: 0 }", async () => {
let port = await getPort({ port: 0 });
expect(typeof port).toBe("number");
expect(port).not.toBe(3000);
port = await getPort({ port: "0" as any });
expect(typeof port).toBe("number");
expect(port).not.toBe(3000);
});
});
describe("errors", () => {
beforeEach(() => {
vi.spyOn(console, "log").mockImplementation(() => undefined);
});
afterEach(() => {
vi.resetAllMocks();
});
test("invalid hostname", async () => {
await getPort({ host: "http://localhost:8080", verbose: true }).catch(
(error) => error,
);
expect(console.log).toHaveBeenCalledWith(
'[get-port] Invalid hostname: "http://localhost:8080". Using "127.0.0.1" as fallback.',
);
});
test("invalid hostname (public)", async () => {
await getPort({
host: "http://localhost:8080",
verbose: true,
public: true,
}).catch((error) => error);
expect(console.log).toHaveBeenCalledWith(
'[get-port] Invalid hostname: "http://localhost:8080". Using "0.0.0.0" as fallback.',
);
});
test.skipIf(isWindows)("unavailable port", async () => {
const error = await getPort({
host: "192.168.1.999",
}).catch((error) => error);
expect(error.toString()).toMatchInlineSnapshot(
`"GetPortError: Unable to find a random port on host "192.168.1.999""`,
);
});
test.skipIf(isWindows)("unavailable port (no random)", async () => {
const error = await getPort({
host: "192.168.1.999",
random: false,
}).catch((error) => error);
expect(error.toString()).toMatchInlineSnapshot(
`"GetPortError: Unable to find an available port on host "192.168.1.999" (tried 3000-3100)"`,
);
});
});
describe("internal tools", () => {
describe("_generateRange", () => {
test("returns a normal range [from, to) if from < to", () => {
const from = 1;
const to = 5;
const range = _generateRange(from, to);
expect(range).to.eql([1, 2, 3, 4, 5]);
});
test("returns a singleton array if from = to", () => {
const from = 1;
const to = 1;
const range = _generateRange(from, to);
expect(range).to.eql([1]);
});
test("returns an empty array if from > to", () => {
const from = 5;
const to = 1;
const range = _generateRange(from, to);
expect(range).to.eql([]);
});
});
});
vi.mock("node:os", () => {
return {
networkInterfaces: vi.fn(),
};
});
describe("_getLocalHosts", () => {
test("should return the allowed host addresses", () => {
vi.mocked(networkInterfaces).mockImplementation(() => ({
eth0: [
{
address: "192.168.1.100",
family: "IPv4",
internal: false,
netmask: "0",
mac: "0",
cidr: "",
},
{
address: "fe80::1",
family: "IPv6",
internal: false,
scopeid: 1,
netmask: "0",
mac: "0",
cidr: "",
},
],
lo: [
{
address: "127.0.0.1",
family: "IPv4",
internal: true,
netmask: "0",
mac: "0",
cidr: "",
},
{
address: "169.254.0.1",
family: "IPv4",
internal: false,
netmask: "0",
mac: "0",
cidr: "",
},
],
}));
// call the function with additional hosts
const additionalHosts = ["192.168.1.200"];
const result = _getLocalHosts(additionalHosts);
expect(result).toEqual(["192.168.1.200", "192.168.1.100"]);
vi.clearAllMocks();
});
});
================================================
FILE: test/socket.test.ts
================================================
import { describe, test, expect } from "vitest";
import { isSocketSupported, getSocketAddress } from "../src";
describe("socket", () => {
test("isSocketSupported", async () => {
expect(await isSocketSupported()).toBe(true);
});
test("getSocketAddress", async () => {
const addr = getSocketAddress({ name: "test", pid: true, random: true });
expect(addr).toMatch(/test-\d+-\d+\.sock/);
});
});
================================================
FILE: test/utils.ts
================================================
import { createServer, Server } from "node:net";
export function blockPort(port: number, host?: string): Promise<Server> {
return new Promise((resolve) => {
const blocker = createServer();
blocker.listen(port, host, () => {
resolve(blocker);
});
});
}
export async function blockPorts(
ports: number[],
host?: string,
): Promise<Server[]> {
const portBlockers: Server[] = [];
for (const port of ports) {
const blocker = await blockPort(port, host);
portBlockers.push(blocker);
}
return portBlockers;
}
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"esModuleInterop": true
},
"include": ["src"]
}
gitextract_9ta45hy0/ ├── .github/ │ └── workflows/ │ └── ci.yml ├── .gitignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── eslint.config.mjs ├── package.json ├── renovate.json ├── src/ │ ├── _internal.ts │ ├── get-port.ts │ ├── index.ts │ ├── socket.ts │ ├── types.ts │ └── unsafe-ports.ts ├── test/ │ ├── index.test.ts │ ├── socket.test.ts │ └── utils.ts └── tsconfig.json
SYMBOL INDEX (27 symbols across 6 files)
FILE: src/_internal.ts
class GetPortError (line 6) | class GetPortError extends Error {
method constructor (line 8) | constructor(
function _log (line 16) | function _log(verbose: boolean, message: string) {
function _generateRange (line 22) | function _generateRange(from: number, to: number): number[] {
function _tryPort (line 33) | function _tryPort(
function _getLocalHosts (line 52) | function _getLocalHosts(additional: HostAddress[]): HostAddress[] {
function _findPort (line 69) | async function _findPort(
function _fmtOnHost (line 81) | function _fmtOnHost(hostname: string | undefined) {
constant HOSTNAME_RE (line 85) | const HOSTNAME_RE = /^(?!-)[\d.:A-Za-z-]{1,63}(?<!-)$/;
function _validateHostname (line 87) | function _validateHostname(
FILE: src/get-port.ts
function getPort (line 22) | async function getPort(
function getRandomPort (line 123) | async function getRandomPort(host?: HostAddress) {
function waitForPort (line 131) | async function waitForPort(
function checkPort (line 148) | async function checkPort(
FILE: src/socket.ts
type GetSocketOptions (line 6) | interface GetSocketOptions {
function getSocketAddress (line 28) | function getSocketAddress(opts: GetSocketOptions): string {
function isSocketSupported (line 60) | async function isSocketSupported(): Promise<boolean> {
function cleanSocket (line 89) | async function cleanSocket(path: string): Promise<void> {
FILE: src/types.ts
type GetPortOptions (line 1) | interface GetPortOptions {
type WaitForPortOptions (line 13) | interface WaitForPortOptions {
type GetPortInput (line 19) | type GetPortInput = Partial<GetPortOptions> | number | string;
type HostAddress (line 21) | type HostAddress = undefined | string;
type PortNumber (line 23) | type PortNumber = number;
FILE: src/unsafe-ports.ts
function isUnsafePort (line 86) | function isUnsafePort(port: number) {
function isSafePort (line 90) | function isSafePort(port: number) {
FILE: test/utils.ts
function blockPort (line 3) | function blockPort(port: number, host?: string): Promise<Server> {
function blockPorts (line 12) | async function blockPorts(
Condensed preview — 19 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (39K chars).
[
{
"path": ".github/workflows/ci.yml",
"chars": 653,
"preview": "name: ci\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\n\njobs:\n ci:\n runs-on: ${"
},
{
"path": ".gitignore",
"chars": 42,
"preview": ".vscode\nnode_modules\n*.log*\ndist\ncoverage\n"
},
{
"path": ".prettierrc",
"chars": 3,
"preview": "{}\n"
},
{
"path": "CHANGELOG.md",
"chars": 11183,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github."
},
{
"path": "LICENSE",
"chars": 1078,
"preview": "MIT License\n\nCopyright (c) Pooya Parsa <pooya@pi0.io>\n\nPermission is hereby granted, free of charge, to any person obtai"
},
{
"path": "README.md",
"chars": 3006,
"preview": "# 🔌 get-port-please\n\nGet an available TCP port to listen\n\n[![npm version][npm-version-src]][npm-version-href]\n[![npm dow"
},
{
"path": "eslint.config.mjs",
"chars": 134,
"preview": "import unjs from \"eslint-config-unjs\";\n\n// https://github.com/unjs/eslint-config\nexport default unjs({\n ignores: [],\n "
},
{
"path": "package.json",
"chars": 1075,
"preview": "{\n \"name\": \"get-port-please\",\n \"version\": \"3.2.0\",\n \"description\": \"Get an available TCP port to listen\",\n \"reposito"
},
{
"path": "renovate.json",
"chars": 49,
"preview": "{\n \"extends\": [\"github>unjs/renovate-config\"]\n}\n"
},
{
"path": "src/_internal.ts",
"chars": 2522,
"preview": "import { createServer, AddressInfo } from \"node:net\";\nimport { networkInterfaces } from \"node:os\";\nimport { isSafePort }"
},
{
"path": "src/get-port.ts",
"chars": 4409,
"preview": "import { isSafePort } from \"./unsafe-ports\";\n\nimport type {\n GetPortInput,\n PortNumber,\n GetPortOptions,\n HostAddres"
},
{
"path": "src/index.ts",
"chars": 111,
"preview": "export * from \"./types\";\nexport * from \"./get-port\";\nexport * from \"./unsafe-ports\";\nexport * from \"./socket\";\n"
},
{
"path": "src/socket.ts",
"chars": 2466,
"preview": "import { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { Server } from \"node:net\";\nimport { rm } fro"
},
{
"path": "src/types.ts",
"chars": 545,
"preview": "export interface GetPortOptions {\n name: string;\n random: boolean;\n port: number;\n ports: number[];\n portRange: [fr"
},
{
"path": "src/unsafe-ports.ts",
"chars": 1875,
"preview": "// https://chromium.googlesource.com/chromium/src.git/+/refs/heads/master/net/base/port_util.cc\n\nconst unsafePorts = new"
},
{
"path": "test/index.test.ts",
"chars": 5998,
"preview": "import { Server } from \"node:net\";\nimport { networkInterfaces } from \"node:os\";\nimport { describe, test, expect, beforeE"
},
{
"path": "test/socket.test.ts",
"chars": 414,
"preview": "import { describe, test, expect } from \"vitest\";\nimport { isSocketSupported, getSocketAddress } from \"../src\";\n\ndescribe"
},
{
"path": "test/utils.ts",
"chars": 548,
"preview": "import { createServer, Server } from \"node:net\";\n\nexport function blockPort(port: number, host?: string): Promise<Server"
},
{
"path": "tsconfig.json",
"chars": 161,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ESNext\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"Node\",\n \"esModuleIn"
}
]
About this extraction
This page contains the full source code of the unjs/get-port-please GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 19 files (35.4 KB), approximately 11.4k tokens, and a symbol index with 27 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.