825 lines
21 KiB
JavaScript
825 lines
21 KiB
JavaScript
import { createServer as createServer$2 } from 'node:http';
|
|
import { createServer as createServer$1 } from 'node:https';
|
|
import { promisify } from 'node:util';
|
|
import { promises, existsSync, writeFileSync, chmodSync, readFileSync, constants, statSync } from 'node:fs';
|
|
import os, { networkInterfaces as networkInterfaces$1 } from 'node:os';
|
|
import { g as gray, c as cyan, u as underline, b as bold } from '../shared/nuxi.a3b9dacd.mjs';
|
|
import { createServer } from 'net';
|
|
import { networkInterfaces } from 'os';
|
|
import require$$0 from 'fs';
|
|
import require$$0$1 from 'path';
|
|
import require$$0$2 from 'http';
|
|
import require$$1 from 'https';
|
|
import { d as defu } from '../shared/nuxi.d0ea9d71.mjs';
|
|
import childProcess from 'node:child_process';
|
|
import { join } from 'node:path';
|
|
import 'tty';
|
|
|
|
var dist = {};
|
|
|
|
Object.defineProperty(dist, '__esModule', { value: true });
|
|
|
|
const fs = require$$0;
|
|
const path = require$$0$1;
|
|
|
|
const _memo = {
|
|
_pid: process.pid
|
|
};
|
|
async function getMemo(config) {
|
|
const options = getOptions(config);
|
|
try {
|
|
const memo = JSON.parse(await fs.promises.readFile(options.file, "utf-8")) || {};
|
|
if (!memo._pid) {
|
|
throw new Error("Memo lacks _pid");
|
|
}
|
|
if (memo._pid === _memo._pid || !isAlive(memo.pid)) {
|
|
Object.assign(_memo, memo);
|
|
_memo._pid = process.pid;
|
|
}
|
|
} catch (e) {
|
|
}
|
|
return _memo;
|
|
}
|
|
async function setMemo(memo, config) {
|
|
const options = getOptions(config);
|
|
Object.assign(_memo, memo);
|
|
_memo._pid = process.pid;
|
|
try {
|
|
await fs.promises.mkdir(options.dir);
|
|
} catch (e) {
|
|
}
|
|
try {
|
|
await fs.promises.writeFile(options.file, JSON.stringify(_memo), "utf-8");
|
|
} catch (e) {
|
|
}
|
|
}
|
|
function getOptions(config) {
|
|
const options = {...config};
|
|
options.name = options.name || "default";
|
|
options.dir = options.dir || path.resolve(process.cwd(), "node_modules/.cache/fs-memo");
|
|
options.file = options.file || path.resolve(options.dir, options.name + ".json");
|
|
return options;
|
|
}
|
|
function isAlive(pid) {
|
|
try {
|
|
process.kill(pid, 0);
|
|
return true;
|
|
} catch (e) {
|
|
return e.code === "EPERM";
|
|
}
|
|
}
|
|
|
|
var getMemo_1 = dist.getMemo = getMemo;
|
|
var setMemo_1 = dist.setMemo = setMemo;
|
|
|
|
const unsafePorts = /* @__PURE__ */ new Set([
|
|
1,
|
|
7,
|
|
9,
|
|
11,
|
|
13,
|
|
15,
|
|
17,
|
|
19,
|
|
20,
|
|
21,
|
|
22,
|
|
23,
|
|
25,
|
|
37,
|
|
42,
|
|
43,
|
|
53,
|
|
69,
|
|
77,
|
|
79,
|
|
87,
|
|
95,
|
|
101,
|
|
102,
|
|
103,
|
|
104,
|
|
109,
|
|
110,
|
|
111,
|
|
113,
|
|
115,
|
|
117,
|
|
119,
|
|
123,
|
|
135,
|
|
137,
|
|
139,
|
|
143,
|
|
161,
|
|
179,
|
|
389,
|
|
427,
|
|
465,
|
|
512,
|
|
513,
|
|
514,
|
|
515,
|
|
526,
|
|
530,
|
|
531,
|
|
532,
|
|
540,
|
|
548,
|
|
554,
|
|
556,
|
|
563,
|
|
587,
|
|
601,
|
|
636,
|
|
989,
|
|
990,
|
|
993,
|
|
995,
|
|
1719,
|
|
1720,
|
|
1723,
|
|
2049,
|
|
3659,
|
|
4045,
|
|
5060,
|
|
5061,
|
|
6e3,
|
|
6566,
|
|
6665,
|
|
6666,
|
|
6667,
|
|
6668,
|
|
6669,
|
|
6697,
|
|
10080
|
|
]);
|
|
function isUnsafePort(port) {
|
|
return unsafePorts.has(port);
|
|
}
|
|
function isSafePort(port) {
|
|
return !isUnsafePort(port);
|
|
}
|
|
|
|
function log(...args) {
|
|
console.log("[get-port]", ...args);
|
|
}
|
|
async function getPort(config = {}) {
|
|
if (typeof config === "number" || typeof config === "string") {
|
|
config = { port: parseInt(config + "") || 0 };
|
|
}
|
|
const options = {
|
|
name: "default",
|
|
random: false,
|
|
ports: [],
|
|
portRange: [],
|
|
alternativePortRange: [3e3, 3100],
|
|
host: void 0,
|
|
memoName: "port",
|
|
verbose: false,
|
|
...config,
|
|
port: parseInt(process.env.PORT || "") || config.port || 3e3
|
|
};
|
|
if (options.random) {
|
|
return getRandomPort(options.host);
|
|
}
|
|
const portsToCheck = [
|
|
options.port,
|
|
...options.ports,
|
|
...generateRange(...options.portRange)
|
|
].filter((port) => {
|
|
if (!port) {
|
|
return false;
|
|
}
|
|
if (!isSafePort(port)) {
|
|
if (options.verbose) {
|
|
log("Ignoring unsafe port:", port);
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
const memoOptions = { name: options.memoName, dir: options.memoDir };
|
|
const memoKey = "port_" + options.name;
|
|
const memo = await getMemo_1(memoOptions);
|
|
if (memo[memoKey]) {
|
|
portsToCheck.push(memo[memoKey]);
|
|
}
|
|
let availablePort = await findPort(portsToCheck, options.host, options.verbose, false);
|
|
if (!availablePort) {
|
|
availablePort = await findPort(generateRange(...options.alternativePortRange), options.host, options.verbose);
|
|
if (options.verbose) {
|
|
log(`Unable to find an available port (tried ${portsToCheck.join(", ") || "-"}). Using alternative port:`, availablePort);
|
|
}
|
|
}
|
|
await setMemo_1({ [memoKey]: availablePort }, memoOptions);
|
|
return availablePort;
|
|
}
|
|
async function getRandomPort(host) {
|
|
const port = await checkPort(0, host);
|
|
if (port === false) {
|
|
throw new Error("Unable to obtain an available random port number!");
|
|
}
|
|
return port;
|
|
}
|
|
async function checkPort(port, host = process.env.HOST, _verbose) {
|
|
if (!host) {
|
|
host = getLocalHosts([void 0, "0.0.0.0"]);
|
|
}
|
|
if (!Array.isArray(host)) {
|
|
return _checkPort(port, host);
|
|
}
|
|
for (const _host of host) {
|
|
const _port = await _checkPort(port, _host);
|
|
if (_port === false) {
|
|
if (port < 1024 && _verbose) {
|
|
log("Unable to listen to priviliged port:", `${_host}:${port}`);
|
|
}
|
|
return false;
|
|
}
|
|
if (port === 0 && _port !== 0) {
|
|
port = _port;
|
|
}
|
|
}
|
|
return port;
|
|
}
|
|
function generateRange(from, to) {
|
|
if (to < from) {
|
|
return [];
|
|
}
|
|
const r = [];
|
|
for (let i = from; i < to; i++) {
|
|
r.push(i);
|
|
}
|
|
return r;
|
|
}
|
|
function _checkPort(port, host) {
|
|
return new Promise((resolve) => {
|
|
const server = createServer();
|
|
server.unref();
|
|
server.on("error", (err) => {
|
|
if (err.code === "EINVAL" || err.code === "EADDRNOTAVAIL") {
|
|
resolve(port !== 0 && isSafePort(port) && port);
|
|
} else {
|
|
resolve(false);
|
|
}
|
|
});
|
|
server.listen({ port, host }, () => {
|
|
const { port: port2 } = server.address();
|
|
server.close(() => {
|
|
resolve(isSafePort(port2) && port2);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
function getLocalHosts(additional) {
|
|
const hosts = new Set(additional);
|
|
for (const _interface of Object.values(networkInterfaces())) {
|
|
for (const config of _interface) {
|
|
hosts.add(config.address);
|
|
}
|
|
}
|
|
return Array.from(hosts);
|
|
}
|
|
async function findPort(ports, host, _verbose = false, _random = true) {
|
|
for (const port of ports) {
|
|
const r = await checkPort(port, host, _verbose);
|
|
if (r) {
|
|
return r;
|
|
}
|
|
}
|
|
if (_random) {
|
|
const randomPort = await getRandomPort(host);
|
|
if (_verbose) {
|
|
log(`Unable to find an available port (tried ${ports.join(", ") || "-"}). Using random port:`, randomPort);
|
|
}
|
|
return randomPort;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
var httpShutdown = {exports: {}};
|
|
|
|
(function (module, exports) {
|
|
var http = require$$0$2;
|
|
var https = require$$1;
|
|
|
|
/**
|
|
* Expose `addShutdown`.
|
|
*/
|
|
exports = module.exports = addShutdown;
|
|
|
|
/**
|
|
* Adds shutdown functionaility to the `http.Server` object
|
|
* @param {http.Server} server The server to add shutdown functionaility to
|
|
*/
|
|
function addShutdown(server) {
|
|
var connections = {};
|
|
var isShuttingDown = false;
|
|
var connectionCounter = 0;
|
|
|
|
function destroy(socket, force) {
|
|
if (force || (socket._isIdle && isShuttingDown)) {
|
|
socket.destroy();
|
|
delete connections[socket._connectionId];
|
|
}
|
|
}
|
|
function onConnection(socket) {
|
|
var id = connectionCounter++;
|
|
socket._isIdle = true;
|
|
socket._connectionId = id;
|
|
connections[id] = socket;
|
|
|
|
socket.on('close', function() {
|
|
delete connections[id];
|
|
});
|
|
}
|
|
server.on('request', function(req, res) {
|
|
req.socket._isIdle = false;
|
|
|
|
res.on('finish', function() {
|
|
req.socket._isIdle = true;
|
|
destroy(req.socket);
|
|
});
|
|
});
|
|
|
|
server.on('connection', onConnection);
|
|
server.on('secureConnection', onConnection);
|
|
|
|
function shutdown(force, cb) {
|
|
isShuttingDown = true;
|
|
server.close(function(err) {
|
|
if (cb) {
|
|
process.nextTick(function() { cb(err); });
|
|
}
|
|
});
|
|
|
|
Object.keys(connections).forEach(function(key) {
|
|
destroy(connections[key], force);
|
|
});
|
|
}
|
|
server.shutdown = function(cb) {
|
|
shutdown(false, cb);
|
|
};
|
|
|
|
server.forceShutdown = function(cb) {
|
|
shutdown(true, cb);
|
|
};
|
|
|
|
return server;
|
|
}
|
|
/**
|
|
* Extends the {http.Server} object with shutdown functionaility.
|
|
* @return {http.Server} The decorated server object
|
|
*/
|
|
exports.extend = function() {
|
|
http.Server.prototype.withShutdown = function() {
|
|
return addShutdown(this);
|
|
};
|
|
|
|
https.Server.prototype.withShutdown = function() {
|
|
return addShutdown(this);
|
|
};
|
|
};
|
|
} (httpShutdown, httpShutdown.exports));
|
|
|
|
const addShutdown = httpShutdown.exports;
|
|
|
|
const { platform, arch } = process;
|
|
const getWslDrivesMountPoint = (() => {
|
|
const defaultMountPoint = "/mnt/";
|
|
let mountPoint;
|
|
return async function() {
|
|
if (mountPoint) {
|
|
return mountPoint;
|
|
}
|
|
const configFilePath = "/etc/wsl.conf";
|
|
let isConfigFileExists = false;
|
|
try {
|
|
await promises.access(configFilePath, constants.F_OK);
|
|
isConfigFileExists = true;
|
|
} catch {
|
|
}
|
|
if (!isConfigFileExists) {
|
|
return defaultMountPoint;
|
|
}
|
|
const configContent = await promises.readFile(configFilePath, { encoding: "utf8" });
|
|
const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
|
|
if (!configMountPoint) {
|
|
return defaultMountPoint;
|
|
}
|
|
mountPoint = configMountPoint.groups.mountPoint.trim();
|
|
mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
|
|
return mountPoint;
|
|
};
|
|
})();
|
|
const pTryEach = async (array, mapper) => {
|
|
let latestError;
|
|
for (const item of array) {
|
|
try {
|
|
return await mapper(item);
|
|
} catch (error) {
|
|
latestError = error;
|
|
}
|
|
}
|
|
throw latestError;
|
|
};
|
|
const baseOpen = async (options) => {
|
|
options = {
|
|
wait: false,
|
|
background: false,
|
|
newInstance: false,
|
|
allowNonzeroExitCode: false,
|
|
...options
|
|
};
|
|
if (Array.isArray(options.app)) {
|
|
return pTryEach(options.app, (singleApp) => baseOpen({
|
|
...options,
|
|
app: singleApp
|
|
}));
|
|
}
|
|
let { name: app, arguments: appArguments = [] } = options.app || {};
|
|
appArguments = [...appArguments];
|
|
if (Array.isArray(app)) {
|
|
return pTryEach(app, (appName) => baseOpen({
|
|
...options,
|
|
app: {
|
|
name: appName,
|
|
arguments: appArguments
|
|
}
|
|
}));
|
|
}
|
|
let command;
|
|
const cliArguments = [];
|
|
const childProcessOptions = {};
|
|
if (platform === "darwin") {
|
|
command = "open";
|
|
if (options.wait) {
|
|
cliArguments.push("--wait-apps");
|
|
}
|
|
if (options.background) {
|
|
cliArguments.push("--background");
|
|
}
|
|
if (options.newInstance) {
|
|
cliArguments.push("--new");
|
|
}
|
|
if (app) {
|
|
cliArguments.push("-a", app);
|
|
}
|
|
} else if (platform === "win32" || isWsl() && !isDocker()) {
|
|
const mountPoint = await getWslDrivesMountPoint();
|
|
command = isWsl() ? `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe` : `${process.env.SYSTEMROOT}\\System32\\WindowsPowerShell\\v1.0\\powershell`;
|
|
cliArguments.push(
|
|
"-NoProfile",
|
|
"-NonInteractive",
|
|
"\u2013ExecutionPolicy",
|
|
"Bypass",
|
|
"-EncodedCommand"
|
|
);
|
|
if (!isWsl()) {
|
|
childProcessOptions.windowsVerbatimArguments = true;
|
|
}
|
|
const encodedArguments = ["Start"];
|
|
if (options.wait) {
|
|
encodedArguments.push("-Wait");
|
|
}
|
|
if (app) {
|
|
encodedArguments.push(`"\`"${app}\`""`, "-ArgumentList");
|
|
if (options.target) {
|
|
appArguments.unshift(options.target);
|
|
}
|
|
} else if (options.target) {
|
|
encodedArguments.push(`"${options.target}"`);
|
|
}
|
|
if (appArguments.length > 0) {
|
|
appArguments = appArguments.map((argument) => `"\`"${argument}\`""`);
|
|
encodedArguments.push(appArguments.join(","));
|
|
}
|
|
options.target = Buffer.from(encodedArguments.join(" "), "utf16le").toString("base64");
|
|
} else {
|
|
if (app) {
|
|
command = app;
|
|
} else {
|
|
command = "xdg-open";
|
|
const useSystemXdgOpen = process.versions.electron || platform === "android";
|
|
if (!useSystemXdgOpen) {
|
|
command = join(os.tmpdir(), "xdg-open");
|
|
if (!existsSync(command)) {
|
|
try {
|
|
writeFileSync(
|
|
join(os.tmpdir(), "xdg-open"),
|
|
await import('./xdg-open.mjs').then((r) => r.xdgOpenScript()),
|
|
"utf8"
|
|
);
|
|
chmodSync(command, 493);
|
|
} catch {
|
|
command = "xdg-open";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (appArguments.length > 0) {
|
|
cliArguments.push(...appArguments);
|
|
}
|
|
if (!options.wait) {
|
|
childProcessOptions.stdio = "ignore";
|
|
childProcessOptions.detached = true;
|
|
}
|
|
}
|
|
if (options.target) {
|
|
cliArguments.push(options.target);
|
|
}
|
|
if (platform === "darwin" && appArguments.length > 0) {
|
|
cliArguments.push("--args", ...appArguments);
|
|
}
|
|
const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
|
|
if (options.wait) {
|
|
return new Promise((resolve, reject) => {
|
|
subprocess.once("error", reject);
|
|
subprocess.once("close", (exitCode) => {
|
|
if (options.allowNonzeroExitCode && exitCode > 0) {
|
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
return;
|
|
}
|
|
resolve(subprocess);
|
|
});
|
|
});
|
|
}
|
|
subprocess.unref();
|
|
return subprocess;
|
|
};
|
|
const open = (target, options = {}) => {
|
|
if (typeof target !== "string") {
|
|
throw new TypeError("Expected a `target`");
|
|
}
|
|
return baseOpen({
|
|
...options,
|
|
target
|
|
});
|
|
};
|
|
const openApp = (name, options) => {
|
|
if (typeof name !== "string") {
|
|
throw new TypeError("Expected a `name`");
|
|
}
|
|
const { arguments: appArguments = [] } = options || {};
|
|
if (appArguments !== void 0 && appArguments !== null && !Array.isArray(appArguments)) {
|
|
throw new TypeError("Expected `appArguments` as Array type");
|
|
}
|
|
return baseOpen({
|
|
...options,
|
|
app: {
|
|
name,
|
|
arguments: appArguments
|
|
}
|
|
});
|
|
};
|
|
function detectArchBinary(binary) {
|
|
if (typeof binary === "string" || Array.isArray(binary)) {
|
|
return binary;
|
|
}
|
|
const { [arch]: archBinary } = binary;
|
|
if (!archBinary) {
|
|
throw new Error(`${arch} is not supported`);
|
|
}
|
|
return archBinary;
|
|
}
|
|
function detectPlatformBinary({ [platform]: platformBinary }, { wsl }) {
|
|
if (wsl && isWsl()) {
|
|
return detectArchBinary(wsl);
|
|
}
|
|
if (!platformBinary) {
|
|
throw new Error(`${platform} is not supported`);
|
|
}
|
|
return detectArchBinary(platformBinary);
|
|
}
|
|
const apps = {};
|
|
defineLazyProperty(apps, "chrome", () => detectPlatformBinary({
|
|
darwin: "google chrome",
|
|
win32: "chrome",
|
|
linux: ["google-chrome", "google-chrome-stable", "chromium"]
|
|
}, {
|
|
wsl: {
|
|
ia32: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
|
|
x64: ["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]
|
|
}
|
|
}));
|
|
defineLazyProperty(apps, "firefox", () => detectPlatformBinary({
|
|
darwin: "firefox",
|
|
win32: "C:\\Program Files\\Mozilla Firefox\\firefox.exe",
|
|
linux: "firefox"
|
|
}, {
|
|
wsl: "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
|
|
}));
|
|
defineLazyProperty(apps, "edge", () => detectPlatformBinary({
|
|
darwin: "microsoft edge",
|
|
win32: "msedge",
|
|
linux: ["microsoft-edge", "microsoft-edge-dev"]
|
|
}, {
|
|
wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
|
|
}));
|
|
open.apps = apps;
|
|
open.openApp = openApp;
|
|
function defineLazyProperty(object, propertyName, valueGetter) {
|
|
const define = (value) => Object.defineProperty(object, propertyName, { value, enumerable: true, writable: true });
|
|
Object.defineProperty(object, propertyName, {
|
|
configurable: true,
|
|
enumerable: true,
|
|
get() {
|
|
const result = valueGetter();
|
|
define(result);
|
|
return result;
|
|
},
|
|
set(value) {
|
|
define(value);
|
|
}
|
|
});
|
|
return object;
|
|
}
|
|
function _isWsl() {
|
|
if (process.platform !== "linux") {
|
|
return false;
|
|
}
|
|
if (os.release().toLowerCase().includes("microsoft")) {
|
|
if (isDocker()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
try {
|
|
return readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
let isWSLCached;
|
|
function isWsl() {
|
|
if (isWSLCached === void 0) {
|
|
isWSLCached = _isWsl();
|
|
}
|
|
return isWSLCached;
|
|
}
|
|
function hasDockerEnvironment() {
|
|
try {
|
|
statSync("/.dockerenv");
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
function hasDockerCGroup() {
|
|
try {
|
|
return readFileSync("/proc/self/cgroup", "utf8").includes("docker");
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
let isDockerCached;
|
|
function isDocker() {
|
|
if (isDockerCached === void 0) {
|
|
isDockerCached = hasDockerEnvironment() || hasDockerCGroup();
|
|
}
|
|
return isDockerCached;
|
|
}
|
|
|
|
async function listen(handle, options_ = {}) {
|
|
options_ = defu(options_, {
|
|
port: process.env.PORT || 3e3,
|
|
hostname: process.env.HOST || "",
|
|
showURL: true,
|
|
baseURL: "/",
|
|
open: false,
|
|
clipboard: false,
|
|
isTest: process.env.NODE_ENV === "test",
|
|
isProd: process.env.NODE_ENV === "production",
|
|
autoClose: true
|
|
});
|
|
if (options_.isTest) {
|
|
options_.showURL = false;
|
|
}
|
|
if (options_.isProd || options_.isTest) {
|
|
options_.open = false;
|
|
options_.clipboard = false;
|
|
}
|
|
const port = await getPort({
|
|
port: Number(options_.port),
|
|
verbose: !options_.isTest,
|
|
host: options_.hostname
|
|
});
|
|
let server;
|
|
let addr;
|
|
const getURL = (host, baseURL) => {
|
|
const anyV4 = addr?.addr === "0.0.0.0";
|
|
const anyV6 = addr?.addr === "[::]";
|
|
return `${addr.proto}://${host || options_.hostname || (anyV4 || anyV6 ? "localhost" : addr.addr)}:${addr.port}${baseURL || options_.baseURL}`;
|
|
};
|
|
let https = false;
|
|
if (options_.https) {
|
|
const { key, cert } = await resolveCert({ ...options_.https }, options_.hostname);
|
|
https = { key, cert };
|
|
server = createServer$1({ key, cert }, handle);
|
|
addShutdown(server);
|
|
await promisify(server.listen.bind(server))(port, options_.hostname);
|
|
const _addr = server.address();
|
|
addr = { proto: "https", addr: formatAddress(_addr), port: _addr.port };
|
|
} else {
|
|
server = createServer$2(handle);
|
|
addShutdown(server);
|
|
await promisify(server.listen.bind(server))(port, options_.hostname);
|
|
const _addr = server.address();
|
|
addr = { proto: "http", addr: formatAddress(_addr), port: _addr.port };
|
|
}
|
|
let _closed = false;
|
|
const close = () => {
|
|
if (_closed) {
|
|
return Promise.resolve();
|
|
}
|
|
_closed = true;
|
|
return promisify(server.shutdown)();
|
|
};
|
|
if (options_.clipboard) {
|
|
const clipboardy = await import('./index3.mjs').then((r) => r.default || r);
|
|
await clipboardy.write(getURL()).catch(() => {
|
|
options_.clipboard = false;
|
|
});
|
|
}
|
|
const showURL = (options) => {
|
|
const add = options_.clipboard ? gray("(copied to clipboard)") : "";
|
|
const lines = [];
|
|
const baseURL = options?.baseURL || options_.baseURL || "";
|
|
const name = options?.name ? ` (${options.name})` : "";
|
|
const anyV4 = addr?.addr === "0.0.0.0";
|
|
const anyV6 = addr?.addr === "[::]";
|
|
if (anyV4 || anyV6) {
|
|
lines.push(` > Local${name}: ${formatURL(getURL("localhost", baseURL))} ${add}`);
|
|
for (const addr2 of getNetworkInterfaces(anyV4)) {
|
|
lines.push(` > Network${name}: ${formatURL(getURL(addr2, baseURL))}`);
|
|
}
|
|
} else {
|
|
lines.push(` > Listening${name}: ${formatURL(getURL(void 0, baseURL))} ${add}`);
|
|
}
|
|
console.log("\n" + lines.join("\n") + "\n");
|
|
};
|
|
if (options_.showURL) {
|
|
showURL();
|
|
}
|
|
const _open = async () => {
|
|
await open(getURL()).catch(() => {
|
|
});
|
|
};
|
|
if (options_.open) {
|
|
await _open();
|
|
}
|
|
if (options_.autoClose) {
|
|
process.on("exit", () => close());
|
|
}
|
|
return {
|
|
url: getURL(),
|
|
https,
|
|
server,
|
|
open: _open,
|
|
showURL,
|
|
close
|
|
};
|
|
}
|
|
async function resolveCert(options, host) {
|
|
if (options.key && options.cert) {
|
|
const isInline = (s = "") => s.startsWith("--");
|
|
const r = (s) => isInline(s) ? s : promises.readFile(s, "utf8");
|
|
return {
|
|
key: await r(options.key),
|
|
cert: await r(options.cert)
|
|
};
|
|
}
|
|
const { generateCA, generateSSLCert } = await import('./cert.mjs');
|
|
const ca = await generateCA();
|
|
const cert = await generateSSLCert({
|
|
caCert: ca.cert,
|
|
caKey: ca.key,
|
|
domains: options.domains || ["localhost", "127.0.0.1", "::1", host].filter(Boolean),
|
|
validityDays: options.validityDays || 1
|
|
});
|
|
return cert;
|
|
}
|
|
function getNetworkInterfaces(v4Only = true) {
|
|
const addrs = /* @__PURE__ */ new Set();
|
|
for (const details of Object.values(networkInterfaces$1())) {
|
|
if (details) {
|
|
for (const d of details) {
|
|
if (!d.internal && !(d.mac === "00:00:00:00:00:00") && !d.address.startsWith("fe80::") && !(v4Only && (d.family === "IPv6" || +d.family === 6))) {
|
|
addrs.add(formatAddress(d));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return [...addrs].sort();
|
|
}
|
|
function formatAddress(addr) {
|
|
return addr.family === "IPv6" || addr.family === 6 ? `[${addr.address}]` : addr.address;
|
|
}
|
|
function formatURL(url) {
|
|
return cyan(underline(decodeURI(url).replace(/:(\d+)\//g, `:${bold("$1")}/`)));
|
|
}
|
|
|
|
export { listen };
|