Full Code of unjs/get-port-please for AI

main 32a2f925b3f3 cached
19 files
35.4 KB
11.4k tokens
27 symbols
1 requests
Download .txt
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"]
}
Download .txt
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
Download .txt
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.

Copied to clipboard!