index.mjs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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. async function getPort(config) {
  93. if (typeof config === "number" || typeof config === "string") {
  94. config = { port: parseInt(config + "") };
  95. }
  96. const options = {
  97. name: "default",
  98. random: false,
  99. port: parseInt(process.env.PORT || "") || 3e3,
  100. ports: [],
  101. portRange: [3e3, 3100],
  102. host: void 0,
  103. memoName: "port",
  104. ...config
  105. };
  106. if (options.random) {
  107. return getRandomPort(options.host);
  108. }
  109. const portsToCheck = [
  110. options.port,
  111. ...options.ports,
  112. ...generateRange(options.portRange[0], options.portRange[1])
  113. ].filter((port) => port && isSafePort(port));
  114. const memoOptions = { name: options.memoName, dir: options.memoDir };
  115. const memoKey = "port_" + options.name;
  116. const memo = await getMemo(memoOptions);
  117. if (memo[memoKey]) {
  118. portsToCheck.push(memo[memoKey]);
  119. }
  120. const availablePort = await findPort(portsToCheck, options.host);
  121. await setMemo({ [memoKey]: availablePort }, memoOptions);
  122. return availablePort;
  123. }
  124. async function getRandomPort(host) {
  125. const port = await checkPort(0, host);
  126. if (port === false) {
  127. throw new Error("Unable to obtain an available random port number!");
  128. }
  129. return port;
  130. }
  131. async function waitForPort(port, opts = {}) {
  132. const delay = opts.delay || 500;
  133. const retries = opts.retries || 4;
  134. for (let i = retries; i > 0; i--) {
  135. if (await checkPort(port, opts.host) === false) {
  136. return;
  137. }
  138. await new Promise((resolve) => setTimeout(resolve, delay));
  139. }
  140. throw new Error(`Timeout waiting for port ${port} after ${retries} retries with ${delay}ms interval.`);
  141. }
  142. async function checkPort(port, host = process.env.HOST) {
  143. if (!host) {
  144. host = getLocalHosts([void 0, "0.0.0.0"]);
  145. }
  146. if (!Array.isArray(host)) {
  147. return _checkPort(port, host);
  148. }
  149. for (const _host of host) {
  150. const _port = await _checkPort(port, _host);
  151. if (_port === false) {
  152. return false;
  153. }
  154. if (port === 0 && _port !== 0) {
  155. port = _port;
  156. }
  157. }
  158. return port;
  159. }
  160. function generateRange(from, to) {
  161. if (to < from) {
  162. return [];
  163. }
  164. const r = [];
  165. for (let i = from; i < to; i++) {
  166. r.push(i);
  167. }
  168. return r;
  169. }
  170. function _checkPort(port, host) {
  171. return new Promise((resolve) => {
  172. const server = createServer();
  173. server.unref();
  174. server.on("error", (err) => {
  175. if (err.code === "EINVAL" || err.code === "EADDRNOTAVAIL") {
  176. resolve(port !== 0 && isSafePort(port) && port);
  177. } else {
  178. resolve(false);
  179. }
  180. });
  181. server.listen({ port, host }, () => {
  182. const { port: port2 } = server.address();
  183. server.close(() => {
  184. resolve(isSafePort(port2) && port2);
  185. });
  186. });
  187. });
  188. }
  189. function getLocalHosts(additional) {
  190. const hosts = new Set(additional);
  191. for (const _interface of Object.values(networkInterfaces())) {
  192. for (const config of _interface) {
  193. hosts.add(config.address);
  194. }
  195. }
  196. return Array.from(hosts);
  197. }
  198. async function findPort(ports, host) {
  199. for (const port of ports) {
  200. const r = await checkPort(port, host);
  201. if (r) {
  202. return r;
  203. }
  204. }
  205. return getRandomPort(host);
  206. }
  207. export { checkPort, getPort, getRandomPort, isSafePort, isUnsafePort, waitForPort };