@stacksjs/rpx 0.2.0 → 0.3.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
@@ -77,16 +77,15 @@ var config = await loadConfig({
77
77
  validityDays: 180,
78
78
  verbose: false
79
79
  },
80
+ etcHostsCleanup: false,
80
81
  verbose: true
81
82
  }
82
83
  });
83
- // src/start.ts
84
- import * as fs3 from "fs";
85
- import * as http from "http";
86
- import * as https from "https";
87
- import * as net from "net";
88
- import os7 from "os";
89
- import path6 from "path";
84
+ // src/hosts.ts
85
+ import { spawn } from "child_process";
86
+ import fs from "fs";
87
+ import os3 from "os";
88
+ import path2 from "path";
90
89
  import process3 from "process";
91
90
 
92
91
  // node_modules/@stacksjs/cli/dist/index.js
@@ -20397,12 +20396,167 @@ var quotes = collect([
20397
20396
  "Security is mostly a superstition. Life is either a daring adventure or nothing."
20398
20397
  ]);
20399
20398
  var export_prompts = import_prompts.default;
20400
- // package.json
20401
- var version = "0.2.0";
20402
20399
 
20400
+ // src/utils.ts
20401
+ function debugLog(category, message, verbose) {
20402
+ if (verbose || config.verbose) {
20403
+ console.debug(`[rpx:${category}] ${message}`);
20404
+ }
20405
+ }
20406
+
20407
+ // src/hosts.ts
20408
+ var hostsFilePath = process3.platform === "win32" ? path2.join(process3.env.windir || "C:\\Windows", "System32", "drivers", "etc", "hosts") : "/etc/hosts";
20409
+ async function sudoWrite(operation, content) {
20410
+ return new Promise((resolve3, reject) => {
20411
+ if (process3.platform === "win32") {
20412
+ reject(new Error("Administrator privileges required on Windows"));
20413
+ return;
20414
+ }
20415
+ const tmpFile = path2.join(os3.tmpdir(), "hosts.tmp");
20416
+ try {
20417
+ if (operation === "append") {
20418
+ const currentContent = fs.readFileSync(hostsFilePath, "utf8");
20419
+ fs.writeFileSync(tmpFile, currentContent + content, "utf8");
20420
+ } else {
20421
+ fs.writeFileSync(tmpFile, content, "utf8");
20422
+ }
20423
+ const sudo = spawn("sudo", ["cp", tmpFile, hostsFilePath]);
20424
+ sudo.on("close", (code) => {
20425
+ try {
20426
+ fs.unlinkSync(tmpFile);
20427
+ if (code === 0)
20428
+ resolve3();
20429
+ else
20430
+ reject(new Error(`sudo process exited with code ${code}`));
20431
+ } catch (err2) {
20432
+ reject(err2);
20433
+ }
20434
+ });
20435
+ sudo.on("error", (err2) => {
20436
+ try {
20437
+ fs.unlinkSync(tmpFile);
20438
+ } catch {
20439
+ }
20440
+ reject(err2);
20441
+ });
20442
+ } catch (err2) {
20443
+ reject(err2);
20444
+ }
20445
+ });
20446
+ }
20447
+ async function addHosts(hosts) {
20448
+ debugLog("hosts", `Adding hosts: ${hosts.join(", ")}`, config.verbose);
20449
+ debugLog("hosts", `Using hosts file at: ${hostsFilePath}`, config.verbose);
20450
+ try {
20451
+ const existingContent = await fs.promises.readFile(hostsFilePath, "utf-8");
20452
+ const newEntries = hosts.filter((host) => {
20453
+ const ipv4Entry = `127.0.0.1 ${host}`;
20454
+ const ipv6Entry = `::1 ${host}`;
20455
+ return !existingContent.includes(ipv4Entry) && !existingContent.includes(ipv6Entry);
20456
+ });
20457
+ if (newEntries.length === 0) {
20458
+ debugLog("hosts", "All hosts already exist in hosts file", config.verbose);
20459
+ log.info("All hosts are already in the hosts file");
20460
+ return;
20461
+ }
20462
+ const hostEntries = newEntries.map((host) => `\n# Added by rpx\n127.0.0.1 ${host}\n::1 ${host}`).join("\n");
20463
+ try {
20464
+ await fs.promises.appendFile(hostsFilePath, hostEntries, { flag: "a" });
20465
+ log.success(`Added new hosts: ${newEntries.join(", ")}`);
20466
+ } catch (writeErr) {
20467
+ if (writeErr.code === "EACCES") {
20468
+ debugLog("hosts", "Permission denied, attempting with sudo", config.verbose);
20469
+ try {
20470
+ await sudoWrite("append", hostEntries);
20471
+ log.success(`Added new hosts with sudo: ${newEntries.join(", ")}`);
20472
+ } catch (sudoErr) {
20473
+ log.error("Failed to modify hosts file automatically");
20474
+ log.warn("Please add these entries to your hosts file manually:");
20475
+ hostEntries.split("\n").forEach((entry) => log.warn(entry));
20476
+ if (process3.platform === "win32") {
20477
+ log.warn("\nOn Windows:");
20478
+ log.warn("1. Run notepad as administrator");
20479
+ log.warn("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");
20480
+ } else {
20481
+ log.warn("\nOn Unix systems:");
20482
+ log.warn(`sudo nano ${hostsFilePath}`);
20483
+ }
20484
+ throw new Error("Failed to modify hosts file: manual intervention required");
20485
+ }
20486
+ } else {
20487
+ throw writeErr;
20488
+ }
20489
+ }
20490
+ } catch (err2) {
20491
+ const error = err2;
20492
+ log.error(`Failed to manage hosts file: ${error.message}`);
20493
+ throw error;
20494
+ }
20495
+ }
20496
+ async function removeHosts(hosts) {
20497
+ debugLog("hosts", `Removing hosts: ${hosts.join(", ")}`, config.verbose);
20498
+ try {
20499
+ const content = await fs.promises.readFile(hostsFilePath, "utf-8");
20500
+ const lines = content.split("\n");
20501
+ const filteredLines = lines.filter((line, index) => {
20502
+ if (line.trim() === "# Added by rpx") {
20503
+ lines.splice(index + 1, 2);
20504
+ return false;
20505
+ }
20506
+ return true;
20507
+ });
20508
+ while (filteredLines[filteredLines.length - 1]?.trim() === "")
20509
+ filteredLines.pop();
20510
+ const newContent = `${filteredLines.join("\n")}\n`;
20511
+ try {
20512
+ await fs.promises.writeFile(hostsFilePath, newContent);
20513
+ log.success("Hosts removed successfully");
20514
+ } catch (writeErr) {
20515
+ if (writeErr.code === "EACCES") {
20516
+ debugLog("hosts", "Permission denied, attempting with sudo", config.verbose);
20517
+ try {
20518
+ await sudoWrite("write", newContent);
20519
+ log.success("Hosts removed successfully with sudo");
20520
+ } catch (sudoErr) {
20521
+ log.error("Failed to modify hosts file automatically");
20522
+ log.warn("Please remove these entries from your hosts file manually:");
20523
+ hosts.forEach((host) => {
20524
+ log.warn("# Added by rpx");
20525
+ log.warn(`127.0.0.1 ${host}`);
20526
+ log.warn(`::1 ${host}`);
20527
+ });
20528
+ if (process3.platform === "win32") {
20529
+ log.warn("\nOn Windows:");
20530
+ log.warn("1. Run notepad as administrator");
20531
+ log.warn("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");
20532
+ } else {
20533
+ log.warn("\nOn Unix systems:");
20534
+ log.warn(`sudo nano ${hostsFilePath}`);
20535
+ }
20536
+ throw new Error("Failed to modify hosts file: manual intervention required");
20537
+ }
20538
+ } else {
20539
+ throw writeErr;
20540
+ }
20541
+ }
20542
+ } catch (err2) {
20543
+ const error = err2;
20544
+ log.error(`Failed to remove hosts: ${error.message}`);
20545
+ throw error;
20546
+ }
20547
+ }
20548
+ async function checkHosts(hosts) {
20549
+ debugLog("hosts", `Checking hosts: ${hosts}`, config.verbose);
20550
+ const content = await fs.promises.readFile(hostsFilePath, "utf-8");
20551
+ return hosts.map((host) => {
20552
+ const ipv4Entry = `127.0.0.1 ${host}`;
20553
+ const ipv6Entry = `::1 ${host}`;
20554
+ return content.includes(ipv4Entry) || content.includes(ipv6Entry);
20555
+ });
20556
+ }
20403
20557
  // src/https.ts
20404
- import os5 from "os";
20405
- import path4 from "path";
20558
+ import os7 from "os";
20559
+ import path6 from "path";
20406
20560
 
20407
20561
  // node_modules/@stacksjs/tlsx/dist/index.js
20408
20562
  import fs2 from "fs";
@@ -20441,7 +20595,7 @@ import process8 from "process";
20441
20595
  import process102 from "process";
20442
20596
  import process182 from "process";
20443
20597
  import process112 from "process";
20444
- import os3 from "os";
20598
+ import os5 from "os";
20445
20599
  import tty32 from "tty";
20446
20600
  import process142 from "process";
20447
20601
  import process132 from "process";
@@ -20450,10 +20604,10 @@ import process162 from "process";
20450
20604
  import process172 from "process";
20451
20605
  import process192 from "process";
20452
20606
  import os22 from "os";
20453
- import path2 from "path";
20607
+ import path4 from "path";
20454
20608
  import { resolve as resolve3 } from "path";
20455
20609
  import process22 from "process";
20456
- import fs from "fs";
20610
+ import fs3 from "fs";
20457
20611
  import path22 from "path";
20458
20612
  var __create3 = Object.create;
20459
20613
  var __getProtoOf3 = Object.getPrototypeOf;
@@ -32503,14 +32657,14 @@ var require_tls = __commonJS2((exports, module) => {
32503
32657
  c2.version = c2.session.version = session.version;
32504
32658
  c2.session.sp = session.sp;
32505
32659
  } else {
32506
- var version2;
32660
+ var version;
32507
32661
  for (var i = 1;i < tls.SupportedVersions.length; ++i) {
32508
- version2 = tls.SupportedVersions[i];
32509
- if (version2.minor <= msg.version.minor) {
32662
+ version = tls.SupportedVersions[i];
32663
+ if (version.minor <= msg.version.minor) {
32510
32664
  break;
32511
32665
  }
32512
32666
  }
32513
- c2.version = { major: version2.major, minor: version2.minor };
32667
+ c2.version = { major: version.major, minor: version.minor };
32514
32668
  c2.session.version = c2.version;
32515
32669
  }
32516
32670
  if (session !== null) {
@@ -32681,8 +32835,8 @@ var require_tls = __commonJS2((exports, module) => {
32681
32835
  try {
32682
32836
  var sp = c2.session.sp;
32683
32837
  sp.pre_master_secret = privateKey.decrypt(msg.enc_pre_master_secret);
32684
- var version2 = c2.session.clientHelloVersion;
32685
- if (version2.major !== sp.pre_master_secret.charCodeAt(0) || version2.minor !== sp.pre_master_secret.charCodeAt(1)) {
32838
+ var version = c2.session.clientHelloVersion;
32839
+ if (version.major !== sp.pre_master_secret.charCodeAt(0) || version.minor !== sp.pre_master_secret.charCodeAt(1)) {
32686
32840
  throw new Error("TLS version rollback attack detected.");
32687
32841
  }
32688
32842
  } catch (ex) {
@@ -38625,16 +38779,16 @@ var require_isIP2 = __commonJS22((exports, module) => {
38625
38779
  var IPv6SegmentFormat = "(?:[0-9a-fA-F]{1,4})";
38626
38780
  var IPv6AddressRegExp = new RegExp("^(" + "(?:".concat(IPv6SegmentFormat, ":){7}(?:").concat(IPv6SegmentFormat, "|:)|") + "(?:".concat(IPv6SegmentFormat, ":){6}(?:").concat(IPv4AddressFormat, "|:").concat(IPv6SegmentFormat, "|:)|") + "(?:".concat(IPv6SegmentFormat, ":){5}(?::").concat(IPv4AddressFormat, "|(:").concat(IPv6SegmentFormat, "){1,2}|:)|") + "(?:".concat(IPv6SegmentFormat, ":){4}(?:(:").concat(IPv6SegmentFormat, "){0,1}:").concat(IPv4AddressFormat, "|(:").concat(IPv6SegmentFormat, "){1,3}|:)|") + "(?:".concat(IPv6SegmentFormat, ":){3}(?:(:").concat(IPv6SegmentFormat, "){0,2}:").concat(IPv4AddressFormat, "|(:").concat(IPv6SegmentFormat, "){1,4}|:)|") + "(?:".concat(IPv6SegmentFormat, ":){2}(?:(:").concat(IPv6SegmentFormat, "){0,3}:").concat(IPv4AddressFormat, "|(:").concat(IPv6SegmentFormat, "){1,5}|:)|") + "(?:".concat(IPv6SegmentFormat, ":){1}(?:(:").concat(IPv6SegmentFormat, "){0,4}:").concat(IPv4AddressFormat, "|(:").concat(IPv6SegmentFormat, "){1,6}|:)|") + "(?::((?::".concat(IPv6SegmentFormat, "){0,5}:").concat(IPv4AddressFormat, "|(?::").concat(IPv6SegmentFormat, "){1,7}|:))") + ")(%[0-9a-zA-Z-.:]{1,})?$");
38627
38781
  function isIP(str) {
38628
- var version2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
38782
+ var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
38629
38783
  (0, _assertString.default)(str);
38630
- version2 = String(version2);
38631
- if (!version2) {
38784
+ version = String(version);
38785
+ if (!version) {
38632
38786
  return isIP(str, 4) || isIP(str, 6);
38633
38787
  }
38634
- if (version2 === "4") {
38788
+ if (version === "4") {
38635
38789
  return IPv4AddressRegExp.test(str);
38636
38790
  }
38637
- if (version2 === "6") {
38791
+ if (version === "6") {
38638
38792
  return IPv6AddressRegExp.test(str);
38639
38793
  }
38640
38794
  return false;
@@ -39156,9 +39310,9 @@ var require_isUUID2 = __commonJS22((exports, module) => {
39156
39310
  7: /^[0-9A-F]{8}-[0-9A-F]{4}-7[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
39157
39311
  all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i
39158
39312
  };
39159
- function isUUID(str, version2) {
39313
+ function isUUID(str, version) {
39160
39314
  (0, _assertString.default)(str);
39161
- var pattern = uuid[![undefined, null].includes(version2) ? version2 : "all"];
39315
+ var pattern = uuid[![undefined, null].includes(version) ? version : "all"];
39162
39316
  return !!pattern && pattern.test(str);
39163
39317
  }
39164
39318
  module.exports = exports.default;
@@ -51335,7 +51489,7 @@ var uuidRule2 = createRule2((value, options2, field) => {
51335
51489
  field.report(messages2.uuid, "uuid", field);
51336
51490
  }
51337
51491
  } else {
51338
- const matchesAnyVersion = options2.version.find((version2) => helpers32.isUUID(value, version2));
51492
+ const matchesAnyVersion = options2.version.find((version) => helpers32.isUUID(value, version));
51339
51493
  if (!matchesAnyVersion) {
51340
51494
  field.report(messages2.uuid, "uuid", field, options2);
51341
51495
  }
@@ -51430,8 +51584,8 @@ var VineString2 = class _VineString2 extends BaseLiteralType2 {
51430
51584
  mobile(...args) {
51431
51585
  return this.use(mobileRule2(...args));
51432
51586
  }
51433
- ipAddress(version2) {
51434
- return this.use(ipAddressRule2(version2 ? { version: version2 } : undefined));
51587
+ ipAddress(version) {
51588
+ return this.use(ipAddressRule2(version ? { version } : undefined));
51435
51589
  }
51436
51590
  hexCode() {
51437
51591
  return this.use(hexCodeRule2());
@@ -53944,7 +54098,7 @@ function _supportsColor2(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
53944
54098
  return min;
53945
54099
  }
53946
54100
  if (process112.platform === "win32") {
53947
- const osRelease = os3.release().split(".");
54101
+ const osRelease = os5.release().split(".");
53948
54102
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
53949
54103
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
53950
54104
  }
@@ -56511,18 +56665,18 @@ var config42 = await loadConfig2({
56511
56665
  defaultConfig: {
56512
56666
  altNameIPs: ["127.0.0.1"],
56513
56667
  altNameURIs: ["localhost"],
56514
- organizationName: "stacksjs.org",
56668
+ organizationName: "Local Development",
56515
56669
  countryName: "US",
56516
56670
  stateName: "California",
56517
56671
  localityName: "Playa Vista",
56518
56672
  commonName: "stacks.localhost",
56519
56673
  validityDays: 180,
56520
56674
  hostCertCN: "stacks.localhost",
56521
- domain: "localhost",
56675
+ domain: "stacks.localhost",
56522
56676
  rootCAObject: { certificate: "", privateKey: "" },
56523
- caCertPath: path2.join(os22.homedir(), ".stacks", "ssl", `tlsx.localhost.ca.crt`),
56524
- certPath: path2.join(os22.homedir(), ".stacks", "ssl", `tlsx.localhost.crt`),
56525
- keyPath: path2.join(os22.homedir(), ".stacks", "ssl", `tlsx.localhost.crt.key`),
56677
+ caCertPath: path4.join(os22.homedir(), ".stacks", "ssl", `stacks.localhost.ca.crt`),
56678
+ certPath: path4.join(os22.homedir(), ".stacks", "ssl", `stacks.localhost.crt`),
56679
+ keyPath: path4.join(os22.homedir(), ".stacks", "ssl", `stacks.localhost.crt.key`),
56526
56680
  verbose: false
56527
56681
  }
56528
56682
  });
@@ -56538,10 +56692,10 @@ function findFoldersWithFile(rootDir, fileName) {
56538
56692
  const result = [];
56539
56693
  function search(dir) {
56540
56694
  try {
56541
- const files = fs.readdirSync(dir);
56695
+ const files = fs3.readdirSync(dir);
56542
56696
  for (const file of files) {
56543
56697
  const filePath = path22.join(dir, file);
56544
- const stats = fs.lstatSync(filePath);
56698
+ const stats = fs3.lstatSync(filePath);
56545
56699
  if (stats.isDirectory()) {
56546
56700
  search(filePath);
56547
56701
  } else if (file === fileName) {
@@ -56555,29 +56709,29 @@ function findFoldersWithFile(rootDir, fileName) {
56555
56709
  search(rootDir);
56556
56710
  return result;
56557
56711
  }
56558
- function debugLog(category, message, verbose) {
56712
+ function debugLog2(category, message, verbose) {
56559
56713
  if (verbose || config42.verbose) {
56560
56714
  console.debug(`[tlsx:${category}] ${message}`);
56561
56715
  }
56562
56716
  }
56563
56717
  function randomSerialNumber(verbose) {
56564
- debugLog("cert", "Generating random serial number", verbose);
56718
+ debugLog2("cert", "Generating random serial number", verbose);
56565
56719
  const serialNumber = makeNumberPositive(import_node_forge2.default.util.bytesToHex(import_node_forge2.default.random.getBytesSync(20)));
56566
- debugLog("cert", `Generated serial number: ${serialNumber}`, verbose);
56720
+ debugLog2("cert", `Generated serial number: ${serialNumber}`, verbose);
56567
56721
  return serialNumber;
56568
56722
  }
56569
56723
  function getCertNotBefore(verbose) {
56570
- debugLog("cert", "Calculating certificate not-before date", verbose);
56724
+ debugLog2("cert", "Calculating certificate not-before date", verbose);
56571
56725
  const twoDaysAgo = new Date(Date.now() - 172800000);
56572
56726
  const year = twoDaysAgo.getFullYear();
56573
56727
  const month = (twoDaysAgo.getMonth() + 1).toString().padStart(2, "0");
56574
56728
  const day = twoDaysAgo.getDate().toString().padStart(2, "0");
56575
56729
  const date = new Date(`${year}-${month}-${day}T23:59:59Z`);
56576
- debugLog("cert", `Certificate not-before date: ${date.toISOString()}`, verbose);
56730
+ debugLog2("cert", `Certificate not-before date: ${date.toISOString()}`, verbose);
56577
56731
  return date;
56578
56732
  }
56579
56733
  function getCertNotAfter(notBefore, verbose) {
56580
- debugLog("cert", "Calculating certificate not-after date", verbose);
56734
+ debugLog2("cert", "Calculating certificate not-after date", verbose);
56581
56735
  const validityDays = config42.validityDays;
56582
56736
  const daysInMillis = validityDays * 60 * 60 * 24 * 1000;
56583
56737
  const notAfterDate = new Date(notBefore.getTime() + daysInMillis);
@@ -56585,27 +56739,27 @@ function getCertNotAfter(notBefore, verbose) {
56585
56739
  const month = (notAfterDate.getMonth() + 1).toString().padStart(2, "0");
56586
56740
  const day = notAfterDate.getDate().toString().padStart(2, "0");
56587
56741
  const date = new Date(`${year}-${month}-${day}T23:59:59Z`);
56588
- debugLog("cert", `Certificate not-after date: ${date.toISOString()} (${validityDays} days validity)`, verbose);
56742
+ debugLog2("cert", `Certificate not-after date: ${date.toISOString()} (${validityDays} days validity)`, verbose);
56589
56743
  return date;
56590
56744
  }
56591
56745
  function getCANotAfter(notBefore, verbose) {
56592
- debugLog("cert", "Calculating CA not-after date", verbose);
56746
+ debugLog2("cert", "Calculating CA not-after date", verbose);
56593
56747
  const year = notBefore.getFullYear() + 100;
56594
56748
  const month = (notBefore.getMonth() + 1).toString().padStart(2, "0");
56595
56749
  const day = notBefore.getDate().toString().padStart(2, "0");
56596
56750
  const date = new Date(`${year}-${month}-${day}T23:59:59Z`);
56597
- debugLog("cert", `CA not-after date: ${date.toISOString()} (100 years validity)`, verbose);
56751
+ debugLog2("cert", `CA not-after date: ${date.toISOString()} (100 years validity)`, verbose);
56598
56752
  return date;
56599
56753
  }
56600
56754
  async function createRootCA(options22) {
56601
- debugLog("ca", "Creating new Root CA Certificate", options22?.verbose);
56602
- debugLog("ca", "Generating 2048-bit RSA key pair", options22?.verbose);
56755
+ debugLog2("ca", "Creating new Root CA Certificate", options22?.verbose);
56756
+ debugLog2("ca", "Generating 2048-bit RSA key pair", options22?.verbose);
56603
56757
  const { privateKey, publicKey } = import_node_forge2.pki.rsa.generateKeyPair(2048);
56604
56758
  const mergedOptions = {
56605
56759
  ...config42,
56606
56760
  ...options22 || {}
56607
56761
  };
56608
- debugLog("ca", "Setting certificate attributes", options22?.verbose);
56762
+ debugLog2("ca", "Setting certificate attributes", options22?.verbose);
56609
56763
  const attributes = [
56610
56764
  { shortName: "C", value: mergedOptions.countryName },
56611
56765
  { shortName: "ST", value: mergedOptions.stateName },
@@ -56613,7 +56767,7 @@ async function createRootCA(options22) {
56613
56767
  { shortName: "O", value: "Local Development CA" },
56614
56768
  { shortName: "CN", value: "Local Development Root CA" }
56615
56769
  ];
56616
- debugLog("ca", "Setting certificate extensions", options22?.verbose);
56770
+ debugLog2("ca", "Setting certificate extensions", options22?.verbose);
56617
56771
  const extensions = [
56618
56772
  {
56619
56773
  name: "basicConstraints",
@@ -56630,7 +56784,7 @@ async function createRootCA(options22) {
56630
56784
  name: "subjectKeyIdentifier"
56631
56785
  }
56632
56786
  ];
56633
- debugLog("ca", "Creating CA certificate", options22?.verbose);
56787
+ debugLog2("ca", "Creating CA certificate", options22?.verbose);
56634
56788
  const caCert = import_node_forge2.pki.createCertificate();
56635
56789
  caCert.publicKey = publicKey;
56636
56790
  caCert.serialNumber = randomSerialNumber(options22?.verbose);
@@ -56639,11 +56793,11 @@ async function createRootCA(options22) {
56639
56793
  caCert.setSubject(attributes);
56640
56794
  caCert.setIssuer(attributes);
56641
56795
  caCert.setExtensions(extensions);
56642
- debugLog("ca", "Signing certificate with SHA-256", options22?.verbose);
56796
+ debugLog2("ca", "Signing certificate with SHA-256", options22?.verbose);
56643
56797
  caCert.sign(privateKey, import_node_forge2.default.md.sha256.create());
56644
56798
  const pemCert = import_node_forge2.pki.certificateToPem(caCert);
56645
56799
  const pemKey = import_node_forge2.pki.privateKeyToPem(privateKey);
56646
- debugLog("ca", "Root CA Certificate created successfully", options22?.verbose);
56800
+ debugLog2("ca", "Root CA Certificate created successfully", options22?.verbose);
56647
56801
  return {
56648
56802
  certificate: pemCert,
56649
56803
  privateKey: pemKey,
@@ -56652,34 +56806,41 @@ async function createRootCA(options22) {
56652
56806
  };
56653
56807
  }
56654
56808
  async function generateCert(options22) {
56655
- debugLog("cert", "Generating new host certificate", options22?.verbose);
56656
- debugLog("cert", `Options: ${JSON.stringify(options22)}`, options22?.verbose);
56809
+ debugLog2("cert", "Generating new host certificate", options22?.verbose);
56810
+ debugLog2("cert", `Options: ${JSON.stringify(options22)}`, options22?.verbose);
56657
56811
  if (!options22?.hostCertCN?.trim()) {
56658
- debugLog("cert", "Error: hostCertCN is required", options22?.verbose);
56812
+ debugLog2("cert", "Error: hostCertCN is required", options22?.verbose);
56659
56813
  throw new Error('"hostCertCN" must be a String');
56660
56814
  }
56661
56815
  if (!options22.domain?.trim()) {
56662
- debugLog("cert", "Error: domain is required", options22?.verbose);
56816
+ debugLog2("cert", "Error: domain is required", options22?.verbose);
56663
56817
  throw new Error('"domain" must be a String');
56664
56818
  }
56665
56819
  if (!options22.rootCAObject || !options22.rootCAObject.certificate || !options22.rootCAObject.privateKey) {
56666
- debugLog("cert", "Error: rootCAObject is invalid or missing", options22?.verbose);
56820
+ debugLog2("cert", "Error: rootCAObject is invalid or missing", options22?.verbose);
56667
56821
  throw new Error('"rootCAObject" must be an Object with the properties "certificate" & "privateKey"');
56668
56822
  }
56669
- debugLog("cert", "Converting Root CA PEM to forge objects", options22?.verbose);
56823
+ debugLog2("cert", "Converting Root CA PEM to forge objects", options22?.verbose);
56670
56824
  const caCert = import_node_forge2.pki.certificateFromPem(options22.rootCAObject.certificate);
56671
56825
  const caKey = import_node_forge2.pki.privateKeyFromPem(options22.rootCAObject.privateKey);
56672
- debugLog("cert", "Generating 2048-bit RSA key pair for host certificate", options22?.verbose);
56826
+ debugLog2("cert", "Generating 2048-bit RSA key pair for host certificate", options22?.verbose);
56673
56827
  const hostKeys = import_node_forge2.pki.rsa.generateKeyPair(2048);
56674
- debugLog("cert", "Setting certificate attributes", options22?.verbose);
56828
+ debugLog2("cert", "Setting certificate attributes", options22?.verbose);
56675
56829
  const attributes = [
56676
- { shortName: "C", value: config42.countryName },
56677
- { shortName: "ST", value: config42.stateName },
56678
- { shortName: "L", value: config42.localityName },
56679
- { shortName: "O", value: "Local Development" },
56680
- { shortName: "CN", value: "*.localhost" }
56830
+ { shortName: "C", value: options22.countryName || config42.countryName },
56831
+ { shortName: "ST", value: options22.stateName || config42.stateName },
56832
+ { shortName: "L", value: options22.localityName || config42.localityName },
56833
+ { shortName: "O", value: options22.organizationName || config42.organizationName },
56834
+ { shortName: "CN", value: options22.commonName || config42.commonName }
56835
+ ];
56836
+ const domain = options22.domain || config42.domain;
56837
+ const wildcardDomain = `*.${domain.includes(".") ? domain.split(".").slice(1).join(".") : domain}`;
56838
+ const altNames = [
56839
+ { type: 2, value: wildcardDomain },
56840
+ { type: 2, value: "localhost" },
56841
+ { type: 2, value: domain }
56681
56842
  ];
56682
- debugLog("cert", "Setting certificate extensions", options22?.verbose);
56843
+ debugLog2("cert", "Setting certificate extensions", options22?.verbose);
56683
56844
  const extensions = [
56684
56845
  {
56685
56846
  name: "basicConstraints",
@@ -56699,30 +56860,25 @@ async function generateCert(options22) {
56699
56860
  },
56700
56861
  {
56701
56862
  name: "subjectAltName",
56702
- altNames: [
56703
- { type: 2, value: "*.localhost" },
56704
- { type: 2, value: "localhost" },
56705
- { type: 2, value: "stacks.localhost" },
56706
- { type: 2, value: options22.domain }
56707
- ]
56863
+ altNames
56708
56864
  }
56709
56865
  ];
56710
- debugLog("cert", "Creating new host certificate", options22?.verbose);
56866
+ debugLog2("cert", "Creating new host certificate", options22?.verbose);
56711
56867
  const newHostCert = import_node_forge2.pki.createCertificate();
56712
56868
  newHostCert.publicKey = hostKeys.publicKey;
56713
- debugLog("cert", "Setting certificate properties", options22?.verbose);
56869
+ debugLog2("cert", "Setting certificate properties", options22?.verbose);
56714
56870
  newHostCert.serialNumber = randomSerialNumber(options22?.verbose);
56715
56871
  newHostCert.validity.notBefore = getCertNotBefore(options22?.verbose);
56716
56872
  newHostCert.validity.notAfter = getCertNotAfter(newHostCert.validity.notBefore, options22?.verbose);
56717
56873
  newHostCert.setSubject(attributes);
56718
56874
  newHostCert.setIssuer(caCert.subject.attributes);
56719
56875
  newHostCert.setExtensions(extensions);
56720
- debugLog("cert", "Signing certificate with SHA-256", options22?.verbose);
56876
+ debugLog2("cert", "Signing certificate with SHA-256", options22?.verbose);
56721
56877
  newHostCert.sign(caKey, import_node_forge2.default.md.sha256.create());
56722
- debugLog("cert", "Converting certificate to PEM format", options22?.verbose);
56878
+ debugLog2("cert", "Converting certificate to PEM format", options22?.verbose);
56723
56879
  const pemHostCert = import_node_forge2.pki.certificateToPem(newHostCert);
56724
56880
  const pemHostKey = import_node_forge2.pki.privateKeyToPem(hostKeys.privateKey);
56725
- debugLog("cert", "Host certificate generated successfully", options22?.verbose);
56881
+ debugLog2("cert", "Host certificate generated successfully", options22?.verbose);
56726
56882
  return {
56727
56883
  certificate: pemHostCert,
56728
56884
  privateKey: pemHostKey,
@@ -56731,81 +56887,81 @@ async function generateCert(options22) {
56731
56887
  };
56732
56888
  }
56733
56889
  async function addCertToSystemTrustStoreAndSaveCert(cert, caCert, options22) {
56734
- debugLog("trust", `Adding certificate to system trust store with options: ${JSON.stringify(options22)}`, options22?.verbose);
56735
- debugLog("trust", "Storing certificate and private key", options22?.verbose);
56890
+ debugLog2("trust", `Adding certificate to system trust store with options: ${JSON.stringify(options22)}`, options22?.verbose);
56891
+ debugLog2("trust", "Storing certificate and private key", options22?.verbose);
56736
56892
  const certPath = storeCert(cert, options22);
56737
- debugLog("trust", "Storing CA certificate", options22?.verbose);
56893
+ debugLog2("trust", "Storing CA certificate", options22?.verbose);
56738
56894
  const caCertPath = storeCACert(caCert, options22);
56739
56895
  const platform22 = os4.platform();
56740
- debugLog("trust", `Detected platform: ${platform22}`, options22?.verbose);
56896
+ debugLog2("trust", `Detected platform: ${platform22}`, options22?.verbose);
56741
56897
  const args = "TC, C, C";
56742
56898
  if (platform22 === "darwin") {
56743
- debugLog("trust", "Adding certificate to macOS keychain", options22?.verbose);
56899
+ debugLog2("trust", "Adding certificate to macOS keychain", options22?.verbose);
56744
56900
  await runCommand(`sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${caCertPath}`);
56745
56901
  } else if (platform22 === "win32") {
56746
- debugLog("trust", "Adding certificate to Windows certificate store", options22?.verbose);
56902
+ debugLog2("trust", "Adding certificate to Windows certificate store", options22?.verbose);
56747
56903
  await runCommand(`certutil -f -v -addstore -enterprise Root ${caCertPath}`);
56748
56904
  } else if (platform22 === "linux") {
56749
- debugLog("trust", "Adding certificate to Linux certificate store", options22?.verbose);
56905
+ debugLog2("trust", "Adding certificate to Linux certificate store", options22?.verbose);
56750
56906
  const rootDirectory = os4.homedir();
56751
56907
  const targetFileName = "cert9.db";
56752
- debugLog("trust", `Searching for certificate databases in ${rootDirectory}`, options22?.verbose);
56908
+ debugLog2("trust", `Searching for certificate databases in ${rootDirectory}`, options22?.verbose);
56753
56909
  const foldersWithFile = findFoldersWithFile(rootDirectory, targetFileName);
56754
56910
  for (const folder of foldersWithFile) {
56755
- debugLog("trust", `Processing certificate database in ${folder}`, options22?.verbose);
56911
+ debugLog2("trust", `Processing certificate database in ${folder}`, options22?.verbose);
56756
56912
  try {
56757
- debugLog("trust", `Attempting to delete existing cert for ${config42.commonName}`, options22?.verbose);
56913
+ debugLog2("trust", `Attempting to delete existing cert for ${config42.commonName}`, options22?.verbose);
56758
56914
  await runCommand(`certutil -d sql:${folder} -D -n ${config42.commonName}`);
56759
56915
  } catch (error) {
56760
- debugLog("trust", `Warning: Error deleting existing cert: ${error}`, options22?.verbose);
56916
+ debugLog2("trust", `Warning: Error deleting existing cert: ${error}`, options22?.verbose);
56761
56917
  console.warn(`Error deleting existing cert: ${error}`);
56762
56918
  }
56763
- debugLog("trust", `Adding new certificate to ${folder}`, options22?.verbose);
56919
+ debugLog2("trust", `Adding new certificate to ${folder}`, options22?.verbose);
56764
56920
  await runCommand(`certutil -d sql:${folder} -A -t ${args} -n ${config42.commonName} -i ${caCertPath}`);
56765
56921
  log2.info(`Cert added to ${folder}`);
56766
56922
  }
56767
56923
  } else {
56768
- debugLog("trust", `Error: Unsupported platform ${platform22}`, options22?.verbose);
56924
+ debugLog2("trust", `Error: Unsupported platform ${platform22}`, options22?.verbose);
56769
56925
  throw new Error(`Unsupported platform: ${platform22}`);
56770
56926
  }
56771
- debugLog("trust", "Certificate successfully added to system trust store", options22?.verbose);
56927
+ debugLog2("trust", "Certificate successfully added to system trust store", options22?.verbose);
56772
56928
  return certPath;
56773
56929
  }
56774
56930
  function storeCert(cert, options22) {
56775
- debugLog("storage", `Storing certificate and private key with options: ${JSON.stringify(options22)}`, options22?.verbose);
56931
+ debugLog2("storage", `Storing certificate and private key with options: ${JSON.stringify(options22)}`, options22?.verbose);
56776
56932
  const certPath = options22?.certPath || config42.certPath;
56777
56933
  const certKeyPath = options22?.keyPath || config42.keyPath;
56778
- debugLog("storage", `Certificate path: ${certPath}`, options22?.verbose);
56779
- debugLog("storage", `Private key path: ${certKeyPath}`, options22?.verbose);
56934
+ debugLog2("storage", `Certificate path: ${certPath}`, options22?.verbose);
56935
+ debugLog2("storage", `Private key path: ${certKeyPath}`, options22?.verbose);
56780
56936
  const certDir = path3.dirname(certPath);
56781
56937
  if (!fs2.existsSync(certDir)) {
56782
- debugLog("storage", `Creating certificate directory: ${certDir}`, options22?.verbose);
56938
+ debugLog2("storage", `Creating certificate directory: ${certDir}`, options22?.verbose);
56783
56939
  fs2.mkdirSync(certDir, { recursive: true });
56784
56940
  }
56785
- debugLog("storage", "Writing certificate file", options22?.verbose);
56941
+ debugLog2("storage", "Writing certificate file", options22?.verbose);
56786
56942
  fs2.writeFileSync(certPath, cert.certificate);
56787
56943
  const certKeyDir = path3.dirname(certKeyPath);
56788
56944
  if (!fs2.existsSync(certKeyDir)) {
56789
- debugLog("storage", `Creating private key directory: ${certKeyDir}`, options22?.verbose);
56945
+ debugLog2("storage", `Creating private key directory: ${certKeyDir}`, options22?.verbose);
56790
56946
  fs2.mkdirSync(certKeyDir, { recursive: true });
56791
56947
  }
56792
- debugLog("storage", "Writing private key file", options22?.verbose);
56948
+ debugLog2("storage", "Writing private key file", options22?.verbose);
56793
56949
  fs2.writeFileSync(certKeyPath, cert.privateKey);
56794
- debugLog("storage", "Certificate and private key stored successfully", options22?.verbose);
56950
+ debugLog2("storage", "Certificate and private key stored successfully", options22?.verbose);
56795
56951
  return certPath;
56796
56952
  }
56797
56953
  function storeCACert(caCert, options22) {
56798
- debugLog("storage", "Storing CA certificate", options22?.verbose);
56954
+ debugLog2("storage", "Storing CA certificate", options22?.verbose);
56799
56955
  const caCertPath = options22?.caCertPath || config42.caCertPath;
56800
- debugLog("storage", `CA certificate path: ${caCertPath}`, options22?.verbose);
56956
+ debugLog2("storage", `CA certificate path: ${caCertPath}`, options22?.verbose);
56801
56957
  const caCertDir = path3.dirname(caCertPath);
56802
56958
  if (!fs2.existsSync(caCertDir)) {
56803
- debugLog("storage", `Creating CA certificate directory: ${caCertDir}`, options22?.verbose);
56959
+ debugLog2("storage", `Creating CA certificate directory: ${caCertDir}`, options22?.verbose);
56804
56960
  fs2.mkdirSync(caCertDir, { recursive: true });
56805
56961
  }
56806
- debugLog("storage", "Writing CA certificate file", options22?.verbose);
56962
+ debugLog2("storage", "Writing CA certificate file", options22?.verbose);
56807
56963
  fs2.writeFileSync(caCertPath, caCert);
56808
- debugLog("storage", "CA certificate stored successfully", options22?.verbose);
56964
+ debugLog2("storage", "CA certificate stored successfully", options22?.verbose);
56809
56965
  return caCertPath;
56810
56966
  }
56811
56967
  var export_tls = import_node_forge2.tls;
@@ -56813,28 +56969,37 @@ var export_pki = import_node_forge2.pki;
56813
56969
  var export_forge = import_node_forge2.default;
56814
56970
 
56815
56971
  // src/https.ts
56816
- var defaultHttpsConfig = {
56817
- domain: "stacks.localhost",
56818
- hostCertCN: "stacks.localhost",
56819
- caCertPath: path4.join(os5.homedir(), ".stacks", "ssl", `stacks.localhost.ca.crt`),
56820
- certPath: path4.join(os5.homedir(), ".stacks", "ssl", `stacks.localhost.crt`),
56821
- keyPath: path4.join(os5.homedir(), ".stacks", "ssl", `stacks.localhost.crt.key`),
56822
- altNameIPs: ["127.0.0.1"],
56823
- altNameURIs: ["localhost"],
56824
- organizationName: "stacksjs.org",
56825
- countryName: "US",
56826
- stateName: "California",
56827
- localityName: "Playa Vista",
56828
- commonName: "stacks.localhost",
56829
- validityDays: 180,
56830
- verbose: false
56831
- };
56972
+ function httpsConfig() {
56973
+ const domain = config.to || "stacks.localhost";
56974
+ const defaultConfig = {
56975
+ domain,
56976
+ hostCertCN: domain,
56977
+ caCertPath: path6.join(os7.homedir(), ".stacks", "ssl", `${domain}.ca.crt`),
56978
+ certPath: path6.join(os7.homedir(), ".stacks", "ssl", `${domain}.crt`),
56979
+ keyPath: path6.join(os7.homedir(), ".stacks", "ssl", `${domain}.crt.key`),
56980
+ altNameIPs: ["127.0.0.1"],
56981
+ altNameURIs: ["localhost"],
56982
+ organizationName: "stacksjs.org",
56983
+ countryName: "US",
56984
+ stateName: "California",
56985
+ localityName: "Playa Vista",
56986
+ commonName: domain,
56987
+ validityDays: 180,
56988
+ verbose: false
56989
+ };
56990
+ if (config.https === true)
56991
+ return defaultConfig;
56992
+ return {
56993
+ ...defaultConfig,
56994
+ ...config.https
56995
+ };
56996
+ }
56832
56997
  async function generateCertificate(domain) {
56833
56998
  if (config.https === true)
56834
- config.https = defaultHttpsConfig;
56999
+ config.https = httpsConfig();
56835
57000
  else if (config.https === false)
56836
57001
  return;
56837
- domain = domain ?? config.https.altNameURIs[0];
57002
+ domain = domain ?? config.https.domain;
56838
57003
  log.info(`Generating a self-signed SSL certificate for: ${domain}`);
56839
57004
  const caCert = await createRootCA(config.https);
56840
57005
  const hostCert = await generateCert({
@@ -56856,91 +57021,100 @@ async function generateCertificate(domain) {
56856
57021
  await addCertToSystemTrustStoreAndSaveCert(hostCert, caCert.certificate, config.https);
56857
57022
  log.success("Certificate generated");
56858
57023
  }
56859
-
56860
- // src/utils.ts
56861
- function debugLog2(category, message, verbose) {
56862
- if (verbose || config.verbose) {
56863
- console.debug(`[rpx:${category}] ${message}`);
56864
- }
56865
- }
57024
+ // src/start.ts
57025
+ import * as fs5 from "fs";
57026
+ import * as http from "http";
57027
+ import * as https from "https";
57028
+ import * as net from "net";
57029
+ import os8 from "os";
57030
+ import path7 from "path";
57031
+ import process9 from "process";
57032
+ // package.json
57033
+ var version = "0.3.0";
56866
57034
 
56867
57035
  // src/start.ts
56868
57036
  var activeServers = new Set;
56869
- function cleanup() {
56870
- debugLog2("cleanup", "Starting cleanup process", config.verbose);
57037
+ async function cleanup() {
57038
+ debugLog("cleanup", "Starting cleanup process", config.verbose);
56871
57039
  console.log(`\n`);
56872
57040
  log.info("Shutting down proxy servers...");
56873
- const closePromises = Array.from(activeServers).map((server) => new Promise((resolve4) => {
57041
+ const cleanupPromises = [];
57042
+ const serverClosePromises = Array.from(activeServers).map((server) => new Promise((resolve4) => {
56874
57043
  server.close(() => {
56875
- debugLog2("cleanup", "Server closed successfully", config.verbose);
57044
+ debugLog("cleanup", "Server closed successfully", config.verbose);
56876
57045
  resolve4();
56877
57046
  });
56878
57047
  }));
56879
- Promise.all(closePromises).then(() => {
56880
- debugLog2("cleanup", "All servers closed successfully", config.verbose);
56881
- log.success("All proxy servers shut down successfully");
56882
- process3.exit(0);
56883
- }).catch((err3) => {
56884
- debugLog2("cleanup", `Error during shutdown: ${err3}`, config.verbose);
56885
- log.error("Error during shutdown:", err3);
56886
- process3.exit(1);
56887
- });
57048
+ cleanupPromises.push(...serverClosePromises);
57049
+ if (config.etcHostsCleanup) {
57050
+ debugLog("cleanup", "Cleaning up hosts file entries", config.verbose);
57051
+ try {
57052
+ const toUrl = new URL(config.to.startsWith("http") ? config.to : `http://${config.to}`);
57053
+ const hostname = toUrl.hostname;
57054
+ if (!hostname.includes("localhost") && !hostname.includes("127.0.0.1")) {
57055
+ log.info("Cleaning up hosts file entries...");
57056
+ cleanupPromises.push(removeHosts([hostname]).then(() => {
57057
+ debugLog("cleanup", `Removed hosts entry for ${hostname}`, config.verbose);
57058
+ }).catch((err3) => {
57059
+ debugLog("cleanup", `Failed to remove hosts entry: ${err3}`, config.verbose);
57060
+ log.warn(`Failed to clean up hosts file entry for ${hostname}:`, err3);
57061
+ }));
57062
+ }
57063
+ } catch (err3) {
57064
+ debugLog("cleanup", `Error parsing URL during hosts cleanup: ${err3}`, config.verbose);
57065
+ log.warn("Failed to parse URL for hosts cleanup:", err3);
57066
+ }
57067
+ }
57068
+ try {
57069
+ await Promise.all(cleanupPromises);
57070
+ debugLog("cleanup", "All cleanup tasks completed successfully", config.verbose);
57071
+ log.success("All cleanup tasks completed successfully");
57072
+ process9.exit(0);
57073
+ } catch (err3) {
57074
+ debugLog("cleanup", `Error during cleanup: ${err3}`, config.verbose);
57075
+ log.error("Error during cleanup:", err3);
57076
+ process9.exit(1);
57077
+ }
56888
57078
  }
56889
- process3.on("SIGINT", cleanup);
56890
- process3.on("SIGTERM", cleanup);
56891
- process3.on("uncaughtException", (err3) => {
56892
- debugLog2("process", `Uncaught exception: ${err3}`, config.verbose);
57079
+ process9.on("SIGINT", cleanup);
57080
+ process9.on("SIGTERM", cleanup);
57081
+ process9.on("uncaughtException", (err3) => {
57082
+ debugLog("process", `Uncaught exception: ${err3}`, config.verbose);
56893
57083
  log.error("Uncaught exception:", err3);
56894
57084
  cleanup();
56895
57085
  });
56896
57086
  async function loadSSLConfig(options3) {
56897
- debugLog2("ssl", "Loading SSL configuration", options3.verbose);
56898
- if (options3.https === true) {
56899
- options3.https = {
56900
- domain: "stacks.localhost",
56901
- hostCertCN: "stacks.localhost",
56902
- caCertPath: path6.join(os7.homedir(), ".stacks", "ssl", `stacks.localhost.ca.crt`),
56903
- certPath: path6.join(os7.homedir(), ".stacks", "ssl", `stacks.localhost.crt`),
56904
- keyPath: path6.join(os7.homedir(), ".stacks", "ssl", `stacks.localhost.crt.key`),
56905
- altNameIPs: ["127.0.0.1"],
56906
- altNameURIs: ["localhost"],
56907
- organizationName: "stacksjs.org",
56908
- countryName: "US",
56909
- stateName: "California",
56910
- localityName: "Playa Vista",
56911
- commonName: "stacks.localhost",
56912
- validityDays: 180,
56913
- verbose: false
56914
- };
56915
- } else if (options3.https === false) {
57087
+ debugLog("ssl", "Loading SSL configuration", options3.verbose);
57088
+ if (options3.https === true)
57089
+ options3.https = httpsConfig();
57090
+ else if (options3.https === false)
56916
57091
  return null;
56917
- }
56918
57092
  if (!options3.https?.keyPath && !options3.https?.certPath) {
56919
- debugLog2("ssl", "No SSL configuration provided", options3.verbose);
57093
+ debugLog("ssl", "No SSL configuration provided", options3.verbose);
56920
57094
  return null;
56921
57095
  }
56922
57096
  if (options3.https?.keyPath && !options3.https?.certPath || !options3.https?.keyPath && options3.https?.certPath) {
56923
57097
  const missing = !options3.https?.keyPath ? "keyPath" : "certPath";
56924
- debugLog2("ssl", `Invalid SSL configuration - missing ${missing}`, options3.verbose);
57098
+ debugLog("ssl", `Invalid SSL configuration - missing ${missing}`, options3.verbose);
56925
57099
  throw new Error(`SSL Configuration requires both keyPath and certPath. Missing: ${missing}`);
56926
57100
  }
56927
57101
  try {
56928
57102
  if (!options3.https?.keyPath || !options3.https?.certPath)
56929
57103
  return null;
56930
57104
  try {
56931
- debugLog2("ssl", "Reading SSL certificate files", options3.verbose);
56932
- const key = await fs3.promises.readFile(options3.https?.keyPath, "utf8");
56933
- const cert = await fs3.promises.readFile(options3.https?.certPath, "utf8");
56934
- debugLog2("ssl", "SSL configuration loaded successfully", options3.verbose);
57105
+ debugLog("ssl", "Reading SSL certificate files", options3.verbose);
57106
+ const key = await fs5.promises.readFile(options3.https?.keyPath, "utf8");
57107
+ const cert = await fs5.promises.readFile(options3.https?.certPath, "utf8");
57108
+ debugLog("ssl", "SSL configuration loaded successfully", options3.verbose);
56935
57109
  return { key, cert };
56936
57110
  } catch (error) {
56937
57111
  if (error.code === "ENOENT") {
56938
- debugLog2("ssl", "Certificates not found, generating new ones", options3.verbose);
56939
- await generateCertificate();
56940
- debugLog2("ssl", "Reading newly generated certificates", options3.verbose);
56941
- const key = await fs3.promises.readFile(options3.https?.keyPath, "utf8");
56942
- const cert = await fs3.promises.readFile(options3.https?.certPath, "utf8");
56943
- debugLog2("ssl", "New SSL certificates loaded successfully", options3.verbose);
57112
+ debugLog("ssl", "Certificates not found, generating new ones", options3.verbose);
57113
+ await generateCertificate(options3.to);
57114
+ debugLog("ssl", "Reading newly generated certificates", options3.verbose);
57115
+ const key = await fs5.promises.readFile(options3.https?.keyPath, "utf8");
57116
+ const cert = await fs5.promises.readFile(options3.https?.certPath, "utf8");
57117
+ debugLog("ssl", "New SSL certificates loaded successfully", options3.verbose);
56944
57118
  return { key, cert };
56945
57119
  }
56946
57120
  throw error;
@@ -56948,22 +57122,22 @@ async function loadSSLConfig(options3) {
56948
57122
  } catch (err3) {
56949
57123
  const error = err3;
56950
57124
  const detail = error.code === "ENOENT" ? `File not found: ${error.path}` : error.message;
56951
- debugLog2("ssl", `SSL configuration error: ${error}`, options3.verbose);
57125
+ debugLog("ssl", `SSL configuration error: ${error}`, options3.verbose);
56952
57126
  throw new Error(`SSL Configuration Error: ${detail}`);
56953
57127
  }
56954
57128
  }
56955
57129
  function isPortInUse(port, hostname, verbose) {
56956
- debugLog2("port", `Checking if port ${port} is in use on ${hostname}`, verbose);
57130
+ debugLog("port", `Checking if port ${port} is in use on ${hostname}`, verbose);
56957
57131
  return new Promise((resolve4) => {
56958
57132
  const server = net.createServer();
56959
57133
  server.once("error", (err3) => {
56960
57134
  if (err3.code === "EADDRINUSE") {
56961
- debugLog2("port", `Port ${port} is in use`, verbose);
57135
+ debugLog("port", `Port ${port} is in use`, verbose);
56962
57136
  resolve4(true);
56963
57137
  }
56964
57138
  });
56965
57139
  server.once("listening", () => {
56966
- debugLog2("port", `Port ${port} is available`, verbose);
57140
+ debugLog("port", `Port ${port} is available`, verbose);
56967
57141
  server.close();
56968
57142
  resolve4(false);
56969
57143
  });
@@ -56971,17 +57145,17 @@ function isPortInUse(port, hostname, verbose) {
56971
57145
  });
56972
57146
  }
56973
57147
  async function findAvailablePort(startPort, hostname, verbose) {
56974
- debugLog2("port", `Finding available port starting from ${startPort}`, verbose);
57148
+ debugLog("port", `Finding available port starting from ${startPort}`, verbose);
56975
57149
  let port = startPort;
56976
57150
  while (await isPortInUse(port, hostname, verbose)) {
56977
- debugLog2("port", `Port ${port} is in use, trying ${port + 1}`, verbose);
57151
+ debugLog("port", `Port ${port} is in use, trying ${port + 1}`, verbose);
56978
57152
  port++;
56979
57153
  }
56980
- debugLog2("port", `Found available port: ${port}`, verbose);
57154
+ debugLog("port", `Found available port: ${port}`, verbose);
56981
57155
  return port;
56982
57156
  }
56983
57157
  async function testConnection(hostname, port, verbose) {
56984
- debugLog2("connection", `Testing connection to ${hostname}:${port}`, verbose);
57158
+ debugLog("connection", `Testing connection to ${hostname}:${port}`, verbose);
56985
57159
  return new Promise((resolve4, reject) => {
56986
57160
  const socket = net.connect({
56987
57161
  host: hostname,
@@ -56989,58 +57163,89 @@ async function testConnection(hostname, port, verbose) {
56989
57163
  timeout: 5000
56990
57164
  });
56991
57165
  socket.once("connect", () => {
56992
- debugLog2("connection", `Successfully connected to ${hostname}:${port}`, verbose);
57166
+ debugLog("connection", `Successfully connected to ${hostname}:${port}`, verbose);
56993
57167
  socket.end();
56994
57168
  resolve4();
56995
57169
  });
56996
57170
  socket.once("timeout", () => {
56997
- debugLog2("connection", `Connection to ${hostname}:${port} timed out`, verbose);
57171
+ debugLog("connection", `Connection to ${hostname}:${port} timed out`, verbose);
56998
57172
  socket.destroy();
56999
57173
  reject(new Error(`Connection to ${hostname}:${port} timed out`));
57000
57174
  });
57001
57175
  socket.once("error", (err3) => {
57002
- debugLog2("connection", `Failed to connect to ${hostname}:${port}: ${err3}`, verbose);
57176
+ debugLog("connection", `Failed to connect to ${hostname}:${port}: ${err3}`, verbose);
57003
57177
  socket.destroy();
57004
57178
  reject(new Error(`Failed to connect to ${hostname}:${port}: ${err3.message}`));
57005
57179
  });
57006
57180
  });
57007
57181
  }
57008
57182
  async function startServer(options3) {
57009
- debugLog2("server", `Starting server with options: ${JSON.stringify(options3)}`, options3?.verbose);
57183
+ debugLog("server", `Starting server with options: ${JSON.stringify(options3)}`, options3?.verbose);
57010
57184
  if (!options3)
57011
57185
  options3 = config;
57012
57186
  if (!options3.from)
57013
57187
  options3.from = config.from;
57014
57188
  if (!options3.to)
57015
57189
  options3.to = config.to;
57190
+ const fromUrl = new URL(options3.from.startsWith("http") ? options3.from : `http://${options3.from}`);
57191
+ const toUrl = new URL(options3.to.startsWith("http") ? options3.to : `http://${options3.to}`);
57192
+ const fromPort = Number.parseInt(fromUrl.port) || (fromUrl.protocol.includes("https:") ? 443 : 80);
57193
+ const hostsToCheck = [toUrl.hostname];
57194
+ if (!toUrl.hostname.includes("localhost") && !toUrl.hostname.includes("127.0.0.1")) {
57195
+ debugLog("hosts", `Checking if hosts file entry exists for: ${toUrl.hostname}`, options3.verbose);
57196
+ try {
57197
+ const hostsExist = await checkHosts(hostsToCheck);
57198
+ if (!hostsExist[0]) {
57199
+ log.info(`Adding ${toUrl.hostname} to hosts file...`);
57200
+ log.info("This may require sudo/administrator privileges");
57201
+ try {
57202
+ await addHosts(hostsToCheck);
57203
+ } catch (addError) {
57204
+ log.error("Failed to add hosts entry:", addError.message);
57205
+ log.warn("You can manually add this entry to your hosts file:");
57206
+ log.warn(`127.0.0.1 ${toUrl.hostname}`);
57207
+ log.warn(`::1 ${toUrl.hostname}`);
57208
+ if (process9.platform === "win32") {
57209
+ log.warn("On Windows:");
57210
+ log.warn("1. Run notepad as administrator");
57211
+ log.warn("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");
57212
+ } else {
57213
+ log.warn("On Unix systems:");
57214
+ log.warn("sudo nano /etc/hosts");
57215
+ }
57216
+ }
57217
+ } else {
57218
+ debugLog("hosts", `Host entry already exists for ${toUrl.hostname}`, options3.verbose);
57219
+ }
57220
+ } catch (checkError) {
57221
+ log.error("Failed to check hosts file:", checkError.message);
57222
+ }
57223
+ }
57016
57224
  if (config.https) {
57017
57225
  if (config.https === true)
57018
- config.https = defaultHttpsConfig;
57019
- const domain = config.https.altNameURIs?.[0] || new URL(options3.to).hostname;
57226
+ config.https = httpsConfig();
57227
+ const domain = toUrl.hostname;
57020
57228
  if (typeof options3.https !== "boolean" && options3.https) {
57021
- options3.https.keyPath = config.https.keyPath || path6.join(os7.homedir(), ".stacks", "ssl", `${domain}.crt.key`);
57022
- options3.https.certPath = config.https.certPath || path6.join(os7.homedir(), ".stacks", "ssl", `${domain}.crt`);
57023
- debugLog2("server", `HTTPS enabled, using cert paths: ${options3.https.keyPath}, ${options3.https.certPath}`, options3.verbose);
57229
+ options3.https.keyPath = config.https.keyPath || path7.join(os8.homedir(), ".stacks", "ssl", `${domain}.crt.key`);
57230
+ options3.https.certPath = config.https.certPath || path7.join(os8.homedir(), ".stacks", "ssl", `${domain}.crt`);
57231
+ debugLog("server", `HTTPS enabled, using cert paths: ${options3.https.keyPath}, ${options3.https.certPath}`, options3.verbose);
57024
57232
  }
57025
57233
  }
57026
- const fromUrl = new URL(options3.from.startsWith("http") ? options3.from : `http://${options3.from}`);
57027
- const toUrl = new URL(options3.to.startsWith("http") ? options3.to : `http://${options3.to}`);
57028
- const fromPort = Number.parseInt(fromUrl.port) || (fromUrl.protocol.includes("https:") ? 443 : 80);
57029
- debugLog2("server", `Parsed URLs - from: ${fromUrl}, to: ${toUrl}`, options3.verbose);
57234
+ debugLog("server", `Parsed URLs - from: ${fromUrl}, to: ${toUrl}`, options3.verbose);
57030
57235
  try {
57031
57236
  await testConnection(fromUrl.hostname, fromPort, options3.verbose);
57032
57237
  } catch (err3) {
57033
- debugLog2("server", `Connection test failed: ${err3}`, options3.verbose);
57238
+ debugLog("server", `Connection test failed: ${err3}`, options3.verbose);
57034
57239
  log.error(err3.message);
57035
- process3.exit(1);
57240
+ process9.exit(1);
57036
57241
  }
57037
57242
  let sslConfig = null;
57038
57243
  if (config.https) {
57039
57244
  try {
57040
57245
  sslConfig = await loadSSLConfig(options3);
57041
57246
  } catch (err3) {
57042
- debugLog2("server", `SSL config failed, attempting to generate certificates: ${err3}`, options3.verbose);
57043
- await generateCertificate();
57247
+ debugLog("server", `SSL config failed, attempting to generate certificates: ${err3}`, options3.verbose);
57248
+ await generateCertificate(options3.to);
57044
57249
  sslConfig = await loadSSLConfig(options3);
57045
57250
  }
57046
57251
  }
@@ -57057,9 +57262,9 @@ async function startServer(options3) {
57057
57262
  });
57058
57263
  }
57059
57264
  async function createProxyServer(from, to, fromPort, listenPort, hostname, sourceUrl, ssl, verbose) {
57060
- debugLog2("proxy", `Creating proxy server ${from} -> ${to}`, verbose);
57265
+ debugLog("proxy", `Creating proxy server ${from} -> ${to}`, verbose);
57061
57266
  const requestHandler = (req, res) => {
57062
- debugLog2("request", `Incoming request: ${req.method} ${req.url}`, verbose);
57267
+ debugLog("request", `Incoming request: ${req.method} ${req.url}`, verbose);
57063
57268
  const proxyOptions = {
57064
57269
  hostname: sourceUrl.hostname,
57065
57270
  port: fromPort,
@@ -57070,9 +57275,9 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57070
57275
  host: sourceUrl.host
57071
57276
  }
57072
57277
  };
57073
- debugLog2("request", `Proxy request options: ${JSON.stringify(proxyOptions)}`, verbose);
57278
+ debugLog("request", `Proxy request options: ${JSON.stringify(proxyOptions)}`, verbose);
57074
57279
  const proxyReq = http.request(proxyOptions, (proxyRes) => {
57075
- debugLog2("response", `Proxy response received with status ${proxyRes.statusCode}`, verbose);
57280
+ debugLog("response", `Proxy response received with status ${proxyRes.statusCode}`, verbose);
57076
57281
  const headers = {
57077
57282
  ...proxyRes.headers,
57078
57283
  "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
@@ -57082,7 +57287,7 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57082
57287
  proxyRes.pipe(res);
57083
57288
  });
57084
57289
  proxyReq.on("error", (err3) => {
57085
- debugLog2("request", `Proxy request failed: ${err3}`, verbose);
57290
+ debugLog("request", `Proxy request failed: ${err3}`, verbose);
57086
57291
  log.error("Proxy request failed:", err3);
57087
57292
  res.writeHead(502);
57088
57293
  res.end(`Proxy Error: ${err3.message}`);
@@ -57109,11 +57314,11 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57109
57314
  allowHTTP1: true,
57110
57315
  ALPNProtocols: ["h2", "http/1.1"]
57111
57316
  } : undefined;
57112
- debugLog2("server", `Creating server with SSL config: ${!!ssl}`, verbose);
57317
+ debugLog("server", `Creating server with SSL config: ${!!ssl}`, verbose);
57113
57318
  const server = ssl && serverOptions ? https.createServer(serverOptions, requestHandler) : http.createServer(requestHandler);
57114
57319
  if (ssl) {
57115
57320
  server.on("secureConnection", (tlsSocket) => {
57116
- debugLog2("tls", `TLS Connection established: ${JSON.stringify({
57321
+ debugLog("tls", `TLS Connection established: ${JSON.stringify({
57117
57322
  protocol: tlsSocket.getProtocol?.(),
57118
57323
  cipher: tlsSocket.getCipher?.(),
57119
57324
  authorized: tlsSocket.authorized,
@@ -57124,7 +57329,7 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57124
57329
  activeServers.add(server);
57125
57330
  return new Promise((resolve4, reject) => {
57126
57331
  server.listen(listenPort, hostname, () => {
57127
- debugLog2("server", `Server listening on port ${listenPort}`, verbose);
57332
+ debugLog("server", `Server listening on port ${listenPort}`, verbose);
57128
57333
  console.log("");
57129
57334
  console.log(` ${green(bold("reverse-proxy"))} ${green(`v${version}`)}`);
57130
57335
  console.log("");
@@ -57141,13 +57346,13 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57141
57346
  resolve4();
57142
57347
  });
57143
57348
  server.on("error", (err3) => {
57144
- debugLog2("server", `Server error: ${err3}`, verbose);
57349
+ debugLog("server", `Server error: ${err3}`, verbose);
57145
57350
  reject(err3);
57146
57351
  });
57147
57352
  });
57148
57353
  }
57149
57354
  async function setupReverseProxy(options3) {
57150
- debugLog2("setup", `Setting up reverse proxy: ${JSON.stringify(options3)}`, options3.verbose);
57355
+ debugLog("setup", `Setting up reverse proxy: ${JSON.stringify(options3)}`, options3.verbose);
57151
57356
  const { from, to, fromPort, sourceUrl, ssl, verbose } = options3;
57152
57357
  const httpPort = 80;
57153
57358
  const httpsPort = 443;
@@ -57156,72 +57361,72 @@ async function setupReverseProxy(options3) {
57156
57361
  if (ssl) {
57157
57362
  const isHttpPortBusy = await isPortInUse(httpPort, hostname, verbose);
57158
57363
  if (!isHttpPortBusy) {
57159
- debugLog2("setup", "Starting HTTP redirect server", verbose);
57364
+ debugLog("setup", "Starting HTTP redirect server", verbose);
57160
57365
  startHttpRedirectServer(verbose);
57161
57366
  } else {
57162
- debugLog2("setup", "Port 80 is in use, skipping HTTP redirect", verbose);
57367
+ debugLog("setup", "Port 80 is in use, skipping HTTP redirect", verbose);
57163
57368
  log.warn("Port 80 is in use, HTTP to HTTPS redirect will not be available");
57164
57369
  }
57165
57370
  }
57166
57371
  const targetPort = ssl ? httpsPort : httpPort;
57167
57372
  const isTargetPortBusy = await isPortInUse(targetPort, hostname, verbose);
57168
57373
  if (isTargetPortBusy) {
57169
- debugLog2("setup", `Port ${targetPort} is busy, finding alternative`, verbose);
57374
+ debugLog("setup", `Port ${targetPort} is busy, finding alternative`, verbose);
57170
57375
  const availablePort = await findAvailablePort(ssl ? 8443 : 8080, hostname, verbose);
57171
57376
  log.warn(`Port ${targetPort} is in use. Using port ${availablePort} instead.`);
57172
57377
  log.info(`You can use 'sudo lsof -i :${targetPort}' (Unix) or 'netstat -ano | findstr :${targetPort}' (Windows) to check what's using the port.`);
57173
57378
  await createProxyServer(from, to, fromPort, availablePort, hostname, sourceUrl, ssl, verbose);
57174
57379
  } else {
57175
- debugLog2("setup", `Using default port ${targetPort}`, verbose);
57380
+ debugLog("setup", `Using default port ${targetPort}`, verbose);
57176
57381
  await createProxyServer(from, to, fromPort, targetPort, hostname, sourceUrl, ssl, verbose);
57177
57382
  }
57178
57383
  } catch (err3) {
57179
- debugLog2("setup", `Setup failed: ${err3}`, verbose);
57384
+ debugLog("setup", `Setup failed: ${err3}`, verbose);
57180
57385
  log.error(`Failed to setup reverse proxy: ${err3.message}`);
57181
57386
  cleanup();
57182
57387
  }
57183
57388
  }
57184
57389
  function startHttpRedirectServer(verbose) {
57185
- debugLog2("redirect", "Starting HTTP redirect server", verbose);
57390
+ debugLog("redirect", "Starting HTTP redirect server", verbose);
57186
57391
  const server = http.createServer((req, res) => {
57187
57392
  const host = req.headers.host || "";
57188
- debugLog2("redirect", `Redirecting request from ${host}${req.url} to HTTPS`, verbose);
57393
+ debugLog("redirect", `Redirecting request from ${host}${req.url} to HTTPS`, verbose);
57189
57394
  res.writeHead(301, {
57190
57395
  Location: `https://${host}${req.url}`
57191
57396
  });
57192
57397
  res.end();
57193
57398
  }).listen(80);
57194
57399
  activeServers.add(server);
57195
- debugLog2("redirect", "HTTP redirect server started", verbose);
57400
+ debugLog("redirect", "HTTP redirect server started", verbose);
57196
57401
  }
57197
57402
  function startProxy(options3) {
57198
57403
  const finalOptions = {
57199
57404
  ...config,
57200
57405
  ...options3
57201
57406
  };
57202
- debugLog2("proxy", `Starting proxy with options: ${JSON.stringify({
57407
+ debugLog("proxy", `Starting proxy with options: ${JSON.stringify({
57203
57408
  from: finalOptions.from,
57204
57409
  to: finalOptions.to,
57205
57410
  https: finalOptions.https
57206
57411
  })}`, finalOptions.verbose);
57207
57412
  startServer(finalOptions).catch((err3) => {
57208
- debugLog2("proxy", `Failed to start proxy: ${err3}`, finalOptions.verbose);
57413
+ debugLog("proxy", `Failed to start proxy: ${err3}`, finalOptions.verbose);
57209
57414
  log.error(`Failed to start proxy: ${err3.message}`);
57210
57415
  cleanup();
57211
57416
  });
57212
57417
  }
57213
57418
  function startProxies(options3) {
57214
57419
  if (Array.isArray(options3)) {
57215
- debugLog2("proxies", `Starting multiple proxies: ${options3.length}`, options3[0]?.verbose);
57420
+ debugLog("proxies", `Starting multiple proxies: ${options3.length}`, options3[0]?.verbose);
57216
57421
  Promise.all(options3.map((option) => startServer(option))).catch((err3) => {
57217
- debugLog2("proxies", `Failed to start proxies: ${err3}`, options3[0]?.verbose);
57422
+ debugLog("proxies", `Failed to start proxies: ${err3}`, options3[0]?.verbose);
57218
57423
  log.error("Failed to start proxies:", err3);
57219
57424
  cleanup();
57220
57425
  });
57221
57426
  } else if (options3) {
57222
- debugLog2("proxies", "Starting single proxy", options3.verbose);
57427
+ debugLog("proxies", "Starting single proxy", options3.verbose);
57223
57428
  startServer(options3).catch((err3) => {
57224
- debugLog2("proxies", `Failed to start proxy: ${err3}`, options3.verbose);
57429
+ debugLog("proxies", `Failed to start proxy: ${err3}`, options3.verbose);
57225
57430
  log.error("Failed to start proxy:", err3);
57226
57431
  cleanup();
57227
57432
  });
@@ -57233,6 +57438,12 @@ export {
57233
57438
  startProxies,
57234
57439
  startHttpRedirectServer,
57235
57440
  setupReverseProxy,
57441
+ removeHosts,
57442
+ httpsConfig,
57443
+ hostsFilePath,
57444
+ generateCertificate,
57236
57445
  config,
57237
- cleanup
57446
+ cleanup,
57447
+ checkHosts,
57448
+ addHosts
57238
57449
  };