portless 0.15.0 → 0.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -6
- package/dist/{chunk-PCBKLZK2.js → chunk-T7CGCIRO.js} +64 -18
- package/dist/cli.js +341 -168
- package/dist/index.d.ts +21 -1
- package/dist/index.js +5 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
checkHostResolution,
|
|
8
8
|
cleanHostsFile,
|
|
9
9
|
createHttpRedirectServer,
|
|
10
|
+
createLoopbackConnection,
|
|
10
11
|
createProxyServer,
|
|
11
12
|
fixOwnership,
|
|
12
13
|
formatUrl,
|
|
@@ -14,9 +15,10 @@ import {
|
|
|
14
15
|
isErrnoException,
|
|
15
16
|
isProcessAlive,
|
|
16
17
|
parseHostname,
|
|
18
|
+
parseHostnames,
|
|
17
19
|
shouldAutoSyncHosts,
|
|
18
20
|
syncHostsFile
|
|
19
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-T7CGCIRO.js";
|
|
20
22
|
|
|
21
23
|
// src/colors.ts
|
|
22
24
|
function supportsColor() {
|
|
@@ -501,15 +503,16 @@ async function generateHostCertAsync(stateDir, hostname) {
|
|
|
501
503
|
fixOwnership(keyPath, certPath);
|
|
502
504
|
return { certPath, keyPath };
|
|
503
505
|
}
|
|
504
|
-
function createSNICallback(stateDir, defaultCert, defaultKey,
|
|
506
|
+
function createSNICallback(stateDir, defaultCert, defaultKey, tlds = "localhost", caCert) {
|
|
505
507
|
const cache = /* @__PURE__ */ new Map();
|
|
506
508
|
const pending = /* @__PURE__ */ new Map();
|
|
509
|
+
const configuredTlds = Array.isArray(tlds) ? tlds : [tlds];
|
|
507
510
|
const defaultCtx = tls.createSecureContext({
|
|
508
511
|
cert: caCert ? Buffer.concat([defaultCert, caCert]) : defaultCert,
|
|
509
512
|
key: defaultKey
|
|
510
513
|
});
|
|
511
514
|
return (servername, cb) => {
|
|
512
|
-
if (servername ===
|
|
515
|
+
if (servername === "localhost" && configuredTlds.includes("localhost")) {
|
|
513
516
|
cb(null, defaultCtx);
|
|
514
517
|
return;
|
|
515
518
|
}
|
|
@@ -1256,6 +1259,9 @@ function findGitRoot(startDir) {
|
|
|
1256
1259
|
}
|
|
1257
1260
|
return null;
|
|
1258
1261
|
}
|
|
1262
|
+
function applyWorktreePrefix(baseName, worktree) {
|
|
1263
|
+
return worktree ? `${worktree.prefix}.${baseName}` : baseName;
|
|
1264
|
+
}
|
|
1259
1265
|
var DEFAULT_BRANCHES = /* @__PURE__ */ new Set(["main", "master"]);
|
|
1260
1266
|
function branchToPrefix(branch) {
|
|
1261
1267
|
if (!branch || branch === "HEAD" || DEFAULT_BRANCHES.has(branch)) return null;
|
|
@@ -1584,8 +1590,25 @@ function validateTld(tld) {
|
|
|
1584
1590
|
}
|
|
1585
1591
|
return null;
|
|
1586
1592
|
}
|
|
1593
|
+
function parseTldList(value, source = "TLD") {
|
|
1594
|
+
const trimmed = value.trim();
|
|
1595
|
+
if (!trimmed) return [];
|
|
1596
|
+
const tlds = [];
|
|
1597
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1598
|
+
for (const rawPart of trimmed.split(",")) {
|
|
1599
|
+
const tld = rawPart.trim().toLowerCase();
|
|
1600
|
+
const err = validateTld(tld);
|
|
1601
|
+
if (err) throw new Error(source === "TLD" ? err : `${source}: ${err}`);
|
|
1602
|
+
if (!seen.has(tld)) {
|
|
1603
|
+
seen.add(tld);
|
|
1604
|
+
tlds.push(tld);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
return tlds;
|
|
1608
|
+
}
|
|
1587
1609
|
var TLD_FILE = "proxy.tld";
|
|
1588
|
-
|
|
1610
|
+
var TLDS_FILE = "proxy.tlds";
|
|
1611
|
+
function readLegacyTldFromDir(dir) {
|
|
1589
1612
|
try {
|
|
1590
1613
|
const raw = fs3.readFileSync(path3.join(dir, TLD_FILE), "utf-8").trim();
|
|
1591
1614
|
return raw || DEFAULT_TLD;
|
|
@@ -1593,23 +1616,43 @@ function readTldFromDir(dir) {
|
|
|
1593
1616
|
return DEFAULT_TLD;
|
|
1594
1617
|
}
|
|
1595
1618
|
}
|
|
1596
|
-
function
|
|
1597
|
-
|
|
1598
|
-
|
|
1619
|
+
function readTldsFromDir(dir) {
|
|
1620
|
+
try {
|
|
1621
|
+
const raw = fs3.readFileSync(path3.join(dir, TLDS_FILE), "utf-8").trim();
|
|
1622
|
+
const parsed = raw.startsWith("[") ? JSON.parse(raw) : raw.split(/\r?\n/).flatMap((line) => line.split(",")).map((line) => line.trim()).filter(Boolean);
|
|
1623
|
+
if (!Array.isArray(parsed)) return [readLegacyTldFromDir(dir)];
|
|
1624
|
+
const tlds = parsed.flatMap((value) => typeof value === "string" ? parseTldList(value) : []);
|
|
1625
|
+
return tlds.length > 0 ? [...new Set(tlds)] : [DEFAULT_TLD];
|
|
1626
|
+
} catch {
|
|
1627
|
+
return [readLegacyTldFromDir(dir)];
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
function writeTldsFile(dir, tlds) {
|
|
1631
|
+
const uniqueTlds = [...new Set(tlds)];
|
|
1632
|
+
const tldsPath = path3.join(dir, TLDS_FILE);
|
|
1633
|
+
const tldPath = path3.join(dir, TLD_FILE);
|
|
1634
|
+
if (uniqueTlds.length === 1 && uniqueTlds[0] === DEFAULT_TLD) {
|
|
1635
|
+
try {
|
|
1636
|
+
fs3.unlinkSync(tldsPath);
|
|
1637
|
+
} catch {
|
|
1638
|
+
}
|
|
1599
1639
|
try {
|
|
1600
|
-
fs3.unlinkSync(
|
|
1640
|
+
fs3.unlinkSync(tldPath);
|
|
1601
1641
|
} catch {
|
|
1602
1642
|
}
|
|
1603
1643
|
} else {
|
|
1604
|
-
fs3.writeFileSync(
|
|
1644
|
+
fs3.writeFileSync(tldsPath, uniqueTlds.join("\n") + "\n", { mode: 420 });
|
|
1645
|
+
fs3.writeFileSync(tldPath, uniqueTlds[0] ?? DEFAULT_TLD, { mode: 420 });
|
|
1605
1646
|
}
|
|
1606
1647
|
}
|
|
1607
|
-
function
|
|
1648
|
+
function writeTldFile(dir, tld) {
|
|
1649
|
+
writeTldsFile(dir, [tld]);
|
|
1650
|
+
}
|
|
1651
|
+
function getDefaultTlds() {
|
|
1608
1652
|
const val = process.env.PORTLESS_TLD?.trim().toLowerCase();
|
|
1609
|
-
if (!val) return DEFAULT_TLD;
|
|
1610
|
-
const
|
|
1611
|
-
|
|
1612
|
-
return val;
|
|
1653
|
+
if (!val) return [DEFAULT_TLD];
|
|
1654
|
+
const tlds = parseTldList(val, "PORTLESS_TLD");
|
|
1655
|
+
return tlds.length > 0 ? tlds : [DEFAULT_TLD];
|
|
1613
1656
|
}
|
|
1614
1657
|
function isHttpsEnvDisabled() {
|
|
1615
1658
|
const val = process.env.PORTLESS_HTTPS;
|
|
@@ -1628,14 +1671,17 @@ function readPersistedProxyState() {
|
|
|
1628
1671
|
const port = readPortFromDir(dir);
|
|
1629
1672
|
if (port !== null) {
|
|
1630
1673
|
const tls2 = readTlsMarker(dir);
|
|
1631
|
-
const
|
|
1674
|
+
const tlds = readTldsFromDir(dir);
|
|
1675
|
+
const tld = tlds[0] ?? DEFAULT_TLD;
|
|
1632
1676
|
const lanIp = readLanMarker(dir);
|
|
1633
|
-
return { port, tls: tls2, tld, lanMode: lanIp !== null ||
|
|
1677
|
+
return { port, tls: tls2, tld, tlds, lanMode: lanIp !== null || tlds.includes("local") };
|
|
1634
1678
|
}
|
|
1635
1679
|
return null;
|
|
1636
1680
|
}
|
|
1637
1681
|
function buildProxyStartConfig(options) {
|
|
1638
|
-
const
|
|
1682
|
+
const requestedTlds = options.tlds && options.tlds.length > 0 ? [...options.tlds] : [options.tld];
|
|
1683
|
+
const effectiveTlds = options.lanMode ? ["local"] : [...new Set(requestedTlds)];
|
|
1684
|
+
const effectiveTld = effectiveTlds[0] ?? DEFAULT_TLD;
|
|
1639
1685
|
const args = [];
|
|
1640
1686
|
if (options.foreground) {
|
|
1641
1687
|
args.push("--foreground");
|
|
@@ -1661,8 +1707,10 @@ function buildProxyStartConfig(options) {
|
|
|
1661
1707
|
args.push(INTERNAL_LAN_IP_FLAG, options.lanIp);
|
|
1662
1708
|
}
|
|
1663
1709
|
}
|
|
1664
|
-
} else if (effectiveTld !== DEFAULT_TLD) {
|
|
1665
|
-
|
|
1710
|
+
} else if (effectiveTlds.length > 1 || effectiveTld !== DEFAULT_TLD) {
|
|
1711
|
+
for (const tld of effectiveTlds) {
|
|
1712
|
+
args.push("--tld", tld);
|
|
1713
|
+
}
|
|
1666
1714
|
}
|
|
1667
1715
|
if (options.useWildcard) {
|
|
1668
1716
|
args.push("--wildcard");
|
|
@@ -1670,7 +1718,7 @@ function buildProxyStartConfig(options) {
|
|
|
1670
1718
|
if (options.skipTrust) {
|
|
1671
1719
|
args.push("--skip-trust");
|
|
1672
1720
|
}
|
|
1673
|
-
return { effectiveTld, args };
|
|
1721
|
+
return { effectiveTld, effectiveTlds, args };
|
|
1674
1722
|
}
|
|
1675
1723
|
async function discoverState() {
|
|
1676
1724
|
if (process.env.PORTLESS_STATE_DIR) {
|
|
@@ -1679,14 +1727,25 @@ async function discoverState() {
|
|
|
1679
1727
|
const lanIp = readLanMarker(dir2);
|
|
1680
1728
|
if (await isProxyRunning(port) || await isPortListening(port)) {
|
|
1681
1729
|
const tls2 = readTlsMarker(dir2);
|
|
1682
|
-
const
|
|
1683
|
-
|
|
1730
|
+
const tlds3 = readTldsFromDir(dir2);
|
|
1731
|
+
const tld = tlds3[0] ?? DEFAULT_TLD;
|
|
1732
|
+
return {
|
|
1733
|
+
dir: dir2,
|
|
1734
|
+
port,
|
|
1735
|
+
tls: tls2,
|
|
1736
|
+
tld,
|
|
1737
|
+
tlds: tlds3,
|
|
1738
|
+
lanMode: lanIp !== null || tlds3.includes("local"),
|
|
1739
|
+
lanIp
|
|
1740
|
+
};
|
|
1684
1741
|
}
|
|
1742
|
+
const tlds2 = readTldsFromDir(dir2);
|
|
1685
1743
|
return {
|
|
1686
1744
|
dir: dir2,
|
|
1687
1745
|
port,
|
|
1688
1746
|
tls: readTlsMarker(dir2),
|
|
1689
|
-
tld:
|
|
1747
|
+
tld: tlds2[0] ?? DEFAULT_TLD,
|
|
1748
|
+
tlds: tlds2,
|
|
1690
1749
|
lanMode: lanIp !== null,
|
|
1691
1750
|
lanIp: null
|
|
1692
1751
|
};
|
|
@@ -1695,14 +1754,16 @@ async function discoverState() {
|
|
|
1695
1754
|
if (userPort !== null) {
|
|
1696
1755
|
if (await isProxyRunning(userPort)) {
|
|
1697
1756
|
const tls2 = readTlsMarker(USER_STATE_DIR);
|
|
1698
|
-
const
|
|
1757
|
+
const tlds2 = readTldsFromDir(USER_STATE_DIR);
|
|
1758
|
+
const tld = tlds2[0] ?? DEFAULT_TLD;
|
|
1699
1759
|
const lanIp = readLanMarker(USER_STATE_DIR);
|
|
1700
1760
|
return {
|
|
1701
1761
|
dir: USER_STATE_DIR,
|
|
1702
1762
|
port: userPort,
|
|
1703
1763
|
tls: tls2,
|
|
1704
1764
|
tld,
|
|
1705
|
-
|
|
1765
|
+
tlds: tlds2,
|
|
1766
|
+
lanMode: lanIp !== null || tlds2.includes("local"),
|
|
1706
1767
|
lanIp
|
|
1707
1768
|
};
|
|
1708
1769
|
}
|
|
@@ -1711,14 +1772,16 @@ async function discoverState() {
|
|
|
1711
1772
|
if (legacyPort !== null) {
|
|
1712
1773
|
if (await isProxyRunning(legacyPort)) {
|
|
1713
1774
|
const tls2 = readTlsMarker(LEGACY_SYSTEM_STATE_DIR);
|
|
1714
|
-
const
|
|
1775
|
+
const tlds2 = readTldsFromDir(LEGACY_SYSTEM_STATE_DIR);
|
|
1776
|
+
const tld = tlds2[0] ?? DEFAULT_TLD;
|
|
1715
1777
|
const lanIp = readLanMarker(LEGACY_SYSTEM_STATE_DIR);
|
|
1716
1778
|
return {
|
|
1717
1779
|
dir: LEGACY_SYSTEM_STATE_DIR,
|
|
1718
1780
|
port: legacyPort,
|
|
1719
1781
|
tls: tls2,
|
|
1720
1782
|
tld,
|
|
1721
|
-
|
|
1783
|
+
tlds: tlds2,
|
|
1784
|
+
lanMode: lanIp !== null || tlds2.includes("local"),
|
|
1722
1785
|
lanIp
|
|
1723
1786
|
};
|
|
1724
1787
|
}
|
|
@@ -1730,17 +1793,28 @@ async function discoverState() {
|
|
|
1730
1793
|
const dir2 = resolveStateDir(port);
|
|
1731
1794
|
const markerTls = readTlsMarker(dir2);
|
|
1732
1795
|
const tls2 = markerTls || port === getProtocolPort(true);
|
|
1733
|
-
const
|
|
1796
|
+
const tlds2 = readTldsFromDir(dir2);
|
|
1797
|
+
const tld = tlds2[0] ?? DEFAULT_TLD;
|
|
1734
1798
|
const lanIp = readLanMarker(dir2);
|
|
1735
|
-
return {
|
|
1799
|
+
return {
|
|
1800
|
+
dir: dir2,
|
|
1801
|
+
port,
|
|
1802
|
+
tls: tls2,
|
|
1803
|
+
tld,
|
|
1804
|
+
tlds: tlds2,
|
|
1805
|
+
lanMode: lanIp !== null || tlds2.includes("local"),
|
|
1806
|
+
lanIp
|
|
1807
|
+
};
|
|
1736
1808
|
}
|
|
1737
1809
|
}
|
|
1738
1810
|
const dir = resolveStateDir(configuredPort);
|
|
1811
|
+
const tlds = readTldsFromDir(dir);
|
|
1739
1812
|
return {
|
|
1740
1813
|
dir,
|
|
1741
1814
|
port: configuredPort,
|
|
1742
1815
|
tls: readTlsMarker(dir),
|
|
1743
|
-
tld:
|
|
1816
|
+
tld: tlds[0] ?? DEFAULT_TLD,
|
|
1817
|
+
tlds,
|
|
1744
1818
|
lanMode: readLanMarker(dir) !== null,
|
|
1745
1819
|
lanIp: null
|
|
1746
1820
|
};
|
|
@@ -1798,7 +1872,7 @@ function isProxyRunning(port, tls2 = false) {
|
|
|
1798
1872
|
}
|
|
1799
1873
|
function isPortListening(port) {
|
|
1800
1874
|
return new Promise((resolve4) => {
|
|
1801
|
-
const socket =
|
|
1875
|
+
const socket = createLoopbackConnection(port);
|
|
1802
1876
|
let settled = false;
|
|
1803
1877
|
const finish = (result) => {
|
|
1804
1878
|
if (settled) return;
|
|
@@ -2025,6 +2099,7 @@ var PORTLESS_STATE_FILES = [
|
|
|
2025
2099
|
"proxy.tls",
|
|
2026
2100
|
"proxy.custom-cert",
|
|
2027
2101
|
"proxy.tld",
|
|
2102
|
+
"proxy.tlds",
|
|
2028
2103
|
"proxy.lan",
|
|
2029
2104
|
"ca-key.pem",
|
|
2030
2105
|
"ca.pem",
|
|
@@ -2797,6 +2872,15 @@ var SYSTEMD_SERVICE = "portless.service";
|
|
|
2797
2872
|
var WINDOWS_TASK_NAME = "Portless Proxy";
|
|
2798
2873
|
var INTERNAL_ELEVATED_ENV = "PORTLESS_INTERNAL_SERVICE_ELEVATED";
|
|
2799
2874
|
var SERVICE_ENV_KEYS = /* @__PURE__ */ new Set(["PORTLESS_SYNC_HOSTS"]);
|
|
2875
|
+
function normalizeTlds(tlds) {
|
|
2876
|
+
return [...new Set(tlds.length > 0 ? tlds : [DEFAULT_TLD])];
|
|
2877
|
+
}
|
|
2878
|
+
function primaryTld(tlds) {
|
|
2879
|
+
return tlds[0] ?? DEFAULT_TLD;
|
|
2880
|
+
}
|
|
2881
|
+
function formatTldList(tlds) {
|
|
2882
|
+
return tlds.map((tld) => `.${tld}`).join(", ");
|
|
2883
|
+
}
|
|
2800
2884
|
var DEFAULT_SERVICE_CONFIG = {
|
|
2801
2885
|
proxyPort: DEFAULT_SERVICE_PORT,
|
|
2802
2886
|
useHttps: true,
|
|
@@ -2806,6 +2890,7 @@ var DEFAULT_SERVICE_CONFIG = {
|
|
|
2806
2890
|
lanIp: null,
|
|
2807
2891
|
lanIpExplicit: false,
|
|
2808
2892
|
tld: DEFAULT_TLD,
|
|
2893
|
+
tlds: [DEFAULT_TLD],
|
|
2809
2894
|
useWildcard: false,
|
|
2810
2895
|
extraEnv: {}
|
|
2811
2896
|
};
|
|
@@ -2892,10 +2977,8 @@ function parseServiceInstallConfig(args, env = process.env, options = {}) {
|
|
|
2892
2977
|
config.lanIpExplicit = true;
|
|
2893
2978
|
}
|
|
2894
2979
|
if (env.PORTLESS_TLD) {
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
if (err) throw new Error(`PORTLESS_TLD: ${err}`);
|
|
2898
|
-
config.tld = tld;
|
|
2980
|
+
config.tlds = normalizeTlds(parseTldList(env.PORTLESS_TLD, "PORTLESS_TLD"));
|
|
2981
|
+
config.tld = primaryTld(config.tlds);
|
|
2899
2982
|
}
|
|
2900
2983
|
const envWildcard = parseBooleanEnv(env.PORTLESS_WILDCARD);
|
|
2901
2984
|
if (envWildcard !== null) {
|
|
@@ -2907,6 +2990,7 @@ function parseServiceInstallConfig(args, env = process.env, options = {}) {
|
|
|
2907
2990
|
config.proxyPort = getProtocolPort(config.useHttps);
|
|
2908
2991
|
}
|
|
2909
2992
|
const tokens = args[0] === "service" ? args.slice(2) : args;
|
|
2993
|
+
let tldFlagSeen = false;
|
|
2910
2994
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
2911
2995
|
const token = tokens[i];
|
|
2912
2996
|
switch (token) {
|
|
@@ -2931,10 +3015,10 @@ function parseServiceInstallConfig(args, env = process.env, options = {}) {
|
|
|
2931
3015
|
i += 1;
|
|
2932
3016
|
break;
|
|
2933
3017
|
case "--tld": {
|
|
2934
|
-
const
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
3018
|
+
const tlds = parseTldList(getFlagValue(tokens, i, token));
|
|
3019
|
+
config.tlds = normalizeTlds([...tldFlagSeen ? config.tlds : [], ...tlds]);
|
|
3020
|
+
config.tld = primaryTld(config.tlds);
|
|
3021
|
+
tldFlagSeen = true;
|
|
2938
3022
|
i += 1;
|
|
2939
3023
|
break;
|
|
2940
3024
|
}
|
|
@@ -2974,6 +3058,9 @@ function parseServiceInstallConfig(args, env = process.env, options = {}) {
|
|
|
2974
3058
|
if (!config.lanMode) {
|
|
2975
3059
|
config.lanIp = null;
|
|
2976
3060
|
config.lanIpExplicit = false;
|
|
3061
|
+
} else {
|
|
3062
|
+
config.tlds = ["local"];
|
|
3063
|
+
config.tld = "local";
|
|
2977
3064
|
}
|
|
2978
3065
|
return config;
|
|
2979
3066
|
}
|
|
@@ -3019,6 +3106,7 @@ function buildProxyCommand(entryScript, serviceConfig) {
|
|
|
3019
3106
|
lanIp: serviceConfig.lanIp,
|
|
3020
3107
|
lanIpExplicit: serviceConfig.lanIpExplicit,
|
|
3021
3108
|
tld: serviceConfig.tld,
|
|
3109
|
+
tlds: serviceConfig.tlds,
|
|
3022
3110
|
useWildcard: serviceConfig.useWildcard,
|
|
3023
3111
|
foreground: true,
|
|
3024
3112
|
includePort: true,
|
|
@@ -3041,8 +3129,8 @@ function buildServiceEnv(ctx) {
|
|
|
3041
3129
|
}
|
|
3042
3130
|
if (ctx.config.lanMode) {
|
|
3043
3131
|
env.PORTLESS_TLD = "local";
|
|
3044
|
-
} else if (ctx.config.tld !== DEFAULT_TLD) {
|
|
3045
|
-
env.PORTLESS_TLD = ctx.config.
|
|
3132
|
+
} else if (ctx.config.tlds.length > 1 || ctx.config.tld !== DEFAULT_TLD) {
|
|
3133
|
+
env.PORTLESS_TLD = ctx.config.tlds.join(",");
|
|
3046
3134
|
}
|
|
3047
3135
|
if (ctx.platform === "win32") {
|
|
3048
3136
|
env.USERPROFILE = ctx.user.home;
|
|
@@ -3127,6 +3215,10 @@ function buildServiceSpec(options) {
|
|
|
3127
3215
|
...options.installConfig,
|
|
3128
3216
|
extraEnv: options.installConfig?.extraEnv ?? {}
|
|
3129
3217
|
};
|
|
3218
|
+
installConfig.tlds = installConfig.lanMode ? ["local"] : normalizeTlds(
|
|
3219
|
+
options.installConfig?.tlds ?? (options.installConfig?.tld ? [options.installConfig.tld] : installConfig.tlds)
|
|
3220
|
+
);
|
|
3221
|
+
installConfig.tld = primaryTld(installConfig.tlds);
|
|
3130
3222
|
const stateDir = options.stateDir || installConfig.stateDir || defaultStateDir(options.platform, options.userHome);
|
|
3131
3223
|
const normalizedConfig = {
|
|
3132
3224
|
...installConfig,
|
|
@@ -3617,7 +3709,7 @@ async function printServiceStatus(entryScript, runner) {
|
|
|
3617
3709
|
` Proxy on ${config.proxyPort}: ${status.proxyRunning ? "responding" : "not responding"}`
|
|
3618
3710
|
);
|
|
3619
3711
|
console.log(` HTTPS: ${config.useHttps ? "yes" : "no"}`);
|
|
3620
|
-
console.log(`
|
|
3712
|
+
console.log(` TLDs: ${config.lanMode ? ".local" : formatTldList(config.tlds)}`);
|
|
3621
3713
|
console.log(` LAN mode: ${config.lanMode ? "yes" : "no"}`);
|
|
3622
3714
|
if (config.lanIpExplicit && config.lanIp) {
|
|
3623
3715
|
console.log(` LAN IP: ${config.lanIp}`);
|
|
@@ -3645,7 +3737,7 @@ ${colors_default.bold("Install options:")}
|
|
|
3645
3737
|
--https Enable HTTPS
|
|
3646
3738
|
--lan Enable LAN mode
|
|
3647
3739
|
--ip <address> Pin a specific LAN IP
|
|
3648
|
-
--tld <tld> Use a custom TLD outside LAN mode
|
|
3740
|
+
--tld <tld> Use a custom TLD outside LAN mode, repeatable
|
|
3649
3741
|
--wildcard Allow subdomain fallback
|
|
3650
3742
|
--cert <path> Use a custom TLS certificate
|
|
3651
3743
|
--key <path> Use a custom TLS private key
|
|
@@ -3695,7 +3787,17 @@ var DEBOUNCE_MS = 100;
|
|
|
3695
3787
|
var POLL_INTERVAL_MS = 3e3;
|
|
3696
3788
|
var EXIT_TIMEOUT_MS = 2e3;
|
|
3697
3789
|
var SUDO_SPAWN_TIMEOUT_MS = 3e4;
|
|
3698
|
-
function
|
|
3790
|
+
function normalizeTlds2(tlds) {
|
|
3791
|
+
return [...new Set(tlds.length > 0 ? tlds : [DEFAULT_TLD])];
|
|
3792
|
+
}
|
|
3793
|
+
function primaryTld2(tlds) {
|
|
3794
|
+
return tlds[0] ?? DEFAULT_TLD;
|
|
3795
|
+
}
|
|
3796
|
+
function formatTldList2(tlds) {
|
|
3797
|
+
return tlds.map((tld) => `.${tld}`).join(", ");
|
|
3798
|
+
}
|
|
3799
|
+
function defaultProxyConfig(tlds, useHttps, lanMode) {
|
|
3800
|
+
const effectiveTlds = lanMode ? ["local"] : normalizeTlds2(tlds);
|
|
3699
3801
|
return {
|
|
3700
3802
|
useHttps,
|
|
3701
3803
|
customCertPath: null,
|
|
@@ -3703,13 +3805,14 @@ function defaultProxyConfig(tld, useHttps, lanMode) {
|
|
|
3703
3805
|
lanMode,
|
|
3704
3806
|
lanIp: null,
|
|
3705
3807
|
lanIpExplicit: false,
|
|
3706
|
-
tld:
|
|
3808
|
+
tld: primaryTld2(effectiveTlds),
|
|
3809
|
+
tlds: effectiveTlds,
|
|
3707
3810
|
useWildcard: false
|
|
3708
3811
|
};
|
|
3709
3812
|
}
|
|
3710
3813
|
function resolveProxyConfig(options) {
|
|
3711
3814
|
const config = defaultProxyConfig(
|
|
3712
|
-
options.
|
|
3815
|
+
options.defaultTlds,
|
|
3713
3816
|
options.useHttps,
|
|
3714
3817
|
options.explicit.lanMode ? options.lanMode : options.persistedLanMode
|
|
3715
3818
|
);
|
|
@@ -3730,8 +3833,9 @@ function resolveProxyConfig(options) {
|
|
|
3730
3833
|
if (!options.lanMode) {
|
|
3731
3834
|
config.lanIp = null;
|
|
3732
3835
|
config.lanIpExplicit = false;
|
|
3733
|
-
if (!options.explicit.
|
|
3734
|
-
config.
|
|
3836
|
+
if (!options.explicit.tlds) {
|
|
3837
|
+
config.tlds = normalizeTlds2(options.defaultTlds);
|
|
3838
|
+
config.tld = primaryTld2(config.tlds);
|
|
3735
3839
|
}
|
|
3736
3840
|
}
|
|
3737
3841
|
}
|
|
@@ -3740,8 +3844,9 @@ function resolveProxyConfig(options) {
|
|
|
3740
3844
|
config.lanIp = options.lanIp;
|
|
3741
3845
|
config.lanIpExplicit = true;
|
|
3742
3846
|
}
|
|
3743
|
-
if (options.explicit.
|
|
3744
|
-
config.
|
|
3847
|
+
if (options.explicit.tlds) {
|
|
3848
|
+
config.tlds = normalizeTlds2(options.tlds);
|
|
3849
|
+
config.tld = primaryTld2(config.tlds);
|
|
3745
3850
|
}
|
|
3746
3851
|
if (options.explicit.useWildcard) {
|
|
3747
3852
|
config.useWildcard = options.useWildcard;
|
|
@@ -3751,6 +3856,7 @@ function resolveProxyConfig(options) {
|
|
|
3751
3856
|
config.lanIpExplicit = false;
|
|
3752
3857
|
}
|
|
3753
3858
|
if (config.lanMode) {
|
|
3859
|
+
config.tlds = ["local"];
|
|
3754
3860
|
config.tld = "local";
|
|
3755
3861
|
if (!config.lanIpExplicit) {
|
|
3756
3862
|
config.lanIp = null;
|
|
@@ -3764,15 +3870,17 @@ function resolveProxyConfig(options) {
|
|
|
3764
3870
|
}
|
|
3765
3871
|
function readCurrentProxyConfig(dir) {
|
|
3766
3872
|
const lanIp = readLanMarker(dir);
|
|
3767
|
-
const
|
|
3873
|
+
const tlds = readTldsFromDir(dir);
|
|
3874
|
+
const tld = primaryTld2(tlds);
|
|
3768
3875
|
return {
|
|
3769
3876
|
useHttps: readTlsMarker(dir),
|
|
3770
3877
|
customCertPath: null,
|
|
3771
3878
|
customKeyPath: null,
|
|
3772
|
-
lanMode: lanIp !== null ||
|
|
3879
|
+
lanMode: lanIp !== null || tlds.includes("local"),
|
|
3773
3880
|
lanIp,
|
|
3774
3881
|
lanIpExplicit: false,
|
|
3775
3882
|
tld,
|
|
3883
|
+
tlds,
|
|
3776
3884
|
useWildcard: false
|
|
3777
3885
|
};
|
|
3778
3886
|
}
|
|
@@ -3793,9 +3901,9 @@ function getProxyConfigMismatchMessages(desiredConfig, actualConfig, explicit) {
|
|
|
3793
3901
|
desiredConfig.useHttps ? "requested HTTPS, but the running proxy is using HTTP" : "requested HTTP, but the running proxy is using HTTPS"
|
|
3794
3902
|
);
|
|
3795
3903
|
}
|
|
3796
|
-
if (explicit.
|
|
3904
|
+
if (explicit.tlds && (desiredConfig.tlds.length !== actualConfig.tlds.length || desiredConfig.tlds.some((tld, index) => tld !== actualConfig.tlds[index]))) {
|
|
3797
3905
|
messages.push(
|
|
3798
|
-
`requested
|
|
3906
|
+
`requested ${formatTldList2(desiredConfig.tlds)}, but the running proxy is using ${formatTldList2(actualConfig.tlds)}`
|
|
3799
3907
|
);
|
|
3800
3908
|
}
|
|
3801
3909
|
return messages;
|
|
@@ -3810,6 +3918,7 @@ function formatProxyStartCommand(proxyPort, config) {
|
|
|
3810
3918
|
lanIp: config.lanIpExplicit ? config.lanIp : null,
|
|
3811
3919
|
lanIpExplicit: config.lanIpExplicit,
|
|
3812
3920
|
tld: config.tld,
|
|
3921
|
+
tlds: config.tlds,
|
|
3813
3922
|
useWildcard: config.useWildcard,
|
|
3814
3923
|
includePort: proxyPort !== getDefaultPort(config.useHttps),
|
|
3815
3924
|
proxyPort
|
|
@@ -3903,7 +4012,46 @@ function formatProcessExitSuffix(code, signal) {
|
|
|
3903
4012
|
if (code !== null) return ` (exit ${code})`;
|
|
3904
4013
|
return "";
|
|
3905
4014
|
}
|
|
3906
|
-
function
|
|
4015
|
+
function buildHostnames(name, tlds) {
|
|
4016
|
+
return parseHostnames(name, normalizeTlds2(tlds));
|
|
4017
|
+
}
|
|
4018
|
+
function formatUrls(hostnames, proxyPort, tls2) {
|
|
4019
|
+
return hostnames.map((hostname) => formatUrl(hostname, proxyPort, tls2));
|
|
4020
|
+
}
|
|
4021
|
+
function formatViteAllowedHosts(tlds) {
|
|
4022
|
+
return tlds.map((configuredTld) => `.${configuredTld}`).join(",");
|
|
4023
|
+
}
|
|
4024
|
+
function addRoutes(store, hostnames, port, pid, force = false) {
|
|
4025
|
+
const registered = [];
|
|
4026
|
+
const killedPids = [];
|
|
4027
|
+
try {
|
|
4028
|
+
for (const hostname of hostnames) {
|
|
4029
|
+
const killedPid = store.addRoute(hostname, port, pid, force);
|
|
4030
|
+
registered.push(hostname);
|
|
4031
|
+
if (killedPid !== void 0) {
|
|
4032
|
+
killedPids.push(killedPid);
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
} catch (err) {
|
|
4036
|
+
for (const hostname of registered) {
|
|
4037
|
+
try {
|
|
4038
|
+
store.removeRoute(hostname, pid);
|
|
4039
|
+
} catch {
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
throw err;
|
|
4043
|
+
}
|
|
4044
|
+
return [...new Set(killedPids)];
|
|
4045
|
+
}
|
|
4046
|
+
function removeRoutes(store, hostnames, ownerPid) {
|
|
4047
|
+
for (const hostname of hostnames) {
|
|
4048
|
+
try {
|
|
4049
|
+
store.removeRoute(hostname, ownerPid);
|
|
4050
|
+
} catch {
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
function startProxyServer(store, proxyPort, tld, tlds, tlsOptions, lanIp, strict, customCert = false) {
|
|
3907
4055
|
store.ensureDir();
|
|
3908
4056
|
const isTls = !!tlsOptions;
|
|
3909
4057
|
const mdnsSupport = isMdnsSupported();
|
|
@@ -3995,6 +4143,7 @@ function startProxyServer(store, proxyPort, tld, tlsOptions, lanIp, strict, cust
|
|
|
3995
4143
|
getRoutes: () => cachedRoutes,
|
|
3996
4144
|
proxyPort,
|
|
3997
4145
|
tld,
|
|
4146
|
+
tlds,
|
|
3998
4147
|
strict,
|
|
3999
4148
|
onError: (msg) => console.error(colors_default.red(msg)),
|
|
4000
4149
|
tls: tlsOptions
|
|
@@ -4033,11 +4182,11 @@ function startProxyServer(store, proxyPort, tld, tlsOptions, lanIp, strict, cust
|
|
|
4033
4182
|
fs9.writeFileSync(store.portFilePath, proxyPort.toString(), { mode: FILE_MODE });
|
|
4034
4183
|
writeTlsMarker(store.dir, isTls);
|
|
4035
4184
|
writeCustomCertMarker(store.dir, isTls && customCert);
|
|
4036
|
-
|
|
4185
|
+
writeTldsFile(store.dir, tlds);
|
|
4037
4186
|
writeLanMarker(store.dir, activeLanIp);
|
|
4038
4187
|
fixOwnership(store.dir, store.pidPath, store.portFilePath);
|
|
4039
4188
|
const proto = isTls ? "HTTPS/2" : "HTTP";
|
|
4040
|
-
const tldLabel = tld !== DEFAULT_TLD ? ` (
|
|
4189
|
+
const tldLabel = tlds.length > 1 || tld !== DEFAULT_TLD ? ` (TLDs: ${formatTldList2(tlds)})` : "";
|
|
4041
4190
|
const modeLabel = strict === false ? " (wildcard)" : "";
|
|
4042
4191
|
console.log(
|
|
4043
4192
|
colors_default.green(`${proto} proxy listening on port ${proxyPort}${tldLabel}${modeLabel}`)
|
|
@@ -4255,28 +4404,29 @@ function listRoutes(store, proxyPort, tls2) {
|
|
|
4255
4404
|
console.log();
|
|
4256
4405
|
}
|
|
4257
4406
|
function resolveProxyDesiredState(lanMode) {
|
|
4258
|
-
const
|
|
4407
|
+
const envTlds = getDefaultTlds();
|
|
4408
|
+
const envTld = primaryTld2(envTlds);
|
|
4259
4409
|
const explicit = {
|
|
4260
4410
|
useHttps: process.env.PORTLESS_HTTPS !== void 0,
|
|
4261
4411
|
customCert: false,
|
|
4262
4412
|
lanMode: process.env.PORTLESS_LAN !== void 0,
|
|
4263
4413
|
lanIp: process.env.PORTLESS_LAN_IP !== void 0,
|
|
4264
|
-
|
|
4414
|
+
tlds: process.env.PORTLESS_TLD !== void 0,
|
|
4265
4415
|
useWildcard: process.env.PORTLESS_WILDCARD !== void 0
|
|
4266
4416
|
};
|
|
4267
4417
|
const desiredConfig = resolveProxyConfig({
|
|
4268
4418
|
persistedLanMode: lanMode,
|
|
4269
4419
|
explicit,
|
|
4270
|
-
|
|
4420
|
+
defaultTlds: envTlds,
|
|
4271
4421
|
useHttps: !isHttpsEnvDisabled(),
|
|
4272
4422
|
customCertPath: null,
|
|
4273
4423
|
customKeyPath: null,
|
|
4274
4424
|
lanMode: isLanEnvEnabled(),
|
|
4275
4425
|
lanIp: process.env.PORTLESS_LAN_IP || null,
|
|
4276
|
-
|
|
4426
|
+
tlds: envTlds,
|
|
4277
4427
|
useWildcard: isWildcardEnvEnabled()
|
|
4278
4428
|
});
|
|
4279
|
-
return { explicit, desiredConfig, envTld };
|
|
4429
|
+
return { explicit, desiredConfig, envTld, envTlds };
|
|
4280
4430
|
}
|
|
4281
4431
|
async function ensureProxyRunning(proxyPort, tls2, desired) {
|
|
4282
4432
|
const { explicit, desiredConfig } = desired;
|
|
@@ -4292,8 +4442,9 @@ async function ensureProxyRunning(proxyPort, tls2, desired) {
|
|
|
4292
4442
|
if (!explicit.useHttps && persisted.tls !== desiredConfig.useHttps) {
|
|
4293
4443
|
startConfig.useHttps = persisted.tls;
|
|
4294
4444
|
}
|
|
4295
|
-
if (!explicit.
|
|
4296
|
-
startConfig.
|
|
4445
|
+
if (!explicit.tlds && (persisted.tlds.length !== desiredConfig.tlds.length || persisted.tlds.some((tld, index) => tld !== desiredConfig.tlds[index]))) {
|
|
4446
|
+
startConfig.tlds = persisted.tlds;
|
|
4447
|
+
startConfig.tld = primaryTld2(persisted.tlds);
|
|
4297
4448
|
}
|
|
4298
4449
|
if (!explicit.lanMode && persisted.lanMode !== desiredConfig.lanMode) {
|
|
4299
4450
|
startConfig.lanMode = persisted.lanMode;
|
|
@@ -4329,6 +4480,7 @@ async function ensureProxyRunning(proxyPort, tls2, desired) {
|
|
|
4329
4480
|
lanIp: startConfig.lanIpExplicit ? startConfig.lanIp : null,
|
|
4330
4481
|
lanIpExplicit: startConfig.lanIpExplicit,
|
|
4331
4482
|
tld: startConfig.tld,
|
|
4483
|
+
tlds: startConfig.tlds,
|
|
4332
4484
|
useWildcard: startConfig.useWildcard,
|
|
4333
4485
|
includePort: startPort !== void 0,
|
|
4334
4486
|
proxyPort: startPort
|
|
@@ -4363,7 +4515,7 @@ async function ensureProxyRunning(proxyPort, tls2, desired) {
|
|
|
4363
4515
|
}
|
|
4364
4516
|
return { started: true, state: discovered };
|
|
4365
4517
|
}
|
|
4366
|
-
async function runApp(initialStore, proxyPort, stateDir, name, commandArgs, tls2,
|
|
4518
|
+
async function runApp(initialStore, proxyPort, stateDir, name, commandArgs, tls2, tlds, force, autoInfo, desiredPort, lanMode = false, lanIp) {
|
|
4367
4519
|
let store = initialStore;
|
|
4368
4520
|
console.log(chalk.blue.bold(`
|
|
4369
4521
|
portless
|
|
@@ -4410,12 +4562,12 @@ portless
|
|
|
4410
4562
|
console.error(colors_default.red(`Error: ${err.message}`));
|
|
4411
4563
|
process.exit(1);
|
|
4412
4564
|
}
|
|
4413
|
-
|
|
4565
|
+
buildHostnames(name, tlds);
|
|
4414
4566
|
const ensureResult = await ensureProxyRunning(proxyPort, tls2, desired);
|
|
4415
4567
|
if (ensureResult.started) {
|
|
4416
4568
|
proxyPort = ensureResult.state.port;
|
|
4417
4569
|
stateDir = ensureResult.state.dir;
|
|
4418
|
-
|
|
4570
|
+
tlds = ensureResult.state.tlds;
|
|
4419
4571
|
tls2 = ensureResult.state.tls;
|
|
4420
4572
|
lanMode = ensureResult.state.lanMode;
|
|
4421
4573
|
lanIp = ensureResult.state.lanIp;
|
|
@@ -4437,20 +4589,22 @@ portless
|
|
|
4437
4589
|
}
|
|
4438
4590
|
lanMode = runningConfig.lanMode;
|
|
4439
4591
|
lanIp = runningConfig.lanIp;
|
|
4592
|
+
tlds = runningConfig.tlds;
|
|
4440
4593
|
console.log(chalk.gray("-- Proxy is running"));
|
|
4441
4594
|
}
|
|
4442
|
-
const
|
|
4443
|
-
|
|
4595
|
+
const hostnames = buildHostnames(name, tlds);
|
|
4596
|
+
const hostname = hostnames[0];
|
|
4597
|
+
if (desired.explicit.tlds && (desired.envTlds.some((envConfiguredTld, index) => envConfiguredTld !== tlds[index]) || desired.envTlds.length !== tlds.length)) {
|
|
4444
4598
|
console.warn(
|
|
4445
4599
|
chalk.yellow(
|
|
4446
|
-
`Warning: PORTLESS_TLD=${desired.
|
|
4600
|
+
`Warning: PORTLESS_TLD=${desired.envTlds.join(",")} but the running proxy uses ${formatTldList2(tlds)}.`
|
|
4447
4601
|
)
|
|
4448
4602
|
);
|
|
4449
4603
|
}
|
|
4450
4604
|
if (lanIp) {
|
|
4451
|
-
console.log(chalk.gray(`-- ${
|
|
4605
|
+
console.log(chalk.gray(`-- ${hostnames.join(", ")} (LAN: ${lanIp})`));
|
|
4452
4606
|
} else {
|
|
4453
|
-
console.log(chalk.gray(`-- ${
|
|
4607
|
+
console.log(chalk.gray(`-- ${hostnames.join(", ")} (auto-resolves to 127.0.0.1)`));
|
|
4454
4608
|
}
|
|
4455
4609
|
if (autoInfo) {
|
|
4456
4610
|
const baseName = autoInfo.prefix ? name.slice(autoInfo.prefix.length + 1) : name;
|
|
@@ -4465,9 +4619,9 @@ portless
|
|
|
4465
4619
|
} else {
|
|
4466
4620
|
console.log(colors_default.green(`-- Using port ${port}`));
|
|
4467
4621
|
}
|
|
4468
|
-
let
|
|
4622
|
+
let killedPids = [];
|
|
4469
4623
|
try {
|
|
4470
|
-
|
|
4624
|
+
killedPids = addRoutes(store, hostnames, port, process.pid, force);
|
|
4471
4625
|
} catch (err) {
|
|
4472
4626
|
if (err instanceof RouteConflictError) {
|
|
4473
4627
|
console.error(colors_default.red(`Error: ${err.message}`));
|
|
@@ -4475,13 +4629,20 @@ portless
|
|
|
4475
4629
|
}
|
|
4476
4630
|
throw err;
|
|
4477
4631
|
}
|
|
4478
|
-
if (
|
|
4479
|
-
console.log(colors_default.yellow(`Killed existing process
|
|
4632
|
+
if (killedPids.length > 0) {
|
|
4633
|
+
console.log(colors_default.yellow(`Killed existing process(es): ${killedPids.join(", ")}`));
|
|
4480
4634
|
}
|
|
4481
4635
|
const finalUrl = formatUrl(hostname, proxyPort, tls2);
|
|
4636
|
+
const allUrls = formatUrls(hostnames, proxyPort, tls2);
|
|
4482
4637
|
console.log(chalk.cyan.bold(`
|
|
4483
4638
|
-> ${finalUrl}
|
|
4484
4639
|
`));
|
|
4640
|
+
for (const extraUrl of allUrls.slice(1)) {
|
|
4641
|
+
console.log(chalk.cyan(` also -> ${extraUrl}`));
|
|
4642
|
+
}
|
|
4643
|
+
if (allUrls.length > 1) {
|
|
4644
|
+
console.log("");
|
|
4645
|
+
}
|
|
4485
4646
|
if (lanIp) {
|
|
4486
4647
|
console.log(chalk.green(` LAN -> ${finalUrl}`));
|
|
4487
4648
|
console.log(chalk.gray(" (accessible from other devices on the same WiFi network)\n"));
|
|
@@ -4594,7 +4755,7 @@ portless
|
|
|
4594
4755
|
} catch {
|
|
4595
4756
|
}
|
|
4596
4757
|
try {
|
|
4597
|
-
store
|
|
4758
|
+
removeRoutes(store, hostnames, process.pid);
|
|
4598
4759
|
} catch {
|
|
4599
4760
|
}
|
|
4600
4761
|
process.exit(1);
|
|
@@ -4628,7 +4789,7 @@ portless
|
|
|
4628
4789
|
PORT: port.toString(),
|
|
4629
4790
|
...hostBind ? { HOST: hostBind } : {},
|
|
4630
4791
|
PORTLESS_URL: finalUrl,
|
|
4631
|
-
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS:
|
|
4792
|
+
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: formatViteAllowedHosts(tlds),
|
|
4632
4793
|
// Note: EXPO_PACKAGER_PROXY_URL is not used — expo-dev-client removed
|
|
4633
4794
|
// baked-in pinging, making this env var ineffective. Expo handles its
|
|
4634
4795
|
// own LAN discovery natively.
|
|
@@ -4648,7 +4809,7 @@ portless
|
|
|
4648
4809
|
} catch {
|
|
4649
4810
|
}
|
|
4650
4811
|
try {
|
|
4651
|
-
store
|
|
4812
|
+
removeRoutes(store, hostnames, process.pid);
|
|
4652
4813
|
} catch {
|
|
4653
4814
|
}
|
|
4654
4815
|
}
|
|
@@ -4941,7 +5102,7 @@ ${colors_default.bold("Options:")}
|
|
|
4941
5102
|
--cert <path> Use a custom TLS certificate
|
|
4942
5103
|
--key <path> Use a custom TLS private key
|
|
4943
5104
|
--foreground Run proxy in foreground (for debugging)
|
|
4944
|
-
--tld <tld> Use a custom TLD instead of .localhost
|
|
5105
|
+
--tld <tld> Use a custom TLD instead of .localhost; repeat for more
|
|
4945
5106
|
--wildcard Allow unregistered subdomains to fall back to parent route
|
|
4946
5107
|
--state-dir <path> Use a custom state directory with service install
|
|
4947
5108
|
--app-port <number> Use a fixed port for the app (skip auto-assignment)
|
|
@@ -4958,7 +5119,7 @@ ${colors_default.bold("Environment variables:")}
|
|
|
4958
5119
|
PORTLESS_HTTPS=0 Disable HTTPS (same as --no-tls)
|
|
4959
5120
|
PORTLESS_LAN=1 Enable LAN mode when set to 1 (set in .bashrc / .zshrc)
|
|
4960
5121
|
PORTLESS_LAN_IP=<address> Pin a specific LAN IP for LAN mode
|
|
4961
|
-
PORTLESS_TLD=<tld>
|
|
5122
|
+
PORTLESS_TLD=<tld>[,<tld>] Use one or more TLDs (e.g. localhost,test)
|
|
4962
5123
|
PORTLESS_WILDCARD=1 Allow unregistered subdomains to fall back to parent route
|
|
4963
5124
|
PORTLESS_SYNC_HOSTS=0 Disable auto-sync of ${HOSTS_DISPLAY} (on by default)
|
|
4964
5125
|
PORTLESS_TAILSCALE=1 Share apps on your Tailscale network (same as --tailscale)
|
|
@@ -4970,7 +5131,7 @@ ${colors_default.bold("Environment variables:")}
|
|
|
4970
5131
|
${colors_default.bold("Child process environment:")}
|
|
4971
5132
|
PORT Ephemeral port the child should listen on
|
|
4972
5133
|
HOST Usually 127.0.0.1 (omitted for Expo in LAN mode)
|
|
4973
|
-
PORTLESS_URL
|
|
5134
|
+
PORTLESS_URL Primary public URL of the app
|
|
4974
5135
|
PORTLESS_LAN Set to 1 when proxy is in LAN mode
|
|
4975
5136
|
PORTLESS_TAILSCALE_URL Tailscale URL of the app (when --tailscale is active)
|
|
4976
5137
|
PORTLESS_NGROK_URL ngrok URL of the app (when --ngrok is active)
|
|
@@ -4996,7 +5157,7 @@ ${colors_default.bold("Reserved names:")}
|
|
|
4996
5157
|
process.exit(0);
|
|
4997
5158
|
}
|
|
4998
5159
|
function printVersion() {
|
|
4999
|
-
console.log("0.15.
|
|
5160
|
+
console.log("0.15.2");
|
|
5000
5161
|
process.exit(0);
|
|
5001
5162
|
}
|
|
5002
5163
|
async function handleTrust() {
|
|
@@ -5271,8 +5432,8 @@ ${colors_default.bold("Examples:")}
|
|
|
5271
5432
|
const name = positional[0];
|
|
5272
5433
|
const worktree = skipWorktree ? null : detectWorktreePrefix();
|
|
5273
5434
|
const effectiveName = worktree ? `${worktree.prefix}.${name}` : name;
|
|
5274
|
-
const { port, tls: tls2,
|
|
5275
|
-
const hostname =
|
|
5435
|
+
const { port, tls: tls2, tlds } = await discoverState();
|
|
5436
|
+
const hostname = buildHostnames(effectiveName, tlds)[0];
|
|
5276
5437
|
const url = formatUrl(hostname, port, tls2);
|
|
5277
5438
|
process.stdout.write(url + "\n");
|
|
5278
5439
|
}
|
|
@@ -5293,7 +5454,7 @@ ${colors_default.bold("Examples:")}
|
|
|
5293
5454
|
`);
|
|
5294
5455
|
process.exit(0);
|
|
5295
5456
|
}
|
|
5296
|
-
const { dir,
|
|
5457
|
+
const { dir, tlds } = await discoverState();
|
|
5297
5458
|
const store = new RouteStore(dir, {
|
|
5298
5459
|
onWarning: (msg) => console.warn(colors_default.yellow(msg))
|
|
5299
5460
|
});
|
|
@@ -5304,15 +5465,15 @@ ${colors_default.bold("Examples:")}
|
|
|
5304
5465
|
console.error(colors_default.cyan(" portless alias --remove <name>"));
|
|
5305
5466
|
process.exit(1);
|
|
5306
5467
|
}
|
|
5307
|
-
const
|
|
5468
|
+
const hostnames2 = buildHostnames(aliasName2, tlds);
|
|
5308
5469
|
const routes = store.loadRoutes();
|
|
5309
|
-
const existing = routes.find((r) => r.hostname
|
|
5470
|
+
const existing = routes.find((r) => hostnames2.includes(r.hostname) && r.pid === 0);
|
|
5310
5471
|
if (!existing) {
|
|
5311
|
-
console.error(colors_default.red(`Error: No alias found for "${
|
|
5472
|
+
console.error(colors_default.red(`Error: No alias found for "${hostnames2.join(", ")}".`));
|
|
5312
5473
|
process.exit(1);
|
|
5313
5474
|
}
|
|
5314
|
-
store
|
|
5315
|
-
console.log(colors_default.green(`Removed alias: ${
|
|
5475
|
+
removeRoutes(store, hostnames2);
|
|
5476
|
+
console.log(colors_default.green(`Removed alias: ${hostnames2.join(", ")}`));
|
|
5316
5477
|
return;
|
|
5317
5478
|
}
|
|
5318
5479
|
const aliasName = args[1];
|
|
@@ -5326,15 +5487,15 @@ ${colors_default.bold("Examples:")}
|
|
|
5326
5487
|
console.error(colors_default.cyan(" portless alias my-postgres 5432"));
|
|
5327
5488
|
process.exit(1);
|
|
5328
5489
|
}
|
|
5329
|
-
const
|
|
5490
|
+
const hostnames = buildHostnames(aliasName, tlds);
|
|
5330
5491
|
const port = parseInt(aliasPort, 10);
|
|
5331
5492
|
if (isNaN(port) || port < 1 || port > 65535) {
|
|
5332
5493
|
console.error(colors_default.red(`Error: Invalid port "${aliasPort}". Must be 1-65535.`));
|
|
5333
5494
|
process.exit(1);
|
|
5334
5495
|
}
|
|
5335
5496
|
const force = args.includes("--force");
|
|
5336
|
-
store
|
|
5337
|
-
console.log(colors_default.green(`Alias registered: ${
|
|
5497
|
+
addRoutes(store, hostnames, port, 0, force);
|
|
5498
|
+
console.log(colors_default.green(`Alias registered: ${hostnames.join(", ")} -> 127.0.0.1:${port}`));
|
|
5338
5499
|
}
|
|
5339
5500
|
async function handleHosts(args) {
|
|
5340
5501
|
if (args[1] === "--help" || args[1] === "-h") {
|
|
@@ -5524,6 +5685,7 @@ ${colors_default.bold("Options:")}
|
|
|
5524
5685
|
port: getDefaultPort(!isHttpsEnvDisabled()),
|
|
5525
5686
|
tls: !isHttpsEnvDisabled(),
|
|
5526
5687
|
tld: DEFAULT_TLD,
|
|
5688
|
+
tlds: [DEFAULT_TLD],
|
|
5527
5689
|
lanMode: isLanEnvEnabled(),
|
|
5528
5690
|
lanIp: null
|
|
5529
5691
|
};
|
|
@@ -5545,12 +5707,14 @@ ${colors_default.bold("Options:")}
|
|
|
5545
5707
|
const proxyUsesCustomCert = proxyTls && readCustomCertMarker(state.dir);
|
|
5546
5708
|
const stateExists = fs9.existsSync(state.dir);
|
|
5547
5709
|
console.log(colors_default.blue.bold("\nportless doctor\n"));
|
|
5548
|
-
console.log(`Version: ${"0.15.
|
|
5710
|
+
console.log(`Version: ${"0.15.2"}`);
|
|
5549
5711
|
console.log(`Node.js: ${process.versions.node}`);
|
|
5550
5712
|
console.log(`Platform: ${process.platform} ${process.arch}`);
|
|
5551
5713
|
console.log(`State dir: ${state.dir}`);
|
|
5552
5714
|
console.log(`Proxy target: ${formatUrl("127.0.0.1", proxyPort, proxyTls)}`);
|
|
5553
|
-
console.log(
|
|
5715
|
+
console.log(
|
|
5716
|
+
`Mode: ${proxyTls ? "HTTPS" : "HTTP"}, ${formatTldList2(state.tlds)}${state.lanMode ? ", LAN" : ""}`
|
|
5717
|
+
);
|
|
5554
5718
|
console.log("");
|
|
5555
5719
|
const nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
|
|
5556
5720
|
if (nodeMajor >= 24) {
|
|
@@ -5822,6 +5986,7 @@ ${colors_default.bold("Usage:")}
|
|
|
5822
5986
|
${colors_default.cyan("portless proxy start --foreground")} Start in foreground (for debugging)
|
|
5823
5987
|
${colors_default.cyan("portless proxy start -p 1355")} Start on a custom port (no sudo)
|
|
5824
5988
|
${colors_default.cyan("portless proxy start --tld test")} Use .test instead of .localhost
|
|
5989
|
+
${colors_default.cyan("portless proxy start --tld localhost --tld test")} Serve both TLDs
|
|
5825
5990
|
${colors_default.cyan("portless proxy start --wildcard")} Allow unregistered subdomains to fall back to parent
|
|
5826
5991
|
${colors_default.cyan("portless proxy stop")} Stop the proxy
|
|
5827
5992
|
|
|
@@ -5883,34 +6048,41 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
5883
6048
|
}
|
|
5884
6049
|
hasExplicitPort = true;
|
|
5885
6050
|
}
|
|
5886
|
-
let
|
|
6051
|
+
let tlds;
|
|
5887
6052
|
try {
|
|
5888
|
-
|
|
6053
|
+
tlds = getDefaultTlds();
|
|
5889
6054
|
} catch (err) {
|
|
5890
6055
|
console.error(colors_default.red(`Error: ${err.message}`));
|
|
5891
6056
|
process.exit(1);
|
|
5892
6057
|
}
|
|
5893
|
-
const
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
6058
|
+
const tldFlagValues = [];
|
|
6059
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
6060
|
+
if (args[i] === "--tld") {
|
|
6061
|
+
const tldValue = args[i + 1];
|
|
6062
|
+
if (!tldValue || tldValue.startsWith("-")) {
|
|
6063
|
+
console.error(colors_default.red("Error: --tld requires a TLD value (e.g. test, localhost)."));
|
|
6064
|
+
process.exit(1);
|
|
6065
|
+
}
|
|
6066
|
+
tldFlagValues.push(tldValue);
|
|
6067
|
+
i += 1;
|
|
5899
6068
|
}
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
6069
|
+
}
|
|
6070
|
+
if (tldFlagValues.length > 0) {
|
|
6071
|
+
try {
|
|
6072
|
+
tlds = normalizeTlds2(tldFlagValues.flatMap((value) => parseTldList(value)));
|
|
6073
|
+
} catch (err) {
|
|
6074
|
+
console.error(colors_default.red(`Error: ${err.message}`));
|
|
5904
6075
|
process.exit(1);
|
|
5905
6076
|
}
|
|
5906
6077
|
}
|
|
6078
|
+
let tld = primaryTld2(tlds);
|
|
5907
6079
|
const useWildcard = args.includes("--wildcard") || isWildcardEnvEnabled();
|
|
5908
6080
|
const explicit = {
|
|
5909
6081
|
useHttps: hasHttpsFlag || hasNoTls || customCertPath !== null || customKeyPath !== null || process.env.PORTLESS_HTTPS !== void 0,
|
|
5910
6082
|
customCert: customCertPath !== null || customKeyPath !== null,
|
|
5911
6083
|
lanMode: process.env.PORTLESS_LAN !== void 0,
|
|
5912
6084
|
lanIp: process.env.PORTLESS_LAN_IP !== void 0,
|
|
5913
|
-
|
|
6085
|
+
tlds: tldFlagValues.length > 0 || process.env.PORTLESS_TLD !== void 0,
|
|
5914
6086
|
useWildcard: args.includes("--wildcard") || process.env.PORTLESS_WILDCARD !== void 0
|
|
5915
6087
|
};
|
|
5916
6088
|
let stateDir = resolveStateDir(proxyPort);
|
|
@@ -5928,13 +6100,13 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
5928
6100
|
const desiredConfig = resolveProxyConfig({
|
|
5929
6101
|
persistedLanMode,
|
|
5930
6102
|
explicit,
|
|
5931
|
-
|
|
6103
|
+
defaultTlds: getDefaultTlds(),
|
|
5932
6104
|
useHttps: wantHttps || !!(customCertPath && customKeyPath),
|
|
5933
6105
|
customCertPath,
|
|
5934
6106
|
customKeyPath,
|
|
5935
6107
|
lanMode: isLanEnvEnabled(),
|
|
5936
6108
|
lanIp: process.env.PORTLESS_LAN_IP || null,
|
|
5937
|
-
|
|
6109
|
+
tlds,
|
|
5938
6110
|
useWildcard
|
|
5939
6111
|
});
|
|
5940
6112
|
const lanMode = desiredConfig.lanMode;
|
|
@@ -5942,31 +6114,35 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
5942
6114
|
customCertPath = desiredConfig.customCertPath;
|
|
5943
6115
|
customKeyPath = desiredConfig.customKeyPath;
|
|
5944
6116
|
tld = desiredConfig.tld;
|
|
6117
|
+
tlds = desiredConfig.tlds;
|
|
5945
6118
|
const desiredWildcard = desiredConfig.useWildcard;
|
|
5946
6119
|
let lanIp = desiredConfig.lanIpExplicit ? desiredConfig.lanIp : null;
|
|
5947
6120
|
if (!hasExplicitPort && runningPort === null) {
|
|
5948
6121
|
proxyPort = getDefaultPort(useHttps);
|
|
5949
6122
|
stateDir = resolveStateDir(proxyPort);
|
|
5950
6123
|
}
|
|
5951
|
-
if (lanMode &&
|
|
5952
|
-
const
|
|
5953
|
-
if (
|
|
6124
|
+
if (lanMode && tldFlagValues.length > 0) {
|
|
6125
|
+
const userTlds = tldFlagValues.join(", ");
|
|
6126
|
+
if (userTlds !== "local") {
|
|
5954
6127
|
console.warn(
|
|
5955
6128
|
chalk.yellow(
|
|
5956
|
-
`Warning: --lan forces .local TLD (mDNS requirement). Ignoring --tld ${
|
|
6129
|
+
`Warning: --lan forces .local TLD (mDNS requirement). Ignoring --tld ${userTlds}.`
|
|
5957
6130
|
)
|
|
5958
6131
|
);
|
|
5959
6132
|
}
|
|
5960
6133
|
}
|
|
5961
|
-
const
|
|
5962
|
-
|
|
5963
|
-
|
|
6134
|
+
for (const configuredTld of tlds) {
|
|
6135
|
+
const riskyReason = RISKY_TLDS.get(configuredTld);
|
|
6136
|
+
if (riskyReason && !lanMode) {
|
|
6137
|
+
console.warn(colors_default.yellow(`Warning: .${configuredTld}: ${riskyReason}`));
|
|
6138
|
+
}
|
|
5964
6139
|
}
|
|
5965
6140
|
const syncDisabled = process.env.PORTLESS_SYNC_HOSTS === "0" || process.env.PORTLESS_SYNC_HOSTS === "false";
|
|
5966
|
-
|
|
6141
|
+
const nonDefaultTlds = tlds.filter((configuredTld) => configuredTld !== DEFAULT_TLD);
|
|
6142
|
+
if (nonDefaultTlds.length > 0 && !lanMode && syncDisabled) {
|
|
5967
6143
|
console.warn(
|
|
5968
6144
|
colors_default.yellow(
|
|
5969
|
-
`Warning:
|
|
6145
|
+
`Warning: ${formatTldList2(nonDefaultTlds)} domains require ${HOSTS_DISPLAY} entries to resolve to 127.0.0.1.`
|
|
5970
6146
|
)
|
|
5971
6147
|
);
|
|
5972
6148
|
console.warn(colors_default.yellow("Hosts sync is disabled. To add entries manually, run:"));
|
|
@@ -6028,6 +6204,7 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
6028
6204
|
lanIp: desiredConfig.lanIpExplicit ? lanIp : null,
|
|
6029
6205
|
lanIpExplicit: desiredConfig.lanIpExplicit,
|
|
6030
6206
|
tld,
|
|
6207
|
+
tlds,
|
|
6031
6208
|
useWildcard: desiredWildcard
|
|
6032
6209
|
};
|
|
6033
6210
|
if (!isWindows && proxyPort < PRIVILEGED_PORT_THRESHOLD && (process.getuid?.() ?? -1) !== 0) {
|
|
@@ -6044,6 +6221,7 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
6044
6221
|
lanIp: desiredConfig.lanIpExplicit ? lanIp : null,
|
|
6045
6222
|
lanIpExplicit: desiredConfig.lanIpExplicit,
|
|
6046
6223
|
tld,
|
|
6224
|
+
tlds,
|
|
6047
6225
|
useWildcard: desiredWildcard,
|
|
6048
6226
|
foreground: isForeground,
|
|
6049
6227
|
includePort: true,
|
|
@@ -6161,7 +6339,7 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
6161
6339
|
cert,
|
|
6162
6340
|
key,
|
|
6163
6341
|
ca,
|
|
6164
|
-
SNICallback: createSNICallback(stateDir, cert, key,
|
|
6342
|
+
SNICallback: createSNICallback(stateDir, cert, key, tlds, ca)
|
|
6165
6343
|
};
|
|
6166
6344
|
}
|
|
6167
6345
|
}
|
|
@@ -6171,6 +6349,7 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
6171
6349
|
store,
|
|
6172
6350
|
proxyPort,
|
|
6173
6351
|
tld,
|
|
6352
|
+
tlds,
|
|
6174
6353
|
tlsOptions,
|
|
6175
6354
|
lanIp,
|
|
6176
6355
|
desiredWildcard ? false : void 0,
|
|
@@ -6199,6 +6378,7 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
6199
6378
|
lanIp: desiredConfig.lanIpExplicit ? lanIp : null,
|
|
6200
6379
|
lanIpExplicit: desiredConfig.lanIpExplicit,
|
|
6201
6380
|
tld,
|
|
6381
|
+
tlds,
|
|
6202
6382
|
useWildcard: desiredWildcard,
|
|
6203
6383
|
foreground: true,
|
|
6204
6384
|
includePort: true,
|
|
@@ -6291,8 +6471,8 @@ async function handleDefaultSingle(cwd, scriptName, appConfig) {
|
|
|
6291
6471
|
nameSource = inferred.source;
|
|
6292
6472
|
}
|
|
6293
6473
|
const worktree = detectWorktreePrefix(cwd);
|
|
6294
|
-
const effectiveName =
|
|
6295
|
-
const { dir, port, tls: tls2,
|
|
6474
|
+
const effectiveName = applyWorktreePrefix(baseName, worktree);
|
|
6475
|
+
const { dir, port, tls: tls2, tlds, lanMode, lanIp } = await discoverState();
|
|
6296
6476
|
const store = new RouteStore(dir, {
|
|
6297
6477
|
onWarning: (msg) => console.warn(colors_default.yellow(msg))
|
|
6298
6478
|
});
|
|
@@ -6303,7 +6483,7 @@ async function handleDefaultSingle(cwd, scriptName, appConfig) {
|
|
|
6303
6483
|
effectiveName,
|
|
6304
6484
|
resolved,
|
|
6305
6485
|
tls2,
|
|
6306
|
-
|
|
6486
|
+
tlds,
|
|
6307
6487
|
false,
|
|
6308
6488
|
{ nameSource, prefix: worktree?.prefix, prefixSource: worktree?.source },
|
|
6309
6489
|
appConfig?.appPort,
|
|
@@ -6343,13 +6523,13 @@ function pipeOutput(child, prefix) {
|
|
|
6343
6523
|
prefixStream(child.stdout, process.stdout, prefix);
|
|
6344
6524
|
prefixStream(child.stderr, process.stderr, prefix);
|
|
6345
6525
|
}
|
|
6346
|
-
async function spawnProxiedApp(app, stateDir, proxyPort, tls2,
|
|
6526
|
+
async function spawnProxiedApp(app, stateDir, proxyPort, tls2, tlds, exitCodes) {
|
|
6347
6527
|
const usesPortless = app.commandArgs[0] === "portless";
|
|
6348
6528
|
const pkgEnv = { ...process.env };
|
|
6349
6529
|
pkgEnv.PATH = augmentedPath(pkgEnv, app.pkg.dir);
|
|
6350
6530
|
let env;
|
|
6351
6531
|
let store = null;
|
|
6352
|
-
let
|
|
6532
|
+
let hostnames = [];
|
|
6353
6533
|
let displayUrl;
|
|
6354
6534
|
if (usesPortless) {
|
|
6355
6535
|
env = pkgEnv;
|
|
@@ -6359,17 +6539,17 @@ async function spawnProxiedApp(app, stateDir, proxyPort, tls2, tld, exitCodes) {
|
|
|
6359
6539
|
onWarning: (msg) => console.warn(colors_default.yellow(`[${app.name}] ${msg}`))
|
|
6360
6540
|
});
|
|
6361
6541
|
const appPort = app.appPort ?? await findFreePort();
|
|
6362
|
-
|
|
6363
|
-
const
|
|
6364
|
-
const url =
|
|
6542
|
+
hostnames = buildHostnames(app.name, tlds);
|
|
6543
|
+
const urls = formatUrls(hostnames, proxyPort, tls2);
|
|
6544
|
+
const url = urls[0];
|
|
6365
6545
|
displayUrl = url;
|
|
6366
|
-
|
|
6367
|
-
store.addRoute(hostname, appPort, process.pid);
|
|
6546
|
+
addRoutes(store, hostnames, appPort, process.pid);
|
|
6368
6547
|
env = {
|
|
6369
6548
|
...pkgEnv,
|
|
6370
6549
|
PORT: String(appPort),
|
|
6371
6550
|
HOST: "127.0.0.1",
|
|
6372
|
-
PORTLESS_URL: url
|
|
6551
|
+
PORTLESS_URL: url,
|
|
6552
|
+
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: formatViteAllowedHosts(tlds)
|
|
6373
6553
|
};
|
|
6374
6554
|
if (tls2) {
|
|
6375
6555
|
const caPath = path9.join(stateDir, "ca.pem");
|
|
@@ -6381,7 +6561,7 @@ async function spawnProxiedApp(app, stateDir, proxyPort, tls2, tld, exitCodes) {
|
|
|
6381
6561
|
const child = spawnChildProcess(app.commandArgs, env, app.pkg.dir);
|
|
6382
6562
|
pipeOutput(child, chalk.cyan(`[${app.name}]`));
|
|
6383
6563
|
const capturedStore = store;
|
|
6384
|
-
const
|
|
6564
|
+
const capturedHostnames = hostnames;
|
|
6385
6565
|
child.on("exit", (code, signal) => {
|
|
6386
6566
|
exitCodes.set(app.name, code);
|
|
6387
6567
|
if (code !== 0 && code !== null) {
|
|
@@ -6389,14 +6569,11 @@ async function spawnProxiedApp(app, stateDir, proxyPort, tls2, tld, exitCodes) {
|
|
|
6389
6569
|
} else if (signal) {
|
|
6390
6570
|
console.error(colors_default.yellow(`[${app.name}] killed by ${signal}`));
|
|
6391
6571
|
}
|
|
6392
|
-
if (capturedStore &&
|
|
6393
|
-
|
|
6394
|
-
capturedStore.removeRoute(capturedHostname, process.pid);
|
|
6395
|
-
} catch {
|
|
6396
|
-
}
|
|
6572
|
+
if (capturedStore && capturedHostnames.length > 0) {
|
|
6573
|
+
removeRoutes(capturedStore, capturedHostnames, process.pid);
|
|
6397
6574
|
}
|
|
6398
6575
|
});
|
|
6399
|
-
const route = store &&
|
|
6576
|
+
const route = store && hostnames.length > 0 ? { store, hostnames } : null;
|
|
6400
6577
|
return { child, displayUrl, route };
|
|
6401
6578
|
}
|
|
6402
6579
|
function spawnTaskApp(app, exitCodes) {
|
|
@@ -6454,6 +6631,7 @@ async function handleDefaultMulti(wsRoot, globalScript, extraArgs = []) {
|
|
|
6454
6631
|
}
|
|
6455
6632
|
}
|
|
6456
6633
|
const apps = [];
|
|
6634
|
+
const worktree = detectWorktreePrefix(wsRoot);
|
|
6457
6635
|
for (const pkg of packages) {
|
|
6458
6636
|
const rel = path9.relative(wsRoot, pkg.dir).replace(/\\/g, "/");
|
|
6459
6637
|
const rootOverride = loaded ? resolveAppConfig(loaded.config, loaded.configDir, pkg.dir) : null;
|
|
@@ -6495,6 +6673,7 @@ async function handleDefaultMulti(wsRoot, globalScript, extraArgs = []) {
|
|
|
6495
6673
|
name = pkgLabel === projectName ? projectName : `${pkgLabel}.${projectName}`;
|
|
6496
6674
|
label = pkg.scope ? `@${pkg.scope}/${pkg.name}` : pkg.name ?? rel;
|
|
6497
6675
|
}
|
|
6676
|
+
name = applyWorktreePrefix(name, worktree);
|
|
6498
6677
|
apps.push({ pkg, name, label, commandArgs, appPort: appOverride.appPort, proxied });
|
|
6499
6678
|
}
|
|
6500
6679
|
if (apps.length === 0) {
|
|
@@ -6507,7 +6686,7 @@ async function handleDefaultMulti(wsRoot, globalScript, extraArgs = []) {
|
|
|
6507
6686
|
console.log(chalk.blue.bold(`
|
|
6508
6687
|
portless
|
|
6509
6688
|
`));
|
|
6510
|
-
let { dir, port, tls: tls2,
|
|
6689
|
+
let { dir, port, tls: tls2, tlds } = await discoverState();
|
|
6511
6690
|
if (proxiedApps.length > 0) {
|
|
6512
6691
|
let multiDesired;
|
|
6513
6692
|
try {
|
|
@@ -6521,9 +6700,9 @@ portless
|
|
|
6521
6700
|
dir = ensureResult.state.dir;
|
|
6522
6701
|
port = ensureResult.state.port;
|
|
6523
6702
|
tls2 = ensureResult.state.tls;
|
|
6524
|
-
|
|
6703
|
+
tlds = ensureResult.state.tlds;
|
|
6525
6704
|
} else {
|
|
6526
|
-
({ dir, port, tls: tls2,
|
|
6705
|
+
({ dir, port, tls: tls2, tlds } = await discoverState());
|
|
6527
6706
|
}
|
|
6528
6707
|
if (tls2 && !isCATrusted(dir)) {
|
|
6529
6708
|
await handleTrust();
|
|
@@ -6531,12 +6710,12 @@ portless
|
|
|
6531
6710
|
}
|
|
6532
6711
|
const useTurbo = loaded?.config.turbo !== false && hasTurboConfig(wsRoot);
|
|
6533
6712
|
if (useTurbo) {
|
|
6534
|
-
await runWithTurbo(wsRoot, dir, port, tls2,
|
|
6713
|
+
await runWithTurbo(wsRoot, dir, port, tls2, tlds, scriptName, proxiedApps, taskApps, extraArgs);
|
|
6535
6714
|
} else {
|
|
6536
|
-
await runWithDirectSpawn(dir, port, tls2,
|
|
6715
|
+
await runWithDirectSpawn(dir, port, tls2, tlds, proxiedApps, taskApps);
|
|
6537
6716
|
}
|
|
6538
6717
|
}
|
|
6539
|
-
async function runWithTurbo(wsRoot, stateDir, proxyPort, tls2,
|
|
6718
|
+
async function runWithTurbo(wsRoot, stateDir, proxyPort, tls2, tlds, scriptName, proxiedApps, taskApps, extraArgs = []) {
|
|
6540
6719
|
const store = new RouteStore(stateDir, {
|
|
6541
6720
|
onWarning: (msg) => console.warn(colors_default.yellow(msg))
|
|
6542
6721
|
});
|
|
@@ -6550,17 +6729,17 @@ async function runWithTurbo(wsRoot, stateDir, proxyPort, tls2, tld, scriptName,
|
|
|
6550
6729
|
continue;
|
|
6551
6730
|
}
|
|
6552
6731
|
const appPort = app.appPort ?? await findFreePort();
|
|
6553
|
-
const
|
|
6554
|
-
const
|
|
6555
|
-
const url =
|
|
6732
|
+
const hostnames = buildHostnames(app.name, tlds);
|
|
6733
|
+
const urls = formatUrls(hostnames, proxyPort, tls2);
|
|
6734
|
+
const url = urls[0];
|
|
6556
6735
|
appUrls.push({ label: app.label, url });
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
routes.push({ hostname });
|
|
6736
|
+
addRoutes(store, hostnames, appPort, process.pid);
|
|
6737
|
+
routes.push({ hostnames });
|
|
6560
6738
|
const entry = {
|
|
6561
6739
|
PORT: String(appPort),
|
|
6562
6740
|
HOST: "127.0.0.1",
|
|
6563
|
-
PORTLESS_URL: url
|
|
6741
|
+
PORTLESS_URL: url,
|
|
6742
|
+
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: formatViteAllowedHosts(tlds)
|
|
6564
6743
|
};
|
|
6565
6744
|
if (tls2) {
|
|
6566
6745
|
const caPath = path9.join(stateDir, "ca.pem");
|
|
@@ -6603,11 +6782,8 @@ async function runWithTurbo(wsRoot, stateDir, proxyPort, tls2, tld, scriptName,
|
|
|
6603
6782
|
killTree(turboChild, "SIGKILL");
|
|
6604
6783
|
}
|
|
6605
6784
|
}, SIGKILL_TIMEOUT_MS).unref();
|
|
6606
|
-
for (const {
|
|
6607
|
-
|
|
6608
|
-
store.removeRoute(hostname, process.pid);
|
|
6609
|
-
} catch {
|
|
6610
|
-
}
|
|
6785
|
+
for (const { hostnames } of routes) {
|
|
6786
|
+
removeRoutes(store, hostnames, process.pid);
|
|
6611
6787
|
}
|
|
6612
6788
|
removeManifest();
|
|
6613
6789
|
};
|
|
@@ -6621,7 +6797,7 @@ async function runWithTurbo(wsRoot, stateDir, proxyPort, tls2, tld, scriptName,
|
|
|
6621
6797
|
process.exit(exitCode);
|
|
6622
6798
|
}
|
|
6623
6799
|
}
|
|
6624
|
-
async function runWithDirectSpawn(stateDir, proxyPort, tls2,
|
|
6800
|
+
async function runWithDirectSpawn(stateDir, proxyPort, tls2, tlds, proxiedApps, taskApps) {
|
|
6625
6801
|
const children = [];
|
|
6626
6802
|
const exitCodes = /* @__PURE__ */ new Map();
|
|
6627
6803
|
const appUrls = [];
|
|
@@ -6632,7 +6808,7 @@ async function runWithDirectSpawn(stateDir, proxyPort, tls2, tld, proxiedApps, t
|
|
|
6632
6808
|
stateDir,
|
|
6633
6809
|
proxyPort,
|
|
6634
6810
|
tls2,
|
|
6635
|
-
|
|
6811
|
+
tlds,
|
|
6636
6812
|
exitCodes
|
|
6637
6813
|
);
|
|
6638
6814
|
children.push(child);
|
|
@@ -6667,11 +6843,8 @@ async function runWithDirectSpawn(stateDir, proxyPort, tls2, tld, proxiedApps, t
|
|
|
6667
6843
|
}
|
|
6668
6844
|
}
|
|
6669
6845
|
}, SIGKILL_TIMEOUT_MS).unref();
|
|
6670
|
-
for (const { store,
|
|
6671
|
-
|
|
6672
|
-
store.removeRoute(hostname, process.pid);
|
|
6673
|
-
} catch {
|
|
6674
|
-
}
|
|
6846
|
+
for (const { store, hostnames } of routeEntries) {
|
|
6847
|
+
removeRoutes(store, hostnames, process.pid);
|
|
6675
6848
|
}
|
|
6676
6849
|
};
|
|
6677
6850
|
process.on("SIGINT", cleanup);
|
|
@@ -6730,7 +6903,7 @@ async function handleRunMode(args, globalScript) {
|
|
|
6730
6903
|
}
|
|
6731
6904
|
const worktree = detectWorktreePrefix();
|
|
6732
6905
|
const effectiveName = worktree ? `${worktree.prefix}.${baseName}` : baseName;
|
|
6733
|
-
const { dir, port, tls: tls2,
|
|
6906
|
+
const { dir, port, tls: tls2, tlds, lanMode, lanIp } = await discoverState();
|
|
6734
6907
|
const store = new RouteStore(dir, {
|
|
6735
6908
|
onWarning: (msg) => console.warn(colors_default.yellow(msg))
|
|
6736
6909
|
});
|
|
@@ -6741,7 +6914,7 @@ async function handleRunMode(args, globalScript) {
|
|
|
6741
6914
|
effectiveName,
|
|
6742
6915
|
parsed.commandArgs,
|
|
6743
6916
|
tls2,
|
|
6744
|
-
|
|
6917
|
+
tlds,
|
|
6745
6918
|
parsed.force,
|
|
6746
6919
|
{ nameSource, prefix: worktree?.prefix, prefixSource: worktree?.source },
|
|
6747
6920
|
parsed.appPort,
|
|
@@ -6767,7 +6940,7 @@ async function handleNamedMode(args) {
|
|
|
6767
6940
|
}
|
|
6768
6941
|
const safeName = parsed.name.split(".").map((label) => truncateLabel(label)).join(".");
|
|
6769
6942
|
parseHostname(safeName, DEFAULT_TLD);
|
|
6770
|
-
const { dir, port, tls: tls2,
|
|
6943
|
+
const { dir, port, tls: tls2, tlds, lanMode, lanIp } = await discoverState();
|
|
6771
6944
|
const store = new RouteStore(dir, {
|
|
6772
6945
|
onWarning: (msg) => console.warn(colors_default.yellow(msg))
|
|
6773
6946
|
});
|
|
@@ -6778,7 +6951,7 @@ async function handleNamedMode(args) {
|
|
|
6778
6951
|
safeName,
|
|
6779
6952
|
parsed.commandArgs,
|
|
6780
6953
|
tls2,
|
|
6781
|
-
|
|
6954
|
+
tlds,
|
|
6782
6955
|
parsed.force,
|
|
6783
6956
|
void 0,
|
|
6784
6957
|
parsed.appPort,
|