@stacksjs/rpx 0.5.0 → 0.6.0

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.js CHANGED
@@ -20467,7 +20467,7 @@ var quotes = collect([
20467
20467
  ]);
20468
20468
  var export_prompts = import_prompts.default;
20469
20469
  // package.json
20470
- var version = "0.5.0";
20470
+ var version = "0.6.0";
20471
20471
 
20472
20472
  // src/config.ts
20473
20473
  import { homedir } from "os";
@@ -20526,6 +20526,7 @@ async function loadConfig({ name, cwd, defaultConfig }) {
20526
20526
  var defaultConfig = {
20527
20527
  from: "localhost:5173",
20528
20528
  to: "stacks.localhost",
20529
+ cleanUrls: false,
20529
20530
  https: {
20530
20531
  basePath: "",
20531
20532
  caCertPath: join2(homedir(), ".stacks", "ssl", `stacks.localhost.ca.crt`),
@@ -20541,14 +20542,179 @@ var config4 = await loadConfig({
20541
20542
  });
20542
20543
 
20543
20544
  // src/hosts.ts
20544
- import { spawn } from "child_process";
20545
- import fs5 from "fs";
20546
- import os3 from "os";
20547
- import path4 from "path";
20545
+ import { exec } from "child_process";
20546
+ import fs from "fs";
20547
+ import os2 from "os";
20548
+ import path from "path";
20548
20549
  import process3 from "process";
20550
+ import { promisify } from "util";
20551
+
20552
+ // src/utils.ts
20553
+ function debugLog(category, message, verbose) {
20554
+ if (verbose) {
20555
+ console.debug(`[rpx:${category}] ${message}`);
20556
+ }
20557
+ }
20558
+ function extractHostname(options2) {
20559
+ if (isMultiProxyOptions(options2)) {
20560
+ return options2.proxies.map((proxy) => {
20561
+ const domain = proxy.to || "stacks.localhost";
20562
+ return domain.startsWith("http") ? new URL(domain).hostname : domain;
20563
+ });
20564
+ }
20565
+ if (isSingleProxyOptions(options2)) {
20566
+ const domain = options2.to || "stacks.localhost";
20567
+ return [domain.startsWith("http") ? new URL(domain).hostname : domain];
20568
+ }
20569
+ return ["stacks.localhost"];
20570
+ }
20571
+ function isValidRootCA(value) {
20572
+ return typeof value === "object" && value !== null && "certificate" in value && "privateKey" in value && typeof value.certificate === "string" && typeof value.privateKey === "string";
20573
+ }
20574
+ function getPrimaryDomain(options2) {
20575
+ if (!options2)
20576
+ return "stacks.localhost";
20577
+ if (isMultiProxyOptions(options2) && options2.proxies.length > 0)
20578
+ return options2.proxies[0].to || "stacks.localhost";
20579
+ if (isSingleProxyOptions(options2))
20580
+ return options2.to || "stacks.localhost";
20581
+ return "stacks.localhost";
20582
+ }
20583
+ function isMultiProxyConfig(options2) {
20584
+ return "proxies" in options2 && Array.isArray(options2.proxies);
20585
+ }
20586
+ function isMultiProxyOptions(options2) {
20587
+ return "proxies" in options2 && Array.isArray(options2.proxies);
20588
+ }
20589
+ function isSingleProxyOptions(options2) {
20590
+ return "to" in options2 && typeof options2.to === "string";
20591
+ }
20592
+
20593
+ // src/hosts.ts
20594
+ var execAsync = promisify(exec);
20595
+ var hostsFilePath = process3.platform === "win32" ? path.join(process3.env.windir || "C:\\Windows", "System32", "drivers", "etc", "hosts") : "/etc/hosts";
20596
+ async function execSudo(command) {
20597
+ if (process3.platform === "win32")
20598
+ throw new Error("Administrator privileges required on Windows");
20599
+ try {
20600
+ await execAsync(`sudo ${command}`);
20601
+ } catch (error) {
20602
+ throw new Error(`Failed to execute sudo command: ${error.message}`);
20603
+ }
20604
+ }
20605
+ async function addHosts(hosts, verbose) {
20606
+ debugLog("hosts", `Adding hosts: ${hosts.join(", ")}`, verbose);
20607
+ debugLog("hosts", `Using hosts file at: ${hostsFilePath}`, verbose);
20608
+ try {
20609
+ const existingContent = await fs.promises.readFile(hostsFilePath, "utf-8");
20610
+ const newEntries = hosts.filter((host) => {
20611
+ const ipv4Entry = `127.0.0.1 ${host}`;
20612
+ const ipv6Entry = `::1 ${host}`;
20613
+ return !existingContent.includes(ipv4Entry) && !existingContent.includes(ipv6Entry);
20614
+ });
20615
+ if (newEntries.length === 0) {
20616
+ debugLog("hosts", "All hosts already exist in hosts file", verbose);
20617
+ log.info("All hosts are already in the hosts file");
20618
+ return;
20619
+ }
20620
+ const hostEntries = newEntries.map((host) => `
20621
+ # Added by rpx
20622
+ 127.0.0.1 ${host}
20623
+ ::1 ${host}`).join(`
20624
+ `);
20625
+ const tmpFile = path.join(os2.tmpdir(), "hosts.tmp");
20626
+ await fs.promises.writeFile(tmpFile, existingContent + hostEntries, "utf8");
20627
+ try {
20628
+ await execSudo(`cp "${tmpFile}" "${hostsFilePath}"`);
20629
+ log.success(`Added new hosts: ${newEntries.join(", ")}`);
20630
+ } catch (error) {
20631
+ log.error("Failed to modify hosts file automatically");
20632
+ log.warn("Please add these entries to your hosts file manually:");
20633
+ hostEntries.split(`
20634
+ `).forEach((entry) => log.warn(entry));
20635
+ if (process3.platform === "win32") {
20636
+ log.warn(`
20637
+ On Windows:`);
20638
+ log.warn("1. Run notepad as administrator");
20639
+ log.warn("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");
20640
+ } else {
20641
+ log.warn(`
20642
+ On Unix systems:`);
20643
+ log.warn(`sudo nano ${hostsFilePath}`);
20644
+ }
20645
+ throw new Error("Failed to modify hosts file: manual intervention required");
20646
+ } finally {
20647
+ fs.unlinkSync(tmpFile);
20648
+ }
20649
+ } catch (err2) {
20650
+ const error = err2;
20651
+ log.error(`Failed to manage hosts file: ${error.message}`);
20652
+ throw error;
20653
+ }
20654
+ }
20655
+ async function removeHosts(hosts, verbose) {
20656
+ debugLog("hosts", `Removing hosts: ${hosts.join(", ")}`, verbose);
20657
+ try {
20658
+ const content = await fs.promises.readFile(hostsFilePath, "utf-8");
20659
+ const lines = content.split(`
20660
+ `);
20661
+ const filteredLines = lines.filter((line, index) => {
20662
+ if (line.trim() === "# Added by rpx") {
20663
+ lines.splice(index + 1, 2);
20664
+ return false;
20665
+ }
20666
+ return true;
20667
+ });
20668
+ while (filteredLines[filteredLines.length - 1]?.trim() === "")
20669
+ filteredLines.pop();
20670
+ const newContent = `${filteredLines.join(`
20671
+ `)}
20672
+ `;
20673
+ const tmpFile = path.join(os2.tmpdir(), "hosts.tmp");
20674
+ await fs.promises.writeFile(tmpFile, newContent, "utf8");
20675
+ try {
20676
+ await execSudo(`cp "${tmpFile}" "${hostsFilePath}"`);
20677
+ log.success("Hosts removed successfully");
20678
+ } catch (error) {
20679
+ log.error("Failed to modify hosts file automatically");
20680
+ log.warn("Please remove these entries from your hosts file manually:");
20681
+ hosts.forEach((host) => {
20682
+ log.warn("# Added by rpx");
20683
+ log.warn(`127.0.0.1 ${host}`);
20684
+ log.warn(`::1 ${host}`);
20685
+ });
20686
+ if (process3.platform === "win32") {
20687
+ log.warn(`
20688
+ On Windows:`);
20689
+ log.warn("1. Run notepad as administrator");
20690
+ log.warn("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");
20691
+ } else {
20692
+ log.warn(`
20693
+ On Unix systems:`);
20694
+ log.warn(`sudo nano ${hostsFilePath}`);
20695
+ }
20696
+ throw new Error("Failed to modify hosts file: manual intervention required");
20697
+ } finally {
20698
+ fs.unlinkSync(tmpFile);
20699
+ }
20700
+ } catch (err2) {
20701
+ const error = err2;
20702
+ log.error(`Failed to remove hosts: ${error.message}`);
20703
+ throw error;
20704
+ }
20705
+ }
20706
+ async function checkHosts(hosts, verbose) {
20707
+ debugLog("hosts", `Checking hosts: ${hosts}`, verbose);
20708
+ const content = await fs.promises.readFile(hostsFilePath, "utf-8");
20709
+ return hosts.map((host) => {
20710
+ const ipv4Entry = `127.0.0.1 ${host}`;
20711
+ const ipv6Entry = `::1 ${host}`;
20712
+ return content.includes(ipv4Entry) || content.includes(ipv6Entry);
20713
+ });
20714
+ }
20549
20715
 
20550
20716
  // src/https.ts
20551
- import fs3 from "fs/promises";
20717
+ import fs5 from "fs/promises";
20552
20718
  import { homedir as homedir2 } from "os";
20553
20719
  import { join as join4 } from "path";
20554
20720
 
@@ -20589,7 +20755,7 @@ import process8 from "process";
20589
20755
  import process102 from "process";
20590
20756
  import process182 from "process";
20591
20757
  import process112 from "process";
20592
- import os2 from "os";
20758
+ import os3 from "os";
20593
20759
  import tty32 from "tty";
20594
20760
  import process142 from "process";
20595
20761
  import process132 from "process";
@@ -20598,11 +20764,11 @@ import process162 from "process";
20598
20764
  import process172 from "process";
20599
20765
  import process192 from "process";
20600
20766
  import os22 from "os";
20601
- import path from "path";
20767
+ import path2 from "path";
20602
20768
  import { resolve as resolve3 } from "path";
20603
20769
  import process22 from "process";
20604
- import fs from "fs";
20605
- import path2 from "path";
20770
+ import fs3 from "fs";
20771
+ import path22 from "path";
20606
20772
  var __create3 = Object.create;
20607
20773
  var __getProtoOf3 = Object.getPrototypeOf;
20608
20774
  var __defProp3 = Object.defineProperty;
@@ -48126,29 +48292,29 @@ var log2 = {
48126
48292
  },
48127
48293
  echo: (...args) => console.log(...args)
48128
48294
  };
48129
- function userDatabasePath2(path22) {
48130
- return projectPath2(`database/${path22 || ""}`);
48295
+ function userDatabasePath2(path23) {
48296
+ return projectPath2(`database/${path23 || ""}`);
48131
48297
  }
48132
- function appPath2(path22) {
48133
- return projectPath2(`app/${path22 || ""}`);
48298
+ function appPath2(path23) {
48299
+ return projectPath2(`app/${path23 || ""}`);
48134
48300
  }
48135
- function commandsPath2(path22) {
48136
- return appPath2(`Commands/${path22 || ""}`);
48301
+ function commandsPath2(path23) {
48302
+ return appPath2(`Commands/${path23 || ""}`);
48137
48303
  }
48138
- function logsPath2(path22) {
48139
- return storagePath2(`logs/${path22 || ""}`);
48304
+ function logsPath2(path23) {
48305
+ return storagePath2(`logs/${path23 || ""}`);
48140
48306
  }
48141
48307
  function projectPath2(filePath = "", options2) {
48142
- let path22 = process52.cwd();
48143
- while (path22.includes("storage"))
48144
- path22 = resolve22(path22, "..");
48145
- const finalPath = resolve22(path22, filePath);
48308
+ let path23 = process52.cwd();
48309
+ while (path23.includes("storage"))
48310
+ path23 = resolve22(path23, "..");
48311
+ const finalPath = resolve22(path23, filePath);
48146
48312
  if (options2?.relative)
48147
48313
  return relative2(process52.cwd(), finalPath);
48148
48314
  return finalPath;
48149
48315
  }
48150
- function storagePath2(path22) {
48151
- return projectPath2(`storage/${path22 || ""}`);
48316
+ function storagePath2(path23) {
48317
+ return projectPath2(`storage/${path23 || ""}`);
48152
48318
  }
48153
48319
  var config6 = {
48154
48320
  ai: {
@@ -53763,7 +53929,7 @@ class Err2 {
53763
53929
  }
53764
53930
  var fromThrowable2 = Result2.fromThrowable;
53765
53931
  var import_prompts2 = __toESM22(require_prompts32(), 1);
53766
- async function exec(command, options2) {
53932
+ async function exec2(command, options2) {
53767
53933
  const cmd = Array.isArray(command) ? command : command.match(/(?:[^\s"]|"[^"]*")+/g);
53768
53934
  log2.debug("exec:", Array.isArray(command) ? command.join(" ") : command, options2);
53769
53935
  log2.debug("cmd:", cmd);
@@ -53809,7 +53975,7 @@ async function runCommand(command, options2) {
53809
53975
  stdio: options2?.stdio ?? [options2?.stdin ?? "inherit", "pipe", "pipe"],
53810
53976
  verbose: options2?.verbose ?? false
53811
53977
  };
53812
- return await exec(command, opts);
53978
+ return await exec2(command, opts);
53813
53979
  }
53814
53980
  var exports_esm2 = {};
53815
53981
  __export3(exports_esm2, {
@@ -54235,7 +54401,7 @@ function _supportsColor2(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
54235
54401
  return min;
54236
54402
  }
54237
54403
  if (process112.platform === "win32") {
54238
- const osRelease = os2.release().split(".");
54404
+ const osRelease = os3.release().split(".");
54239
54405
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
54240
54406
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
54241
54407
  }
@@ -56832,9 +56998,9 @@ var defaultConfig2 = {
56832
56998
  domain: "stacks.localhost",
56833
56999
  rootCA: { certificate: "", privateKey: "" },
56834
57000
  basePath: "",
56835
- caCertPath: path.join(os22.homedir(), ".stacks", "ssl", `stacks.localhost.ca.crt`),
56836
- certPath: path.join(os22.homedir(), ".stacks", "ssl", `stacks.localhost.crt`),
56837
- keyPath: path.join(os22.homedir(), ".stacks", "ssl", `stacks.localhost.crt.key`),
57001
+ caCertPath: path2.join(os22.homedir(), ".stacks", "ssl", `stacks.localhost.ca.crt`),
57002
+ certPath: path2.join(os22.homedir(), ".stacks", "ssl", `stacks.localhost.crt`),
57003
+ keyPath: path2.join(os22.homedir(), ".stacks", "ssl", `stacks.localhost.crt.key`),
56838
57004
  verbose: false
56839
57005
  };
56840
57006
  var config42 = await loadConfig2({
@@ -56853,10 +57019,10 @@ function findFoldersWithFile(rootDir, fileName) {
56853
57019
  const result = [];
56854
57020
  function search(dir) {
56855
57021
  try {
56856
- const files = fs.readdirSync(dir);
57022
+ const files = fs3.readdirSync(dir);
56857
57023
  for (const file of files) {
56858
- const filePath = path2.join(dir, file);
56859
- const stats = fs.lstatSync(filePath);
57024
+ const filePath = path22.join(dir, file);
57025
+ const stats = fs3.lstatSync(filePath);
56860
57026
  if (stats.isDirectory()) {
56861
57027
  search(filePath);
56862
57028
  } else if (file === fileName) {
@@ -56870,26 +57036,26 @@ function findFoldersWithFile(rootDir, fileName) {
56870
57036
  search(rootDir);
56871
57037
  return result;
56872
57038
  }
56873
- function debugLog(category, message, verbose) {
57039
+ function debugLog2(category, message, verbose) {
56874
57040
  if (verbose || config42.verbose) {
56875
57041
  console.debug(`[tlsx:${category}] ${message}`);
56876
57042
  }
56877
57043
  }
56878
57044
  function generateRandomSerial(verbose) {
56879
- debugLog("cert", "Generating random serial number", verbose);
57045
+ debugLog2("cert", "Generating random serial number", verbose);
56880
57046
  const serialNumber = makeNumberPositive(import_node_forge2.default.util.bytesToHex(import_node_forge2.default.random.getBytesSync(20)));
56881
- debugLog("cert", `Generated serial number: ${serialNumber}`, verbose);
57047
+ debugLog2("cert", `Generated serial number: ${serialNumber}`, verbose);
56882
57048
  return serialNumber;
56883
57049
  }
56884
57050
  function calculateValidityDates(options22) {
56885
57051
  const notBeforeDays = options22.notBeforeDays ?? 2;
56886
57052
  const validityDays = options22.validityDays ?? (options22.validityYears ? options22.validityYears * 365 : 180);
56887
- debugLog("cert", "Calculating certificate validity dates", options22.verbose);
57053
+ debugLog2("cert", "Calculating certificate validity dates", options22.verbose);
56888
57054
  const notBefore = new Date(Date.now() - 86400 * notBeforeDays * 1000);
56889
57055
  const notAfter = new Date(notBefore.getTime() + validityDays * 24 * 60 * 60 * 1000);
56890
57056
  notBefore.setUTCHours(0, 0, 0, 0);
56891
57057
  notAfter.setUTCHours(23, 59, 59, 999);
56892
- debugLog("cert", `Validity period: ${notBefore.toISOString()} to ${notAfter.toISOString()}`, options22.verbose);
57058
+ debugLog2("cert", `Validity period: ${notBefore.toISOString()} to ${notAfter.toISOString()}`, options22.verbose);
56893
57059
  return { notBefore, notAfter };
56894
57060
  }
56895
57061
  function generateCertificateExtensions(options22) {
@@ -56922,9 +57088,9 @@ function generateCertificateExtensions(options22) {
56922
57088
  return extensions;
56923
57089
  }
56924
57090
  async function createRootCA(options22 = {}) {
56925
- debugLog("ca", "Creating new Root CA Certificate", options22.verbose);
57091
+ debugLog2("ca", "Creating new Root CA Certificate", options22.verbose);
56926
57092
  const keySize = options22.keySize || 2048;
56927
- debugLog("ca", `Generating ${keySize}-bit RSA key pair`, options22.verbose);
57093
+ debugLog2("ca", `Generating ${keySize}-bit RSA key pair`, options22.verbose);
56928
57094
  const { privateKey, publicKey } = import_node_forge2.pki.rsa.generateKeyPair(keySize);
56929
57095
  const attributes = [
56930
57096
  { shortName: "C", value: options22.countryName || config42.countryName },
@@ -56971,14 +57137,14 @@ async function createRootCA(options22 = {}) {
56971
57137
  };
56972
57138
  }
56973
57139
  async function generateCertificate(options22) {
56974
- debugLog("cert", "Generating new certificate", options22.verbose);
56975
- debugLog("cert", `Options: ${JSON.stringify(options22)}`, options22.verbose);
57140
+ debugLog2("cert", "Generating new certificate", options22.verbose);
57141
+ debugLog2("cert", `Options: ${JSON.stringify(options22)}`, options22.verbose);
56976
57142
  if (!options22.rootCA?.certificate || !options22.rootCA?.privateKey) {
56977
57143
  throw new Error("Root CA certificate and private key are required");
56978
57144
  }
56979
57145
  const caCert = import_node_forge2.pki.certificateFromPem(options22.rootCA.certificate);
56980
57146
  const caKey = import_node_forge2.pki.privateKeyFromPem(options22.rootCA.privateKey);
56981
- debugLog("cert", "Generating 2048-bit RSA key pair for host certificate", options22.verbose);
57147
+ debugLog2("cert", "Generating 2048-bit RSA key pair for host certificate", options22.verbose);
56982
57148
  const keySize = 2048;
56983
57149
  const { privateKey, publicKey } = import_node_forge2.pki.rsa.generateKeyPair(keySize);
56984
57150
  const attributes = options22.certificateAttributes || [
@@ -57009,81 +57175,81 @@ async function generateCertificate(options22) {
57009
57175
  };
57010
57176
  }
57011
57177
  async function addCertToSystemTrustStoreAndSaveCert(cert, caCert, options22) {
57012
- debugLog("trust", `Adding certificate to system trust store with options: ${JSON.stringify(options22)}`, options22?.verbose);
57013
- debugLog("trust", "Storing certificate and private key", options22?.verbose);
57178
+ debugLog2("trust", `Adding certificate to system trust store with options: ${JSON.stringify(options22)}`, options22?.verbose);
57179
+ debugLog2("trust", "Storing certificate and private key", options22?.verbose);
57014
57180
  const certPath = storeCertificate(cert, options22);
57015
- debugLog("trust", "Storing CA certificate", options22?.verbose);
57181
+ debugLog2("trust", "Storing CA certificate", options22?.verbose);
57016
57182
  const caCertPath = storeCACertificate(caCert, options22);
57017
57183
  const platform22 = os4.platform();
57018
- debugLog("trust", `Detected platform: ${platform22}`, options22?.verbose);
57184
+ debugLog2("trust", `Detected platform: ${platform22}`, options22?.verbose);
57019
57185
  const args = "TC, C, C";
57020
57186
  if (platform22 === "darwin") {
57021
- debugLog("trust", "Adding certificate to macOS keychain", options22?.verbose);
57187
+ debugLog2("trust", "Adding certificate to macOS keychain", options22?.verbose);
57022
57188
  await runCommand(`sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${caCertPath}`);
57023
57189
  } else if (platform22 === "win32") {
57024
- debugLog("trust", "Adding certificate to Windows certificate store", options22?.verbose);
57190
+ debugLog2("trust", "Adding certificate to Windows certificate store", options22?.verbose);
57025
57191
  await runCommand(`certutil -f -v -addstore -enterprise Root ${caCertPath}`);
57026
57192
  } else if (platform22 === "linux") {
57027
- debugLog("trust", "Adding certificate to Linux certificate store", options22?.verbose);
57193
+ debugLog2("trust", "Adding certificate to Linux certificate store", options22?.verbose);
57028
57194
  const rootDirectory = os4.homedir();
57029
57195
  const targetFileName = "cert9.db";
57030
- debugLog("trust", `Searching for certificate databases in ${rootDirectory}`, options22?.verbose);
57196
+ debugLog2("trust", `Searching for certificate databases in ${rootDirectory}`, options22?.verbose);
57031
57197
  const foldersWithFile = findFoldersWithFile(rootDirectory, targetFileName);
57032
57198
  for (const folder of foldersWithFile) {
57033
- debugLog("trust", `Processing certificate database in ${folder}`, options22?.verbose);
57199
+ debugLog2("trust", `Processing certificate database in ${folder}`, options22?.verbose);
57034
57200
  try {
57035
- debugLog("trust", `Attempting to delete existing cert for ${config42.commonName}`, options22?.verbose);
57201
+ debugLog2("trust", `Attempting to delete existing cert for ${config42.commonName}`, options22?.verbose);
57036
57202
  await runCommand(`certutil -d sql:${folder} -D -n ${config42.commonName}`);
57037
57203
  } catch (error) {
57038
- debugLog("trust", `Warning: Error deleting existing cert: ${error}`, options22?.verbose);
57204
+ debugLog2("trust", `Warning: Error deleting existing cert: ${error}`, options22?.verbose);
57039
57205
  console.warn(`Error deleting existing cert: ${error}`);
57040
57206
  }
57041
- debugLog("trust", `Adding new certificate to ${folder}`, options22?.verbose);
57207
+ debugLog2("trust", `Adding new certificate to ${folder}`, options22?.verbose);
57042
57208
  await runCommand(`certutil -d sql:${folder} -A -t ${args} -n ${config42.commonName} -i ${caCertPath}`);
57043
57209
  log2.info(`Cert added to ${folder}`);
57044
57210
  }
57045
57211
  } else {
57046
- debugLog("trust", `Error: Unsupported platform ${platform22}`, options22?.verbose);
57212
+ debugLog2("trust", `Error: Unsupported platform ${platform22}`, options22?.verbose);
57047
57213
  throw new Error(`Unsupported platform: ${platform22}`);
57048
57214
  }
57049
- debugLog("trust", "Certificate successfully added to system trust store", options22?.verbose);
57215
+ debugLog2("trust", "Certificate successfully added to system trust store", options22?.verbose);
57050
57216
  return certPath;
57051
57217
  }
57052
57218
  function storeCertificate(cert, options22) {
57053
- debugLog("storage", `Storing certificate and private key with options: ${JSON.stringify(options22)}`, options22?.verbose);
57219
+ debugLog2("storage", `Storing certificate and private key with options: ${JSON.stringify(options22)}`, options22?.verbose);
57054
57220
  const certPath = path3.join(options22?.basePath || config42.basePath, options22?.certPath || config42.certPath);
57055
57221
  const certKeyPath = path3.join(options22?.basePath || config42.basePath, options22?.keyPath || config42.keyPath);
57056
- debugLog("storage", `Certificate path: ${certPath}`, options22?.verbose);
57057
- debugLog("storage", `Private key path: ${certKeyPath}`, options22?.verbose);
57222
+ debugLog2("storage", `Certificate path: ${certPath}`, options22?.verbose);
57223
+ debugLog2("storage", `Private key path: ${certKeyPath}`, options22?.verbose);
57058
57224
  const certDir = path3.dirname(certPath);
57059
57225
  if (!fs2.existsSync(certDir)) {
57060
- debugLog("storage", `Creating certificate directory: ${certDir}`, options22?.verbose);
57226
+ debugLog2("storage", `Creating certificate directory: ${certDir}`, options22?.verbose);
57061
57227
  fs2.mkdirSync(certDir, { recursive: true });
57062
57228
  }
57063
- debugLog("storage", "Writing certificate file", options22?.verbose);
57229
+ debugLog2("storage", "Writing certificate file", options22?.verbose);
57064
57230
  fs2.writeFileSync(certPath, cert.certificate);
57065
57231
  const certKeyDir = path3.dirname(certKeyPath);
57066
57232
  if (!fs2.existsSync(certKeyDir)) {
57067
- debugLog("storage", `Creating private key directory: ${certKeyDir}`, options22?.verbose);
57233
+ debugLog2("storage", `Creating private key directory: ${certKeyDir}`, options22?.verbose);
57068
57234
  fs2.mkdirSync(certKeyDir, { recursive: true });
57069
57235
  }
57070
- debugLog("storage", "Writing private key file", options22?.verbose);
57236
+ debugLog2("storage", "Writing private key file", options22?.verbose);
57071
57237
  fs2.writeFileSync(certKeyPath, cert.privateKey);
57072
- debugLog("storage", "Certificate and private key stored successfully", options22?.verbose);
57238
+ debugLog2("storage", "Certificate and private key stored successfully", options22?.verbose);
57073
57239
  return certPath;
57074
57240
  }
57075
57241
  function storeCACertificate(caCert, options22) {
57076
- debugLog("storage", "Storing CA certificate", options22?.verbose);
57242
+ debugLog2("storage", "Storing CA certificate", options22?.verbose);
57077
57243
  const caCertPath = path3.join(options22?.basePath || config42.basePath, options22?.caCertPath || config42.caCertPath);
57078
- debugLog("storage", `CA certificate path: ${caCertPath}`, options22?.verbose);
57244
+ debugLog2("storage", `CA certificate path: ${caCertPath}`, options22?.verbose);
57079
57245
  const caCertDir = path3.dirname(caCertPath);
57080
57246
  if (!fs2.existsSync(caCertDir)) {
57081
- debugLog("storage", `Creating CA certificate directory: ${caCertDir}`, options22?.verbose);
57247
+ debugLog2("storage", `Creating CA certificate directory: ${caCertDir}`, options22?.verbose);
57082
57248
  fs2.mkdirSync(caCertDir, { recursive: true });
57083
57249
  }
57084
- debugLog("storage", "Writing CA certificate file", options22?.verbose);
57250
+ debugLog2("storage", "Writing CA certificate file", options22?.verbose);
57085
57251
  fs2.writeFileSync(caCertPath, caCert);
57086
- debugLog("storage", "CA certificate stored successfully", options22?.verbose);
57252
+ debugLog2("storage", "CA certificate stored successfully", options22?.verbose);
57087
57253
  return caCertPath;
57088
57254
  }
57089
57255
  var export_tls = import_node_forge2.tls;
@@ -57092,15 +57258,6 @@ var export_forge = import_node_forge2.default;
57092
57258
 
57093
57259
  // src/https.ts
57094
57260
  var cachedSSLConfig = null;
57095
- function isMultiProxyConfig(options3) {
57096
- return "proxies" in options3 && Array.isArray(options3.proxies);
57097
- }
57098
- function isMultiProxyOptions(options3) {
57099
- return "proxies" in options3 && Array.isArray(options3.proxies);
57100
- }
57101
- function isSingleProxyOptions(options3) {
57102
- return "to" in options3 && typeof options3.to === "string";
57103
- }
57104
57261
  function resolveSSLPaths(options3, defaultConfig3) {
57105
57262
  const domain = isMultiProxyConfig(options3) ? options3.proxies[0].to || "stacks.localhost" : options3.to || "stacks.localhost";
57106
57263
  if (typeof options3.https === "object" && typeof defaultConfig3.https === "object") {
@@ -57144,15 +57301,6 @@ function generateWildcardPatterns(domain) {
57144
57301
  patterns.add(`*.${parts.slice(1).join(".")}`);
57145
57302
  return Array.from(patterns);
57146
57303
  }
57147
- function getPrimaryDomain(options3) {
57148
- if (!options3)
57149
- return "stacks.localhost";
57150
- if (isMultiProxyOptions(options3) && options3.proxies.length > 0)
57151
- return options3.proxies[0].to || "stacks.localhost";
57152
- if (isSingleProxyOptions(options3))
57153
- return options3.to || "stacks.localhost";
57154
- return "stacks.localhost";
57155
- }
57156
57304
  function generateSSLPaths(options3) {
57157
57305
  const domain = getPrimaryDomain(options3);
57158
57306
  let basePath = "";
@@ -57190,51 +57338,50 @@ function getAllDomains(options3) {
57190
57338
  return domains;
57191
57339
  }
57192
57340
  async function loadSSLConfig(options3) {
57193
- debugLog2("ssl", `Loading SSL configuration`, options3.verbose);
57341
+ debugLog("ssl", `Loading SSL configuration`, options3.verbose);
57194
57342
  const mergedOptions = {
57195
57343
  ...config4,
57196
57344
  ...options3
57197
57345
  };
57198
57346
  options3.https = httpsConfig(mergedOptions);
57199
57347
  if (!options3.https?.keyPath && !options3.https?.certPath) {
57200
- debugLog2("ssl", "No SSL configuration provided", options3.verbose);
57348
+ debugLog("ssl", "No SSL configuration provided", options3.verbose);
57201
57349
  return null;
57202
57350
  }
57203
57351
  if (options3.https?.keyPath && !options3.https?.certPath || !options3.https?.keyPath && options3.https?.certPath) {
57204
57352
  const missing = !options3.https?.keyPath ? "keyPath" : "certPath";
57205
- debugLog2("ssl", `Invalid SSL configuration - missing ${missing}`, options3.verbose);
57353
+ debugLog("ssl", `Invalid SSL configuration - missing ${missing}`, options3.verbose);
57206
57354
  throw new Error(`SSL Configuration requires both keyPath and certPath. Missing: ${missing}`);
57207
57355
  }
57208
57356
  try {
57209
57357
  if (!options3.https?.keyPath || !options3.https?.certPath)
57210
57358
  return null;
57211
57359
  try {
57212
- debugLog2("ssl", "Reading SSL certificate files", options3.verbose);
57213
- const key = await fs3.readFile(options3.https?.keyPath, "utf8");
57214
- const cert = await fs3.readFile(options3.https?.certPath, "utf8");
57215
- debugLog2("ssl", "SSL configuration loaded successfully", options3.verbose);
57360
+ debugLog("ssl", "Reading SSL certificate files", options3.verbose);
57361
+ const key = await fs5.readFile(options3.https?.keyPath, "utf8");
57362
+ const cert = await fs5.readFile(options3.https?.certPath, "utf8");
57363
+ debugLog("ssl", "SSL configuration loaded successfully", options3.verbose);
57216
57364
  return { key, cert };
57217
57365
  } catch (error) {
57218
- debugLog2("ssl", `Failed to read certificates: ${error}`, options3.verbose);
57366
+ debugLog("ssl", `Failed to read certificates: ${error}`, options3.verbose);
57219
57367
  return null;
57220
57368
  }
57221
57369
  } catch (err3) {
57222
- debugLog2("ssl", `SSL configuration error: ${err3}`, options3.verbose);
57370
+ debugLog("ssl", `SSL configuration error: ${err3}`, options3.verbose);
57223
57371
  throw err3;
57224
57372
  }
57225
57373
  }
57226
57374
  async function generateCertificate2(options3) {
57227
57375
  if (cachedSSLConfig) {
57228
- debugLog2("ssl", "Using cached SSL configuration", options3.verbose);
57376
+ debugLog("ssl", "Using cached SSL configuration", options3.verbose);
57229
57377
  return;
57230
57378
  }
57231
57379
  const domains = isMultiProxyOptions(options3) ? options3.proxies.map((proxy) => proxy.to) : [options3.to];
57232
- debugLog2("ssl", `Generating certificate for domains: ${domains.join(", ")}`, options3.verbose);
57380
+ debugLog("ssl", `Generating certificate for domains: ${domains.join(", ")}`, options3.verbose);
57233
57381
  const rootCAConfig = httpsConfig(options3, options3.verbose);
57234
57382
  log.info("Generating Root CA certificate...");
57235
57383
  const caCert = await createRootCA(rootCAConfig);
57236
57384
  const hostConfig = httpsConfig(options3, options3.verbose);
57237
- console.log("hostConfig", hostConfig);
57238
57385
  log.info(`Generating host certificate for: ${domains.join(", ")}`);
57239
57386
  const hostCert = await generateCertificate({
57240
57387
  ...hostConfig,
@@ -57250,7 +57397,7 @@ async function generateCertificate2(options3) {
57250
57397
  ca: caCert.certificate
57251
57398
  };
57252
57399
  log.success(`Certificate generated successfully for ${domains.length} domain${domains.length > 1 ? "s" : ""}`);
57253
- debugLog2("ssl", `Certificate includes domains: ${domains.join(", ")}`, options3.verbose);
57400
+ debugLog("ssl", `Certificate includes domains: ${domains.join(", ")}`, options3.verbose);
57254
57401
  }
57255
57402
  function getSSLConfig() {
57256
57403
  return cachedSSLConfig;
@@ -57259,29 +57406,29 @@ async function checkExistingCertificates(options3) {
57259
57406
  const name = getPrimaryDomain(options3);
57260
57407
  const paths = generateSSLPaths(options3);
57261
57408
  try {
57262
- debugLog2("ssl", `Checking certificates for ${name} at paths:`, options3?.verbose);
57263
- debugLog2("ssl", `CA: ${paths.caCertPath}`, options3?.verbose);
57264
- debugLog2("ssl", `Cert: ${paths.certPath}`, options3?.verbose);
57265
- debugLog2("ssl", `Key: ${paths.keyPath}`, options3?.verbose);
57266
- const key = await fs3.readFile(paths.keyPath, "utf8");
57267
- const cert = await fs3.readFile(paths.certPath, "utf8");
57409
+ debugLog("ssl", `Checking certificates for ${name} at paths:`, options3?.verbose);
57410
+ debugLog("ssl", `CA: ${paths.caCertPath}`, options3?.verbose);
57411
+ debugLog("ssl", `Cert: ${paths.certPath}`, options3?.verbose);
57412
+ debugLog("ssl", `Key: ${paths.keyPath}`, options3?.verbose);
57413
+ const key = await fs5.readFile(paths.keyPath, "utf8");
57414
+ const cert = await fs5.readFile(paths.certPath, "utf8");
57268
57415
  let ca;
57269
57416
  if (paths.caCertPath) {
57270
57417
  try {
57271
- ca = await fs3.readFile(paths.caCertPath, "utf8");
57418
+ ca = await fs5.readFile(paths.caCertPath, "utf8");
57272
57419
  } catch (err3) {
57273
- debugLog2("ssl", `Failed to read CA cert: ${err3}`, options3?.verbose);
57420
+ debugLog("ssl", `Failed to read CA cert: ${err3}`, options3?.verbose);
57274
57421
  }
57275
57422
  }
57276
57423
  return { key, cert, ca };
57277
57424
  } catch (err3) {
57278
- debugLog2("ssl", `Failed to read certificates: ${err3}`, options3?.verbose);
57425
+ debugLog("ssl", `Failed to read certificates: ${err3}`, options3?.verbose);
57279
57426
  return null;
57280
57427
  }
57281
57428
  }
57282
57429
  function httpsConfig(options3, verbose) {
57283
57430
  const primaryDomain = getPrimaryDomain(options3);
57284
- debugLog2("ssl", `Primary domain: ${primaryDomain}`, verbose);
57431
+ debugLog("ssl", `Primary domain: ${primaryDomain}`, verbose);
57285
57432
  const defaultPaths = generateSSLPaths(options3);
57286
57433
  if (typeof options3.https === "object") {
57287
57434
  const config5 = {
@@ -57331,227 +57478,41 @@ function httpsConfig(options3, verbose) {
57331
57478
  };
57332
57479
  }
57333
57480
 
57334
- // src/utils.ts
57335
- function debugLog2(category, message, verbose) {
57336
- if (verbose) {
57337
- console.debug(`[rpx:${category}] ${message}`);
57338
- }
57339
- }
57340
- function extractHostname(options3) {
57341
- if (isMultiProxyOptions(options3)) {
57342
- return options3.proxies.map((proxy) => {
57343
- const domain = proxy.to || "stacks.localhost";
57344
- return domain.startsWith("http") ? new URL(domain).hostname : domain;
57345
- });
57346
- }
57347
- if (isSingleProxyOptions(options3)) {
57348
- const domain = options3.to || "stacks.localhost";
57349
- return [domain.startsWith("http") ? new URL(domain).hostname : domain];
57350
- }
57351
- return ["stacks.localhost"];
57352
- }
57353
- function isValidRootCA(value) {
57354
- return typeof value === "object" && value !== null && "certificate" in value && "privateKey" in value && typeof value.certificate === "string" && typeof value.privateKey === "string";
57355
- }
57356
-
57357
- // src/hosts.ts
57358
- var hostsFilePath = process3.platform === "win32" ? path4.join(process3.env.windir || "C:\\Windows", "System32", "drivers", "etc", "hosts") : "/etc/hosts";
57359
- async function sudoWrite(operation, content) {
57360
- return new Promise((resolve4, reject) => {
57361
- if (process3.platform === "win32") {
57362
- reject(new Error("Administrator privileges required on Windows"));
57363
- return;
57364
- }
57365
- const tmpFile = path4.join(os3.tmpdir(), "hosts.tmp");
57366
- try {
57367
- if (operation === "append") {
57368
- const currentContent = fs5.readFileSync(hostsFilePath, "utf8");
57369
- fs5.writeFileSync(tmpFile, currentContent + content, "utf8");
57370
- } else {
57371
- fs5.writeFileSync(tmpFile, content, "utf8");
57372
- }
57373
- const sudo = spawn("sudo", ["cp", tmpFile, hostsFilePath]);
57374
- sudo.on("close", (code) => {
57375
- try {
57376
- fs5.unlinkSync(tmpFile);
57377
- if (code === 0)
57378
- resolve4();
57379
- else
57380
- reject(new Error(`sudo process exited with code ${code}`));
57381
- } catch (err3) {
57382
- reject(err3);
57383
- }
57384
- });
57385
- sudo.on("error", (err3) => {
57386
- try {
57387
- fs5.unlinkSync(tmpFile);
57388
- } catch {
57389
- }
57390
- reject(err3);
57391
- });
57392
- } catch (err3) {
57393
- reject(err3);
57394
- }
57395
- });
57396
- }
57397
- async function addHosts(hosts, verbose) {
57398
- debugLog2("hosts", `Adding hosts: ${hosts.join(", ")}`, verbose);
57399
- debugLog2("hosts", `Using hosts file at: ${hostsFilePath}`, verbose);
57400
- try {
57401
- const existingContent = await fs5.promises.readFile(hostsFilePath, "utf-8");
57402
- const newEntries = hosts.filter((host) => {
57403
- const ipv4Entry = `127.0.0.1 ${host}`;
57404
- const ipv6Entry = `::1 ${host}`;
57405
- return !existingContent.includes(ipv4Entry) && !existingContent.includes(ipv6Entry);
57406
- });
57407
- if (newEntries.length === 0) {
57408
- debugLog2("hosts", "All hosts already exist in hosts file", verbose);
57409
- log.info("All hosts are already in the hosts file");
57410
- return;
57411
- }
57412
- const hostEntries = newEntries.map((host) => `
57413
- # Added by rpx
57414
- 127.0.0.1 ${host}
57415
- ::1 ${host}`).join(`
57416
- `);
57417
- try {
57418
- await fs5.promises.appendFile(hostsFilePath, hostEntries, { flag: "a" });
57419
- log.success(`Added new hosts: ${newEntries.join(", ")}`);
57420
- } catch (writeErr) {
57421
- if (writeErr.code === "EACCES") {
57422
- debugLog2("hosts", "Permission denied, attempting with sudo", verbose);
57423
- try {
57424
- await sudoWrite("append", hostEntries);
57425
- log.success(`Added new hosts with sudo: ${newEntries.join(", ")}`);
57426
- } catch (sudoErr) {
57427
- log.error("Failed to modify hosts file automatically");
57428
- log.warn("Please add these entries to your hosts file manually:");
57429
- hostEntries.split(`
57430
- `).forEach((entry) => log.warn(entry));
57431
- if (process3.platform === "win32") {
57432
- log.warn(`
57433
- On Windows:`);
57434
- log.warn("1. Run notepad as administrator");
57435
- log.warn("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");
57436
- } else {
57437
- log.warn(`
57438
- On Unix systems:`);
57439
- log.warn(`sudo nano ${hostsFilePath}`);
57440
- }
57441
- throw new Error("Failed to modify hosts file: manual intervention required");
57442
- }
57443
- } else {
57444
- throw writeErr;
57445
- }
57446
- }
57447
- } catch (err3) {
57448
- const error = err3;
57449
- log.error(`Failed to manage hosts file: ${error.message}`);
57450
- throw error;
57451
- }
57452
- }
57453
- async function removeHosts(hosts, verbose) {
57454
- debugLog2("hosts", `Removing hosts: ${hosts.join(", ")}`, verbose);
57455
- try {
57456
- const content = await fs5.promises.readFile(hostsFilePath, "utf-8");
57457
- const lines = content.split(`
57458
- `);
57459
- const filteredLines = lines.filter((line, index) => {
57460
- if (line.trim() === "# Added by rpx") {
57461
- lines.splice(index + 1, 2);
57462
- return false;
57463
- }
57464
- return true;
57465
- });
57466
- while (filteredLines[filteredLines.length - 1]?.trim() === "")
57467
- filteredLines.pop();
57468
- const newContent = `${filteredLines.join(`
57469
- `)}
57470
- `;
57471
- try {
57472
- await fs5.promises.writeFile(hostsFilePath, newContent);
57473
- log.success("Hosts removed successfully");
57474
- } catch (writeErr) {
57475
- if (writeErr.code === "EACCES") {
57476
- debugLog2("hosts", "Permission denied, attempting with sudo", verbose);
57477
- try {
57478
- await sudoWrite("write", newContent);
57479
- log.success("Hosts removed successfully with sudo");
57480
- } catch (sudoErr) {
57481
- log.error("Failed to modify hosts file automatically");
57482
- log.warn("Please remove these entries from your hosts file manually:");
57483
- hosts.forEach((host) => {
57484
- log.warn("# Added by rpx");
57485
- log.warn(`127.0.0.1 ${host}`);
57486
- log.warn(`::1 ${host}`);
57487
- });
57488
- if (process3.platform === "win32") {
57489
- log.warn(`
57490
- On Windows:`);
57491
- log.warn("1. Run notepad as administrator");
57492
- log.warn("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");
57493
- } else {
57494
- log.warn(`
57495
- On Unix systems:`);
57496
- log.warn(`sudo nano ${hostsFilePath}`);
57497
- }
57498
- throw new Error("Failed to modify hosts file: manual intervention required");
57499
- }
57500
- } else {
57501
- throw writeErr;
57502
- }
57503
- }
57504
- } catch (err3) {
57505
- const error = err3;
57506
- log.error(`Failed to remove hosts: ${error.message}`);
57507
- throw error;
57508
- }
57509
- }
57510
- async function checkHosts(hosts, verbose) {
57511
- debugLog2("hosts", `Checking hosts: ${hosts}`, verbose);
57512
- const content = await fs5.promises.readFile(hostsFilePath, "utf-8");
57513
- return hosts.map((host) => {
57514
- const ipv4Entry = `127.0.0.1 ${host}`;
57515
- const ipv6Entry = `::1 ${host}`;
57516
- return content.includes(ipv4Entry) || content.includes(ipv6Entry);
57517
- });
57518
- }
57519
-
57520
57481
  // src/start.ts
57521
57482
  var activeServers = new Set;
57522
57483
  async function cleanup(options3) {
57523
- debugLog2("cleanup", "Starting cleanup process", options3?.verbose);
57484
+ debugLog("cleanup", "Starting cleanup process", options3?.verbose);
57524
57485
  console.log(`
57525
57486
  `);
57526
57487
  log.info("Shutting down proxy servers...");
57527
57488
  const cleanupPromises = [];
57528
57489
  const serverClosePromises = Array.from(activeServers).map((server) => new Promise((resolve4) => {
57529
57490
  server.close(() => {
57530
- debugLog2("cleanup", "Server closed successfully", options3?.verbose);
57491
+ debugLog("cleanup", "Server closed successfully", options3?.verbose);
57531
57492
  resolve4();
57532
57493
  });
57533
57494
  }));
57534
57495
  cleanupPromises.push(...serverClosePromises);
57535
57496
  if (options3?.etcHostsCleanup && options3.domains?.length) {
57536
- debugLog2("cleanup", "Cleaning up hosts file entries", options3?.verbose);
57497
+ debugLog("cleanup", "Cleaning up hosts file entries", options3?.verbose);
57537
57498
  const domainsToClean = options3.domains.filter((domain) => !domain.includes("localhost"));
57538
57499
  if (domainsToClean.length > 0) {
57539
57500
  log.info("Cleaning up hosts file entries...");
57540
57501
  cleanupPromises.push(removeHosts(domainsToClean, options3?.verbose).then(() => {
57541
- debugLog2("cleanup", `Removed hosts entries for ${domainsToClean.join(", ")}`, options3?.verbose);
57502
+ debugLog("cleanup", `Removed hosts entries for ${domainsToClean.join(", ")}`, options3?.verbose);
57542
57503
  }).catch((err3) => {
57543
- debugLog2("cleanup", `Failed to remove hosts entries: ${err3}`, options3?.verbose);
57504
+ debugLog("cleanup", `Failed to remove hosts entries: ${err3}`, options3?.verbose);
57544
57505
  log.warn(`Failed to clean up hosts file entries for ${domainsToClean.join(", ")}:`, err3);
57545
57506
  }));
57546
57507
  }
57547
57508
  }
57548
57509
  try {
57549
57510
  await Promise.all(cleanupPromises);
57550
- debugLog2("cleanup", "All cleanup tasks completed successfully", options3?.verbose);
57511
+ debugLog("cleanup", "All cleanup tasks completed successfully", options3?.verbose);
57551
57512
  log.success("All cleanup tasks completed successfully");
57552
57513
  process9.exit(0);
57553
57514
  } catch (err3) {
57554
- debugLog2("cleanup", `Error during cleanup: ${err3}`, options3?.verbose);
57515
+ debugLog("cleanup", `Error during cleanup: ${err3}`, options3?.verbose);
57555
57516
  log.error("Error during cleanup:", err3);
57556
57517
  process9.exit(1);
57557
57518
  }
@@ -57559,22 +57520,22 @@ async function cleanup(options3) {
57559
57520
  process9.on("SIGINT", cleanup);
57560
57521
  process9.on("SIGTERM", cleanup);
57561
57522
  process9.on("uncaughtException", (err3) => {
57562
- debugLog2("process", `Uncaught exception: ${err3}`, true);
57523
+ debugLog("process", `Uncaught exception: ${err3}`, true);
57563
57524
  log.error("Uncaught exception:", err3);
57564
57525
  cleanup();
57565
57526
  });
57566
57527
  function isPortInUse(port, hostname, verbose) {
57567
- debugLog2("port", `Checking if port ${port} is in use on ${hostname}`, verbose);
57528
+ debugLog("port", `Checking if port ${port} is in use on ${hostname}`, verbose);
57568
57529
  return new Promise((resolve4) => {
57569
57530
  const server = net.createServer();
57570
57531
  server.once("error", (err3) => {
57571
57532
  if (err3.code === "EADDRINUSE") {
57572
- debugLog2("port", `Port ${port} is in use`, verbose);
57533
+ debugLog("port", `Port ${port} is in use`, verbose);
57573
57534
  resolve4(true);
57574
57535
  }
57575
57536
  });
57576
57537
  server.once("listening", () => {
57577
- debugLog2("port", `Port ${port} is available`, verbose);
57538
+ debugLog("port", `Port ${port} is available`, verbose);
57578
57539
  server.close();
57579
57540
  resolve4(false);
57580
57541
  });
@@ -57582,17 +57543,17 @@ function isPortInUse(port, hostname, verbose) {
57582
57543
  });
57583
57544
  }
57584
57545
  async function findAvailablePort(startPort, hostname, verbose) {
57585
- debugLog2("port", `Finding available port starting from ${startPort}`, verbose);
57546
+ debugLog("port", `Finding available port starting from ${startPort}`, verbose);
57586
57547
  let port = startPort;
57587
57548
  while (await isPortInUse(port, hostname, verbose)) {
57588
- debugLog2("port", `Port ${port} is in use, trying ${port + 1}`, verbose);
57549
+ debugLog("port", `Port ${port} is in use, trying ${port + 1}`, verbose);
57589
57550
  port++;
57590
57551
  }
57591
- debugLog2("port", `Found available port: ${port}`, verbose);
57552
+ debugLog("port", `Found available port: ${port}`, verbose);
57592
57553
  return port;
57593
57554
  }
57594
57555
  async function testConnection(hostname, port, verbose) {
57595
- debugLog2("connection", `Testing connection to ${hostname}:${port}`, verbose);
57556
+ debugLog("connection", `Testing connection to ${hostname}:${port}`, verbose);
57596
57557
  return new Promise((resolve4, reject) => {
57597
57558
  const socket = net.connect({
57598
57559
  host: hostname,
@@ -57600,30 +57561,30 @@ async function testConnection(hostname, port, verbose) {
57600
57561
  timeout: 5000
57601
57562
  });
57602
57563
  socket.once("connect", () => {
57603
- debugLog2("connection", `Successfully connected to ${hostname}:${port}`, verbose);
57564
+ debugLog("connection", `Successfully connected to ${hostname}:${port}`, verbose);
57604
57565
  socket.end();
57605
57566
  resolve4();
57606
57567
  });
57607
57568
  socket.once("timeout", () => {
57608
- debugLog2("connection", `Connection to ${hostname}:${port} timed out`, verbose);
57569
+ debugLog("connection", `Connection to ${hostname}:${port} timed out`, verbose);
57609
57570
  socket.destroy();
57610
57571
  reject(new Error(`Connection to ${hostname}:${port} timed out`));
57611
57572
  });
57612
57573
  socket.once("error", (err3) => {
57613
- debugLog2("connection", `Failed to connect to ${hostname}:${port}: ${err3}`, verbose);
57574
+ debugLog("connection", `Failed to connect to ${hostname}:${port}: ${err3}`, verbose);
57614
57575
  socket.destroy();
57615
57576
  reject(new Error(`Failed to connect to ${hostname}:${port}: ${err3.message}`));
57616
57577
  });
57617
57578
  });
57618
57579
  }
57619
57580
  async function startServer(options3) {
57620
- debugLog2("server", `Starting server with options: ${JSON.stringify(options3)}`, options3.verbose);
57581
+ debugLog("server", `Starting server with options: ${JSON.stringify(options3)}`, options3.verbose);
57621
57582
  const fromUrl = new URL((options3.from?.startsWith("http") ? options3.from : `http://${options3.from}`) || "localhost:5173");
57622
57583
  const toUrl = new URL((options3.to?.startsWith("http") ? options3.to : `http://${options3.to}`) || "stacks.localhost");
57623
57584
  const fromPort = Number.parseInt(fromUrl.port) || (fromUrl.protocol.includes("https:") ? 443 : 80);
57624
57585
  const hostsToCheck = [toUrl.hostname];
57625
57586
  if (!toUrl.hostname.includes("localhost") && !toUrl.hostname.includes("127.0.0.1")) {
57626
- debugLog2("hosts", `Checking if hosts file entry exists for: ${toUrl.hostname}`, options3?.verbose);
57587
+ debugLog("hosts", `Checking if hosts file entry exists for: ${toUrl.hostname}`, options3?.verbose);
57627
57588
  try {
57628
57589
  const hostsExist = await checkHosts(hostsToCheck, options3.verbose);
57629
57590
  if (!hostsExist[0]) {
@@ -57646,7 +57607,7 @@ async function startServer(options3) {
57646
57607
  }
57647
57608
  }
57648
57609
  } else {
57649
- debugLog2("hosts", `Host entry already exists for ${toUrl.hostname}`, options3.verbose);
57610
+ debugLog("hosts", `Host entry already exists for ${toUrl.hostname}`, options3.verbose);
57650
57611
  }
57651
57612
  } catch (checkError) {
57652
57613
  log.error("Failed to check hosts file:", checkError.message);
@@ -57655,7 +57616,7 @@ async function startServer(options3) {
57655
57616
  try {
57656
57617
  await testConnection(fromUrl.hostname, fromPort, options3.verbose);
57657
57618
  } catch (err3) {
57658
- debugLog2("server", `Connection test failed: ${err3}`, options3.verbose);
57619
+ debugLog("server", `Connection test failed: ${err3}`, options3.verbose);
57659
57620
  log.error(err3.message);
57660
57621
  process9.exit(1);
57661
57622
  }
@@ -57669,17 +57630,17 @@ async function startServer(options3) {
57669
57630
  });
57670
57631
  }
57671
57632
  try {
57672
- debugLog2("ssl", `Attempting to load SSL configuration for ${toUrl.hostname}`, options3.verbose);
57633
+ debugLog("ssl", `Attempting to load SSL configuration for ${toUrl.hostname}`, options3.verbose);
57673
57634
  sslConfig = await loadSSLConfig({
57674
57635
  ...options3,
57675
57636
  to: toUrl.hostname,
57676
57637
  https: options3.https
57677
57638
  });
57678
57639
  } catch (loadError) {
57679
- debugLog2("ssl", `Failed to load certificates, will generate new ones: ${loadError}`, options3.verbose);
57640
+ debugLog("ssl", `Failed to load certificates, will generate new ones: ${loadError}`, options3.verbose);
57680
57641
  }
57681
57642
  if (!sslConfig) {
57682
- debugLog2("ssl", `Generating new certificates for ${toUrl.hostname}`, options3.verbose);
57643
+ debugLog("ssl", `Generating new certificates for ${toUrl.hostname}`, options3.verbose);
57683
57644
  await generateCertificate2({
57684
57645
  ...options3,
57685
57646
  from: fromUrl.toString(),
@@ -57696,11 +57657,11 @@ async function startServer(options3) {
57696
57657
  }
57697
57658
  }
57698
57659
  } catch (err3) {
57699
- debugLog2("server", `SSL setup failed: ${err3}`, options3.verbose);
57660
+ debugLog("server", `SSL setup failed: ${err3}`, options3.verbose);
57700
57661
  throw err3;
57701
57662
  }
57702
57663
  }
57703
- debugLog2("server", `Setting up reverse proxy with SSL config for ${toUrl.hostname}`, options3.verbose);
57664
+ debugLog("server", `Setting up reverse proxy with SSL config for ${toUrl.hostname}`, options3.verbose);
57704
57665
  await setupReverseProxy({
57705
57666
  ...options3,
57706
57667
  from: options3.from || "localhost:5173",
@@ -57713,23 +57674,69 @@ async function startServer(options3) {
57713
57674
  ssl: sslConfig
57714
57675
  });
57715
57676
  }
57716
- async function createProxyServer(from, to, fromPort, listenPort, hostname, sourceUrl, ssl, verbose) {
57717
- debugLog2("proxy", `Creating proxy server ${from} -> ${to}`, verbose);
57677
+ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourceUrl, ssl, verbose, cleanUrls) {
57678
+ debugLog("proxy", `Creating proxy server ${from} -> ${to} with cleanUrls: ${cleanUrls}`, verbose);
57718
57679
  const requestHandler = (req, res) => {
57719
- debugLog2("request", `Incoming request: ${req.method} ${req.url}`, verbose);
57680
+ debugLog("request", `Incoming request: ${req.method} ${req.url}`, verbose);
57681
+ let path4 = req.url || "/";
57682
+ if (cleanUrls) {
57683
+ if (!path4.match(/\.[a-z0-9]+$/i)) {
57684
+ if (path4.endsWith("/")) {
57685
+ path4 = `${path4}index.html`;
57686
+ } else {
57687
+ path4 = `${path4}.html`;
57688
+ }
57689
+ }
57690
+ }
57720
57691
  const proxyOptions = {
57721
57692
  hostname: sourceUrl.hostname,
57722
57693
  port: fromPort,
57723
- path: req.url,
57694
+ path: path4,
57724
57695
  method: req.method,
57725
57696
  headers: {
57726
57697
  ...req.headers,
57727
57698
  host: sourceUrl.host
57728
57699
  }
57729
57700
  };
57730
- debugLog2("request", `Proxy request options: ${JSON.stringify(proxyOptions)}`, verbose);
57701
+ debugLog("request", `Proxy request options: ${JSON.stringify(proxyOptions)}`, verbose);
57731
57702
  const proxyReq = http.request(proxyOptions, (proxyRes) => {
57732
- debugLog2("response", `Proxy response received with status ${proxyRes.statusCode}`, verbose);
57703
+ debugLog("response", `Proxy response received with status ${proxyRes.statusCode}`, verbose);
57704
+ if (cleanUrls && proxyRes.statusCode === 404) {
57705
+ const alternativePaths = [];
57706
+ if (path4.endsWith(".html")) {
57707
+ alternativePaths.push(path4.slice(0, -5));
57708
+ } else if (!path4.match(/\.[a-z0-9]+$/i)) {
57709
+ alternativePaths.push(`${path4}.html`);
57710
+ }
57711
+ if (!path4.endsWith("/")) {
57712
+ alternativePaths.push(`${path4}/index.html`);
57713
+ }
57714
+ if (alternativePaths.length > 0) {
57715
+ debugLog("cleanUrls", `Trying alternative paths: ${alternativePaths.join(", ")}`, verbose);
57716
+ const tryNextPath = (paths) => {
57717
+ if (paths.length === 0) {
57718
+ res.writeHead(proxyRes.statusCode || 404, proxyRes.headers);
57719
+ proxyRes.pipe(res);
57720
+ return;
57721
+ }
57722
+ const altPath = paths[0];
57723
+ const altOptions = { ...proxyOptions, path: altPath };
57724
+ const altReq = http.request(altOptions, (altRes) => {
57725
+ if (altRes.statusCode === 200) {
57726
+ debugLog("cleanUrls", `Found matching path: ${altPath}`, verbose);
57727
+ res.writeHead(altRes.statusCode, altRes.headers);
57728
+ altRes.pipe(res);
57729
+ } else {
57730
+ tryNextPath(paths.slice(1));
57731
+ }
57732
+ });
57733
+ altReq.on("error", () => tryNextPath(paths.slice(1)));
57734
+ altReq.end();
57735
+ };
57736
+ tryNextPath(alternativePaths);
57737
+ return;
57738
+ }
57739
+ }
57733
57740
  const headers = {
57734
57741
  ...proxyRes.headers,
57735
57742
  "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
@@ -57739,7 +57746,7 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57739
57746
  proxyRes.pipe(res);
57740
57747
  });
57741
57748
  proxyReq.on("error", (err3) => {
57742
- debugLog2("request", `Proxy request failed: ${err3}`, verbose);
57749
+ debugLog("request", `Proxy request failed: ${err3}`, verbose);
57743
57750
  log.error("Proxy request failed:", err3);
57744
57751
  res.writeHead(502);
57745
57752
  res.end(`Proxy Error: ${err3.message}`);
@@ -57766,11 +57773,11 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57766
57773
  allowHTTP1: true,
57767
57774
  ALPNProtocols: ["h2", "http/1.1"]
57768
57775
  } : undefined;
57769
- debugLog2("server", `Creating server with SSL config: ${!!ssl}`, verbose);
57776
+ debugLog("server", `Creating server with SSL config: ${!!ssl}`, verbose);
57770
57777
  const server = ssl && serverOptions ? https.createServer(serverOptions, requestHandler) : http.createServer(requestHandler);
57771
57778
  if (ssl) {
57772
57779
  server.on("secureConnection", (tlsSocket) => {
57773
- debugLog2("tls", `TLS Connection established: ${JSON.stringify({
57780
+ debugLog("tls", `TLS Connection established: ${JSON.stringify({
57774
57781
  protocol: tlsSocket.getProtocol?.(),
57775
57782
  cipher: tlsSocket.getCipher?.(),
57776
57783
  authorized: tlsSocket.authorized,
@@ -57781,7 +57788,7 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57781
57788
  activeServers.add(server);
57782
57789
  return new Promise((resolve4, reject) => {
57783
57790
  server.listen(listenPort, hostname, () => {
57784
- debugLog2("server", `Server listening on port ${listenPort}`, verbose);
57791
+ debugLog("server", `Server listening on port ${listenPort}`, verbose);
57785
57792
  console.log("");
57786
57793
  console.log(` ${green(bold("reverse-proxy"))} ${green(`v${version}`)}`);
57787
57794
  console.log("");
@@ -57795,16 +57802,19 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57795
57802
  console.log(` - HTTP/2 enabled`);
57796
57803
  console.log(` - HSTS enabled`);
57797
57804
  }
57805
+ if (cleanUrls) {
57806
+ console.log(` ${green("\u279C")} Clean URLs enabled`);
57807
+ }
57798
57808
  resolve4();
57799
57809
  });
57800
57810
  server.on("error", (err3) => {
57801
- debugLog2("server", `Server error: ${err3}`, verbose);
57811
+ debugLog("server", `Server error: ${err3}`, verbose);
57802
57812
  reject(err3);
57803
57813
  });
57804
57814
  });
57805
57815
  }
57806
57816
  async function setupReverseProxy(options3) {
57807
- debugLog2("setup", `Setting up reverse proxy: ${JSON.stringify(options3)}`, options3.verbose);
57817
+ debugLog("setup", `Setting up reverse proxy: ${JSON.stringify(options3)}`, options3.verbose);
57808
57818
  const { from, to, fromPort, sourceUrl, ssl, verbose, etcHostsCleanup, portManager } = options3;
57809
57819
  const httpPort = 80;
57810
57820
  const httpsPort = 443;
@@ -57813,11 +57823,11 @@ async function setupReverseProxy(options3) {
57813
57823
  if (ssl && !portManager?.usedPorts.has(httpPort)) {
57814
57824
  const isHttpPortBusy = await isPortInUse(httpPort, hostname, verbose);
57815
57825
  if (!isHttpPortBusy) {
57816
- debugLog2("setup", "Starting HTTP redirect server", verbose);
57826
+ debugLog("setup", "Starting HTTP redirect server", verbose);
57817
57827
  startHttpRedirectServer(verbose);
57818
57828
  portManager?.usedPorts.add(httpPort);
57819
57829
  } else {
57820
- debugLog2("setup", "Port 80 is in use, skipping HTTP redirect", verbose);
57830
+ debugLog("setup", "Port 80 is in use, skipping HTTP redirect", verbose);
57821
57831
  log.warn("Port 80 is in use, HTTP to HTTPS redirect will not be available");
57822
57832
  }
57823
57833
  }
@@ -57835,7 +57845,7 @@ async function setupReverseProxy(options3) {
57835
57845
  }
57836
57846
  await createProxyServer(from, to, fromPort, finalPort, hostname, sourceUrl, ssl, verbose);
57837
57847
  } catch (err3) {
57838
- debugLog2("setup", `Setup failed: ${err3}`, verbose);
57848
+ debugLog("setup", `Setup failed: ${err3}`, verbose);
57839
57849
  log.error(`Failed to setup reverse proxy: ${err3.message}`);
57840
57850
  cleanup({
57841
57851
  domains: [to],
@@ -57845,34 +57855,35 @@ async function setupReverseProxy(options3) {
57845
57855
  }
57846
57856
  }
57847
57857
  function startHttpRedirectServer(verbose) {
57848
- debugLog2("redirect", "Starting HTTP redirect server", verbose);
57858
+ debugLog("redirect", "Starting HTTP redirect server", verbose);
57849
57859
  const server = http.createServer((req, res) => {
57850
57860
  const host = req.headers.host || "";
57851
- debugLog2("redirect", `Redirecting request from ${host}${req.url} to HTTPS`, verbose);
57861
+ debugLog("redirect", `Redirecting request from ${host}${req.url} to HTTPS`, verbose);
57852
57862
  res.writeHead(301, {
57853
57863
  Location: `https://${host}${req.url}`
57854
57864
  });
57855
57865
  res.end();
57856
57866
  }).listen(80);
57857
57867
  activeServers.add(server);
57858
- debugLog2("redirect", "HTTP redirect server started", verbose);
57868
+ debugLog("redirect", "HTTP redirect server started", verbose);
57859
57869
  }
57860
57870
  function startProxy(options3) {
57861
57871
  const mergedOptions = {
57862
57872
  ...config4,
57863
57873
  ...options3
57864
57874
  };
57865
- debugLog2("proxy", `Starting proxy with options: ${JSON.stringify(mergedOptions)}`, mergedOptions?.verbose);
57875
+ debugLog("proxy", `Starting proxy with options: ${JSON.stringify(mergedOptions)}`, mergedOptions?.verbose);
57866
57876
  const serverOptions = {
57867
57877
  from: mergedOptions.from,
57868
57878
  to: mergedOptions.to,
57879
+ cleanUrls: mergedOptions.cleanUrls,
57869
57880
  https: httpsConfig(mergedOptions),
57870
57881
  etcHostsCleanup: mergedOptions.etcHostsCleanup,
57871
57882
  verbose: mergedOptions.verbose
57872
57883
  };
57873
57884
  console.log("serverOptions", serverOptions);
57874
57885
  startServer(serverOptions).catch((err3) => {
57875
- debugLog2("proxy", `Failed to start proxy: ${err3}`, mergedOptions.verbose);
57886
+ debugLog("proxy", `Failed to start proxy: ${err3}`, mergedOptions.verbose);
57876
57887
  log.error(`Failed to start proxy: ${err3.message}`);
57877
57888
  cleanup({
57878
57889
  domains: [mergedOptions.to],
@@ -57882,7 +57893,7 @@ function startProxy(options3) {
57882
57893
  });
57883
57894
  }
57884
57895
  async function startProxies(options3) {
57885
- debugLog2("proxies", "Starting proxy setup", options3?.verbose);
57896
+ debugLog("proxies", "Starting proxy setup", options3?.verbose);
57886
57897
  const mergedOptions = {
57887
57898
  ...config4,
57888
57899
  ...options3
@@ -57891,10 +57902,10 @@ async function startProxies(options3) {
57891
57902
  if (mergedOptions.https) {
57892
57903
  const existingSSLConfig = await checkExistingCertificates(mergedOptions);
57893
57904
  if (existingSSLConfig) {
57894
- debugLog2("ssl", `Using existing certificates for ${primaryDomain}`, mergedOptions.verbose);
57905
+ debugLog("ssl", `Using existing certificates for ${primaryDomain}`, mergedOptions.verbose);
57895
57906
  mergedOptions._cachedSSLConfig = existingSSLConfig;
57896
57907
  } else {
57897
- debugLog2("ssl", `No valid certificates found for ${primaryDomain}, generating new ones`, mergedOptions.verbose);
57908
+ debugLog("ssl", `No valid certificates found for ${primaryDomain}, generating new ones`, mergedOptions.verbose);
57898
57909
  await generateCertificate2(mergedOptions);
57899
57910
  const sslConfig2 = await checkExistingCertificates(mergedOptions);
57900
57911
  if (!sslConfig2) {
@@ -57907,11 +57918,13 @@ async function startProxies(options3) {
57907
57918
  ...proxy,
57908
57919
  https: mergedOptions.https,
57909
57920
  etcHostsCleanup: mergedOptions.etcHostsCleanup,
57921
+ cleanUrls: mergedOptions.cleanUrls,
57910
57922
  verbose: mergedOptions.verbose,
57911
57923
  _cachedSSLConfig: mergedOptions._cachedSSLConfig
57912
57924
  })) : [{
57913
57925
  from: mergedOptions.from || "localhost:5173",
57914
57926
  to: mergedOptions.to || "stacks.localhost",
57927
+ cleanUrls: mergedOptions.cleanUrls || false,
57915
57928
  https: mergedOptions.https,
57916
57929
  etcHostsCleanup: mergedOptions.etcHostsCleanup,
57917
57930
  verbose: mergedOptions.verbose,
@@ -57927,24 +57940,25 @@ async function startProxies(options3) {
57927
57940
  process9.on("SIGINT", cleanupHandler);
57928
57941
  process9.on("SIGTERM", cleanupHandler);
57929
57942
  process9.on("uncaughtException", (err3) => {
57930
- debugLog2("process", `Uncaught exception: ${err3}`, true);
57943
+ debugLog("process", `Uncaught exception: ${err3}`, true);
57931
57944
  console.error("Uncaught exception:", err3);
57932
57945
  cleanupHandler();
57933
57946
  });
57934
57947
  for (const option of proxyOptions) {
57935
57948
  try {
57936
57949
  const domain = option.to || "stacks.localhost";
57937
- debugLog2("proxy", `Starting proxy for ${domain} with SSL config: ${!!sslConfig}`, option.verbose);
57950
+ debugLog("proxy", `Starting proxy for ${domain} with SSL config: ${!!sslConfig}`, option.verbose);
57938
57951
  await startServer({
57939
57952
  from: option.from || "localhost:5173",
57940
57953
  to: domain,
57941
- https: option.https ?? false,
57954
+ cleanUrls: option.cleanUrls || false,
57955
+ https: option.https || false,
57942
57956
  etcHostsCleanup: option.etcHostsCleanup || false,
57943
57957
  verbose: option.verbose || false,
57944
57958
  _cachedSSLConfig: sslConfig
57945
57959
  });
57946
57960
  } catch (err3) {
57947
- debugLog2("proxies", `Failed to start proxy for ${option.to}: ${err3}`, option.verbose);
57961
+ debugLog("proxies", `Failed to start proxy for ${option.to}: ${err3}`, option.verbose);
57948
57962
  console.error(`Failed to start proxy for ${option.to}:`, err3);
57949
57963
  cleanupHandler();
57950
57964
  }
@@ -57976,7 +57990,7 @@ export {
57976
57990
  generateCertificate2 as generateCertificate,
57977
57991
  extractHostname,
57978
57992
  src_default as default,
57979
- debugLog2 as debugLog,
57993
+ debugLog,
57980
57994
  config4 as config,
57981
57995
  cleanup,
57982
57996
  checkHosts,