portless 0.8.0 → 0.9.1

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
@@ -9,26 +9,29 @@ Replace port numbers with stable, named .localhost URLs for local development. F
9
9
 
10
10
  ## Install
11
11
 
12
+ **Global (recommended):**
13
+
12
14
  ```bash
13
15
  npm install -g portless
14
16
  ```
15
17
 
16
- > Install globally. Do not add as a project dependency or run via npx.
18
+ **Or as a project dev dependency:**
19
+
20
+ ```bash
21
+ npm install -D portless
22
+ ```
23
+
24
+ > portless is pre-1.0. When installed per-project, different contributors may run different versions. The state directory format may change between releases, which can require re-running `portless trust`.
17
25
 
18
26
  ## Run your app
19
27
 
20
28
  ```bash
21
- # Enable HTTPS (one-time setup, auto-generates certs)
22
- portless proxy start --https
23
-
24
29
  portless myapp next dev
25
30
  # -> https://myapp.localhost
26
-
27
- # Without --https, runs on port 1355
28
- portless myapp next dev
29
- # -> http://myapp.localhost:1355
30
31
  ```
31
32
 
33
+ HTTPS with HTTP/2 is enabled by default. On first run, portless generates a local CA, trusts it, and binds port 443 (auto-elevates with sudo on macOS/Linux). Use `--no-tls` for plain HTTP.
34
+
32
35
  The proxy auto-starts when you run an app. A random port (4000--4999) is assigned via the `PORT` environment variable. Most frameworks (Next.js, Express, Nuxt, etc.) respect this automatically. For frameworks that ignore `PORT` (Vite, Astro, React Router, Angular, Expo, React Native), portless auto-injects `--port` and `--host` flags.
33
36
 
34
37
  ## Use in package.json
@@ -47,45 +50,45 @@ Organize services with subdomains:
47
50
 
48
51
  ```bash
49
52
  portless api.myapp pnpm start
50
- # -> http://api.myapp.localhost:1355
53
+ # -> https://api.myapp.localhost
51
54
 
52
55
  portless docs.myapp next dev
53
- # -> http://docs.myapp.localhost:1355
56
+ # -> https://docs.myapp.localhost
54
57
  ```
55
58
 
56
- By default, only explicitly registered subdomains are routed (strict mode). Use `--wildcard` when starting the proxy to allow any subdomain of a registered route to fall back to that app (e.g. `tenant1.myapp.localhost:1355` routes to the `myapp` app without extra registration).
59
+ By default, only explicitly registered subdomains are routed (strict mode). Use `--wildcard` when starting the proxy to allow any subdomain of a registered route to fall back to that app (e.g. `tenant1.myapp.localhost` routes to the `myapp` app without extra registration).
57
60
 
58
61
  ## Git Worktrees
59
62
 
60
63
  `portless run` automatically detects git worktrees. In a linked worktree, the branch name is prepended as a subdomain so each worktree gets its own URL without any config changes:
61
64
 
62
65
  ```bash
63
- # Main worktree -- no prefix
64
- portless run next dev # -> http://myapp.localhost:1355
66
+ # Main worktree (no prefix)
67
+ portless run next dev # -> https://myapp.localhost
65
68
 
66
69
  # Linked worktree on branch "fix-ui"
67
- portless run next dev # -> http://fix-ui.myapp.localhost:1355
70
+ portless run next dev # -> https://fix-ui.myapp.localhost
68
71
  ```
69
72
 
70
73
  Use `--name` to override the inferred base name while keeping the worktree prefix:
71
74
 
72
75
  ```bash
73
- portless run --name myapp next dev # -> http://fix-ui.myapp.localhost:1355
76
+ portless run --name myapp next dev # -> https://fix-ui.myapp.localhost
74
77
  ```
75
78
 
76
- Put `portless run` in your `package.json` once and it works everywhere -- the main checkout uses the plain name, each worktree gets a unique subdomain. No collisions, no `--force`.
79
+ Put `portless run` in your `package.json` once and it works everywhere. The main checkout uses the plain name, each worktree gets a unique subdomain. No collisions, no `--force`.
77
80
 
78
81
  ## Custom TLD
79
82
 
80
83
  By default, portless uses `.localhost` which auto-resolves to `127.0.0.1` in most browsers. If you prefer a different TLD (e.g. `.test`), use `--tld`:
81
84
 
82
85
  ```bash
83
- sudo portless proxy start --https --tld test
86
+ portless proxy start --tld test
84
87
  portless myapp next dev
85
88
  # -> https://myapp.test
86
89
  ```
87
90
 
88
- The proxy auto-syncs `/etc/hosts` for custom TLDs when started with sudo, so `.test` domains resolve correctly.
91
+ The proxy auto-syncs `/etc/hosts` for custom TLDs, so `.test` domains resolve correctly.
89
92
 
90
93
  Recommended: `.test` (IANA-reserved, no collision risk). Avoid `.local` (conflicts with mDNS/Bonjour) and `.dev` (Google-owned, forces HTTPS via HSTS).
91
94
 
@@ -93,40 +96,35 @@ Recommended: `.test` (IANA-reserved, no collision risk). Avoid `.local` (conflic
93
96
 
94
97
  ```mermaid
95
98
  flowchart TD
96
- Browser["Browser<br>myapp.localhost:1355"]
97
- Proxy["portless proxy<br>(port 1355)"]
99
+ Browser["Browser<br>myapp.localhost"]
100
+ Proxy["portless proxy<br>(port 80 or 443)"]
98
101
  App1[":4123<br>myapp"]
99
102
  App2[":4567<br>api"]
100
103
 
101
- Browser -->|port 1355| Proxy
104
+ Browser --> Proxy
102
105
  Proxy --> App1
103
106
  Proxy --> App2
104
107
  ```
105
108
 
106
- 1. **Start the proxy** -- auto-starts when you run an app, or start explicitly with `portless proxy start`
107
- 2. **Run apps** -- `portless <name> <command>` assigns a free port and registers with the proxy
108
- 3. **Access via URL** -- `http://<name>.localhost:1355` routes through the proxy to your app
109
+ 1. **Start the proxy**: auto-starts when you run an app, or start explicitly with `portless proxy start`
110
+ 2. **Run apps**: `portless <name> <command>` assigns a free port and registers with the proxy
111
+ 3. **Access via URL**: `https://<name>.localhost` routes through the proxy to your app
109
112
 
110
113
  ## HTTP/2 + HTTPS
111
114
 
112
- Enable HTTP/2 for faster dev server page loads. 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.
113
-
114
- ```bash
115
- # Start with HTTPS/2 -- generates certs and trusts them automatically
116
- portless proxy start --https
117
-
118
- # First run prompts for sudo once to add the CA to your system trust store.
119
- # After that, no prompts. No browser warnings.
115
+ 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.
120
116
 
121
- # Make it permanent (add to .bashrc / .zshrc)
122
- export PORTLESS_HTTPS=1
123
- portless proxy start # HTTPS by default now
117
+ On first run, portless generates a local CA and adds it to your system trust store. No browser warnings. No manual setup.
124
118
 
119
+ ```bash
125
120
  # Use your own certs (e.g., from mkcert)
126
121
  portless proxy start --cert ./cert.pem --key ./key.pem
127
122
 
128
- # If you skipped sudo on first run, trust the CA later
129
- sudo portless trust
123
+ # Disable HTTPS (plain HTTP on port 80)
124
+ portless proxy start --no-tls
125
+
126
+ # If you skipped the trust prompt on first run, trust the CA later
127
+ portless trust
130
128
  ```
131
129
 
132
130
  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.
@@ -135,7 +133,7 @@ On Linux, `portless trust` supports Debian/Ubuntu, Arch, Fedora/RHEL/CentOS, and
135
133
 
136
134
  ```bash
137
135
  portless run [--name <name>] <cmd> [args...] # Infer name (or override with --name), run through proxy
138
- portless <name> <cmd> [args...] # Run app at http://<name>.localhost:1355
136
+ portless <name> <cmd> [args...] # Run app at https://<name>.localhost
139
137
  portless alias <name> <port> # Register a static route (e.g. for Docker)
140
138
  portless alias <name> <port> --force # Overwrite an existing route
141
139
  portless alias --remove <name> # Remove a static route
@@ -148,9 +146,9 @@ portless hosts clean # Remove portless entries from /etc/hosts
148
146
  PORTLESS=0 pnpm dev # Bypasses proxy, uses default port
149
147
 
150
148
  # Proxy control
151
- portless proxy start # Start the proxy (port 1355, daemon)
152
- portless proxy start --https # Start with HTTP/2 + TLS
153
- portless proxy start -p 80 # Start on port 80 (requires sudo)
149
+ portless proxy start # Start the HTTPS proxy (port 443, daemon)
150
+ portless proxy start --no-tls # Start without HTTPS (port 80)
151
+ portless proxy start -p 1355 # Start on a custom port (no sudo)
154
152
  portless proxy start --foreground # Start in foreground (for debugging)
155
153
  portless proxy start --wildcard # Allow unregistered subdomains to fall back to parent
156
154
  portless proxy stop # Stop the proxy
@@ -159,11 +157,11 @@ portless proxy stop # Stop the proxy
159
157
  ### Options
160
158
 
161
159
  ```
162
- -p, --port <number> Port for the proxy (default: 1355)
163
- --https Enable HTTP/2 + TLS with auto-generated certs
164
- --cert <path> Use a custom TLS certificate (implies --https)
165
- --key <path> Use a custom TLS private key (implies --https)
166
- --no-tls Disable HTTPS (overrides PORTLESS_HTTPS)
160
+ -p, --port <number> Port for the proxy (default: 443, or 80 with --no-tls)
161
+ --no-tls Disable HTTPS (use plain HTTP on port 80)
162
+ --https Enable HTTPS (default, accepted for compatibility)
163
+ --cert <path> Use a custom TLS certificate
164
+ --key <path> Use a custom TLS private key
167
165
  --foreground Run proxy in foreground instead of daemon
168
166
  --tld <tld> Use a custom TLD instead of .localhost (e.g. test)
169
167
  --wildcard Allow unregistered subdomains to fall back to parent route
@@ -178,7 +176,7 @@ portless proxy stop # Stop the proxy
178
176
  # Configuration
179
177
  PORTLESS_PORT=<number> Override the default proxy port
180
178
  PORTLESS_APP_PORT=<number> Use a fixed port for the app (same as --app-port)
181
- PORTLESS_HTTPS=1 Always enable HTTPS
179
+ PORTLESS_HTTPS HTTPS on by default; set to 0 to disable (same as --no-tls)
182
180
  PORTLESS_TLD=<tld> Use a custom TLD (e.g. test; default: localhost)
183
181
  PORTLESS_WILDCARD=1 Allow unregistered subdomains to fall back to parent route
184
182
  PORTLESS_SYNC_HOSTS=1 Auto-sync /etc/hosts (auto-enabled for custom TLDs)
@@ -199,8 +197,8 @@ PORTLESS_URL Public URL (e.g. https://myapp.localhost)
199
197
  If Safari can't find your `.localhost` URL:
200
198
 
201
199
  ```bash
202
- sudo portless hosts sync # Add current routes to /etc/hosts
203
- sudo portless hosts clean # Clean up later
200
+ portless hosts sync # Add current routes to /etc/hosts
201
+ portless hosts clean # Clean up later
204
202
  ```
205
203
 
206
204
  Auto-syncs `/etc/hosts` for custom TLDs (e.g. `--tld test`). For `.localhost`, set `PORTLESS_SYNC_HOSTS=1` to enable. Disable with `PORTLESS_SYNC_HOSTS=0`.
@@ -215,7 +213,7 @@ If your frontend dev server (e.g. Vite, webpack) proxies API requests to another
215
213
  server: {
216
214
  proxy: {
217
215
  "/api": {
218
- target: "http://api.myapp.localhost:1355",
216
+ target: "https://api.myapp.localhost",
219
217
  changeOrigin: true,
220
218
  ws: true,
221
219
  },
@@ -229,12 +227,14 @@ server: {
229
227
  devServer: {
230
228
  proxy: [{
231
229
  context: ["/api"],
232
- target: "http://api.myapp.localhost:1355",
230
+ target: "https://api.myapp.localhost",
233
231
  changeOrigin: true,
234
232
  }],
235
233
  }
236
234
  ```
237
235
 
236
+ If your tooling doesn't trust the portless CA, point Node.js at it: `NODE_EXTRA_CA_CERTS=/tmp/portless/ca.pem` (or `~/.portless/ca.pem` when the proxy runs on a non-privileged port like 1355). Alternatively, use `--no-tls` for plain HTTP.
237
+
238
238
  Portless detects this misconfiguration and responds with `508 Loop Detected` along with a message pointing to this fix.
239
239
 
240
240
  ## Development
@@ -286,6 +286,9 @@ function getRequestHost(req) {
286
286
  if (typeof authority === "string" && authority) return authority;
287
287
  return req.headers.host || "";
288
288
  }
289
+ function isEncrypted(req) {
290
+ return !!req.socket.encrypted;
291
+ }
289
292
  function buildForwardedHeaders(req, tls) {
290
293
  const headers = {};
291
294
  const remoteAddress = req.socket.remoteAddress || "127.0.0.1";
@@ -313,8 +316,8 @@ function createProxyServer(options) {
313
316
  tls
314
317
  } = options;
315
318
  const tldSuffix = `.${tld}`;
316
- const isTls = !!tls;
317
319
  const handleRequest = (req, res) => {
320
+ const reqTls = isEncrypted(req);
318
321
  res.setHeader(PORTLESS_HEADER, "1");
319
322
  const routes = getRoutes();
320
323
  const host = getRequestHost(req).split(":")[0];
@@ -335,7 +338,7 @@ function createProxyServer(options) {
335
338
  "Loop Detected",
336
339
  `<div class="content"><p class="desc">This request has passed through portless ${hops} times. This usually means a dev server (Vite, webpack, etc.) is proxying requests back through portless without rewriting the Host header.</p><div class="section"><p class="label">Fix: add changeOrigin to your proxy config</p><pre class="terminal">proxy: {
337
340
  "/api": {
338
- target: "http://&lt;backend&gt;${escapeHtml(tldSuffix)}:&lt;port&gt;",
341
+ target: "${reqTls ? "https" : "http"}://&lt;backend&gt;${escapeHtml(tldSuffix)}${reqTls ? "" : ":&lt;port&gt;"}",
339
342
  changeOrigin: true,
340
343
  },
341
344
  }</pre></div></div>`
@@ -348,7 +351,7 @@ function createProxyServer(options) {
348
351
  const safeHost = escapeHtml(host);
349
352
  const strippedHost = host.endsWith(tldSuffix) ? host.slice(0, -tldSuffix.length) : host;
350
353
  const safeSuggestion = escapeHtml(strippedHost);
351
- const routesList = routes.length > 0 ? `<div class="section"><p class="label">Active apps</p><ul class="card">${routes.map((r) => `<li><a href="${escapeHtml(formatUrl(r.hostname, proxyPort, isTls))}" class="card-link"><span class="name">${escapeHtml(r.hostname)}</span><span class="meta"><code class="port">127.0.0.1:${escapeHtml(String(r.port))}</code><span class="arrow">${ARROW_SVG}</span></span></a></li>`).join("")}</ul></div>` : '<p class="empty">No apps running.</p>';
354
+ const routesList = routes.length > 0 ? `<div class="section"><p class="label">Active apps</p><ul class="card">${routes.map((r) => `<li><a href="${escapeHtml(formatUrl(r.hostname, proxyPort, reqTls))}" class="card-link"><span class="name">${escapeHtml(r.hostname)}</span><span class="meta"><code class="port">127.0.0.1:${escapeHtml(String(r.port))}</code><span class="arrow">${ARROW_SVG}</span></span></a></li>`).join("")}</ul></div>` : '<p class="empty">No apps running.</p>';
352
355
  res.writeHead(404, { "Content-Type": "text/html" });
353
356
  res.end(
354
357
  renderPage(
@@ -359,7 +362,7 @@ function createProxyServer(options) {
359
362
  );
360
363
  return;
361
364
  }
362
- const forwardedHeaders = buildForwardedHeaders(req, isTls);
365
+ const forwardedHeaders = buildForwardedHeaders(req, reqTls);
363
366
  const proxyReqHeaders = { ...req.headers };
364
367
  for (const [key, value] of Object.entries(forwardedHeaders)) {
365
368
  proxyReqHeaders[key] = value;
@@ -380,7 +383,7 @@ function createProxyServer(options) {
380
383
  },
381
384
  (proxyRes) => {
382
385
  const responseHeaders = { ...proxyRes.headers };
383
- if (isTls) {
386
+ if (reqTls) {
384
387
  for (const h of HOP_BY_HOP_HEADERS) {
385
388
  delete responseHeaders[h];
386
389
  }
@@ -442,7 +445,7 @@ function createProxyServer(options) {
442
445
  socket.destroy();
443
446
  return;
444
447
  }
445
- const forwardedHeaders = buildForwardedHeaders(req, isTls);
448
+ const forwardedHeaders = buildForwardedHeaders(req, isEncrypted(req));
446
449
  const proxyReqHeaders = { ...req.headers };
447
450
  for (const [key, value] of Object.entries(forwardedHeaders)) {
448
451
  proxyReqHeaders[key] = value;
@@ -513,8 +516,19 @@ function createProxyServer(options) {
513
516
  h2Server.on("upgrade", (req, socket, head) => {
514
517
  handleUpgrade(req, socket, head);
515
518
  });
516
- const plainServer = http.createServer(handleRequest);
517
- plainServer.on("upgrade", handleUpgrade);
519
+ const plainServer = http.createServer((req, res) => {
520
+ const host = getRequestHost(req).split(":")[0] || "localhost";
521
+ const location = `https://${host}${proxyPort === 443 ? "" : `:${proxyPort}`}${req.url || "/"}`;
522
+ res.writeHead(302, { Location: location, [PORTLESS_HEADER]: "1" });
523
+ res.end();
524
+ });
525
+ plainServer.on("upgrade", (req, socket) => {
526
+ const host = getRequestHost(req);
527
+ console.warn(
528
+ `[portless] Dropped plain-HTTP WebSocket upgrade for ${host}; use wss:// instead`
529
+ );
530
+ socket.destroy();
531
+ });
518
532
  const wrapper = net.createServer((socket) => {
519
533
  socket.on("error", () => {
520
534
  socket.destroy();
@@ -545,6 +559,15 @@ function createProxyServer(options) {
545
559
  httpServer.on("upgrade", handleUpgrade);
546
560
  return httpServer;
547
561
  }
562
+ function createHttpRedirectServer(httpsPort) {
563
+ return http.createServer((req, res) => {
564
+ const host = (req.headers.host || "localhost").split(":")[0];
565
+ const portSuffix = httpsPort === 443 ? "" : `:${httpsPort}`;
566
+ const location = `https://${host}${portSuffix}${req.url || "/"}`;
567
+ res.writeHead(302, { Location: location, [PORTLESS_HEADER]: "1" });
568
+ res.end();
569
+ });
570
+ }
548
571
 
549
572
  // src/hosts.ts
550
573
  import * as fs2 from "fs";
@@ -637,7 +660,7 @@ import * as path2 from "path";
637
660
  import * as readline from "readline";
638
661
  import { execSync, spawn } from "child_process";
639
662
  var isWindows2 = process.platform === "win32";
640
- var DEFAULT_PROXY_PORT = 1355;
663
+ var FALLBACK_PROXY_PORT = 1355;
641
664
  var PRIVILEGED_PORT_THRESHOLD = 1024;
642
665
  var SYSTEM_STATE_DIR = isWindows2 ? path2.join(os.tmpdir(), "portless") : "/tmp/portless";
643
666
  var USER_STATE_DIR = path2.join(os.homedir(), ".portless");
@@ -656,13 +679,16 @@ var SIGNAL_CODES = {
656
679
  SIGKILL: 9,
657
680
  SIGTERM: 15
658
681
  };
659
- function getDefaultPort() {
682
+ function getProtocolPort(tls) {
683
+ return tls ? 443 : 80;
684
+ }
685
+ function getDefaultPort(tls) {
660
686
  const envPort = process.env.PORTLESS_PORT;
661
687
  if (envPort) {
662
688
  const port = parseInt(envPort, 10);
663
689
  if (!isNaN(port) && port >= 1 && port <= 65535) return port;
664
690
  }
665
- return DEFAULT_PROXY_PORT;
691
+ return tls === void 0 ? FALLBACK_PROXY_PORT : getProtocolPort(tls);
666
692
  }
667
693
  function resolveStateDir(port) {
668
694
  if (process.env.PORTLESS_STATE_DIR) return process.env.PORTLESS_STATE_DIR;
@@ -701,15 +727,15 @@ var DEFAULT_TLD = "localhost";
701
727
  var RISKY_TLDS = /* @__PURE__ */ new Map([
702
728
  ["local", "conflicts with mDNS/Bonjour on macOS"],
703
729
  ["dev", "Google-owned; browsers force HTTPS via preloaded HSTS"],
704
- ["com", "public TLD -- DNS requests will leak to the internet"],
705
- ["org", "public TLD -- DNS requests will leak to the internet"],
706
- ["net", "public TLD -- DNS requests will leak to the internet"],
707
- ["io", "public TLD -- DNS requests will leak to the internet"],
708
- ["app", "public TLD -- DNS requests will leak to the internet"],
709
- ["edu", "public TLD -- DNS requests will leak to the internet"],
710
- ["gov", "public TLD -- DNS requests will leak to the internet"],
711
- ["mil", "public TLD -- DNS requests will leak to the internet"],
712
- ["int", "public TLD -- DNS requests will leak to the internet"]
730
+ ["com", "public TLD; DNS requests will leak to the internet"],
731
+ ["org", "public TLD; DNS requests will leak to the internet"],
732
+ ["net", "public TLD; DNS requests will leak to the internet"],
733
+ ["io", "public TLD; DNS requests will leak to the internet"],
734
+ ["app", "public TLD; DNS requests will leak to the internet"],
735
+ ["edu", "public TLD; DNS requests will leak to the internet"],
736
+ ["gov", "public TLD; DNS requests will leak to the internet"],
737
+ ["mil", "public TLD; DNS requests will leak to the internet"],
738
+ ["int", "public TLD; DNS requests will leak to the internet"]
713
739
  ]);
714
740
  function validateTld(tld) {
715
741
  if (!tld) return "TLD cannot be empty";
@@ -745,9 +771,9 @@ function getDefaultTld() {
745
771
  if (err) throw new Error(`PORTLESS_TLD: ${err}`);
746
772
  return val;
747
773
  }
748
- function isHttpsEnvEnabled() {
774
+ function isHttpsEnvDisabled() {
749
775
  const val = process.env.PORTLESS_HTTPS;
750
- return val === "1" || val === "true";
776
+ return val === "0" || val === "false";
751
777
  }
752
778
  function isWildcardEnvEnabled() {
753
779
  const val = process.env.PORTLESS_WILDCARD;
@@ -777,8 +803,8 @@ async function discoverState() {
777
803
  return { dir: SYSTEM_STATE_DIR, port: systemPort, tls, tld };
778
804
  }
779
805
  }
780
- const defaultPort = getDefaultPort();
781
- const probePorts = /* @__PURE__ */ new Set([defaultPort, 443, 80]);
806
+ const configuredPort = getDefaultPort();
807
+ const probePorts = /* @__PURE__ */ new Set([443, 80, FALLBACK_PROXY_PORT, configuredPort]);
782
808
  for (const port of probePorts) {
783
809
  if (await isProxyRunning(port)) {
784
810
  const dir = resolveStateDir(port);
@@ -787,7 +813,12 @@ async function discoverState() {
787
813
  return { dir, port, tls, tld };
788
814
  }
789
815
  }
790
- return { dir: resolveStateDir(defaultPort), port: defaultPort, tls: false, tld: getDefaultTld() };
816
+ return {
817
+ dir: resolveStateDir(configuredPort),
818
+ port: configuredPort,
819
+ tls: false,
820
+ tld: getDefaultTld()
821
+ };
791
822
  }
792
823
  async function findFreePort(minPort = MIN_APP_PORT, maxPort = MAX_APP_PORT) {
793
824
  if (minPort > maxPort) {
@@ -1093,7 +1124,8 @@ var RouteStore = class _RouteStore {
1093
1124
  getRoutesPath() {
1094
1125
  return this.routesPath;
1095
1126
  }
1096
- // -- Locking ---------------------------------------------------------------
1127
+ // Locking
1128
+ // ---------------------------------------------------------------------------
1097
1129
  static sleepBuffer = new Int32Array(new SharedArrayBuffer(4));
1098
1130
  syncSleep(ms) {
1099
1131
  Atomics.wait(_RouteStore.sleepBuffer, 0, 0, ms);
@@ -1128,7 +1160,8 @@ var RouteStore = class _RouteStore {
1128
1160
  } catch {
1129
1161
  }
1130
1162
  }
1131
- // -- Route I/O -------------------------------------------------------------
1163
+ // Route I/O
1164
+ // ---------------------------------------------------------------------------
1132
1165
  isProcessAlive(pid) {
1133
1166
  try {
1134
1167
  process.kill(pid, 0);
@@ -1219,6 +1252,7 @@ export {
1219
1252
  parseHostname,
1220
1253
  PORTLESS_HEADER,
1221
1254
  createProxyServer,
1255
+ createHttpRedirectServer,
1222
1256
  extractManagedBlock,
1223
1257
  removeBlock,
1224
1258
  buildBlock,
@@ -1227,18 +1261,20 @@ export {
1227
1261
  getManagedHostnames,
1228
1262
  checkHostResolution,
1229
1263
  isWindows2 as isWindows,
1264
+ FALLBACK_PROXY_PORT,
1230
1265
  PRIVILEGED_PORT_THRESHOLD,
1266
+ WAIT_FOR_PROXY_MAX_ATTEMPTS,
1267
+ WAIT_FOR_PROXY_INTERVAL_MS,
1268
+ getProtocolPort,
1231
1269
  getDefaultPort,
1232
1270
  resolveStateDir,
1233
- readTlsMarker,
1234
1271
  writeTlsMarker,
1235
1272
  DEFAULT_TLD,
1236
1273
  RISKY_TLDS,
1237
1274
  validateTld,
1238
- readTldFromDir,
1239
1275
  writeTldFile,
1240
1276
  getDefaultTld,
1241
- isHttpsEnvEnabled,
1277
+ isHttpsEnvDisabled,
1242
1278
  isWildcardEnvEnabled,
1243
1279
  discoverState,
1244
1280
  findFreePort,