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