@stacksjs/rpx 0.1.1 → 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,14 +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";
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";
88
89
  import process3 from "process";
89
90
 
90
91
  // node_modules/@stacksjs/cli/dist/index.js
@@ -20395,8 +20396,167 @@ var quotes = collect([
20395
20396
  "Security is mostly a superstition. Life is either a daring adventure or nothing."
20396
20397
  ]);
20397
20398
  var export_prompts = import_prompts.default;
20398
- // package.json
20399
- var version = "0.1.1";
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
+ }
20557
+ // src/https.ts
20558
+ import os7 from "os";
20559
+ import path6 from "path";
20400
20560
 
20401
20561
  // node_modules/@stacksjs/tlsx/dist/index.js
20402
20562
  import fs2 from "fs";
@@ -20435,7 +20595,7 @@ import process8 from "process";
20435
20595
  import process102 from "process";
20436
20596
  import process182 from "process";
20437
20597
  import process112 from "process";
20438
- import os3 from "os";
20598
+ import os5 from "os";
20439
20599
  import tty32 from "tty";
20440
20600
  import process142 from "process";
20441
20601
  import process132 from "process";
@@ -20444,10 +20604,10 @@ import process162 from "process";
20444
20604
  import process172 from "process";
20445
20605
  import process192 from "process";
20446
20606
  import os22 from "os";
20447
- import path2 from "path";
20607
+ import path4 from "path";
20448
20608
  import { resolve as resolve3 } from "path";
20449
20609
  import process22 from "process";
20450
- import fs from "fs";
20610
+ import fs3 from "fs";
20451
20611
  import path22 from "path";
20452
20612
  var __create3 = Object.create;
20453
20613
  var __getProtoOf3 = Object.getPrototypeOf;
@@ -32497,14 +32657,14 @@ var require_tls = __commonJS2((exports, module) => {
32497
32657
  c2.version = c2.session.version = session.version;
32498
32658
  c2.session.sp = session.sp;
32499
32659
  } else {
32500
- var version2;
32660
+ var version;
32501
32661
  for (var i = 1;i < tls.SupportedVersions.length; ++i) {
32502
- version2 = tls.SupportedVersions[i];
32503
- if (version2.minor <= msg.version.minor) {
32662
+ version = tls.SupportedVersions[i];
32663
+ if (version.minor <= msg.version.minor) {
32504
32664
  break;
32505
32665
  }
32506
32666
  }
32507
- c2.version = { major: version2.major, minor: version2.minor };
32667
+ c2.version = { major: version.major, minor: version.minor };
32508
32668
  c2.session.version = c2.version;
32509
32669
  }
32510
32670
  if (session !== null) {
@@ -32675,8 +32835,8 @@ var require_tls = __commonJS2((exports, module) => {
32675
32835
  try {
32676
32836
  var sp = c2.session.sp;
32677
32837
  sp.pre_master_secret = privateKey.decrypt(msg.enc_pre_master_secret);
32678
- var version2 = c2.session.clientHelloVersion;
32679
- 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)) {
32680
32840
  throw new Error("TLS version rollback attack detected.");
32681
32841
  }
32682
32842
  } catch (ex) {
@@ -38619,16 +38779,16 @@ var require_isIP2 = __commonJS22((exports, module) => {
38619
38779
  var IPv6SegmentFormat = "(?:[0-9a-fA-F]{1,4})";
38620
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,})?$");
38621
38781
  function isIP(str) {
38622
- var version2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
38782
+ var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
38623
38783
  (0, _assertString.default)(str);
38624
- version2 = String(version2);
38625
- if (!version2) {
38784
+ version = String(version);
38785
+ if (!version) {
38626
38786
  return isIP(str, 4) || isIP(str, 6);
38627
38787
  }
38628
- if (version2 === "4") {
38788
+ if (version === "4") {
38629
38789
  return IPv4AddressRegExp.test(str);
38630
38790
  }
38631
- if (version2 === "6") {
38791
+ if (version === "6") {
38632
38792
  return IPv6AddressRegExp.test(str);
38633
38793
  }
38634
38794
  return false;
@@ -39150,9 +39310,9 @@ var require_isUUID2 = __commonJS22((exports, module) => {
39150
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,
39151
39311
  all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i
39152
39312
  };
39153
- function isUUID(str, version2) {
39313
+ function isUUID(str, version) {
39154
39314
  (0, _assertString.default)(str);
39155
- var pattern = uuid[![undefined, null].includes(version2) ? version2 : "all"];
39315
+ var pattern = uuid[![undefined, null].includes(version) ? version : "all"];
39156
39316
  return !!pattern && pattern.test(str);
39157
39317
  }
39158
39318
  module.exports = exports.default;
@@ -51329,7 +51489,7 @@ var uuidRule2 = createRule2((value, options2, field) => {
51329
51489
  field.report(messages2.uuid, "uuid", field);
51330
51490
  }
51331
51491
  } else {
51332
- const matchesAnyVersion = options2.version.find((version2) => helpers32.isUUID(value, version2));
51492
+ const matchesAnyVersion = options2.version.find((version) => helpers32.isUUID(value, version));
51333
51493
  if (!matchesAnyVersion) {
51334
51494
  field.report(messages2.uuid, "uuid", field, options2);
51335
51495
  }
@@ -51424,8 +51584,8 @@ var VineString2 = class _VineString2 extends BaseLiteralType2 {
51424
51584
  mobile(...args) {
51425
51585
  return this.use(mobileRule2(...args));
51426
51586
  }
51427
- ipAddress(version2) {
51428
- return this.use(ipAddressRule2(version2 ? { version: version2 } : undefined));
51587
+ ipAddress(version) {
51588
+ return this.use(ipAddressRule2(version ? { version } : undefined));
51429
51589
  }
51430
51590
  hexCode() {
51431
51591
  return this.use(hexCodeRule2());
@@ -53938,7 +54098,7 @@ function _supportsColor2(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
53938
54098
  return min;
53939
54099
  }
53940
54100
  if (process112.platform === "win32") {
53941
- const osRelease = os3.release().split(".");
54101
+ const osRelease = os5.release().split(".");
53942
54102
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
53943
54103
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
53944
54104
  }
@@ -56505,18 +56665,18 @@ var config42 = await loadConfig2({
56505
56665
  defaultConfig: {
56506
56666
  altNameIPs: ["127.0.0.1"],
56507
56667
  altNameURIs: ["localhost"],
56508
- organizationName: "stacksjs.org",
56668
+ organizationName: "Local Development",
56509
56669
  countryName: "US",
56510
56670
  stateName: "California",
56511
56671
  localityName: "Playa Vista",
56512
56672
  commonName: "stacks.localhost",
56513
56673
  validityDays: 180,
56514
56674
  hostCertCN: "stacks.localhost",
56515
- domain: "localhost",
56675
+ domain: "stacks.localhost",
56516
56676
  rootCAObject: { certificate: "", privateKey: "" },
56517
- caCertPath: path2.join(os22.homedir(), ".stacks", "ssl", `tlsx.localhost.ca.crt`),
56518
- certPath: path2.join(os22.homedir(), ".stacks", "ssl", `tlsx.localhost.crt`),
56519
- 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`),
56520
56680
  verbose: false
56521
56681
  }
56522
56682
  });
@@ -56532,10 +56692,10 @@ function findFoldersWithFile(rootDir, fileName) {
56532
56692
  const result = [];
56533
56693
  function search(dir) {
56534
56694
  try {
56535
- const files = fs.readdirSync(dir);
56695
+ const files = fs3.readdirSync(dir);
56536
56696
  for (const file of files) {
56537
56697
  const filePath = path22.join(dir, file);
56538
- const stats = fs.lstatSync(filePath);
56698
+ const stats = fs3.lstatSync(filePath);
56539
56699
  if (stats.isDirectory()) {
56540
56700
  search(filePath);
56541
56701
  } else if (file === fileName) {
@@ -56549,29 +56709,29 @@ function findFoldersWithFile(rootDir, fileName) {
56549
56709
  search(rootDir);
56550
56710
  return result;
56551
56711
  }
56552
- function debugLog(category, message, verbose) {
56712
+ function debugLog2(category, message, verbose) {
56553
56713
  if (verbose || config42.verbose) {
56554
56714
  console.debug(`[tlsx:${category}] ${message}`);
56555
56715
  }
56556
56716
  }
56557
56717
  function randomSerialNumber(verbose) {
56558
- debugLog("cert", "Generating random serial number", verbose);
56718
+ debugLog2("cert", "Generating random serial number", verbose);
56559
56719
  const serialNumber = makeNumberPositive(import_node_forge2.default.util.bytesToHex(import_node_forge2.default.random.getBytesSync(20)));
56560
- debugLog("cert", `Generated serial number: ${serialNumber}`, verbose);
56720
+ debugLog2("cert", `Generated serial number: ${serialNumber}`, verbose);
56561
56721
  return serialNumber;
56562
56722
  }
56563
56723
  function getCertNotBefore(verbose) {
56564
- debugLog("cert", "Calculating certificate not-before date", verbose);
56724
+ debugLog2("cert", "Calculating certificate not-before date", verbose);
56565
56725
  const twoDaysAgo = new Date(Date.now() - 172800000);
56566
56726
  const year = twoDaysAgo.getFullYear();
56567
56727
  const month = (twoDaysAgo.getMonth() + 1).toString().padStart(2, "0");
56568
56728
  const day = twoDaysAgo.getDate().toString().padStart(2, "0");
56569
56729
  const date = new Date(`${year}-${month}-${day}T23:59:59Z`);
56570
- debugLog("cert", `Certificate not-before date: ${date.toISOString()}`, verbose);
56730
+ debugLog2("cert", `Certificate not-before date: ${date.toISOString()}`, verbose);
56571
56731
  return date;
56572
56732
  }
56573
56733
  function getCertNotAfter(notBefore, verbose) {
56574
- debugLog("cert", "Calculating certificate not-after date", verbose);
56734
+ debugLog2("cert", "Calculating certificate not-after date", verbose);
56575
56735
  const validityDays = config42.validityDays;
56576
56736
  const daysInMillis = validityDays * 60 * 60 * 24 * 1000;
56577
56737
  const notAfterDate = new Date(notBefore.getTime() + daysInMillis);
@@ -56579,27 +56739,27 @@ function getCertNotAfter(notBefore, verbose) {
56579
56739
  const month = (notAfterDate.getMonth() + 1).toString().padStart(2, "0");
56580
56740
  const day = notAfterDate.getDate().toString().padStart(2, "0");
56581
56741
  const date = new Date(`${year}-${month}-${day}T23:59:59Z`);
56582
- 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);
56583
56743
  return date;
56584
56744
  }
56585
56745
  function getCANotAfter(notBefore, verbose) {
56586
- debugLog("cert", "Calculating CA not-after date", verbose);
56746
+ debugLog2("cert", "Calculating CA not-after date", verbose);
56587
56747
  const year = notBefore.getFullYear() + 100;
56588
56748
  const month = (notBefore.getMonth() + 1).toString().padStart(2, "0");
56589
56749
  const day = notBefore.getDate().toString().padStart(2, "0");
56590
56750
  const date = new Date(`${year}-${month}-${day}T23:59:59Z`);
56591
- 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);
56592
56752
  return date;
56593
56753
  }
56594
56754
  async function createRootCA(options22) {
56595
- debugLog("ca", "Creating new Root CA Certificate", options22?.verbose);
56596
- 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);
56597
56757
  const { privateKey, publicKey } = import_node_forge2.pki.rsa.generateKeyPair(2048);
56598
56758
  const mergedOptions = {
56599
56759
  ...config42,
56600
56760
  ...options22 || {}
56601
56761
  };
56602
- debugLog("ca", "Setting certificate attributes", options22?.verbose);
56762
+ debugLog2("ca", "Setting certificate attributes", options22?.verbose);
56603
56763
  const attributes = [
56604
56764
  { shortName: "C", value: mergedOptions.countryName },
56605
56765
  { shortName: "ST", value: mergedOptions.stateName },
@@ -56607,7 +56767,7 @@ async function createRootCA(options22) {
56607
56767
  { shortName: "O", value: "Local Development CA" },
56608
56768
  { shortName: "CN", value: "Local Development Root CA" }
56609
56769
  ];
56610
- debugLog("ca", "Setting certificate extensions", options22?.verbose);
56770
+ debugLog2("ca", "Setting certificate extensions", options22?.verbose);
56611
56771
  const extensions = [
56612
56772
  {
56613
56773
  name: "basicConstraints",
@@ -56624,7 +56784,7 @@ async function createRootCA(options22) {
56624
56784
  name: "subjectKeyIdentifier"
56625
56785
  }
56626
56786
  ];
56627
- debugLog("ca", "Creating CA certificate", options22?.verbose);
56787
+ debugLog2("ca", "Creating CA certificate", options22?.verbose);
56628
56788
  const caCert = import_node_forge2.pki.createCertificate();
56629
56789
  caCert.publicKey = publicKey;
56630
56790
  caCert.serialNumber = randomSerialNumber(options22?.verbose);
@@ -56633,11 +56793,11 @@ async function createRootCA(options22) {
56633
56793
  caCert.setSubject(attributes);
56634
56794
  caCert.setIssuer(attributes);
56635
56795
  caCert.setExtensions(extensions);
56636
- debugLog("ca", "Signing certificate with SHA-256", options22?.verbose);
56796
+ debugLog2("ca", "Signing certificate with SHA-256", options22?.verbose);
56637
56797
  caCert.sign(privateKey, import_node_forge2.default.md.sha256.create());
56638
56798
  const pemCert = import_node_forge2.pki.certificateToPem(caCert);
56639
56799
  const pemKey = import_node_forge2.pki.privateKeyToPem(privateKey);
56640
- debugLog("ca", "Root CA Certificate created successfully", options22?.verbose);
56800
+ debugLog2("ca", "Root CA Certificate created successfully", options22?.verbose);
56641
56801
  return {
56642
56802
  certificate: pemCert,
56643
56803
  privateKey: pemKey,
@@ -56646,34 +56806,41 @@ async function createRootCA(options22) {
56646
56806
  };
56647
56807
  }
56648
56808
  async function generateCert(options22) {
56649
- debugLog("cert", "Generating new host certificate", options22?.verbose);
56650
- 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);
56651
56811
  if (!options22?.hostCertCN?.trim()) {
56652
- debugLog("cert", "Error: hostCertCN is required", options22?.verbose);
56812
+ debugLog2("cert", "Error: hostCertCN is required", options22?.verbose);
56653
56813
  throw new Error('"hostCertCN" must be a String');
56654
56814
  }
56655
56815
  if (!options22.domain?.trim()) {
56656
- debugLog("cert", "Error: domain is required", options22?.verbose);
56816
+ debugLog2("cert", "Error: domain is required", options22?.verbose);
56657
56817
  throw new Error('"domain" must be a String');
56658
56818
  }
56659
56819
  if (!options22.rootCAObject || !options22.rootCAObject.certificate || !options22.rootCAObject.privateKey) {
56660
- debugLog("cert", "Error: rootCAObject is invalid or missing", options22?.verbose);
56820
+ debugLog2("cert", "Error: rootCAObject is invalid or missing", options22?.verbose);
56661
56821
  throw new Error('"rootCAObject" must be an Object with the properties "certificate" & "privateKey"');
56662
56822
  }
56663
- debugLog("cert", "Converting Root CA PEM to forge objects", options22?.verbose);
56823
+ debugLog2("cert", "Converting Root CA PEM to forge objects", options22?.verbose);
56664
56824
  const caCert = import_node_forge2.pki.certificateFromPem(options22.rootCAObject.certificate);
56665
56825
  const caKey = import_node_forge2.pki.privateKeyFromPem(options22.rootCAObject.privateKey);
56666
- 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);
56667
56827
  const hostKeys = import_node_forge2.pki.rsa.generateKeyPair(2048);
56668
- debugLog("cert", "Setting certificate attributes", options22?.verbose);
56828
+ debugLog2("cert", "Setting certificate attributes", options22?.verbose);
56669
56829
  const attributes = [
56670
- { shortName: "C", value: config42.countryName },
56671
- { shortName: "ST", value: config42.stateName },
56672
- { shortName: "L", value: config42.localityName },
56673
- { shortName: "O", value: "Local Development" },
56674
- { 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 }
56675
56842
  ];
56676
- debugLog("cert", "Setting certificate extensions", options22?.verbose);
56843
+ debugLog2("cert", "Setting certificate extensions", options22?.verbose);
56677
56844
  const extensions = [
56678
56845
  {
56679
56846
  name: "basicConstraints",
@@ -56693,30 +56860,25 @@ async function generateCert(options22) {
56693
56860
  },
56694
56861
  {
56695
56862
  name: "subjectAltName",
56696
- altNames: [
56697
- { type: 2, value: "*.localhost" },
56698
- { type: 2, value: "localhost" },
56699
- { type: 2, value: "stacks.localhost" },
56700
- { type: 2, value: options22.domain }
56701
- ]
56863
+ altNames
56702
56864
  }
56703
56865
  ];
56704
- debugLog("cert", "Creating new host certificate", options22?.verbose);
56866
+ debugLog2("cert", "Creating new host certificate", options22?.verbose);
56705
56867
  const newHostCert = import_node_forge2.pki.createCertificate();
56706
56868
  newHostCert.publicKey = hostKeys.publicKey;
56707
- debugLog("cert", "Setting certificate properties", options22?.verbose);
56869
+ debugLog2("cert", "Setting certificate properties", options22?.verbose);
56708
56870
  newHostCert.serialNumber = randomSerialNumber(options22?.verbose);
56709
56871
  newHostCert.validity.notBefore = getCertNotBefore(options22?.verbose);
56710
56872
  newHostCert.validity.notAfter = getCertNotAfter(newHostCert.validity.notBefore, options22?.verbose);
56711
56873
  newHostCert.setSubject(attributes);
56712
56874
  newHostCert.setIssuer(caCert.subject.attributes);
56713
56875
  newHostCert.setExtensions(extensions);
56714
- debugLog("cert", "Signing certificate with SHA-256", options22?.verbose);
56876
+ debugLog2("cert", "Signing certificate with SHA-256", options22?.verbose);
56715
56877
  newHostCert.sign(caKey, import_node_forge2.default.md.sha256.create());
56716
- debugLog("cert", "Converting certificate to PEM format", options22?.verbose);
56878
+ debugLog2("cert", "Converting certificate to PEM format", options22?.verbose);
56717
56879
  const pemHostCert = import_node_forge2.pki.certificateToPem(newHostCert);
56718
56880
  const pemHostKey = import_node_forge2.pki.privateKeyToPem(hostKeys.privateKey);
56719
- debugLog("cert", "Host certificate generated successfully", options22?.verbose);
56881
+ debugLog2("cert", "Host certificate generated successfully", options22?.verbose);
56720
56882
  return {
56721
56883
  certificate: pemHostCert,
56722
56884
  privateKey: pemHostKey,
@@ -56725,81 +56887,81 @@ async function generateCert(options22) {
56725
56887
  };
56726
56888
  }
56727
56889
  async function addCertToSystemTrustStoreAndSaveCert(cert, caCert, options22) {
56728
- debugLog("trust", `Adding certificate to system trust store with options: ${JSON.stringify(options22)}`, options22?.verbose);
56729
- 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);
56730
56892
  const certPath = storeCert(cert, options22);
56731
- debugLog("trust", "Storing CA certificate", options22?.verbose);
56893
+ debugLog2("trust", "Storing CA certificate", options22?.verbose);
56732
56894
  const caCertPath = storeCACert(caCert, options22);
56733
56895
  const platform22 = os4.platform();
56734
- debugLog("trust", `Detected platform: ${platform22}`, options22?.verbose);
56896
+ debugLog2("trust", `Detected platform: ${platform22}`, options22?.verbose);
56735
56897
  const args = "TC, C, C";
56736
56898
  if (platform22 === "darwin") {
56737
- debugLog("trust", "Adding certificate to macOS keychain", options22?.verbose);
56899
+ debugLog2("trust", "Adding certificate to macOS keychain", options22?.verbose);
56738
56900
  await runCommand(`sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${caCertPath}`);
56739
56901
  } else if (platform22 === "win32") {
56740
- debugLog("trust", "Adding certificate to Windows certificate store", options22?.verbose);
56902
+ debugLog2("trust", "Adding certificate to Windows certificate store", options22?.verbose);
56741
56903
  await runCommand(`certutil -f -v -addstore -enterprise Root ${caCertPath}`);
56742
56904
  } else if (platform22 === "linux") {
56743
- debugLog("trust", "Adding certificate to Linux certificate store", options22?.verbose);
56905
+ debugLog2("trust", "Adding certificate to Linux certificate store", options22?.verbose);
56744
56906
  const rootDirectory = os4.homedir();
56745
56907
  const targetFileName = "cert9.db";
56746
- debugLog("trust", `Searching for certificate databases in ${rootDirectory}`, options22?.verbose);
56908
+ debugLog2("trust", `Searching for certificate databases in ${rootDirectory}`, options22?.verbose);
56747
56909
  const foldersWithFile = findFoldersWithFile(rootDirectory, targetFileName);
56748
56910
  for (const folder of foldersWithFile) {
56749
- debugLog("trust", `Processing certificate database in ${folder}`, options22?.verbose);
56911
+ debugLog2("trust", `Processing certificate database in ${folder}`, options22?.verbose);
56750
56912
  try {
56751
- 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);
56752
56914
  await runCommand(`certutil -d sql:${folder} -D -n ${config42.commonName}`);
56753
56915
  } catch (error) {
56754
- debugLog("trust", `Warning: Error deleting existing cert: ${error}`, options22?.verbose);
56916
+ debugLog2("trust", `Warning: Error deleting existing cert: ${error}`, options22?.verbose);
56755
56917
  console.warn(`Error deleting existing cert: ${error}`);
56756
56918
  }
56757
- debugLog("trust", `Adding new certificate to ${folder}`, options22?.verbose);
56919
+ debugLog2("trust", `Adding new certificate to ${folder}`, options22?.verbose);
56758
56920
  await runCommand(`certutil -d sql:${folder} -A -t ${args} -n ${config42.commonName} -i ${caCertPath}`);
56759
56921
  log2.info(`Cert added to ${folder}`);
56760
56922
  }
56761
56923
  } else {
56762
- debugLog("trust", `Error: Unsupported platform ${platform22}`, options22?.verbose);
56924
+ debugLog2("trust", `Error: Unsupported platform ${platform22}`, options22?.verbose);
56763
56925
  throw new Error(`Unsupported platform: ${platform22}`);
56764
56926
  }
56765
- debugLog("trust", "Certificate successfully added to system trust store", options22?.verbose);
56927
+ debugLog2("trust", "Certificate successfully added to system trust store", options22?.verbose);
56766
56928
  return certPath;
56767
56929
  }
56768
56930
  function storeCert(cert, options22) {
56769
- 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);
56770
56932
  const certPath = options22?.certPath || config42.certPath;
56771
56933
  const certKeyPath = options22?.keyPath || config42.keyPath;
56772
- debugLog("storage", `Certificate path: ${certPath}`, options22?.verbose);
56773
- 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);
56774
56936
  const certDir = path3.dirname(certPath);
56775
56937
  if (!fs2.existsSync(certDir)) {
56776
- debugLog("storage", `Creating certificate directory: ${certDir}`, options22?.verbose);
56938
+ debugLog2("storage", `Creating certificate directory: ${certDir}`, options22?.verbose);
56777
56939
  fs2.mkdirSync(certDir, { recursive: true });
56778
56940
  }
56779
- debugLog("storage", "Writing certificate file", options22?.verbose);
56941
+ debugLog2("storage", "Writing certificate file", options22?.verbose);
56780
56942
  fs2.writeFileSync(certPath, cert.certificate);
56781
56943
  const certKeyDir = path3.dirname(certKeyPath);
56782
56944
  if (!fs2.existsSync(certKeyDir)) {
56783
- debugLog("storage", `Creating private key directory: ${certKeyDir}`, options22?.verbose);
56945
+ debugLog2("storage", `Creating private key directory: ${certKeyDir}`, options22?.verbose);
56784
56946
  fs2.mkdirSync(certKeyDir, { recursive: true });
56785
56947
  }
56786
- debugLog("storage", "Writing private key file", options22?.verbose);
56948
+ debugLog2("storage", "Writing private key file", options22?.verbose);
56787
56949
  fs2.writeFileSync(certKeyPath, cert.privateKey);
56788
- debugLog("storage", "Certificate and private key stored successfully", options22?.verbose);
56950
+ debugLog2("storage", "Certificate and private key stored successfully", options22?.verbose);
56789
56951
  return certPath;
56790
56952
  }
56791
56953
  function storeCACert(caCert, options22) {
56792
- debugLog("storage", "Storing CA certificate", options22?.verbose);
56954
+ debugLog2("storage", "Storing CA certificate", options22?.verbose);
56793
56955
  const caCertPath = options22?.caCertPath || config42.caCertPath;
56794
- debugLog("storage", `CA certificate path: ${caCertPath}`, options22?.verbose);
56956
+ debugLog2("storage", `CA certificate path: ${caCertPath}`, options22?.verbose);
56795
56957
  const caCertDir = path3.dirname(caCertPath);
56796
56958
  if (!fs2.existsSync(caCertDir)) {
56797
- debugLog("storage", `Creating CA certificate directory: ${caCertDir}`, options22?.verbose);
56959
+ debugLog2("storage", `Creating CA certificate directory: ${caCertDir}`, options22?.verbose);
56798
56960
  fs2.mkdirSync(caCertDir, { recursive: true });
56799
56961
  }
56800
- debugLog("storage", "Writing CA certificate file", options22?.verbose);
56962
+ debugLog2("storage", "Writing CA certificate file", options22?.verbose);
56801
56963
  fs2.writeFileSync(caCertPath, caCert);
56802
- debugLog("storage", "CA certificate stored successfully", options22?.verbose);
56964
+ debugLog2("storage", "CA certificate stored successfully", options22?.verbose);
56803
56965
  return caCertPath;
56804
56966
  }
56805
56967
  var export_tls = import_node_forge2.tls;
@@ -56807,8 +56969,37 @@ var export_pki = import_node_forge2.pki;
56807
56969
  var export_forge = import_node_forge2.default;
56808
56970
 
56809
56971
  // src/https.ts
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
+ }
56810
56997
  async function generateCertificate(domain) {
56811
- domain = domain ?? config.https.altNameURIs[0];
56998
+ if (config.https === true)
56999
+ config.https = httpsConfig();
57000
+ else if (config.https === false)
57001
+ return;
57002
+ domain = domain ?? config.https.domain;
56812
57003
  log.info(`Generating a self-signed SSL certificate for: ${domain}`);
56813
57004
  const caCert = await createRootCA(config.https);
56814
57005
  const hostCert = await generateCert({
@@ -56830,71 +57021,100 @@ async function generateCertificate(domain) {
56830
57021
  await addCertToSystemTrustStoreAndSaveCert(hostCert, caCert.certificate, config.https);
56831
57022
  log.success("Certificate generated");
56832
57023
  }
56833
-
56834
- // src/utils.ts
56835
- function debugLog2(category, message, verbose) {
56836
- if (verbose || config.verbose) {
56837
- console.debug(`[rpx:${category}] ${message}`);
56838
- }
56839
- }
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";
56840
57034
 
56841
57035
  // src/start.ts
56842
57036
  var activeServers = new Set;
56843
- function cleanup() {
56844
- debugLog2("cleanup", "Starting cleanup process", config.verbose);
57037
+ async function cleanup() {
57038
+ debugLog("cleanup", "Starting cleanup process", config.verbose);
56845
57039
  console.log(`\n`);
56846
57040
  log.info("Shutting down proxy servers...");
56847
- const closePromises = Array.from(activeServers).map((server) => new Promise((resolve4) => {
57041
+ const cleanupPromises = [];
57042
+ const serverClosePromises = Array.from(activeServers).map((server) => new Promise((resolve4) => {
56848
57043
  server.close(() => {
56849
- debugLog2("cleanup", "Server closed successfully", config.verbose);
57044
+ debugLog("cleanup", "Server closed successfully", config.verbose);
56850
57045
  resolve4();
56851
57046
  });
56852
57047
  }));
56853
- Promise.all(closePromises).then(() => {
56854
- debugLog2("cleanup", "All servers closed successfully", config.verbose);
56855
- log.success("All proxy servers shut down successfully");
56856
- process3.exit(0);
56857
- }).catch((err3) => {
56858
- debugLog2("cleanup", `Error during shutdown: ${err3}`, config.verbose);
56859
- log.error("Error during shutdown:", err3);
56860
- process3.exit(1);
56861
- });
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
+ }
56862
57078
  }
56863
- process3.on("SIGINT", cleanup);
56864
- process3.on("SIGTERM", cleanup);
56865
- process3.on("uncaughtException", (err3) => {
56866
- 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);
56867
57083
  log.error("Uncaught exception:", err3);
56868
57084
  cleanup();
56869
57085
  });
56870
57086
  async function loadSSLConfig(options3) {
56871
- debugLog2("ssl", "Loading SSL configuration", options3.verbose);
57087
+ debugLog("ssl", "Loading SSL configuration", options3.verbose);
57088
+ if (options3.https === true)
57089
+ options3.https = httpsConfig();
57090
+ else if (options3.https === false)
57091
+ return null;
56872
57092
  if (!options3.https?.keyPath && !options3.https?.certPath) {
56873
- debugLog2("ssl", "No SSL configuration provided", options3.verbose);
57093
+ debugLog("ssl", "No SSL configuration provided", options3.verbose);
56874
57094
  return null;
56875
57095
  }
56876
57096
  if (options3.https?.keyPath && !options3.https?.certPath || !options3.https?.keyPath && options3.https?.certPath) {
56877
57097
  const missing = !options3.https?.keyPath ? "keyPath" : "certPath";
56878
- debugLog2("ssl", `Invalid SSL configuration - missing ${missing}`, options3.verbose);
57098
+ debugLog("ssl", `Invalid SSL configuration - missing ${missing}`, options3.verbose);
56879
57099
  throw new Error(`SSL Configuration requires both keyPath and certPath. Missing: ${missing}`);
56880
57100
  }
56881
57101
  try {
56882
57102
  if (!options3.https?.keyPath || !options3.https?.certPath)
56883
57103
  return null;
56884
57104
  try {
56885
- debugLog2("ssl", "Reading SSL certificate files", options3.verbose);
56886
- const key = await fs3.promises.readFile(options3.https?.keyPath, "utf8");
56887
- const cert = await fs3.promises.readFile(options3.https?.certPath, "utf8");
56888
- 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);
56889
57109
  return { key, cert };
56890
57110
  } catch (error) {
56891
57111
  if (error.code === "ENOENT") {
56892
- debugLog2("ssl", "Certificates not found, generating new ones", options3.verbose);
56893
- await generateCertificate();
56894
- debugLog2("ssl", "Reading newly generated certificates", options3.verbose);
56895
- const key = await fs3.promises.readFile(options3.https?.keyPath, "utf8");
56896
- const cert = await fs3.promises.readFile(options3.https?.certPath, "utf8");
56897
- 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);
56898
57118
  return { key, cert };
56899
57119
  }
56900
57120
  throw error;
@@ -56902,22 +57122,22 @@ async function loadSSLConfig(options3) {
56902
57122
  } catch (err3) {
56903
57123
  const error = err3;
56904
57124
  const detail = error.code === "ENOENT" ? `File not found: ${error.path}` : error.message;
56905
- debugLog2("ssl", `SSL configuration error: ${error}`, options3.verbose);
57125
+ debugLog("ssl", `SSL configuration error: ${error}`, options3.verbose);
56906
57126
  throw new Error(`SSL Configuration Error: ${detail}`);
56907
57127
  }
56908
57128
  }
56909
57129
  function isPortInUse(port, hostname, verbose) {
56910
- 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);
56911
57131
  return new Promise((resolve4) => {
56912
57132
  const server = net.createServer();
56913
57133
  server.once("error", (err3) => {
56914
57134
  if (err3.code === "EADDRINUSE") {
56915
- debugLog2("port", `Port ${port} is in use`, verbose);
57135
+ debugLog("port", `Port ${port} is in use`, verbose);
56916
57136
  resolve4(true);
56917
57137
  }
56918
57138
  });
56919
57139
  server.once("listening", () => {
56920
- debugLog2("port", `Port ${port} is available`, verbose);
57140
+ debugLog("port", `Port ${port} is available`, verbose);
56921
57141
  server.close();
56922
57142
  resolve4(false);
56923
57143
  });
@@ -56925,17 +57145,17 @@ function isPortInUse(port, hostname, verbose) {
56925
57145
  });
56926
57146
  }
56927
57147
  async function findAvailablePort(startPort, hostname, verbose) {
56928
- debugLog2("port", `Finding available port starting from ${startPort}`, verbose);
57148
+ debugLog("port", `Finding available port starting from ${startPort}`, verbose);
56929
57149
  let port = startPort;
56930
57150
  while (await isPortInUse(port, hostname, verbose)) {
56931
- debugLog2("port", `Port ${port} is in use, trying ${port + 1}`, verbose);
57151
+ debugLog("port", `Port ${port} is in use, trying ${port + 1}`, verbose);
56932
57152
  port++;
56933
57153
  }
56934
- debugLog2("port", `Found available port: ${port}`, verbose);
57154
+ debugLog("port", `Found available port: ${port}`, verbose);
56935
57155
  return port;
56936
57156
  }
56937
57157
  async function testConnection(hostname, port, verbose) {
56938
- debugLog2("connection", `Testing connection to ${hostname}:${port}`, verbose);
57158
+ debugLog("connection", `Testing connection to ${hostname}:${port}`, verbose);
56939
57159
  return new Promise((resolve4, reject) => {
56940
57160
  const socket = net.connect({
56941
57161
  host: hostname,
@@ -56943,54 +57163,89 @@ async function testConnection(hostname, port, verbose) {
56943
57163
  timeout: 5000
56944
57164
  });
56945
57165
  socket.once("connect", () => {
56946
- debugLog2("connection", `Successfully connected to ${hostname}:${port}`, verbose);
57166
+ debugLog("connection", `Successfully connected to ${hostname}:${port}`, verbose);
56947
57167
  socket.end();
56948
57168
  resolve4();
56949
57169
  });
56950
57170
  socket.once("timeout", () => {
56951
- debugLog2("connection", `Connection to ${hostname}:${port} timed out`, verbose);
57171
+ debugLog("connection", `Connection to ${hostname}:${port} timed out`, verbose);
56952
57172
  socket.destroy();
56953
57173
  reject(new Error(`Connection to ${hostname}:${port} timed out`));
56954
57174
  });
56955
57175
  socket.once("error", (err3) => {
56956
- debugLog2("connection", `Failed to connect to ${hostname}:${port}: ${err3}`, verbose);
57176
+ debugLog("connection", `Failed to connect to ${hostname}:${port}: ${err3}`, verbose);
56957
57177
  socket.destroy();
56958
57178
  reject(new Error(`Failed to connect to ${hostname}:${port}: ${err3.message}`));
56959
57179
  });
56960
57180
  });
56961
57181
  }
56962
57182
  async function startServer(options3) {
56963
- debugLog2("server", `Starting server with options: ${JSON.stringify(options3)}`, options3?.verbose);
57183
+ debugLog("server", `Starting server with options: ${JSON.stringify(options3)}`, options3?.verbose);
56964
57184
  if (!options3)
56965
57185
  options3 = config;
56966
57186
  if (!options3.from)
56967
57187
  options3.from = config.from;
56968
57188
  if (!options3.to)
56969
57189
  options3.to = config.to;
56970
- if (config.https) {
56971
- const domain = config.https.altNameURIs?.[0] || new URL(options3.to).hostname;
56972
- options3.keyPath = config.https.keyPath || `/Users/${process3.env.USER}/.stacks/ssl/${domain}.crt.key`;
56973
- options3.certPath = config.https.certPath || `/Users/${process3.env.USER}/.stacks/ssl/${domain}.crt`;
56974
- debugLog2("server", `HTTPS enabled, using cert paths: ${options3.keyPath}, ${options3.certPath}`, options3.verbose);
56975
- }
56976
57190
  const fromUrl = new URL(options3.from.startsWith("http") ? options3.from : `http://${options3.from}`);
56977
57191
  const toUrl = new URL(options3.to.startsWith("http") ? options3.to : `http://${options3.to}`);
56978
57192
  const fromPort = Number.parseInt(fromUrl.port) || (fromUrl.protocol.includes("https:") ? 443 : 80);
56979
- debugLog2("server", `Parsed URLs - from: ${fromUrl}, to: ${toUrl}`, options3.verbose);
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
+ }
57224
+ if (config.https) {
57225
+ if (config.https === true)
57226
+ config.https = httpsConfig();
57227
+ const domain = toUrl.hostname;
57228
+ if (typeof options3.https !== "boolean" && options3.https) {
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);
57232
+ }
57233
+ }
57234
+ debugLog("server", `Parsed URLs - from: ${fromUrl}, to: ${toUrl}`, options3.verbose);
56980
57235
  try {
56981
57236
  await testConnection(fromUrl.hostname, fromPort, options3.verbose);
56982
57237
  } catch (err3) {
56983
- debugLog2("server", `Connection test failed: ${err3}`, options3.verbose);
57238
+ debugLog("server", `Connection test failed: ${err3}`, options3.verbose);
56984
57239
  log.error(err3.message);
56985
- process3.exit(1);
57240
+ process9.exit(1);
56986
57241
  }
56987
57242
  let sslConfig = null;
56988
57243
  if (config.https) {
56989
57244
  try {
56990
57245
  sslConfig = await loadSSLConfig(options3);
56991
57246
  } catch (err3) {
56992
- debugLog2("server", `SSL config failed, attempting to generate certificates: ${err3}`, options3.verbose);
56993
- await generateCertificate();
57247
+ debugLog("server", `SSL config failed, attempting to generate certificates: ${err3}`, options3.verbose);
57248
+ await generateCertificate(options3.to);
56994
57249
  sslConfig = await loadSSLConfig(options3);
56995
57250
  }
56996
57251
  }
@@ -57007,9 +57262,9 @@ async function startServer(options3) {
57007
57262
  });
57008
57263
  }
57009
57264
  async function createProxyServer(from, to, fromPort, listenPort, hostname, sourceUrl, ssl, verbose) {
57010
- debugLog2("proxy", `Creating proxy server ${from} -> ${to}`, verbose);
57265
+ debugLog("proxy", `Creating proxy server ${from} -> ${to}`, verbose);
57011
57266
  const requestHandler = (req, res) => {
57012
- debugLog2("request", `Incoming request: ${req.method} ${req.url}`, verbose);
57267
+ debugLog("request", `Incoming request: ${req.method} ${req.url}`, verbose);
57013
57268
  const proxyOptions = {
57014
57269
  hostname: sourceUrl.hostname,
57015
57270
  port: fromPort,
@@ -57020,9 +57275,9 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57020
57275
  host: sourceUrl.host
57021
57276
  }
57022
57277
  };
57023
- debugLog2("request", `Proxy request options: ${JSON.stringify(proxyOptions)}`, verbose);
57278
+ debugLog("request", `Proxy request options: ${JSON.stringify(proxyOptions)}`, verbose);
57024
57279
  const proxyReq = http.request(proxyOptions, (proxyRes) => {
57025
- debugLog2("response", `Proxy response received with status ${proxyRes.statusCode}`, verbose);
57280
+ debugLog("response", `Proxy response received with status ${proxyRes.statusCode}`, verbose);
57026
57281
  const headers = {
57027
57282
  ...proxyRes.headers,
57028
57283
  "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
@@ -57032,7 +57287,7 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57032
57287
  proxyRes.pipe(res);
57033
57288
  });
57034
57289
  proxyReq.on("error", (err3) => {
57035
- debugLog2("request", `Proxy request failed: ${err3}`, verbose);
57290
+ debugLog("request", `Proxy request failed: ${err3}`, verbose);
57036
57291
  log.error("Proxy request failed:", err3);
57037
57292
  res.writeHead(502);
57038
57293
  res.end(`Proxy Error: ${err3.message}`);
@@ -57059,11 +57314,11 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57059
57314
  allowHTTP1: true,
57060
57315
  ALPNProtocols: ["h2", "http/1.1"]
57061
57316
  } : undefined;
57062
- debugLog2("server", `Creating server with SSL config: ${!!ssl}`, verbose);
57317
+ debugLog("server", `Creating server with SSL config: ${!!ssl}`, verbose);
57063
57318
  const server = ssl && serverOptions ? https.createServer(serverOptions, requestHandler) : http.createServer(requestHandler);
57064
57319
  if (ssl) {
57065
57320
  server.on("secureConnection", (tlsSocket) => {
57066
- debugLog2("tls", `TLS Connection established: ${JSON.stringify({
57321
+ debugLog("tls", `TLS Connection established: ${JSON.stringify({
57067
57322
  protocol: tlsSocket.getProtocol?.(),
57068
57323
  cipher: tlsSocket.getCipher?.(),
57069
57324
  authorized: tlsSocket.authorized,
@@ -57074,7 +57329,7 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57074
57329
  activeServers.add(server);
57075
57330
  return new Promise((resolve4, reject) => {
57076
57331
  server.listen(listenPort, hostname, () => {
57077
- debugLog2("server", `Server listening on port ${listenPort}`, verbose);
57332
+ debugLog("server", `Server listening on port ${listenPort}`, verbose);
57078
57333
  console.log("");
57079
57334
  console.log(` ${green(bold("reverse-proxy"))} ${green(`v${version}`)}`);
57080
57335
  console.log("");
@@ -57091,13 +57346,13 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
57091
57346
  resolve4();
57092
57347
  });
57093
57348
  server.on("error", (err3) => {
57094
- debugLog2("server", `Server error: ${err3}`, verbose);
57349
+ debugLog("server", `Server error: ${err3}`, verbose);
57095
57350
  reject(err3);
57096
57351
  });
57097
57352
  });
57098
57353
  }
57099
57354
  async function setupReverseProxy(options3) {
57100
- debugLog2("setup", `Setting up reverse proxy: ${JSON.stringify(options3)}`, options3.verbose);
57355
+ debugLog("setup", `Setting up reverse proxy: ${JSON.stringify(options3)}`, options3.verbose);
57101
57356
  const { from, to, fromPort, sourceUrl, ssl, verbose } = options3;
57102
57357
  const httpPort = 80;
57103
57358
  const httpsPort = 443;
@@ -57106,73 +57361,72 @@ async function setupReverseProxy(options3) {
57106
57361
  if (ssl) {
57107
57362
  const isHttpPortBusy = await isPortInUse(httpPort, hostname, verbose);
57108
57363
  if (!isHttpPortBusy) {
57109
- debugLog2("setup", "Starting HTTP redirect server", verbose);
57364
+ debugLog("setup", "Starting HTTP redirect server", verbose);
57110
57365
  startHttpRedirectServer(verbose);
57111
57366
  } else {
57112
- debugLog2("setup", "Port 80 is in use, skipping HTTP redirect", verbose);
57367
+ debugLog("setup", "Port 80 is in use, skipping HTTP redirect", verbose);
57113
57368
  log.warn("Port 80 is in use, HTTP to HTTPS redirect will not be available");
57114
57369
  }
57115
57370
  }
57116
57371
  const targetPort = ssl ? httpsPort : httpPort;
57117
57372
  const isTargetPortBusy = await isPortInUse(targetPort, hostname, verbose);
57118
57373
  if (isTargetPortBusy) {
57119
- debugLog2("setup", `Port ${targetPort} is busy, finding alternative`, verbose);
57374
+ debugLog("setup", `Port ${targetPort} is busy, finding alternative`, verbose);
57120
57375
  const availablePort = await findAvailablePort(ssl ? 8443 : 8080, hostname, verbose);
57121
57376
  log.warn(`Port ${targetPort} is in use. Using port ${availablePort} instead.`);
57122
57377
  log.info(`You can use 'sudo lsof -i :${targetPort}' (Unix) or 'netstat -ano | findstr :${targetPort}' (Windows) to check what's using the port.`);
57123
57378
  await createProxyServer(from, to, fromPort, availablePort, hostname, sourceUrl, ssl, verbose);
57124
57379
  } else {
57125
- debugLog2("setup", `Using default port ${targetPort}`, verbose);
57380
+ debugLog("setup", `Using default port ${targetPort}`, verbose);
57126
57381
  await createProxyServer(from, to, fromPort, targetPort, hostname, sourceUrl, ssl, verbose);
57127
57382
  }
57128
57383
  } catch (err3) {
57129
- debugLog2("setup", `Setup failed: ${err3}`, verbose);
57384
+ debugLog("setup", `Setup failed: ${err3}`, verbose);
57130
57385
  log.error(`Failed to setup reverse proxy: ${err3.message}`);
57131
57386
  cleanup();
57132
57387
  }
57133
57388
  }
57134
57389
  function startHttpRedirectServer(verbose) {
57135
- debugLog2("redirect", "Starting HTTP redirect server", verbose);
57390
+ debugLog("redirect", "Starting HTTP redirect server", verbose);
57136
57391
  const server = http.createServer((req, res) => {
57137
57392
  const host = req.headers.host || "";
57138
- debugLog2("redirect", `Redirecting request from ${host}${req.url} to HTTPS`, verbose);
57393
+ debugLog("redirect", `Redirecting request from ${host}${req.url} to HTTPS`, verbose);
57139
57394
  res.writeHead(301, {
57140
57395
  Location: `https://${host}${req.url}`
57141
57396
  });
57142
57397
  res.end();
57143
57398
  }).listen(80);
57144
57399
  activeServers.add(server);
57145
- debugLog2("redirect", "HTTP redirect server started", verbose);
57400
+ debugLog("redirect", "HTTP redirect server started", verbose);
57146
57401
  }
57147
57402
  function startProxy(options3) {
57148
57403
  const finalOptions = {
57149
57404
  ...config,
57150
57405
  ...options3
57151
57406
  };
57152
- debugLog2("proxy", `Starting proxy with options: ${JSON.stringify({
57407
+ debugLog("proxy", `Starting proxy with options: ${JSON.stringify({
57153
57408
  from: finalOptions.from,
57154
57409
  to: finalOptions.to,
57155
- keyPath: finalOptions.https.keyPath,
57156
- certPath: finalOptions.https.certPath
57410
+ https: finalOptions.https
57157
57411
  })}`, finalOptions.verbose);
57158
57412
  startServer(finalOptions).catch((err3) => {
57159
- debugLog2("proxy", `Failed to start proxy: ${err3}`, finalOptions.verbose);
57413
+ debugLog("proxy", `Failed to start proxy: ${err3}`, finalOptions.verbose);
57160
57414
  log.error(`Failed to start proxy: ${err3.message}`);
57161
57415
  cleanup();
57162
57416
  });
57163
57417
  }
57164
57418
  function startProxies(options3) {
57165
57419
  if (Array.isArray(options3)) {
57166
- debugLog2("proxies", `Starting multiple proxies: ${options3.length}`, options3[0]?.verbose);
57420
+ debugLog("proxies", `Starting multiple proxies: ${options3.length}`, options3[0]?.verbose);
57167
57421
  Promise.all(options3.map((option) => startServer(option))).catch((err3) => {
57168
- debugLog2("proxies", `Failed to start proxies: ${err3}`, options3[0]?.verbose);
57422
+ debugLog("proxies", `Failed to start proxies: ${err3}`, options3[0]?.verbose);
57169
57423
  log.error("Failed to start proxies:", err3);
57170
57424
  cleanup();
57171
57425
  });
57172
57426
  } else if (options3) {
57173
- debugLog2("proxies", "Starting single proxy", options3.verbose);
57427
+ debugLog("proxies", "Starting single proxy", options3.verbose);
57174
57428
  startServer(options3).catch((err3) => {
57175
- debugLog2("proxies", `Failed to start proxy: ${err3}`, options3.verbose);
57429
+ debugLog("proxies", `Failed to start proxy: ${err3}`, options3.verbose);
57176
57430
  log.error("Failed to start proxy:", err3);
57177
57431
  cleanup();
57178
57432
  });
@@ -57184,5 +57438,12 @@ export {
57184
57438
  startProxies,
57185
57439
  startHttpRedirectServer,
57186
57440
  setupReverseProxy,
57187
- config
57441
+ removeHosts,
57442
+ httpsConfig,
57443
+ hostsFilePath,
57444
+ generateCertificate,
57445
+ config,
57446
+ cleanup,
57447
+ checkHosts,
57448
+ addHosts
57188
57449
  };