index.mjs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import { createServer } from 'net';
  2. import { networkInterfaces } from 'os';
  3. import { getMemo, setMemo } from 'fs-memo';
  4. const unsafePorts = /* @__PURE__ */ new Set([
  5. 1,
  6. 7,
  7. 9,
  8. 11,
  9. 13,
  10. 15,
  11. 17,
  12. 19,
  13. 20,
  14. 21,
  15. 22,
  16. 23,
  17. 25,
  18. 37,
  19. 42,
  20. 43,
  21. 53,
  22. 69,
  23. 77,
  24. 79,
  25. 87,
  26. 95,
  27. 101,
  28. 102,
  29. 103,
  30. 104,
  31. 109,
  32. 110,
  33. 111,
  34. 113,
  35. 115,
  36. 117,
  37. 119,
  38. 123,
  39. 135,
  40. 137,
  41. 139,
  42. 143,
  43. 161,
  44. 179,
  45. 389,
  46. 427,
  47. 465,
  48. 512,
  49. 513,
  50. 514,
  51. 515,
  52. 526,
  53. 530,
  54. 531,
  55. 532,
  56. 540,
  57. 548,
  58. 554,
  59. 556,
  60. 563,
  61. 587,
  62. 601,
  63. 636,
  64. 989,
  65. 990,
  66. 993,
  67. 995,
  68. 1719,
  69. 1720,
  70. 1723,
  71. 2049,
  72. 3659,
  73. 4045,
  74. 5060,
  75. 5061,
  76. 6e3,
  77. 6566,
  78. 6665,
  79. 6666,
  80. 6667,
  81. 6668,
  82. 6669,
  83. 6697,
  84. 10080
  85. ]);
  86. function isUnsafePort(port) {
  87. return unsafePorts.has(port);
  88. }
  89. function isSafePort(port) {
  90. return !isUnsafePort(port);
  91. }
  92. function log(...args) {
  93. console.log("[get-port]", ...args);
  94. }
  95. async function getPort(config = {}) {
  96. if (typeof config === "number" || typeof config === "string") {
  97. config = { port: parseInt(config + "") || 0 };
  98. }
  99. const options = {
  100. name: "default",
  101. random: false,
  102. ports: [],
  103. portRange: [],
  104. alternativePortRange: [3e3, 3100],
  105. host: void 0,
  106. memoName: "port",
  107. verbose: false,
  108. ...config,
  109. port: parseInt(process.env.PORT || "") || config.port || 3e3
  110. };
  111. if (options.random) {
  112. return getRandomPort(options.host);
  113. }
  114. const portsToCheck = [
  115. options.port,
  116. ...options.ports,
  117. ...generateRange(...options.portRange)
  118. ].filter((port) => {
  119. if (!port) {
  120. return false;
  121. }
  122. if (!isSafePort(port)) {
  123. if (options.verbose) {
  124. log("Ignoring unsafe port:", port);
  125. }
  126. return false;
  127. }
  128. return true;
  129. });
  130. const memoOptions = { name: options.memoName, dir: options.memoDir };
  131. const memoKey = "port_" + options.name;
  132. const memo = await getMemo(memoOptions);
  133. if (memo[memoKey]) {
  134. portsToCheck.push(memo[memoKey]);
  135. }
  136. let availablePort = await findPort(portsToCheck, options.host, options.verbose, false);
  137. if (!availablePort) {
  138. availablePort = await findPort(generateRange(...options.alternativePortRange), options.host, options.verbose);
  139. if (options.verbose) {
  140. log(`Unable to find an available port (tried ${portsToCheck.join(", ") || "-"}). Using alternative port:`, availablePort);
  141. }
  142. }
  143. await setMemo({ [memoKey]: availablePort }, memoOptions);
  144. return availablePort;
  145. }
  146. async function getRandomPort(host) {
  147. const port = await checkPort(0, host);
  148. if (port === false) {
  149. throw new Error("Unable to obtain an available random port number!");
  150. }
  151. return port;
  152. }
  153. async function waitForPort(port, opts = {}) {
  154. const delay = opts.delay || 500;
  155. const retries = opts.retries || 4;
  156. for (let i = retries; i > 0; i--) {
  157. if (await checkPort(port, opts.host) === false) {
  158. return;
  159. }
  160. await new Promise((resolve) => setTimeout(resolve, delay));
  161. }
  162. throw new Error(`Timeout waiting for port ${port} after ${retries} retries with ${delay}ms interval.`);
  163. }
  164. async function checkPort(port, host = process.env.HOST, _verbose) {
  165. if (!host) {
  166. host = getLocalHosts([void 0, "0.0.0.0"]);
  167. }
  168. if (!Array.isArray(host)) {
  169. return _checkPort(port, host);
  170. }
  171. for (const _host of host) {
  172. const _port = await _checkPort(port, _host);
  173. if (_port === false) {
  174. if (port < 1024 && _verbose) {
  175. log("Unable to listen to priviliged port:", `${_host}:${port}`);
  176. }
  177. return false;
  178. }
  179. if (port === 0 && _port !== 0) {
  180. port = _port;
  181. }
  182. }
  183. return port;
  184. }
  185. function generateRange(from, to) {
  186. if (to < from) {
  187. return [];
  188. }
  189. const r = [];
  190. for (let i = from; i < to; i++) {
  191. r.push(i);
  192. }
  193. return r;
  194. }
  195. function _checkPort(port, host) {
  196. return new Promise((resolve) => {
  197. const server = createServer();
  198. server.unref();
  199. server.on("error", (err) => {
  200. if (err.code === "EINVAL" || err.code === "EADDRNOTAVAIL") {
  201. resolve(port !== 0 && isSafePort(port) && port);
  202. } else {
  203. resolve(false);
  204. }
  205. });
  206. server.listen({ port, host }, () => {
  207. const { port: port2 } = server.address();
  208. server.close(() => {
  209. resolve(isSafePort(port2) && port2);
  210. });
  211. });
  212. });
  213. }
  214. function getLocalHosts(additional) {
  215. const hosts = new Set(additional);
  216. for (const _interface of Object.values(networkInterfaces())) {
  217. for (const config of _interface) {
  218. hosts.add(config.address);
  219. }
  220. }
  221. return Array.from(hosts);
  222. }
  223. async function findPort(ports, host, _verbose = false, _random = true) {
  224. for (const port of ports) {
  225. const r = await checkPort(port, host, _verbose);
  226. if (r) {
  227. return r;
  228. }
  229. }
  230. if (_random) {
  231. const randomPort = await getRandomPort(host);
  232. if (_verbose) {
  233. log(`Unable to find an available port (tried ${ports.join(", ") || "-"}). Using random port:`, randomPort);
  234. }
  235. return randomPort;
  236. } else {
  237. return 0;
  238. }
  239. }
  240. export { checkPort, getPort, getRandomPort, isSafePort, isUnsafePort, waitForPort };