portless 0.15.2 → 0.15.4

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 CHANGED
@@ -36,6 +36,8 @@ The proxy auto-starts when you run an app. A random port (4000-4999) is assigned
36
36
 
37
37
  When auto-starting, portless reuses the configuration (port, TLS, TLDs) from the most recent proxy run, so a restart or reboot does not silently revert to defaults. Explicit env vars (`PORTLESS_PORT`, `PORTLESS_HTTPS`, etc.) always take priority.
38
38
 
39
+ Portless stores per-user state in `~/.portless`. When the proxy runs under sudo, it resolves this path from the invoking user's home so the proxy and unprivileged app processes share the same route registrations.
40
+
39
41
  In non-interactive environments (no TTY, or `CI=1`), portless exits with a descriptive error instead of prompting, so task runners like turborepo and CI scripts fail early with a clear message.
40
42
 
41
43
  ## Configuration
@@ -241,6 +243,8 @@ flowchart TD
241
243
  2. **Run apps**: `portless <name> <command>` assigns a free port and registers with the proxy
242
244
  3. **Access via URL**: `https://<name>.localhost` routes through the proxy to your app
243
245
 
246
+ Outside LAN mode, the proxy and its HTTP redirect listener bind only to the IPv4 and IPv6 loopback addresses, `127.0.0.1` and `::1`. They do not accept connections through LAN, VPN, or other network interfaces.
247
+
244
248
  ## HTTP/2 + HTTPS
245
249
 
246
250
  HTTPS with HTTP/2 is enabled by default. Browsers limit HTTP/1.1 to 6 connections per host, which bottlenecks dev servers that serve many unbundled files (Vite, Nuxt, etc.). HTTP/2 multiplexes all requests over a single connection.
@@ -258,7 +262,7 @@ portless proxy start --no-tls
258
262
  portless trust
259
263
  ```
260
264
 
261
- On Linux, `portless trust` supports Debian/Ubuntu, Arch, Fedora/RHEL/CentOS, and openSUSE (via `update-ca-certificates` or `update-ca-trust`). On Windows, it uses `certutil` to add the CA to the system trust store.
265
+ On Linux, `portless trust` supports Debian/Ubuntu, Arch, Fedora/RHEL/CentOS, and openSUSE (via `update-ca-certificates` or `update-ca-trust`). On Windows, it uses `certutil` to add the CA to the system trust store. On WSL, it updates both the Linux trust store and the Windows current-user Root store so Windows browsers trust portless HTTPS certificates.
262
266
 
263
267
  ## Start at OS startup
264
268
 
@@ -285,7 +289,7 @@ portless proxy start --lan --https
285
289
  portless proxy start --lan --ip 192.168.1.42
286
290
  ```
287
291
 
288
- `--lan` switches the proxy to mDNS discovery: services are advertised as `<name>.local` and reachable from any device on the same network. Portless auto-detects your LAN IP and follows Wi-Fi/IP changes automatically, but you can pin another address with `--ip <address>` or by exporting `PORTLESS_LAN_IP`. Set `PORTLESS_LAN=1` in your shell (0/1 boolean) to make LAN mode the default whenever the proxy starts.
292
+ `--lan` explicitly binds the proxy to the IPv4 and IPv6 unspecified addresses, `0.0.0.0` and `::`, and switches to mDNS discovery. This makes services available as `<name>.local` to devices on the same network. Portless auto-detects your LAN IP and follows Wi-Fi/IP changes automatically, but you can pin another address with `--ip <address>` or by exporting `PORTLESS_LAN_IP`. Set `PORTLESS_LAN=1` in your shell (0/1 boolean) to make LAN mode the default whenever the proxy starts.
289
293
 
290
294
  Portless remembers LAN mode via `proxy.lan`, so if you stop a LAN proxy and start it again, it stays in LAN mode. All proxy settings (port, TLS, TLDs, LAN) are persisted and reused on auto-start unless overridden by explicit flags or env vars. Use `PORTLESS_LAN=0` for one start to switch back to `.localhost` mode. If a proxy is already running with different explicit LAN/TLS/TLD settings, portless warns and asks you to stop it first.
291
295
 
@@ -445,7 +449,7 @@ To remove portless data from your machine (proxy state under `~/.portless` and t
445
449
  portless clean
446
450
  ```
447
451
 
448
- macOS/Linux may prompt for `sudo`. Custom certificate paths passed with `--cert` and `--key` are not deleted.
452
+ macOS/Linux may prompt for `sudo`. Custom certificate paths passed with `--cert` and `--key` are not deleted. If trust-store removal fails, portless retains its CA certificate and key so a later `portless clean` can safely retry.
449
453
 
450
454
  ## Safari / DNS
451
455
 
@@ -1,6 +1,32 @@
1
1
  // src/utils.ts
2
2
  import * as fs from "fs";
3
3
  import * as net from "net";
4
+ import * as os from "os";
5
+ import * as path from "path";
6
+ function readPasswdHome(username) {
7
+ try {
8
+ const passwd = fs.readFileSync("/etc/passwd", "utf-8");
9
+ for (const line of passwd.split("\n")) {
10
+ const fields = line.split(":");
11
+ if (fields[0] === username && fields[5]) return fields[5];
12
+ }
13
+ } catch {
14
+ }
15
+ return null;
16
+ }
17
+ function resolveUserHome(options = {}) {
18
+ const platform = options.platform ?? process.platform;
19
+ const env = options.env ?? process.env;
20
+ const homedir2 = options.homedir ?? os.homedir();
21
+ if (platform === "win32") return env.USERPROFILE || homedir2;
22
+ const sudoUser = env.SUDO_USER;
23
+ if (!sudoUser || sudoUser === "root") return homedir2;
24
+ const home = env.HOME;
25
+ if (home && home !== "/root" && home !== "/var/root") return home;
26
+ const passwdHome = (options.passwdHome ?? readPasswdHome)(sudoUser);
27
+ if (passwdHome) return passwdHome;
28
+ return platform === "darwin" ? path.posix.join("/Users", sudoUser) : path.posix.join("/home", sudoUser);
29
+ }
4
30
  function loopbackLookup(_hostname, _options, callback) {
5
31
  callback(null, [
6
32
  { address: "127.0.0.1", family: 4 },
@@ -653,9 +679,9 @@ function createHttpRedirectServer(httpsPort) {
653
679
  // src/hosts.ts
654
680
  import * as fs2 from "fs";
655
681
  import * as dns from "dns";
656
- import * as path from "path";
682
+ import * as path2 from "path";
657
683
  var isWindows = process.platform === "win32";
658
- var HOSTS_PATH = isWindows ? path.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "drivers", "etc", "hosts") : "/etc/hosts";
684
+ var HOSTS_PATH = isWindows ? path2.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "drivers", "etc", "hosts") : "/etc/hosts";
659
685
  var MARKER_START = "# portless-start";
660
686
  var MARKER_END = "# portless-end";
661
687
  function readHostsFile() {
@@ -736,7 +762,7 @@ function checkHostResolution(hostname) {
736
762
 
737
763
  // src/routes.ts
738
764
  import * as fs3 from "fs";
739
- import * as path2 from "path";
765
+ import * as path3 from "path";
740
766
  var STALE_LOCK_THRESHOLD_MS = 1e4;
741
767
  var LOCK_TIMEOUT_MS = 15e3;
742
768
  var LOCK_RETRY_BASE_MS = 10;
@@ -768,10 +794,10 @@ var RouteStore = class _RouteStore {
768
794
  onWarning;
769
795
  constructor(dir, options) {
770
796
  this.dir = dir;
771
- this.routesPath = path2.join(dir, "routes.json");
772
- this.lockPath = path2.join(dir, "routes.lock");
773
- this.pidPath = path2.join(dir, "proxy.pid");
774
- this.portFilePath = path2.join(dir, "proxy.port");
797
+ this.routesPath = path3.join(dir, "routes.json");
798
+ this.lockPath = path3.join(dir, "routes.lock");
799
+ this.pidPath = path3.join(dir, "proxy.pid");
800
+ this.portFilePath = path3.join(dir, "proxy.port");
775
801
  this.onWarning = options?.onWarning;
776
802
  }
777
803
  ensureDir() {
@@ -1033,6 +1059,7 @@ var RouteStore = class _RouteStore {
1033
1059
  };
1034
1060
 
1035
1061
  export {
1062
+ resolveUserHome,
1036
1063
  createLoopbackConnection,
1037
1064
  fixOwnership,
1038
1065
  isErrnoException,