vite-plugin-rpx 0.11.0 → 0.11.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/dist/index.d.ts CHANGED
@@ -7,5 +7,7 @@ export type { SimplifiedPluginOptions } from './simplified-plugin';
7
7
  * @deprecated Use SimplifiedVitePlugin instead to avoid WebSocket port allocation issues
8
8
  */
9
9
  export declare function VitePluginRpx(options: VitePluginRpxOptions): Plugin;
10
+ // Export the simplified plugin types and implementation
10
11
  export { SimplifiedVitePlugin } from './simplified-plugin';
11
- export default SimplifiedVitePlugin;
12
+ // Set the default export to be the simplified plugin
13
+ export default SimplifiedVitePlugin;
package/dist/index.js CHANGED
@@ -4,25 +4,43 @@ var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
7
12
  var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
8
20
  target = mod != null ? __create(__getProtoOf(mod)) : {};
9
21
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
22
  for (let key of __getOwnPropNames(mod))
11
23
  if (!__hasOwnProp.call(to, key))
12
24
  __defProp(to, key, {
13
- get: () => mod[key],
25
+ get: __accessProp.bind(mod, key),
14
26
  enumerable: true
15
27
  });
28
+ if (canCache)
29
+ cache.set(mod, to);
16
30
  return to;
17
31
  };
18
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
19
37
  var __export = (target, all) => {
20
38
  for (var name in all)
21
39
  __defProp(target, name, {
22
40
  get: all[name],
23
41
  enumerable: true,
24
42
  configurable: true,
25
- set: (newValue) => all[name] = () => newValue
43
+ set: __exportSetter.bind(all, name)
26
44
  });
27
45
  };
28
46
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -3319,13 +3337,14 @@ function getSudoPassword() {
3319
3337
  }
3320
3338
  function execSudoSync(command) {
3321
3339
  const sudoPassword = getSudoPassword();
3340
+ const escaped = command.replace(/'/g, `'\\''`);
3322
3341
  if (sudoPassword) {
3323
- return execSync(`echo '${sudoPassword}' | sudo -S ${command}`, {
3342
+ return execSync(`echo '${sudoPassword}' | sudo -S sh -c '${escaped}' 2>/dev/null`, {
3324
3343
  encoding: "utf-8",
3325
3344
  stdio: ["pipe", "pipe", "pipe"]
3326
3345
  });
3327
3346
  }
3328
- return execSync(`sudo ${command}`, { encoding: "utf-8" });
3347
+ return execSync(`sudo sh -c '${escaped}'`, { encoding: "utf-8" });
3329
3348
  }
3330
3349
  function debugLog(category, message, verbose) {
3331
3350
  if (verbose)
@@ -4796,15 +4815,21 @@ import * as net2 from "node:net";
4796
4815
  import * as process18 from "node:process";
4797
4816
 
4798
4817
  // ../rpx/src/logger.ts
4799
- import { createRequire as createRequire2 } from "node:module";
4800
- var require2 = createRequire2(import.meta.url);
4801
- var consolaMod = require2("consola");
4802
- var log = consolaMod.consola;
4818
+ var log = {
4819
+ info: (...args) => console.log("[info]", ...args),
4820
+ success: (...args) => console.log("[success]", ...args),
4821
+ warn: (...args) => console.warn("[warn]", ...args),
4822
+ error: (...args) => console.error("[error]", ...args),
4823
+ debug: (...args) => console.debug("[debug]", ...args),
4824
+ log: (...args) => console.log(...args),
4825
+ start: (...args) => console.log("[start]", ...args),
4826
+ box: (...args) => console.log("[box]", ...args)
4827
+ };
4803
4828
 
4804
4829
  // ../rpx/src/start.ts
4805
4830
  var import_picocolors = __toESM(require_picocolors(), 1);
4806
4831
  // ../rpx/package.json
4807
- var version = "0.11.0";
4832
+ var version = "0.11.1";
4808
4833
 
4809
4834
  // ../rpx/src/config.ts
4810
4835
  import { homedir as homedir3 } from "node:os";
@@ -9847,13 +9872,13 @@ var defaultConfig3 = {
9847
9872
  };
9848
9873
 
9849
9874
  // ../rpx/src/hosts.ts
9875
+ await init_utils();
9850
9876
  import { exec } from "node:child_process";
9851
9877
  import fs2 from "node:fs";
9852
9878
  import os from "node:os";
9853
9879
  import path from "node:path";
9854
9880
  import * as process15 from "node:process";
9855
9881
  import { promisify } from "node:util";
9856
- await init_utils();
9857
9882
  var execAsync = promisify(exec);
9858
9883
  var hostsFilePath = process15.platform === "win32" ? path.join(process15.env.windir || "C:\\Windows", "System32", "drivers", "etc", "hosts") : "/etc/hosts";
9859
9884
  var sudoPrivilegesAcquired = false;
@@ -9861,21 +9886,22 @@ async function execSudo(command) {
9861
9886
  if (process15.platform === "win32")
9862
9887
  throw new Error("Administrator privileges required on Windows");
9863
9888
  const sudoPassword = getSudoPassword();
9889
+ const escaped = command.replace(/'/g, `'\\''`);
9864
9890
  try {
9865
9891
  if (sudoPassword) {
9866
- const { stdout: stdout2 } = await execAsync(`echo '${sudoPassword}' | sudo -S ${command}`);
9892
+ const { stdout: stdout2 } = await execAsync(`echo '${sudoPassword}' | sudo -S sh -c '${escaped}' 2>/dev/null`);
9867
9893
  sudoPrivilegesAcquired = true;
9868
9894
  return stdout2;
9869
9895
  }
9870
9896
  if (sudoPrivilegesAcquired) {
9871
9897
  try {
9872
- const { stdout: stdout2 } = await execAsync(`sudo -n true && sudo -n ${command}`);
9898
+ const { stdout: stdout2 } = await execAsync(`sudo -n sh -c '${escaped}'`);
9873
9899
  return stdout2;
9874
9900
  } catch (error) {
9875
9901
  debugLog("hosts", "Cached sudo privileges expired, requesting again", true);
9876
9902
  }
9877
9903
  }
9878
- const { stdout } = await execAsync(`sudo ${command}`);
9904
+ const { stdout } = await execAsync(`sudo sh -c '${escaped}'`);
9879
9905
  sudoPrivilegesAcquired = true;
9880
9906
  return stdout;
9881
9907
  } catch (error) {
@@ -9889,13 +9915,13 @@ async function addHosts(hosts, verbose) {
9889
9915
  let existingContent;
9890
9916
  try {
9891
9917
  existingContent = await fs2.promises.readFile(hostsFilePath, "utf-8");
9892
- } catch (readErr) {
9893
- debugLog("hosts", `Error reading hosts file: ${readErr}`, verbose);
9894
- log.error(`Failed to read hosts file: ${readErr}`);
9918
+ } catch {
9919
+ debugLog("hosts", "Reading hosts file requires elevated permissions, using sudo", verbose);
9895
9920
  try {
9896
9921
  existingContent = await execSudo(`cat "${hostsFilePath}"`);
9897
9922
  } catch (sudoErr) {
9898
- log.error(`Failed to read hosts file with sudo: ${sudoErr}`);
9923
+ console.log(" Could not read hosts file skipping hosts setup");
9924
+ debugLog("hosts", `sudo read also failed: ${sudoErr}`, verbose);
9899
9925
  throw new Error(`Cannot read hosts file: ${sudoErr}`);
9900
9926
  }
9901
9927
  }
@@ -9906,7 +9932,6 @@ async function addHosts(hosts, verbose) {
9906
9932
  });
9907
9933
  if (newEntries.length === 0) {
9908
9934
  debugLog("hosts", "All hosts already exist in hosts file", verbose);
9909
- log.info("All hosts are already in the hosts file");
9910
9935
  return;
9911
9936
  }
9912
9937
  const hostEntries = newEntries.map((host) => `
@@ -9918,32 +9943,19 @@ async function addHosts(hosts, verbose) {
9918
9943
  try {
9919
9944
  await fs2.promises.writeFile(tmpFile, existingContent + hostEntries, "utf8");
9920
9945
  await execSudo(`cat "${tmpFile}" | tee "${hostsFilePath}" > /dev/null`);
9921
- log.success(`Added new hosts: ${newEntries.join(", ")}`);
9946
+ console.log(` Hosts updated: ${newEntries.join(", ")}`);
9922
9947
  } catch (error) {
9923
- log.warn("Could not modify hosts file automatically");
9924
- log.info("Please add these entries to your hosts file:");
9948
+ console.log(" Could not update hosts file automatically");
9949
+ console.log(" Add these entries to /etc/hosts:");
9925
9950
  newEntries.forEach((host) => {
9926
- log.info(` 127.0.0.1 ${host}`);
9927
- log.info(` ::1 ${host}`);
9951
+ console.log(` 127.0.0.1 ${host}`);
9952
+ console.log(` ::1 ${host}`);
9928
9953
  });
9929
- if (process15.platform === "win32") {
9930
- log.info(`
9931
- On Windows:`);
9932
- log.info("1. Run notepad as administrator");
9933
- log.info("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");
9934
- } else {
9935
- log.info(`
9936
- On Unix systems:`);
9937
- log.info(` sudo nano ${hostsFilePath}`);
9938
- }
9939
- log.info(`
9940
- Or run: buddy setup:ssl`);
9954
+ console.log(` Or run: sudo nano ${hostsFilePath}`);
9941
9955
  } finally {
9942
9956
  try {
9943
9957
  await fs2.promises.unlink(tmpFile);
9944
- } catch (unlinkErr) {
9945
- debugLog("hosts", `Failed to remove temporary file: ${unlinkErr}`, verbose);
9946
- }
9958
+ } catch {}
9947
9959
  }
9948
9960
  } catch (err) {
9949
9961
  const error = err;
@@ -9956,12 +9968,12 @@ async function removeHosts(hosts, verbose) {
9956
9968
  let content;
9957
9969
  try {
9958
9970
  content = await fs2.promises.readFile(hostsFilePath, "utf-8");
9959
- } catch (readErr) {
9960
- debugLog("hosts", `Error reading hosts file: ${readErr}`, verbose);
9971
+ } catch {
9972
+ debugLog("hosts", "Reading hosts file requires elevated permissions, using sudo", verbose);
9961
9973
  try {
9962
9974
  content = await execSudo(`cat "${hostsFilePath}"`);
9963
9975
  } catch (sudoErr) {
9964
- log.error(`Failed to read hosts file with sudo: ${sudoErr}`);
9976
+ debugLog("hosts", `sudo read also failed: ${sudoErr}`, verbose);
9965
9977
  throw new Error(`Cannot read hosts file: ${sudoErr}`);
9966
9978
  }
9967
9979
  }
@@ -9993,14 +10005,9 @@ async function removeHosts(hosts, verbose) {
9993
10005
  try {
9994
10006
  await fs2.promises.writeFile(tmpFile, newContent, "utf8");
9995
10007
  await execSudo(`cat "${tmpFile}" | tee "${hostsFilePath}" > /dev/null`);
9996
- log.success("Hosts removed successfully");
10008
+ debugLog("hosts", "Hosts removed successfully", verbose);
9997
10009
  } catch (error) {
9998
- log.warn("Could not modify hosts file automatically");
9999
- log.info("You may want to remove these entries from your hosts file:");
10000
- hosts.forEach((host) => {
10001
- log.info(` 127.0.0.1 ${host}`);
10002
- log.info(` ::1 ${host}`);
10003
- });
10010
+ debugLog("hosts", "Could not clean up hosts file automatically", verbose);
10004
10011
  } finally {
10005
10012
  try {
10006
10013
  await fs2.promises.unlink(tmpFile);
@@ -19079,10 +19086,12 @@ async function generateCertificate(options) {
19079
19086
  const domains = isMultiProxyOptions(options) ? options.proxies.map((proxy) => proxy.to) : [options.to];
19080
19087
  debugLog("ssl", `Generating certificate for domains: ${domains.join(", ")}`, options.verbose);
19081
19088
  const rootCAConfig = httpsConfig(options, options.verbose);
19082
- log.info("Generating Root CA certificate...");
19089
+ if (options.verbose)
19090
+ log.info("Generating Root CA certificate...");
19083
19091
  const caCert = await QO(rootCAConfig);
19084
19092
  const hostConfig = httpsConfig(options, options.verbose);
19085
- log.info(`Generating host certificate for: ${domains.join(", ")}`);
19093
+ if (options.verbose)
19094
+ log.info(`Generating host certificate for: ${domains.join(", ")}`);
19086
19095
  const hostCert = await YO({
19087
19096
  ...hostConfig,
19088
19097
  rootCA: {
@@ -19106,22 +19115,26 @@ async function generateCertificate(options) {
19106
19115
  const alreadyTrusted = await isCertTrusted(hostConfig.certPath, { verbose: options.verbose, regenerateUntrustedCerts: true });
19107
19116
  if (alreadyTrusted) {
19108
19117
  debugLog("ssl", "Certificate is already trusted, skipping trust store update", options.verbose);
19109
- log.success("Certificate is already trusted in system trust store");
19118
+ if (options.verbose)
19119
+ log.success("Certificate is already trusted in system trust store");
19110
19120
  cachedSSLConfig = {
19111
19121
  key: hostCert.privateKey,
19112
19122
  cert: hostCert.certificate,
19113
19123
  ca: caCert.certificate
19114
19124
  };
19115
- log.success(`Certificate generated successfully for ${domains.length} domain${domains.length > 1 ? "s" : ""}`);
19125
+ if (options.verbose)
19126
+ log.success(`Certificate generated successfully for ${domains.length} domain${domains.length > 1 ? "s" : ""}`);
19116
19127
  return;
19117
19128
  }
19118
- log.info("Adding certificate to system trust store (may require sudo permission)...");
19129
+ if (options.verbose)
19130
+ log.info("Adding certificate to system trust store (may require sudo permission)...");
19119
19131
  let isTrusted = false;
19120
19132
  if (process16.platform === "darwin") {
19121
19133
  try {
19122
19134
  const combinedCmd = `security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${hostConfig.caCertPath}" && security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${hostConfig.certPath}"`;
19123
19135
  execSudoSync(combinedCmd);
19124
- log.success("Successfully added CA and host certificates to system trust store");
19136
+ if (options.verbose)
19137
+ log.success("Successfully added CA and host certificates to system trust store");
19125
19138
  try {
19126
19139
  execSync2(`security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain-db "${hostConfig.certPath}"`, { stdio: "pipe" });
19127
19140
  } catch {}
@@ -19137,7 +19150,8 @@ echo "If you still see certificate warnings, type 'thisisunsafe' on the warning
19137
19150
  `;
19138
19151
  await fs3.writeFile(scriptPath, scriptContent, { mode: 493 });
19139
19152
  } catch (err) {
19140
- log.warn(`Could not add certificate to trust store automatically: ${err}`);
19153
+ if (options.verbose)
19154
+ log.warn(`Could not add certificate to trust store automatically: ${err}`);
19141
19155
  const sslScriptDir = hostConfig.basePath || join7(homedir6(), ".stacks", "ssl");
19142
19156
  const scriptPath = join7(sslScriptDir, "trust-rpx-cert.sh");
19143
19157
  const scriptContent = `#!/bin/bash
@@ -19148,8 +19162,10 @@ echo "Certificates trusted! Please restart your browser."
19148
19162
  echo "If you still see certificate warnings, type 'thisisunsafe' on the warning page in Chrome/Arc browsers."
19149
19163
  `;
19150
19164
  await fs3.writeFile(scriptPath, scriptContent, { mode: 493 });
19151
- log.info(`Created a trust helper script at: ${scriptPath}`);
19152
- log.info(`If you're still having certificate issues, run: sh ${scriptPath}`);
19165
+ if (options.verbose) {
19166
+ log.info(`Created a trust helper script at: ${scriptPath}`);
19167
+ log.info(`If you're still having certificate issues, run: sh ${scriptPath}`);
19168
+ }
19153
19169
  }
19154
19170
  } else if (process16.platform === "linux") {
19155
19171
  try {
@@ -19167,10 +19183,12 @@ echo "RPX certificates installed. Please restart your browser."
19167
19183
  await new Promise((resolve9) => {
19168
19184
  exec2(`sudo bash "${tmpScript}"`, (error) => {
19169
19185
  if (error) {
19170
- log.warn(`Could not trust certificates: ${error}`);
19186
+ if (options.verbose)
19187
+ log.warn(`Could not trust certificates: ${error}`);
19171
19188
  resolve9(false);
19172
19189
  } else {
19173
- log.success("Successfully added certificates to system trust store");
19190
+ if (options.verbose)
19191
+ log.success("Successfully added certificates to system trust store");
19174
19192
  resolve9(true);
19175
19193
  }
19176
19194
  });
@@ -19178,7 +19196,8 @@ echo "RPX certificates installed. Please restart your browser."
19178
19196
  await fs3.unlink(tmpScript).catch(() => {});
19179
19197
  isTrusted = true;
19180
19198
  } catch (error) {
19181
- log.warn(`Failed to trust certificates: ${error}`);
19199
+ if (options.verbose)
19200
+ log.warn(`Failed to trust certificates: ${error}`);
19182
19201
  }
19183
19202
  } else if (process16.platform === "win32") {
19184
19203
  try {
@@ -19193,17 +19212,20 @@ Write-Host "Certificate trusted successfully!"
19193
19212
  const psPath = join7(os2.tmpdir(), "rpx-trust.ps1");
19194
19213
  await fs3.writeFile(psPath, winScript);
19195
19214
  execSync2(`powershell -ExecutionPolicy Bypass -File "${psPath}"`);
19196
- log.success("Successfully added certificate to Windows trust store");
19215
+ if (options.verbose)
19216
+ log.success("Successfully added certificate to Windows trust store");
19197
19217
  isTrusted = true;
19198
19218
  } catch (error) {
19199
- log.warn(`Could not trust certificate: ${error}`);
19219
+ if (options.verbose)
19220
+ log.warn(`Could not trust certificate: ${error}`);
19200
19221
  }
19201
19222
  } else {
19202
19223
  try {
19203
19224
  await UO(hostCert, caCert.certificate, hostConfig);
19204
19225
  isTrusted = true;
19205
19226
  } catch (err) {
19206
- log.warn(`Could not add certificate to trust store: ${err}`);
19227
+ if (options.verbose)
19228
+ log.warn(`Could not add certificate to trust store: ${err}`);
19207
19229
  }
19208
19230
  }
19209
19231
  cachedSSLConfig = {
@@ -19211,8 +19233,9 @@ Write-Host "Certificate trusted successfully!"
19211
19233
  cert: hostCert.certificate,
19212
19234
  ca: caCert.certificate
19213
19235
  };
19214
- log.success(`Certificate generated successfully for ${domains.length} domain${domains.length > 1 ? "s" : ""}`);
19215
- if (!isTrusted) {
19236
+ if (options.verbose)
19237
+ log.success(`Certificate generated successfully for ${domains.length} domain${domains.length > 1 ? "s" : ""}`);
19238
+ if (!isTrusted && options.verbose) {
19216
19239
  log.warn('If you see certificate warnings in Chrome/Arc, type "thisisunsafe" on the warning page');
19217
19240
  log.warn("This will bypass the warning and you should only need to do it once");
19218
19241
  }
@@ -19230,10 +19253,13 @@ async function checkExistingCertificates(options) {
19230
19253
  sslConfig.caCertPath ? fs3.access(sslConfig.caCertPath).then(() => true).catch(() => false) : Promise.resolve(false)
19231
19254
  ]);
19232
19255
  if (!keyExists || !certExists) {
19233
- debugLog("ssl", `Certificate files don't exist: key=${keyExists}, cert=${certExists}`, options.verbose);
19256
+ debugLog("ssl", `Certificate files don't exist: key=${keyExists}, cert=${certExists}, paths: ${sslConfig.keyPath}, ${sslConfig.certPath}`, options.verbose);
19234
19257
  return null;
19235
19258
  }
19236
- const shouldCheckTrust = "regenerateUntrustedCerts" in options ? options.regenerateUntrustedCerts !== false : true;
19259
+ const hasFlag = "regenerateUntrustedCerts" in options;
19260
+ const flagValue = options.regenerateUntrustedCerts;
19261
+ const shouldCheckTrust = hasFlag ? flagValue !== false : true;
19262
+ debugLog("ssl", `Trust check: hasFlag=${hasFlag}, flagValue=${flagValue}, shouldCheckTrust=${shouldCheckTrust}`, options.verbose);
19237
19263
  const certIsTrusted = shouldCheckTrust ? await isCertTrusted(sslConfig.certPath, options) : true;
19238
19264
  if (!certIsTrusted) {
19239
19265
  debugLog("ssl", "Certificate exists but is not trusted, will regenerate", options.verbose);
@@ -19244,10 +19270,25 @@ async function checkExistingCertificates(options) {
19244
19270
  fs3.readFile(sslConfig.certPath, "utf8"),
19245
19271
  caExists && sslConfig.caCertPath ? fs3.readFile(sslConfig.caCertPath, "utf8") : Promise.resolve(undefined)
19246
19272
  ]);
19247
- if (ca && !isValidRootCA(ca)) {
19248
- debugLog("ssl", "Invalid root CA certificate, will regenerate", options.verbose);
19273
+ if (ca && !ca.includes("-----BEGIN CERTIFICATE-----")) {
19274
+ debugLog("ssl", "Invalid root CA certificate content, will regenerate", options.verbose);
19249
19275
  return null;
19250
19276
  }
19277
+ if (isMultiProxyOptions(options)) {
19278
+ try {
19279
+ const { X509Certificate } = await import("node:crypto");
19280
+ const x509 = new X509Certificate(cert);
19281
+ const san = x509.subjectAltName || "";
19282
+ const requiredDomains = options.proxies.map((p3) => p3.to);
19283
+ const missingDomains = requiredDomains.filter((d3) => !san.includes(`DNS:${d3}`));
19284
+ if (missingDomains.length > 0) {
19285
+ debugLog("ssl", `Certificate missing SANs for: ${missingDomains.join(", ")}, will regenerate`, options.verbose);
19286
+ return null;
19287
+ }
19288
+ } catch (err) {
19289
+ debugLog("ssl", `Could not verify cert SANs: ${err}`, options.verbose);
19290
+ }
19291
+ }
19251
19292
  debugLog("ssl", "Successfully loaded existing certificates", options.verbose);
19252
19293
  cachedSSLConfig = { key, cert, ca };
19253
19294
  return cachedSSLConfig;
@@ -20079,7 +20120,8 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
20079
20120
  vitePluginUsage,
20080
20121
  listenPort,
20081
20122
  ssl: true,
20082
- cleanUrls
20123
+ cleanUrls,
20124
+ verbose
20083
20125
  });
20084
20126
  resolve9();
20085
20127
  } catch (err) {
@@ -20099,7 +20141,8 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
20099
20141
  vitePluginUsage,
20100
20142
  listenPort,
20101
20143
  ssl: !!ssl,
20102
- cleanUrls
20144
+ cleanUrls,
20145
+ verbose
20103
20146
  });
20104
20147
  resolve9();
20105
20148
  });
@@ -20132,16 +20175,14 @@ async function setupProxy(options) {
20132
20175
  }
20133
20176
  }
20134
20177
  } else {
20135
- if (to && to.includes("localhost") && !to.match(/^(localhost|127\.0\.0\.1)$/)) {
20178
+ if (process18.platform !== "darwin" && to && to.includes("localhost") && !to.match(/^(localhost|127\.0\.0\.1)$/)) {
20136
20179
  const hostsExist = await checkHosts([to], verbose);
20137
20180
  if (!hostsExist[0]) {
20138
- log.warn(`The hostname ${to} isn't in your hosts file. Adding it now...`);
20181
+ debugLog("hosts", `${to} not found in hosts file, adding...`, verbose);
20139
20182
  try {
20140
20183
  await addHosts([to], verbose);
20141
- log.success(`Added ${to} to your hosts file.`);
20142
20184
  } catch (error) {
20143
- log.error(`Failed to add ${to} to your hosts file: ${error}`);
20144
- log.info(`You may need to manually add '127.0.0.1 ${to}' to your /etc/hosts file.`);
20185
+ debugLog("hosts", `Failed to add ${to} to hosts file: ${error}`, verbose);
20145
20186
  }
20146
20187
  }
20147
20188
  }
@@ -20154,30 +20195,32 @@ async function setupProxy(options) {
20154
20195
  portManager2.usedPorts.add(httpPort);
20155
20196
  } else {
20156
20197
  debugLog("setup", "Port 80 is in use, skipping HTTP redirect", verbose);
20157
- log.warn("Port 80 is in use, HTTP to HTTPS redirect will not be available");
20198
+ if (verbose)
20199
+ log.warn("Port 80 is in use, HTTP to HTTPS redirect will not be available");
20158
20200
  }
20159
20201
  }
20160
20202
  const targetPort = ssl ? httpsPort : httpPort;
20161
20203
  const isTargetPortBusy = await isPortInUse(targetPort, hostname, verbose);
20162
20204
  let finalPort;
20163
20205
  if (isTargetPortBusy) {
20164
- log.warn(`Port ${targetPort} is already in use. This may be another instance of rpx or another service.`);
20206
+ debugLog("setup", `Port ${targetPort} is already in use`, verbose);
20207
+ if (verbose)
20208
+ log.warn(`Port ${targetPort} is already in use. This may be another instance of rpx or another service.`);
20165
20209
  if (targetPort === 443) {
20166
- log.info("HTTPS requires port 443 for standard operation. Using an alternative port instead.");
20167
20210
  finalPort = await portManager2.getNextAvailablePort(3443, true);
20168
- log.info(`Using port ${finalPort} instead. Access your site at https://${to}:${finalPort}`);
20211
+ debugLog("setup", `Using port ${finalPort} instead of ${targetPort}`, verbose);
20212
+ if (verbose)
20213
+ log.info(`Using port ${finalPort} instead. Access your site at https://${to}:${finalPort}`);
20169
20214
  } else {
20170
20215
  finalPort = await portManager2.getNextAvailablePort(targetPort + 1000, true);
20171
- log.info(`Using port ${finalPort} instead. Access your site at http://${to}:${finalPort}`);
20216
+ debugLog("setup", `Using port ${finalPort} instead of ${targetPort}`, verbose);
20217
+ if (verbose)
20218
+ log.info(`Using port ${finalPort} instead. Access your site at http://${to}:${finalPort}`);
20172
20219
  }
20173
20220
  } else {
20174
20221
  finalPort = targetPort;
20175
20222
  portManager2.usedPorts.add(finalPort);
20176
- if (targetPort === 443) {
20177
- log.info(`Using standard HTTPS port 443. Access your site at https://${to}`);
20178
- } else {
20179
- log.info(`Using standard HTTP port 80. Access your site at http://${to}`);
20180
- }
20223
+ debugLog("setup", `Using standard ${targetPort === 443 ? "HTTPS" : "HTTP"} port ${targetPort} for ${to}`, verbose);
20181
20224
  }
20182
20225
  await createProxyServer(from, to, fromPort, finalPort, hostname, sourceUrl, ssl, vitePluginUsage, verbose, cleanUrls, changeOrigin);
20183
20226
  } catch (err) {
@@ -20326,8 +20369,8 @@ async function startProxies(options) {
20326
20369
  const problematicTlds = ["dev", "app", "page", "new", "day", "foo"];
20327
20370
  const reservedTlds = ["test", "localhost", "local", "example", "invalid"];
20328
20371
  const uniqueTlds = [...new Set(customDomains.map((d3) => d3.split(".").pop()?.toLowerCase()))];
20329
- const problematicFound = uniqueTlds.filter((t2) => typeof t2 === "string" && problematicTlds.includes(t2));
20330
- if (problematicFound.length > 0) {
20372
+ const problematicFound = uniqueTlds.filter((t2) => !!t2 && problematicTlds.includes(t2));
20373
+ if (problematicFound.length > 0 && verbose) {
20331
20374
  log.warn(`The following TLDs may not work reliably for local development: ${problematicFound.map((t2) => `.${t2}`).join(", ")}`);
20332
20375
  log.info(` These TLDs have HSTS preloading which can bypass local DNS`);
20333
20376
  log.info(` Consider using reserved TLDs: .test, .localhost, or .local`);
@@ -20337,14 +20380,16 @@ async function startProxies(options) {
20337
20380
  const dnsStarted = await startDnsServer2(customDomains, verbose);
20338
20381
  if (dnsStarted) {
20339
20382
  await setupResolver2(verbose, customDomains);
20340
- const hasReservedOnly = uniqueTlds.every((t2) => typeof t2 === "string" && reservedTlds.includes(t2));
20341
- if (hasReservedOnly) {
20342
- log.success(`DNS server started for ${uniqueTlds.map((t2) => `.${t2}`).join(", ")} domains`);
20343
- } else {
20344
- log.success(`DNS server started for ${uniqueTlds.map((t2) => `.${t2}`).join(", ")} domains (hosts file entries also added)`);
20383
+ if (verbose) {
20384
+ const hasReservedOnly = uniqueTlds.every((t2) => !!t2 && reservedTlds.includes(t2));
20385
+ if (hasReservedOnly) {
20386
+ log.success(`DNS server started for ${uniqueTlds.map((t2) => `.${t2}`).join(", ")} domains`);
20387
+ } else {
20388
+ log.success(`DNS server started for ${uniqueTlds.map((t2) => `.${t2}`).join(", ")} domains (hosts file entries also added)`);
20389
+ }
20345
20390
  }
20346
20391
  } else {
20347
- log.warn("Could not start DNS server - custom domains may not resolve");
20392
+ debugLog("dns", "Could not start DNS server - custom domains may not resolve", verbose);
20348
20393
  }
20349
20394
  }
20350
20395
  const cleanupHandler = async () => {
@@ -20375,47 +20420,143 @@ async function startProxies(options) {
20375
20420
  console.error("Uncaught exception:", err);
20376
20421
  cleanupHandler();
20377
20422
  });
20378
- for (const option of proxyOptions) {
20379
- try {
20423
+ if (sslConfig && proxyOptions.length > 1) {
20424
+ debugLog("proxies", `Creating shared HTTPS server for ${proxyOptions.length} domains`, verbose);
20425
+ const routingTable = new Map;
20426
+ for (const option of proxyOptions) {
20380
20427
  const domain = option.to || "rpx.localhost";
20381
- debugLog("proxy", `Starting proxy for ${domain} with SSL config: ${!!sslConfig}`, option.verbose);
20382
- await startServer({
20383
- from: option.from || "localhost:5173",
20384
- to: domain,
20428
+ const fromUrl = new URL(option.from?.startsWith("http") ? option.from : `http://${option.from}`);
20429
+ const fromPort = Number.parseInt(fromUrl.port) || 80;
20430
+ routingTable.set(domain, {
20431
+ fromPort,
20432
+ sourceHost: fromUrl.host,
20385
20433
  cleanUrls: option.cleanUrls || false,
20386
- https: option.https || false,
20387
- cleanup: option.cleanup || false,
20388
- vitePluginUsage: option.vitePluginUsage || false,
20389
- verbose: option.verbose || false,
20390
- _cachedSSLConfig: sslConfig,
20391
20434
  changeOrigin: option.changeOrigin || false
20392
20435
  });
20436
+ debugLog("proxies", `Route: ${domain} → ${fromUrl.host}`, verbose);
20437
+ if (!domain.includes("localhost") && !domain.includes("127.0.0.1")) {
20438
+ try {
20439
+ const hostsExist = await checkHosts([domain], verbose);
20440
+ if (!hostsExist[0]) {
20441
+ await addHosts([domain], verbose);
20442
+ }
20443
+ } catch {
20444
+ debugLog("hosts", `Could not add hosts entry for ${domain}`, verbose);
20445
+ }
20446
+ }
20447
+ }
20448
+ const isHttpPortBusy = await isPortInUse(80, "0.0.0.0", verbose);
20449
+ if (!isHttpPortBusy) {
20450
+ startHttpRedirectServer(verbose);
20451
+ }
20452
+ const listenPort = 443;
20453
+ const isPortBusy = await isPortInUse(listenPort, "0.0.0.0", verbose);
20454
+ if (isPortBusy) {
20455
+ debugLog("proxies", `Port ${listenPort} is already in use, cannot start shared proxy`, verbose);
20456
+ if (verbose)
20457
+ log.warn(`Port ${listenPort} is in use. Shared HTTPS proxy cannot start.`);
20458
+ return;
20459
+ }
20460
+ try {
20461
+ const bunServer = Bun.serve({
20462
+ port: listenPort,
20463
+ hostname: "0.0.0.0",
20464
+ tls: {
20465
+ key: sslConfig.key,
20466
+ cert: sslConfig.cert,
20467
+ ca: sslConfig.ca,
20468
+ requestCert: false,
20469
+ rejectUnauthorized: false
20470
+ },
20471
+ async fetch(req) {
20472
+ const url = new URL(req.url);
20473
+ const hostHeader = req.headers.get("host") || "";
20474
+ const hostname = hostHeader.split(":")[0];
20475
+ const route = routingTable.get(hostname);
20476
+ if (!route) {
20477
+ debugLog("request", `No route found for host: ${hostname}`, verbose);
20478
+ return new Response(`No proxy configured for ${hostname}`, { status: 404 });
20479
+ }
20480
+ const targetUrl = `http://${route.sourceHost}${url.pathname}${url.search}`;
20481
+ try {
20482
+ const headers = new Headers(req.headers);
20483
+ headers.set("host", route.sourceHost);
20484
+ if (route.changeOrigin) {
20485
+ headers.set("origin", `http://${route.sourceHost}`);
20486
+ }
20487
+ headers.set("x-forwarded-for", "127.0.0.1");
20488
+ headers.set("x-forwarded-proto", "https");
20489
+ headers.set("x-forwarded-host", hostname);
20490
+ const response = await fetch(targetUrl, {
20491
+ method: req.method,
20492
+ headers,
20493
+ body: req.body,
20494
+ redirect: "manual"
20495
+ });
20496
+ const responseHeaders = new Headers(response.headers);
20497
+ if (route.cleanUrls && url.pathname.endsWith(".html")) {
20498
+ const cleanPath = url.pathname.replace(/\.html$/, "");
20499
+ return new Response(null, {
20500
+ status: 301,
20501
+ headers: { Location: cleanPath }
20502
+ });
20503
+ }
20504
+ return new Response(response.body, {
20505
+ status: response.status,
20506
+ statusText: response.statusText,
20507
+ headers: responseHeaders
20508
+ });
20509
+ } catch (err) {
20510
+ debugLog("request", `Proxy error for ${hostname}: ${err}`, verbose);
20511
+ return new Response(`Proxy Error: ${err}`, { status: 502 });
20512
+ }
20513
+ },
20514
+ error(err) {
20515
+ debugLog("server", `Shared proxy server error: ${err}`, verbose);
20516
+ return new Response(`Server Error: ${err.message}`, { status: 500 });
20517
+ }
20518
+ });
20519
+ activeServers.add(bunServer);
20520
+ debugLog("proxies", `Shared HTTPS proxy listening on port ${listenPort} for ${routingTable.size} domains`, verbose);
20393
20521
  } catch (err) {
20394
- debugLog("proxies", `Failed to start proxy for ${option.to}: ${err}`, option.verbose);
20395
- console.error(`Failed to start proxy for ${option.to}:`, err);
20522
+ debugLog("proxies", `Failed to start shared proxy: ${err}`, verbose);
20523
+ console.error("Failed to start shared HTTPS proxy:", err);
20396
20524
  cleanupHandler();
20397
20525
  }
20526
+ } else {
20527
+ for (const option of proxyOptions) {
20528
+ try {
20529
+ const domain = option.to || "rpx.localhost";
20530
+ debugLog("proxy", `Starting proxy for ${domain} with SSL config: ${!!sslConfig}`, option.verbose);
20531
+ await startServer({
20532
+ from: option.from || "localhost:5173",
20533
+ to: domain,
20534
+ cleanUrls: option.cleanUrls || false,
20535
+ https: option.https || false,
20536
+ cleanup: option.cleanup || false,
20537
+ vitePluginUsage: option.vitePluginUsage || false,
20538
+ verbose: option.verbose || false,
20539
+ _cachedSSLConfig: sslConfig,
20540
+ changeOrigin: option.changeOrigin || false
20541
+ });
20542
+ } catch (err) {
20543
+ debugLog("proxies", `Failed to start proxy for ${option.to}: ${err}`, option.verbose);
20544
+ console.error(`Failed to start proxy for ${option.to}:`, err);
20545
+ cleanupHandler();
20546
+ }
20547
+ }
20398
20548
  }
20399
20549
  }
20400
20550
  function logToConsole(options) {
20401
- if (!options?.vitePluginUsage) {
20402
- console.log("");
20403
- console.log(` ${import_picocolors.default.green(import_picocolors.default.bold("rpx"))} ${import_picocolors.default.green(`v${version}`)}`);
20404
- console.log("");
20405
- console.log(` ${import_picocolors.default.green("➜")} ${import_picocolors.default.dim(options?.from ?? "")} ${import_picocolors.default.dim("➜")} ${import_picocolors.default.cyan(options?.ssl ? ` https://${options?.to}` : ` http://${options?.to}`)}`);
20406
- if (options?.listenPort !== (options?.ssl ? 443 : 80))
20407
- console.log(` ${import_picocolors.default.green("➜")} Listening on port ${options?.listenPort}`);
20408
- if (options?.ssl) {
20409
- console.log(` ${import_picocolors.default.green("➜")} SSL enabled with:`);
20410
- console.log(` - TLS 1.2/1.3`);
20411
- console.log(` - Modern cipher suite`);
20412
- console.log(` - HTTP/2 enabled`);
20413
- console.log(` - HSTS enabled`);
20414
- }
20415
- if (options?.cleanUrls) {
20416
- console.log(` ${import_picocolors.default.green("➜")} Clean URLs enabled`);
20417
- }
20418
- }
20551
+ if (options?.vitePluginUsage || !options?.verbose)
20552
+ return;
20553
+ console.log("");
20554
+ console.log(` ${import_picocolors.default.green(import_picocolors.default.bold("rpx"))} ${import_picocolors.default.green(`v${version}`)}`);
20555
+ console.log(` ${import_picocolors.default.green("➜")} ${import_picocolors.default.dim(options?.from ?? "")} ${import_picocolors.default.dim("➜")} ${import_picocolors.default.cyan(options?.ssl ? `https://${options?.to}` : `http://${options?.to}`)}`);
20556
+ if (options?.listenPort !== (options?.ssl ? 443 : 80))
20557
+ console.log(` ${import_picocolors.default.green("➜")} Listening on port ${options?.listenPort}`);
20558
+ if (options?.cleanUrls)
20559
+ console.log(` ${import_picocolors.default.green("➜")} Clean URLs enabled`);
20419
20560
  }
20420
20561
 
20421
20562
  // ../rpx/src/index.ts
@@ -1,6 +1,4 @@
1
1
  import type { Plugin } from 'vite';
2
2
  import type { VitePluginRpxOptions } from './types';
3
3
  export declare function SimplifiedVitePlugin(options?: SimplifiedPluginOptions): Plugin;
4
- export declare interface SimplifiedPluginOptions extends VitePluginRpxOptions {
5
-
6
- }
4
+ export declare interface SimplifiedPluginOptions extends VitePluginRpxOptions {}
package/dist/types.d.ts CHANGED
@@ -6,19 +6,11 @@ export declare interface VitePluginRpxOptions {
6
6
  https?: boolean | TlsConfig
7
7
  cleanUrls?: boolean
8
8
  cleanup?: boolean | {
9
- /**
10
- * Whether to clean up hosts file entries
11
- * @default true
12
- */
13
9
  hosts?: boolean
14
- /**
15
- * Whether to clean up SSL certificates
16
- * @default false
17
- */
18
10
  certs?: boolean
19
11
  }
20
12
  changeOrigin?: boolean
21
13
  verbose?: boolean
22
14
  regenerateUntrustedCerts?: boolean
23
15
  enableHmr?: boolean
24
- }
16
+ }
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import type { SingleReverseProxyConfig } from '@stacksjs/rpx';
2
2
  import type { VitePluginRpxOptions } from './types';
3
3
  export declare function getDefaultSSLConfig(): { caCertPath: string, certPath: string, keyPath: string };
4
- export declare function buildConfig(options: VitePluginRpxOptions, serverUrl?: string): SingleReverseProxyConfig;
4
+ export declare function buildConfig(options: VitePluginRpxOptions, serverUrl?: string): SingleReverseProxyConfig;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vite-plugin-rpx",
3
3
  "type": "module",
4
- "version": "0.11.0",
4
+ "version": "0.11.1",
5
5
  "description": "A modern and smart reverse proxy. Vite plugin.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",
@@ -47,7 +47,7 @@
47
47
  "typecheck": "bunx tsc --noEmit"
48
48
  },
49
49
  "dependencies": {
50
- "@stacksjs/rpx": "0.11.0",
50
+ "@stacksjs/rpx": "0.11.1",
51
51
  "@stacksjs/tlsx": "^0.10.0"
52
52
  },
53
53
  "devDependencies": {