@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/cli.js CHANGED
@@ -17,7 +17,7 @@ var __toESM = (mod, isNodeMode, target) => {
17
17
  };
18
18
 
19
19
  // bin/cli.ts
20
- import os9 from "os";
20
+ import os10 from "os";
21
21
 
22
22
  // node_modules/@stacksjs/cli/dist/index.js
23
23
  import { formatWithOptions } from "util";
@@ -217845,7 +217845,7 @@ var export_mkdirSync = import_fs_extra.mkdirSync;
217845
217845
  var export_fsWatch = import_fs_extra.watch;
217846
217846
  var export_existsSync = import_fs_extra.pathExists;
217847
217847
  // package.json
217848
- var version = "0.2.0";
217848
+ var version = "0.3.0";
217849
217849
 
217850
217850
  // src/config.ts
217851
217851
  import os4 from "os";
@@ -217908,22 +217908,188 @@ var config6 = await loadConfig2({
217908
217908
  validityDays: 180,
217909
217909
  verbose: false
217910
217910
  },
217911
+ etcHostsCleanup: false,
217911
217912
  verbose: true
217912
217913
  }
217913
217914
  });
217914
217915
 
217915
217916
  // src/start.ts
217916
- import * as fs6 from "fs";
217917
+ import * as fs7 from "fs";
217917
217918
  import * as http from "http";
217918
217919
  import * as https from "https";
217919
217920
  import * as net from "net";
217920
- import os8 from "os";
217921
- import path9 from "path";
217922
- import process23 from "process";
217921
+ import os9 from "os";
217922
+ import path10 from "path";
217923
+ import process24 from "process";
217924
+
217925
+ // src/hosts.ts
217926
+ import { spawn } from "child_process";
217927
+ import fs4 from "fs";
217928
+ import os6 from "os";
217929
+ import path7 from "path";
217930
+ import process22 from "process";
217931
+
217932
+ // src/utils.ts
217933
+ function debugLog(category, message, verbose) {
217934
+ if (verbose || config6.verbose) {
217935
+ console.debug(`[rpx:${category}] ${message}`);
217936
+ }
217937
+ }
217938
+
217939
+ // src/hosts.ts
217940
+ var hostsFilePath = process22.platform === "win32" ? path7.join(process22.env.windir || "C:\\Windows", "System32", "drivers", "etc", "hosts") : "/etc/hosts";
217941
+ async function sudoWrite(operation, content) {
217942
+ return new Promise((resolve5, reject) => {
217943
+ if (process22.platform === "win32") {
217944
+ reject(new Error("Administrator privileges required on Windows"));
217945
+ return;
217946
+ }
217947
+ const tmpFile = path7.join(os6.tmpdir(), "hosts.tmp");
217948
+ try {
217949
+ if (operation === "append") {
217950
+ const currentContent = fs4.readFileSync(hostsFilePath, "utf8");
217951
+ fs4.writeFileSync(tmpFile, currentContent + content, "utf8");
217952
+ } else {
217953
+ fs4.writeFileSync(tmpFile, content, "utf8");
217954
+ }
217955
+ const sudo = spawn("sudo", ["cp", tmpFile, hostsFilePath]);
217956
+ sudo.on("close", (code) => {
217957
+ try {
217958
+ fs4.unlinkSync(tmpFile);
217959
+ if (code === 0)
217960
+ resolve5();
217961
+ else
217962
+ reject(new Error(`sudo process exited with code ${code}`));
217963
+ } catch (err3) {
217964
+ reject(err3);
217965
+ }
217966
+ });
217967
+ sudo.on("error", (err3) => {
217968
+ try {
217969
+ fs4.unlinkSync(tmpFile);
217970
+ } catch {
217971
+ }
217972
+ reject(err3);
217973
+ });
217974
+ } catch (err3) {
217975
+ reject(err3);
217976
+ }
217977
+ });
217978
+ }
217979
+ async function addHosts(hosts) {
217980
+ debugLog("hosts", `Adding hosts: ${hosts.join(", ")}`, config6.verbose);
217981
+ debugLog("hosts", `Using hosts file at: ${hostsFilePath}`, config6.verbose);
217982
+ try {
217983
+ const existingContent = await fs4.promises.readFile(hostsFilePath, "utf-8");
217984
+ const newEntries = hosts.filter((host) => {
217985
+ const ipv4Entry = `127.0.0.1 ${host}`;
217986
+ const ipv6Entry = `::1 ${host}`;
217987
+ return !existingContent.includes(ipv4Entry) && !existingContent.includes(ipv6Entry);
217988
+ });
217989
+ if (newEntries.length === 0) {
217990
+ debugLog("hosts", "All hosts already exist in hosts file", config6.verbose);
217991
+ log.info("All hosts are already in the hosts file");
217992
+ return;
217993
+ }
217994
+ const hostEntries = newEntries.map((host) => `\n# Added by rpx\n127.0.0.1 ${host}\n::1 ${host}`).join("\n");
217995
+ try {
217996
+ await fs4.promises.appendFile(hostsFilePath, hostEntries, { flag: "a" });
217997
+ log.success(`Added new hosts: ${newEntries.join(", ")}`);
217998
+ } catch (writeErr) {
217999
+ if (writeErr.code === "EACCES") {
218000
+ debugLog("hosts", "Permission denied, attempting with sudo", config6.verbose);
218001
+ try {
218002
+ await sudoWrite("append", hostEntries);
218003
+ log.success(`Added new hosts with sudo: ${newEntries.join(", ")}`);
218004
+ } catch (sudoErr) {
218005
+ log.error("Failed to modify hosts file automatically");
218006
+ log.warn("Please add these entries to your hosts file manually:");
218007
+ hostEntries.split("\n").forEach((entry) => log.warn(entry));
218008
+ if (process22.platform === "win32") {
218009
+ log.warn("\nOn Windows:");
218010
+ log.warn("1. Run notepad as administrator");
218011
+ log.warn("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");
218012
+ } else {
218013
+ log.warn("\nOn Unix systems:");
218014
+ log.warn(`sudo nano ${hostsFilePath}`);
218015
+ }
218016
+ throw new Error("Failed to modify hosts file: manual intervention required");
218017
+ }
218018
+ } else {
218019
+ throw writeErr;
218020
+ }
218021
+ }
218022
+ } catch (err3) {
218023
+ const error = err3;
218024
+ log.error(`Failed to manage hosts file: ${error.message}`);
218025
+ throw error;
218026
+ }
218027
+ }
218028
+ async function removeHosts(hosts) {
218029
+ debugLog("hosts", `Removing hosts: ${hosts.join(", ")}`, config6.verbose);
218030
+ try {
218031
+ const content = await fs4.promises.readFile(hostsFilePath, "utf-8");
218032
+ const lines = content.split("\n");
218033
+ const filteredLines = lines.filter((line, index) => {
218034
+ if (line.trim() === "# Added by rpx") {
218035
+ lines.splice(index + 1, 2);
218036
+ return false;
218037
+ }
218038
+ return true;
218039
+ });
218040
+ while (filteredLines[filteredLines.length - 1]?.trim() === "")
218041
+ filteredLines.pop();
218042
+ const newContent = `${filteredLines.join("\n")}\n`;
218043
+ try {
218044
+ await fs4.promises.writeFile(hostsFilePath, newContent);
218045
+ log.success("Hosts removed successfully");
218046
+ } catch (writeErr) {
218047
+ if (writeErr.code === "EACCES") {
218048
+ debugLog("hosts", "Permission denied, attempting with sudo", config6.verbose);
218049
+ try {
218050
+ await sudoWrite("write", newContent);
218051
+ log.success("Hosts removed successfully with sudo");
218052
+ } catch (sudoErr) {
218053
+ log.error("Failed to modify hosts file automatically");
218054
+ log.warn("Please remove these entries from your hosts file manually:");
218055
+ hosts.forEach((host) => {
218056
+ log.warn("# Added by rpx");
218057
+ log.warn(`127.0.0.1 ${host}`);
218058
+ log.warn(`::1 ${host}`);
218059
+ });
218060
+ if (process22.platform === "win32") {
218061
+ log.warn("\nOn Windows:");
218062
+ log.warn("1. Run notepad as administrator");
218063
+ log.warn("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");
218064
+ } else {
218065
+ log.warn("\nOn Unix systems:");
218066
+ log.warn(`sudo nano ${hostsFilePath}`);
218067
+ }
218068
+ throw new Error("Failed to modify hosts file: manual intervention required");
218069
+ }
218070
+ } else {
218071
+ throw writeErr;
218072
+ }
218073
+ }
218074
+ } catch (err3) {
218075
+ const error = err3;
218076
+ log.error(`Failed to remove hosts: ${error.message}`);
218077
+ throw error;
218078
+ }
218079
+ }
218080
+ async function checkHosts(hosts) {
218081
+ debugLog("hosts", `Checking hosts: ${hosts}`, config6.verbose);
218082
+ const content = await fs4.promises.readFile(hostsFilePath, "utf-8");
218083
+ return hosts.map((host) => {
218084
+ const ipv4Entry = `127.0.0.1 ${host}`;
218085
+ const ipv6Entry = `::1 ${host}`;
218086
+ return content.includes(ipv4Entry) || content.includes(ipv6Entry);
218087
+ });
218088
+ }
217923
218089
 
217924
218090
  // src/https.ts
217925
- import os7 from "os";
217926
- import path8 from "path";
218091
+ import os8 from "os";
218092
+ import path9 from "path";
217927
218093
 
217928
218094
  // node_modules/@stacksjs/tlsx/dist/index.js
217929
218095
  import fs22 from "fs";
@@ -217962,7 +218128,7 @@ import process82 from "process";
217962
218128
  import process102 from "process";
217963
218129
  import process182 from "process";
217964
218130
  import process112 from "process";
217965
- import os6 from "os";
218131
+ import os7 from "os";
217966
218132
  import tty33 from "tty";
217967
218133
  import process142 from "process";
217968
218134
  import process132 from "process";
@@ -217971,10 +218137,10 @@ import process162 from "process";
217971
218137
  import process172 from "process";
217972
218138
  import process192 from "process";
217973
218139
  import os23 from "os";
217974
- import path7 from "path";
218140
+ import path8 from "path";
217975
218141
  import { resolve as resolve6 } from "path";
217976
- import process22 from "process";
217977
- import fs4 from "fs";
218142
+ import process23 from "process";
218143
+ import fs6 from "fs";
217978
218144
  import path22 from "path";
217979
218145
  var __create4 = Object.create;
217980
218146
  var __getProtoOf4 = Object.getPrototypeOf;
@@ -251465,7 +251631,7 @@ function _supportsColor3(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
251465
251631
  return min;
251466
251632
  }
251467
251633
  if (process112.platform === "win32") {
251468
- const osRelease = os6.release().split(".");
251634
+ const osRelease = os7.release().split(".");
251469
251635
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
251470
251636
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
251471
251637
  }
@@ -254018,7 +254184,7 @@ function isObject33(item) {
254018
254184
  return item && typeof item === "object" && !Array.isArray(item);
254019
254185
  }
254020
254186
  async function loadConfig3({ name, cwd, defaultConfig }) {
254021
- const configPath2 = resolve6(cwd || process22.cwd(), `${name}.config`);
254187
+ const configPath2 = resolve6(cwd || process23.cwd(), `${name}.config`);
254022
254188
  try {
254023
254189
  const importedConfig = await import(configPath2);
254024
254190
  const loadedConfig = importedConfig.default || importedConfig;
@@ -254032,18 +254198,18 @@ var config42 = await loadConfig3({
254032
254198
  defaultConfig: {
254033
254199
  altNameIPs: ["127.0.0.1"],
254034
254200
  altNameURIs: ["localhost"],
254035
- organizationName: "stacksjs.org",
254201
+ organizationName: "Local Development",
254036
254202
  countryName: "US",
254037
254203
  stateName: "California",
254038
254204
  localityName: "Playa Vista",
254039
254205
  commonName: "stacks.localhost",
254040
254206
  validityDays: 180,
254041
254207
  hostCertCN: "stacks.localhost",
254042
- domain: "localhost",
254208
+ domain: "stacks.localhost",
254043
254209
  rootCAObject: { certificate: "", privateKey: "" },
254044
- caCertPath: path7.join(os23.homedir(), ".stacks", "ssl", `tlsx.localhost.ca.crt`),
254045
- certPath: path7.join(os23.homedir(), ".stacks", "ssl", `tlsx.localhost.crt`),
254046
- keyPath: path7.join(os23.homedir(), ".stacks", "ssl", `tlsx.localhost.crt.key`),
254210
+ caCertPath: path8.join(os23.homedir(), ".stacks", "ssl", `stacks.localhost.ca.crt`),
254211
+ certPath: path8.join(os23.homedir(), ".stacks", "ssl", `stacks.localhost.crt`),
254212
+ keyPath: path8.join(os23.homedir(), ".stacks", "ssl", `stacks.localhost.crt.key`),
254047
254213
  verbose: false
254048
254214
  }
254049
254215
  });
@@ -254059,10 +254225,10 @@ function findFoldersWithFile(rootDir, fileName) {
254059
254225
  const result2 = [];
254060
254226
  function search(dir) {
254061
254227
  try {
254062
- const files2 = fs4.readdirSync(dir);
254228
+ const files2 = fs6.readdirSync(dir);
254063
254229
  for (const file of files2) {
254064
254230
  const filePath = path22.join(dir, file);
254065
- const stats = fs4.lstatSync(filePath);
254231
+ const stats = fs6.lstatSync(filePath);
254066
254232
  if (stats.isDirectory()) {
254067
254233
  search(filePath);
254068
254234
  } else if (file === fileName) {
@@ -254076,29 +254242,29 @@ function findFoldersWithFile(rootDir, fileName) {
254076
254242
  search(rootDir);
254077
254243
  return result2;
254078
254244
  }
254079
- function debugLog(category, message, verbose) {
254245
+ function debugLog2(category, message, verbose) {
254080
254246
  if (verbose || config42.verbose) {
254081
254247
  console.debug(`[tlsx:${category}] ${message}`);
254082
254248
  }
254083
254249
  }
254084
254250
  function randomSerialNumber(verbose) {
254085
- debugLog("cert", "Generating random serial number", verbose);
254251
+ debugLog2("cert", "Generating random serial number", verbose);
254086
254252
  const serialNumber = makeNumberPositive(import_node_forge2.default.util.bytesToHex(import_node_forge2.default.random.getBytesSync(20)));
254087
- debugLog("cert", `Generated serial number: ${serialNumber}`, verbose);
254253
+ debugLog2("cert", `Generated serial number: ${serialNumber}`, verbose);
254088
254254
  return serialNumber;
254089
254255
  }
254090
254256
  function getCertNotBefore(verbose) {
254091
- debugLog("cert", "Calculating certificate not-before date", verbose);
254257
+ debugLog2("cert", "Calculating certificate not-before date", verbose);
254092
254258
  const twoDaysAgo = new Date(Date.now() - 172800000);
254093
254259
  const year = twoDaysAgo.getFullYear();
254094
254260
  const month = (twoDaysAgo.getMonth() + 1).toString().padStart(2, "0");
254095
254261
  const day = twoDaysAgo.getDate().toString().padStart(2, "0");
254096
254262
  const date = new Date(`${year}-${month}-${day}T23:59:59Z`);
254097
- debugLog("cert", `Certificate not-before date: ${date.toISOString()}`, verbose);
254263
+ debugLog2("cert", `Certificate not-before date: ${date.toISOString()}`, verbose);
254098
254264
  return date;
254099
254265
  }
254100
254266
  function getCertNotAfter(notBefore, verbose) {
254101
- debugLog("cert", "Calculating certificate not-after date", verbose);
254267
+ debugLog2("cert", "Calculating certificate not-after date", verbose);
254102
254268
  const validityDays = config42.validityDays;
254103
254269
  const daysInMillis = validityDays * 60 * 60 * 24 * 1000;
254104
254270
  const notAfterDate = new Date(notBefore.getTime() + daysInMillis);
@@ -254106,27 +254272,27 @@ function getCertNotAfter(notBefore, verbose) {
254106
254272
  const month = (notAfterDate.getMonth() + 1).toString().padStart(2, "0");
254107
254273
  const day = notAfterDate.getDate().toString().padStart(2, "0");
254108
254274
  const date = new Date(`${year}-${month}-${day}T23:59:59Z`);
254109
- debugLog("cert", `Certificate not-after date: ${date.toISOString()} (${validityDays} days validity)`, verbose);
254275
+ debugLog2("cert", `Certificate not-after date: ${date.toISOString()} (${validityDays} days validity)`, verbose);
254110
254276
  return date;
254111
254277
  }
254112
254278
  function getCANotAfter(notBefore, verbose) {
254113
- debugLog("cert", "Calculating CA not-after date", verbose);
254279
+ debugLog2("cert", "Calculating CA not-after date", verbose);
254114
254280
  const year = notBefore.getFullYear() + 100;
254115
254281
  const month = (notBefore.getMonth() + 1).toString().padStart(2, "0");
254116
254282
  const day = notBefore.getDate().toString().padStart(2, "0");
254117
254283
  const date = new Date(`${year}-${month}-${day}T23:59:59Z`);
254118
- debugLog("cert", `CA not-after date: ${date.toISOString()} (100 years validity)`, verbose);
254284
+ debugLog2("cert", `CA not-after date: ${date.toISOString()} (100 years validity)`, verbose);
254119
254285
  return date;
254120
254286
  }
254121
254287
  async function createRootCA(options23) {
254122
- debugLog("ca", "Creating new Root CA Certificate", options23?.verbose);
254123
- debugLog("ca", "Generating 2048-bit RSA key pair", options23?.verbose);
254288
+ debugLog2("ca", "Creating new Root CA Certificate", options23?.verbose);
254289
+ debugLog2("ca", "Generating 2048-bit RSA key pair", options23?.verbose);
254124
254290
  const { privateKey, publicKey } = import_node_forge2.pki.rsa.generateKeyPair(2048);
254125
254291
  const mergedOptions = {
254126
254292
  ...config42,
254127
254293
  ...options23 || {}
254128
254294
  };
254129
- debugLog("ca", "Setting certificate attributes", options23?.verbose);
254295
+ debugLog2("ca", "Setting certificate attributes", options23?.verbose);
254130
254296
  const attributes = [
254131
254297
  { shortName: "C", value: mergedOptions.countryName },
254132
254298
  { shortName: "ST", value: mergedOptions.stateName },
@@ -254134,7 +254300,7 @@ async function createRootCA(options23) {
254134
254300
  { shortName: "O", value: "Local Development CA" },
254135
254301
  { shortName: "CN", value: "Local Development Root CA" }
254136
254302
  ];
254137
- debugLog("ca", "Setting certificate extensions", options23?.verbose);
254303
+ debugLog2("ca", "Setting certificate extensions", options23?.verbose);
254138
254304
  const extensions = [
254139
254305
  {
254140
254306
  name: "basicConstraints",
@@ -254151,7 +254317,7 @@ async function createRootCA(options23) {
254151
254317
  name: "subjectKeyIdentifier"
254152
254318
  }
254153
254319
  ];
254154
- debugLog("ca", "Creating CA certificate", options23?.verbose);
254320
+ debugLog2("ca", "Creating CA certificate", options23?.verbose);
254155
254321
  const caCert = import_node_forge2.pki.createCertificate();
254156
254322
  caCert.publicKey = publicKey;
254157
254323
  caCert.serialNumber = randomSerialNumber(options23?.verbose);
@@ -254160,11 +254326,11 @@ async function createRootCA(options23) {
254160
254326
  caCert.setSubject(attributes);
254161
254327
  caCert.setIssuer(attributes);
254162
254328
  caCert.setExtensions(extensions);
254163
- debugLog("ca", "Signing certificate with SHA-256", options23?.verbose);
254329
+ debugLog2("ca", "Signing certificate with SHA-256", options23?.verbose);
254164
254330
  caCert.sign(privateKey, import_node_forge2.default.md.sha256.create());
254165
254331
  const pemCert = import_node_forge2.pki.certificateToPem(caCert);
254166
254332
  const pemKey = import_node_forge2.pki.privateKeyToPem(privateKey);
254167
- debugLog("ca", "Root CA Certificate created successfully", options23?.verbose);
254333
+ debugLog2("ca", "Root CA Certificate created successfully", options23?.verbose);
254168
254334
  return {
254169
254335
  certificate: pemCert,
254170
254336
  privateKey: pemKey,
@@ -254173,34 +254339,41 @@ async function createRootCA(options23) {
254173
254339
  };
254174
254340
  }
254175
254341
  async function generateCert(options23) {
254176
- debugLog("cert", "Generating new host certificate", options23?.verbose);
254177
- debugLog("cert", `Options: ${JSON.stringify(options23)}`, options23?.verbose);
254342
+ debugLog2("cert", "Generating new host certificate", options23?.verbose);
254343
+ debugLog2("cert", `Options: ${JSON.stringify(options23)}`, options23?.verbose);
254178
254344
  if (!options23?.hostCertCN?.trim()) {
254179
- debugLog("cert", "Error: hostCertCN is required", options23?.verbose);
254345
+ debugLog2("cert", "Error: hostCertCN is required", options23?.verbose);
254180
254346
  throw new Error('"hostCertCN" must be a String');
254181
254347
  }
254182
254348
  if (!options23.domain?.trim()) {
254183
- debugLog("cert", "Error: domain is required", options23?.verbose);
254349
+ debugLog2("cert", "Error: domain is required", options23?.verbose);
254184
254350
  throw new Error('"domain" must be a String');
254185
254351
  }
254186
254352
  if (!options23.rootCAObject || !options23.rootCAObject.certificate || !options23.rootCAObject.privateKey) {
254187
- debugLog("cert", "Error: rootCAObject is invalid or missing", options23?.verbose);
254353
+ debugLog2("cert", "Error: rootCAObject is invalid or missing", options23?.verbose);
254188
254354
  throw new Error('"rootCAObject" must be an Object with the properties "certificate" & "privateKey"');
254189
254355
  }
254190
- debugLog("cert", "Converting Root CA PEM to forge objects", options23?.verbose);
254356
+ debugLog2("cert", "Converting Root CA PEM to forge objects", options23?.verbose);
254191
254357
  const caCert = import_node_forge2.pki.certificateFromPem(options23.rootCAObject.certificate);
254192
254358
  const caKey = import_node_forge2.pki.privateKeyFromPem(options23.rootCAObject.privateKey);
254193
- debugLog("cert", "Generating 2048-bit RSA key pair for host certificate", options23?.verbose);
254359
+ debugLog2("cert", "Generating 2048-bit RSA key pair for host certificate", options23?.verbose);
254194
254360
  const hostKeys = import_node_forge2.pki.rsa.generateKeyPair(2048);
254195
- debugLog("cert", "Setting certificate attributes", options23?.verbose);
254361
+ debugLog2("cert", "Setting certificate attributes", options23?.verbose);
254196
254362
  const attributes = [
254197
- { shortName: "C", value: config42.countryName },
254198
- { shortName: "ST", value: config42.stateName },
254199
- { shortName: "L", value: config42.localityName },
254200
- { shortName: "O", value: "Local Development" },
254201
- { shortName: "CN", value: "*.localhost" }
254363
+ { shortName: "C", value: options23.countryName || config42.countryName },
254364
+ { shortName: "ST", value: options23.stateName || config42.stateName },
254365
+ { shortName: "L", value: options23.localityName || config42.localityName },
254366
+ { shortName: "O", value: options23.organizationName || config42.organizationName },
254367
+ { shortName: "CN", value: options23.commonName || config42.commonName }
254202
254368
  ];
254203
- debugLog("cert", "Setting certificate extensions", options23?.verbose);
254369
+ const domain = options23.domain || config42.domain;
254370
+ const wildcardDomain = `*.${domain.includes(".") ? domain.split(".").slice(1).join(".") : domain}`;
254371
+ const altNames = [
254372
+ { type: 2, value: wildcardDomain },
254373
+ { type: 2, value: "localhost" },
254374
+ { type: 2, value: domain }
254375
+ ];
254376
+ debugLog2("cert", "Setting certificate extensions", options23?.verbose);
254204
254377
  const extensions = [
254205
254378
  {
254206
254379
  name: "basicConstraints",
@@ -254220,30 +254393,25 @@ async function generateCert(options23) {
254220
254393
  },
254221
254394
  {
254222
254395
  name: "subjectAltName",
254223
- altNames: [
254224
- { type: 2, value: "*.localhost" },
254225
- { type: 2, value: "localhost" },
254226
- { type: 2, value: "stacks.localhost" },
254227
- { type: 2, value: options23.domain }
254228
- ]
254396
+ altNames
254229
254397
  }
254230
254398
  ];
254231
- debugLog("cert", "Creating new host certificate", options23?.verbose);
254399
+ debugLog2("cert", "Creating new host certificate", options23?.verbose);
254232
254400
  const newHostCert = import_node_forge2.pki.createCertificate();
254233
254401
  newHostCert.publicKey = hostKeys.publicKey;
254234
- debugLog("cert", "Setting certificate properties", options23?.verbose);
254402
+ debugLog2("cert", "Setting certificate properties", options23?.verbose);
254235
254403
  newHostCert.serialNumber = randomSerialNumber(options23?.verbose);
254236
254404
  newHostCert.validity.notBefore = getCertNotBefore(options23?.verbose);
254237
254405
  newHostCert.validity.notAfter = getCertNotAfter(newHostCert.validity.notBefore, options23?.verbose);
254238
254406
  newHostCert.setSubject(attributes);
254239
254407
  newHostCert.setIssuer(caCert.subject.attributes);
254240
254408
  newHostCert.setExtensions(extensions);
254241
- debugLog("cert", "Signing certificate with SHA-256", options23?.verbose);
254409
+ debugLog2("cert", "Signing certificate with SHA-256", options23?.verbose);
254242
254410
  newHostCert.sign(caKey, import_node_forge2.default.md.sha256.create());
254243
- debugLog("cert", "Converting certificate to PEM format", options23?.verbose);
254411
+ debugLog2("cert", "Converting certificate to PEM format", options23?.verbose);
254244
254412
  const pemHostCert = import_node_forge2.pki.certificateToPem(newHostCert);
254245
254413
  const pemHostKey = import_node_forge2.pki.privateKeyToPem(hostKeys.privateKey);
254246
- debugLog("cert", "Host certificate generated successfully", options23?.verbose);
254414
+ debugLog2("cert", "Host certificate generated successfully", options23?.verbose);
254247
254415
  return {
254248
254416
  certificate: pemHostCert,
254249
254417
  privateKey: pemHostKey,
@@ -254252,81 +254420,81 @@ async function generateCert(options23) {
254252
254420
  };
254253
254421
  }
254254
254422
  async function addCertToSystemTrustStoreAndSaveCert(cert, caCert, options23) {
254255
- debugLog("trust", `Adding certificate to system trust store with options: ${JSON.stringify(options23)}`, options23?.verbose);
254256
- debugLog("trust", "Storing certificate and private key", options23?.verbose);
254423
+ debugLog2("trust", `Adding certificate to system trust store with options: ${JSON.stringify(options23)}`, options23?.verbose);
254424
+ debugLog2("trust", "Storing certificate and private key", options23?.verbose);
254257
254425
  const certPath = storeCert(cert, options23);
254258
- debugLog("trust", "Storing CA certificate", options23?.verbose);
254426
+ debugLog2("trust", "Storing CA certificate", options23?.verbose);
254259
254427
  const caCertPath = storeCACert(caCert, options23);
254260
254428
  const platform23 = os42.platform();
254261
- debugLog("trust", `Detected platform: ${platform23}`, options23?.verbose);
254429
+ debugLog2("trust", `Detected platform: ${platform23}`, options23?.verbose);
254262
254430
  const args = "TC, C, C";
254263
254431
  if (platform23 === "darwin") {
254264
- debugLog("trust", "Adding certificate to macOS keychain", options23?.verbose);
254432
+ debugLog2("trust", "Adding certificate to macOS keychain", options23?.verbose);
254265
254433
  await runCommand2(`sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${caCertPath}`);
254266
254434
  } else if (platform23 === "win32") {
254267
- debugLog("trust", "Adding certificate to Windows certificate store", options23?.verbose);
254435
+ debugLog2("trust", "Adding certificate to Windows certificate store", options23?.verbose);
254268
254436
  await runCommand2(`certutil -f -v -addstore -enterprise Root ${caCertPath}`);
254269
254437
  } else if (platform23 === "linux") {
254270
- debugLog("trust", "Adding certificate to Linux certificate store", options23?.verbose);
254438
+ debugLog2("trust", "Adding certificate to Linux certificate store", options23?.verbose);
254271
254439
  const rootDirectory = os42.homedir();
254272
254440
  const targetFileName = "cert9.db";
254273
- debugLog("trust", `Searching for certificate databases in ${rootDirectory}`, options23?.verbose);
254441
+ debugLog2("trust", `Searching for certificate databases in ${rootDirectory}`, options23?.verbose);
254274
254442
  const foldersWithFile = findFoldersWithFile(rootDirectory, targetFileName);
254275
254443
  for (const folder of foldersWithFile) {
254276
- debugLog("trust", `Processing certificate database in ${folder}`, options23?.verbose);
254444
+ debugLog2("trust", `Processing certificate database in ${folder}`, options23?.verbose);
254277
254445
  try {
254278
- debugLog("trust", `Attempting to delete existing cert for ${config42.commonName}`, options23?.verbose);
254446
+ debugLog2("trust", `Attempting to delete existing cert for ${config42.commonName}`, options23?.verbose);
254279
254447
  await runCommand2(`certutil -d sql:${folder} -D -n ${config42.commonName}`);
254280
254448
  } catch (error) {
254281
- debugLog("trust", `Warning: Error deleting existing cert: ${error}`, options23?.verbose);
254449
+ debugLog2("trust", `Warning: Error deleting existing cert: ${error}`, options23?.verbose);
254282
254450
  console.warn(`Error deleting existing cert: ${error}`);
254283
254451
  }
254284
- debugLog("trust", `Adding new certificate to ${folder}`, options23?.verbose);
254452
+ debugLog2("trust", `Adding new certificate to ${folder}`, options23?.verbose);
254285
254453
  await runCommand2(`certutil -d sql:${folder} -A -t ${args} -n ${config42.commonName} -i ${caCertPath}`);
254286
254454
  log3.info(`Cert added to ${folder}`);
254287
254455
  }
254288
254456
  } else {
254289
- debugLog("trust", `Error: Unsupported platform ${platform23}`, options23?.verbose);
254457
+ debugLog2("trust", `Error: Unsupported platform ${platform23}`, options23?.verbose);
254290
254458
  throw new Error(`Unsupported platform: ${platform23}`);
254291
254459
  }
254292
- debugLog("trust", "Certificate successfully added to system trust store", options23?.verbose);
254460
+ debugLog2("trust", "Certificate successfully added to system trust store", options23?.verbose);
254293
254461
  return certPath;
254294
254462
  }
254295
254463
  function storeCert(cert, options23) {
254296
- debugLog("storage", `Storing certificate and private key with options: ${JSON.stringify(options23)}`, options23?.verbose);
254464
+ debugLog2("storage", `Storing certificate and private key with options: ${JSON.stringify(options23)}`, options23?.verbose);
254297
254465
  const certPath = options23?.certPath || config42.certPath;
254298
254466
  const certKeyPath = options23?.keyPath || config42.keyPath;
254299
- debugLog("storage", `Certificate path: ${certPath}`, options23?.verbose);
254300
- debugLog("storage", `Private key path: ${certKeyPath}`, options23?.verbose);
254467
+ debugLog2("storage", `Certificate path: ${certPath}`, options23?.verbose);
254468
+ debugLog2("storage", `Private key path: ${certKeyPath}`, options23?.verbose);
254301
254469
  const certDir = path32.dirname(certPath);
254302
254470
  if (!fs22.existsSync(certDir)) {
254303
- debugLog("storage", `Creating certificate directory: ${certDir}`, options23?.verbose);
254471
+ debugLog2("storage", `Creating certificate directory: ${certDir}`, options23?.verbose);
254304
254472
  fs22.mkdirSync(certDir, { recursive: true });
254305
254473
  }
254306
- debugLog("storage", "Writing certificate file", options23?.verbose);
254474
+ debugLog2("storage", "Writing certificate file", options23?.verbose);
254307
254475
  fs22.writeFileSync(certPath, cert.certificate);
254308
254476
  const certKeyDir = path32.dirname(certKeyPath);
254309
254477
  if (!fs22.existsSync(certKeyDir)) {
254310
- debugLog("storage", `Creating private key directory: ${certKeyDir}`, options23?.verbose);
254478
+ debugLog2("storage", `Creating private key directory: ${certKeyDir}`, options23?.verbose);
254311
254479
  fs22.mkdirSync(certKeyDir, { recursive: true });
254312
254480
  }
254313
- debugLog("storage", "Writing private key file", options23?.verbose);
254481
+ debugLog2("storage", "Writing private key file", options23?.verbose);
254314
254482
  fs22.writeFileSync(certKeyPath, cert.privateKey);
254315
- debugLog("storage", "Certificate and private key stored successfully", options23?.verbose);
254483
+ debugLog2("storage", "Certificate and private key stored successfully", options23?.verbose);
254316
254484
  return certPath;
254317
254485
  }
254318
254486
  function storeCACert(caCert, options23) {
254319
- debugLog("storage", "Storing CA certificate", options23?.verbose);
254487
+ debugLog2("storage", "Storing CA certificate", options23?.verbose);
254320
254488
  const caCertPath = options23?.caCertPath || config42.caCertPath;
254321
- debugLog("storage", `CA certificate path: ${caCertPath}`, options23?.verbose);
254489
+ debugLog2("storage", `CA certificate path: ${caCertPath}`, options23?.verbose);
254322
254490
  const caCertDir = path32.dirname(caCertPath);
254323
254491
  if (!fs22.existsSync(caCertDir)) {
254324
- debugLog("storage", `Creating CA certificate directory: ${caCertDir}`, options23?.verbose);
254492
+ debugLog2("storage", `Creating CA certificate directory: ${caCertDir}`, options23?.verbose);
254325
254493
  fs22.mkdirSync(caCertDir, { recursive: true });
254326
254494
  }
254327
- debugLog("storage", "Writing CA certificate file", options23?.verbose);
254495
+ debugLog2("storage", "Writing CA certificate file", options23?.verbose);
254328
254496
  fs22.writeFileSync(caCertPath, caCert);
254329
- debugLog("storage", "CA certificate stored successfully", options23?.verbose);
254497
+ debugLog2("storage", "CA certificate stored successfully", options23?.verbose);
254330
254498
  return caCertPath;
254331
254499
  }
254332
254500
  var export_tls = import_node_forge2.tls;
@@ -254334,28 +254502,37 @@ var export_pki = import_node_forge2.pki;
254334
254502
  var export_forge = import_node_forge2.default;
254335
254503
 
254336
254504
  // src/https.ts
254337
- var defaultHttpsConfig = {
254338
- domain: "stacks.localhost",
254339
- hostCertCN: "stacks.localhost",
254340
- caCertPath: path8.join(os7.homedir(), ".stacks", "ssl", `stacks.localhost.ca.crt`),
254341
- certPath: path8.join(os7.homedir(), ".stacks", "ssl", `stacks.localhost.crt`),
254342
- keyPath: path8.join(os7.homedir(), ".stacks", "ssl", `stacks.localhost.crt.key`),
254343
- altNameIPs: ["127.0.0.1"],
254344
- altNameURIs: ["localhost"],
254345
- organizationName: "stacksjs.org",
254346
- countryName: "US",
254347
- stateName: "California",
254348
- localityName: "Playa Vista",
254349
- commonName: "stacks.localhost",
254350
- validityDays: 180,
254351
- verbose: false
254352
- };
254505
+ function httpsConfig() {
254506
+ const domain = config6.to || "stacks.localhost";
254507
+ const defaultConfig = {
254508
+ domain,
254509
+ hostCertCN: domain,
254510
+ caCertPath: path9.join(os8.homedir(), ".stacks", "ssl", `${domain}.ca.crt`),
254511
+ certPath: path9.join(os8.homedir(), ".stacks", "ssl", `${domain}.crt`),
254512
+ keyPath: path9.join(os8.homedir(), ".stacks", "ssl", `${domain}.crt.key`),
254513
+ altNameIPs: ["127.0.0.1"],
254514
+ altNameURIs: ["localhost"],
254515
+ organizationName: "stacksjs.org",
254516
+ countryName: "US",
254517
+ stateName: "California",
254518
+ localityName: "Playa Vista",
254519
+ commonName: domain,
254520
+ validityDays: 180,
254521
+ verbose: false
254522
+ };
254523
+ if (config6.https === true)
254524
+ return defaultConfig;
254525
+ return {
254526
+ ...defaultConfig,
254527
+ ...config6.https
254528
+ };
254529
+ }
254353
254530
  async function generateCertificate(domain) {
254354
254531
  if (config6.https === true)
254355
- config6.https = defaultHttpsConfig;
254532
+ config6.https = httpsConfig();
254356
254533
  else if (config6.https === false)
254357
254534
  return;
254358
- domain = domain ?? config6.https.altNameURIs[0];
254535
+ domain = domain ?? config6.https.domain;
254359
254536
  log.info(`Generating a self-signed SSL certificate for: ${domain}`);
254360
254537
  const caCert = await createRootCA(config6.https);
254361
254538
  const hostCert = await generateCert({
@@ -254378,90 +254555,89 @@ async function generateCertificate(domain) {
254378
254555
  log.success("Certificate generated");
254379
254556
  }
254380
254557
 
254381
- // src/utils.ts
254382
- function debugLog2(category, message, verbose) {
254383
- if (verbose || config6.verbose) {
254384
- console.debug(`[rpx:${category}] ${message}`);
254385
- }
254386
- }
254387
-
254388
254558
  // src/start.ts
254389
254559
  var activeServers = new Set;
254390
- function cleanup() {
254391
- debugLog2("cleanup", "Starting cleanup process", config6.verbose);
254560
+ async function cleanup() {
254561
+ debugLog("cleanup", "Starting cleanup process", config6.verbose);
254392
254562
  console.log(`\n`);
254393
254563
  log.info("Shutting down proxy servers...");
254394
- const closePromises = Array.from(activeServers).map((server) => new Promise((resolve5) => {
254564
+ const cleanupPromises = [];
254565
+ const serverClosePromises = Array.from(activeServers).map((server) => new Promise((resolve5) => {
254395
254566
  server.close(() => {
254396
- debugLog2("cleanup", "Server closed successfully", config6.verbose);
254567
+ debugLog("cleanup", "Server closed successfully", config6.verbose);
254397
254568
  resolve5();
254398
254569
  });
254399
254570
  }));
254400
- Promise.all(closePromises).then(() => {
254401
- debugLog2("cleanup", "All servers closed successfully", config6.verbose);
254402
- log.success("All proxy servers shut down successfully");
254403
- process23.exit(0);
254404
- }).catch((err4) => {
254405
- debugLog2("cleanup", `Error during shutdown: ${err4}`, config6.verbose);
254406
- log.error("Error during shutdown:", err4);
254407
- process23.exit(1);
254408
- });
254409
- }
254410
- process23.on("SIGINT", cleanup);
254411
- process23.on("SIGTERM", cleanup);
254412
- process23.on("uncaughtException", (err4) => {
254413
- debugLog2("process", `Uncaught exception: ${err4}`, config6.verbose);
254571
+ cleanupPromises.push(...serverClosePromises);
254572
+ if (config6.etcHostsCleanup) {
254573
+ debugLog("cleanup", "Cleaning up hosts file entries", config6.verbose);
254574
+ try {
254575
+ const toUrl = new URL(config6.to.startsWith("http") ? config6.to : `http://${config6.to}`);
254576
+ const hostname = toUrl.hostname;
254577
+ if (!hostname.includes("localhost") && !hostname.includes("127.0.0.1")) {
254578
+ log.info("Cleaning up hosts file entries...");
254579
+ cleanupPromises.push(removeHosts([hostname]).then(() => {
254580
+ debugLog("cleanup", `Removed hosts entry for ${hostname}`, config6.verbose);
254581
+ }).catch((err4) => {
254582
+ debugLog("cleanup", `Failed to remove hosts entry: ${err4}`, config6.verbose);
254583
+ log.warn(`Failed to clean up hosts file entry for ${hostname}:`, err4);
254584
+ }));
254585
+ }
254586
+ } catch (err4) {
254587
+ debugLog("cleanup", `Error parsing URL during hosts cleanup: ${err4}`, config6.verbose);
254588
+ log.warn("Failed to parse URL for hosts cleanup:", err4);
254589
+ }
254590
+ }
254591
+ try {
254592
+ await Promise.all(cleanupPromises);
254593
+ debugLog("cleanup", "All cleanup tasks completed successfully", config6.verbose);
254594
+ log.success("All cleanup tasks completed successfully");
254595
+ process24.exit(0);
254596
+ } catch (err4) {
254597
+ debugLog("cleanup", `Error during cleanup: ${err4}`, config6.verbose);
254598
+ log.error("Error during cleanup:", err4);
254599
+ process24.exit(1);
254600
+ }
254601
+ }
254602
+ process24.on("SIGINT", cleanup);
254603
+ process24.on("SIGTERM", cleanup);
254604
+ process24.on("uncaughtException", (err4) => {
254605
+ debugLog("process", `Uncaught exception: ${err4}`, config6.verbose);
254414
254606
  log.error("Uncaught exception:", err4);
254415
254607
  cleanup();
254416
254608
  });
254417
254609
  async function loadSSLConfig(options4) {
254418
- debugLog2("ssl", "Loading SSL configuration", options4.verbose);
254419
- if (options4.https === true) {
254420
- options4.https = {
254421
- domain: "stacks.localhost",
254422
- hostCertCN: "stacks.localhost",
254423
- caCertPath: path9.join(os8.homedir(), ".stacks", "ssl", `stacks.localhost.ca.crt`),
254424
- certPath: path9.join(os8.homedir(), ".stacks", "ssl", `stacks.localhost.crt`),
254425
- keyPath: path9.join(os8.homedir(), ".stacks", "ssl", `stacks.localhost.crt.key`),
254426
- altNameIPs: ["127.0.0.1"],
254427
- altNameURIs: ["localhost"],
254428
- organizationName: "stacksjs.org",
254429
- countryName: "US",
254430
- stateName: "California",
254431
- localityName: "Playa Vista",
254432
- commonName: "stacks.localhost",
254433
- validityDays: 180,
254434
- verbose: false
254435
- };
254436
- } else if (options4.https === false) {
254610
+ debugLog("ssl", "Loading SSL configuration", options4.verbose);
254611
+ if (options4.https === true)
254612
+ options4.https = httpsConfig();
254613
+ else if (options4.https === false)
254437
254614
  return null;
254438
- }
254439
254615
  if (!options4.https?.keyPath && !options4.https?.certPath) {
254440
- debugLog2("ssl", "No SSL configuration provided", options4.verbose);
254616
+ debugLog("ssl", "No SSL configuration provided", options4.verbose);
254441
254617
  return null;
254442
254618
  }
254443
254619
  if (options4.https?.keyPath && !options4.https?.certPath || !options4.https?.keyPath && options4.https?.certPath) {
254444
254620
  const missing = !options4.https?.keyPath ? "keyPath" : "certPath";
254445
- debugLog2("ssl", `Invalid SSL configuration - missing ${missing}`, options4.verbose);
254621
+ debugLog("ssl", `Invalid SSL configuration - missing ${missing}`, options4.verbose);
254446
254622
  throw new Error(`SSL Configuration requires both keyPath and certPath. Missing: ${missing}`);
254447
254623
  }
254448
254624
  try {
254449
254625
  if (!options4.https?.keyPath || !options4.https?.certPath)
254450
254626
  return null;
254451
254627
  try {
254452
- debugLog2("ssl", "Reading SSL certificate files", options4.verbose);
254453
- const key = await fs6.promises.readFile(options4.https?.keyPath, "utf8");
254454
- const cert = await fs6.promises.readFile(options4.https?.certPath, "utf8");
254455
- debugLog2("ssl", "SSL configuration loaded successfully", options4.verbose);
254628
+ debugLog("ssl", "Reading SSL certificate files", options4.verbose);
254629
+ const key = await fs7.promises.readFile(options4.https?.keyPath, "utf8");
254630
+ const cert = await fs7.promises.readFile(options4.https?.certPath, "utf8");
254631
+ debugLog("ssl", "SSL configuration loaded successfully", options4.verbose);
254456
254632
  return { key, cert };
254457
254633
  } catch (error) {
254458
254634
  if (error.code === "ENOENT") {
254459
- debugLog2("ssl", "Certificates not found, generating new ones", options4.verbose);
254460
- await generateCertificate();
254461
- debugLog2("ssl", "Reading newly generated certificates", options4.verbose);
254462
- const key = await fs6.promises.readFile(options4.https?.keyPath, "utf8");
254463
- const cert = await fs6.promises.readFile(options4.https?.certPath, "utf8");
254464
- debugLog2("ssl", "New SSL certificates loaded successfully", options4.verbose);
254635
+ debugLog("ssl", "Certificates not found, generating new ones", options4.verbose);
254636
+ await generateCertificate(options4.to);
254637
+ debugLog("ssl", "Reading newly generated certificates", options4.verbose);
254638
+ const key = await fs7.promises.readFile(options4.https?.keyPath, "utf8");
254639
+ const cert = await fs7.promises.readFile(options4.https?.certPath, "utf8");
254640
+ debugLog("ssl", "New SSL certificates loaded successfully", options4.verbose);
254465
254641
  return { key, cert };
254466
254642
  }
254467
254643
  throw error;
@@ -254469,22 +254645,22 @@ async function loadSSLConfig(options4) {
254469
254645
  } catch (err4) {
254470
254646
  const error = err4;
254471
254647
  const detail = error.code === "ENOENT" ? `File not found: ${error.path}` : error.message;
254472
- debugLog2("ssl", `SSL configuration error: ${error}`, options4.verbose);
254648
+ debugLog("ssl", `SSL configuration error: ${error}`, options4.verbose);
254473
254649
  throw new Error(`SSL Configuration Error: ${detail}`);
254474
254650
  }
254475
254651
  }
254476
254652
  function isPortInUse(port, hostname, verbose) {
254477
- debugLog2("port", `Checking if port ${port} is in use on ${hostname}`, verbose);
254653
+ debugLog("port", `Checking if port ${port} is in use on ${hostname}`, verbose);
254478
254654
  return new Promise((resolve5) => {
254479
254655
  const server = net.createServer();
254480
254656
  server.once("error", (err4) => {
254481
254657
  if (err4.code === "EADDRINUSE") {
254482
- debugLog2("port", `Port ${port} is in use`, verbose);
254658
+ debugLog("port", `Port ${port} is in use`, verbose);
254483
254659
  resolve5(true);
254484
254660
  }
254485
254661
  });
254486
254662
  server.once("listening", () => {
254487
- debugLog2("port", `Port ${port} is available`, verbose);
254663
+ debugLog("port", `Port ${port} is available`, verbose);
254488
254664
  server.close();
254489
254665
  resolve5(false);
254490
254666
  });
@@ -254492,17 +254668,17 @@ function isPortInUse(port, hostname, verbose) {
254492
254668
  });
254493
254669
  }
254494
254670
  async function findAvailablePort(startPort, hostname, verbose) {
254495
- debugLog2("port", `Finding available port starting from ${startPort}`, verbose);
254671
+ debugLog("port", `Finding available port starting from ${startPort}`, verbose);
254496
254672
  let port = startPort;
254497
254673
  while (await isPortInUse(port, hostname, verbose)) {
254498
- debugLog2("port", `Port ${port} is in use, trying ${port + 1}`, verbose);
254674
+ debugLog("port", `Port ${port} is in use, trying ${port + 1}`, verbose);
254499
254675
  port++;
254500
254676
  }
254501
- debugLog2("port", `Found available port: ${port}`, verbose);
254677
+ debugLog("port", `Found available port: ${port}`, verbose);
254502
254678
  return port;
254503
254679
  }
254504
254680
  async function testConnection(hostname, port, verbose) {
254505
- debugLog2("connection", `Testing connection to ${hostname}:${port}`, verbose);
254681
+ debugLog("connection", `Testing connection to ${hostname}:${port}`, verbose);
254506
254682
  return new Promise((resolve5, reject) => {
254507
254683
  const socket = net.connect({
254508
254684
  host: hostname,
@@ -254510,58 +254686,89 @@ async function testConnection(hostname, port, verbose) {
254510
254686
  timeout: 5000
254511
254687
  });
254512
254688
  socket.once("connect", () => {
254513
- debugLog2("connection", `Successfully connected to ${hostname}:${port}`, verbose);
254689
+ debugLog("connection", `Successfully connected to ${hostname}:${port}`, verbose);
254514
254690
  socket.end();
254515
254691
  resolve5();
254516
254692
  });
254517
254693
  socket.once("timeout", () => {
254518
- debugLog2("connection", `Connection to ${hostname}:${port} timed out`, verbose);
254694
+ debugLog("connection", `Connection to ${hostname}:${port} timed out`, verbose);
254519
254695
  socket.destroy();
254520
254696
  reject(new Error(`Connection to ${hostname}:${port} timed out`));
254521
254697
  });
254522
254698
  socket.once("error", (err4) => {
254523
- debugLog2("connection", `Failed to connect to ${hostname}:${port}: ${err4}`, verbose);
254699
+ debugLog("connection", `Failed to connect to ${hostname}:${port}: ${err4}`, verbose);
254524
254700
  socket.destroy();
254525
254701
  reject(new Error(`Failed to connect to ${hostname}:${port}: ${err4.message}`));
254526
254702
  });
254527
254703
  });
254528
254704
  }
254529
254705
  async function startServer(options4) {
254530
- debugLog2("server", `Starting server with options: ${JSON.stringify(options4)}`, options4?.verbose);
254706
+ debugLog("server", `Starting server with options: ${JSON.stringify(options4)}`, options4?.verbose);
254531
254707
  if (!options4)
254532
254708
  options4 = config6;
254533
254709
  if (!options4.from)
254534
254710
  options4.from = config6.from;
254535
254711
  if (!options4.to)
254536
254712
  options4.to = config6.to;
254713
+ const fromUrl = new URL(options4.from.startsWith("http") ? options4.from : `http://${options4.from}`);
254714
+ const toUrl = new URL(options4.to.startsWith("http") ? options4.to : `http://${options4.to}`);
254715
+ const fromPort = Number.parseInt(fromUrl.port) || (fromUrl.protocol.includes("https:") ? 443 : 80);
254716
+ const hostsToCheck = [toUrl.hostname];
254717
+ if (!toUrl.hostname.includes("localhost") && !toUrl.hostname.includes("127.0.0.1")) {
254718
+ debugLog("hosts", `Checking if hosts file entry exists for: ${toUrl.hostname}`, options4.verbose);
254719
+ try {
254720
+ const hostsExist = await checkHosts(hostsToCheck);
254721
+ if (!hostsExist[0]) {
254722
+ log.info(`Adding ${toUrl.hostname} to hosts file...`);
254723
+ log.info("This may require sudo/administrator privileges");
254724
+ try {
254725
+ await addHosts(hostsToCheck);
254726
+ } catch (addError) {
254727
+ log.error("Failed to add hosts entry:", addError.message);
254728
+ log.warn("You can manually add this entry to your hosts file:");
254729
+ log.warn(`127.0.0.1 ${toUrl.hostname}`);
254730
+ log.warn(`::1 ${toUrl.hostname}`);
254731
+ if (process24.platform === "win32") {
254732
+ log.warn("On Windows:");
254733
+ log.warn("1. Run notepad as administrator");
254734
+ log.warn("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");
254735
+ } else {
254736
+ log.warn("On Unix systems:");
254737
+ log.warn("sudo nano /etc/hosts");
254738
+ }
254739
+ }
254740
+ } else {
254741
+ debugLog("hosts", `Host entry already exists for ${toUrl.hostname}`, options4.verbose);
254742
+ }
254743
+ } catch (checkError) {
254744
+ log.error("Failed to check hosts file:", checkError.message);
254745
+ }
254746
+ }
254537
254747
  if (config6.https) {
254538
254748
  if (config6.https === true)
254539
- config6.https = defaultHttpsConfig;
254540
- const domain = config6.https.altNameURIs?.[0] || new URL(options4.to).hostname;
254749
+ config6.https = httpsConfig();
254750
+ const domain = toUrl.hostname;
254541
254751
  if (typeof options4.https !== "boolean" && options4.https) {
254542
- options4.https.keyPath = config6.https.keyPath || path9.join(os8.homedir(), ".stacks", "ssl", `${domain}.crt.key`);
254543
- options4.https.certPath = config6.https.certPath || path9.join(os8.homedir(), ".stacks", "ssl", `${domain}.crt`);
254544
- debugLog2("server", `HTTPS enabled, using cert paths: ${options4.https.keyPath}, ${options4.https.certPath}`, options4.verbose);
254752
+ options4.https.keyPath = config6.https.keyPath || path10.join(os9.homedir(), ".stacks", "ssl", `${domain}.crt.key`);
254753
+ options4.https.certPath = config6.https.certPath || path10.join(os9.homedir(), ".stacks", "ssl", `${domain}.crt`);
254754
+ debugLog("server", `HTTPS enabled, using cert paths: ${options4.https.keyPath}, ${options4.https.certPath}`, options4.verbose);
254545
254755
  }
254546
254756
  }
254547
- const fromUrl = new URL(options4.from.startsWith("http") ? options4.from : `http://${options4.from}`);
254548
- const toUrl = new URL(options4.to.startsWith("http") ? options4.to : `http://${options4.to}`);
254549
- const fromPort = Number.parseInt(fromUrl.port) || (fromUrl.protocol.includes("https:") ? 443 : 80);
254550
- debugLog2("server", `Parsed URLs - from: ${fromUrl}, to: ${toUrl}`, options4.verbose);
254757
+ debugLog("server", `Parsed URLs - from: ${fromUrl}, to: ${toUrl}`, options4.verbose);
254551
254758
  try {
254552
254759
  await testConnection(fromUrl.hostname, fromPort, options4.verbose);
254553
254760
  } catch (err4) {
254554
- debugLog2("server", `Connection test failed: ${err4}`, options4.verbose);
254761
+ debugLog("server", `Connection test failed: ${err4}`, options4.verbose);
254555
254762
  log.error(err4.message);
254556
- process23.exit(1);
254763
+ process24.exit(1);
254557
254764
  }
254558
254765
  let sslConfig = null;
254559
254766
  if (config6.https) {
254560
254767
  try {
254561
254768
  sslConfig = await loadSSLConfig(options4);
254562
254769
  } catch (err4) {
254563
- debugLog2("server", `SSL config failed, attempting to generate certificates: ${err4}`, options4.verbose);
254564
- await generateCertificate();
254770
+ debugLog("server", `SSL config failed, attempting to generate certificates: ${err4}`, options4.verbose);
254771
+ await generateCertificate(options4.to);
254565
254772
  sslConfig = await loadSSLConfig(options4);
254566
254773
  }
254567
254774
  }
@@ -254578,9 +254785,9 @@ async function startServer(options4) {
254578
254785
  });
254579
254786
  }
254580
254787
  async function createProxyServer(from, to, fromPort, listenPort, hostname, sourceUrl, ssl, verbose) {
254581
- debugLog2("proxy", `Creating proxy server ${from} -> ${to}`, verbose);
254788
+ debugLog("proxy", `Creating proxy server ${from} -> ${to}`, verbose);
254582
254789
  const requestHandler = (req, res) => {
254583
- debugLog2("request", `Incoming request: ${req.method} ${req.url}`, verbose);
254790
+ debugLog("request", `Incoming request: ${req.method} ${req.url}`, verbose);
254584
254791
  const proxyOptions = {
254585
254792
  hostname: sourceUrl.hostname,
254586
254793
  port: fromPort,
@@ -254591,9 +254798,9 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
254591
254798
  host: sourceUrl.host
254592
254799
  }
254593
254800
  };
254594
- debugLog2("request", `Proxy request options: ${JSON.stringify(proxyOptions)}`, verbose);
254801
+ debugLog("request", `Proxy request options: ${JSON.stringify(proxyOptions)}`, verbose);
254595
254802
  const proxyReq = http.request(proxyOptions, (proxyRes) => {
254596
- debugLog2("response", `Proxy response received with status ${proxyRes.statusCode}`, verbose);
254803
+ debugLog("response", `Proxy response received with status ${proxyRes.statusCode}`, verbose);
254597
254804
  const headers = {
254598
254805
  ...proxyRes.headers,
254599
254806
  "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
@@ -254603,7 +254810,7 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
254603
254810
  proxyRes.pipe(res);
254604
254811
  });
254605
254812
  proxyReq.on("error", (err4) => {
254606
- debugLog2("request", `Proxy request failed: ${err4}`, verbose);
254813
+ debugLog("request", `Proxy request failed: ${err4}`, verbose);
254607
254814
  log.error("Proxy request failed:", err4);
254608
254815
  res.writeHead(502);
254609
254816
  res.end(`Proxy Error: ${err4.message}`);
@@ -254630,11 +254837,11 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
254630
254837
  allowHTTP1: true,
254631
254838
  ALPNProtocols: ["h2", "http/1.1"]
254632
254839
  } : undefined;
254633
- debugLog2("server", `Creating server with SSL config: ${!!ssl}`, verbose);
254840
+ debugLog("server", `Creating server with SSL config: ${!!ssl}`, verbose);
254634
254841
  const server = ssl && serverOptions ? https.createServer(serverOptions, requestHandler) : http.createServer(requestHandler);
254635
254842
  if (ssl) {
254636
254843
  server.on("secureConnection", (tlsSocket) => {
254637
- debugLog2("tls", `TLS Connection established: ${JSON.stringify({
254844
+ debugLog("tls", `TLS Connection established: ${JSON.stringify({
254638
254845
  protocol: tlsSocket.getProtocol?.(),
254639
254846
  cipher: tlsSocket.getCipher?.(),
254640
254847
  authorized: tlsSocket.authorized,
@@ -254645,7 +254852,7 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
254645
254852
  activeServers.add(server);
254646
254853
  return new Promise((resolve5, reject) => {
254647
254854
  server.listen(listenPort, hostname, () => {
254648
- debugLog2("server", `Server listening on port ${listenPort}`, verbose);
254855
+ debugLog("server", `Server listening on port ${listenPort}`, verbose);
254649
254856
  console.log("");
254650
254857
  console.log(` ${green(bold("reverse-proxy"))} ${green(`v${version}`)}`);
254651
254858
  console.log("");
@@ -254662,13 +254869,13 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
254662
254869
  resolve5();
254663
254870
  });
254664
254871
  server.on("error", (err4) => {
254665
- debugLog2("server", `Server error: ${err4}`, verbose);
254872
+ debugLog("server", `Server error: ${err4}`, verbose);
254666
254873
  reject(err4);
254667
254874
  });
254668
254875
  });
254669
254876
  }
254670
254877
  async function setupReverseProxy(options4) {
254671
- debugLog2("setup", `Setting up reverse proxy: ${JSON.stringify(options4)}`, options4.verbose);
254878
+ debugLog("setup", `Setting up reverse proxy: ${JSON.stringify(options4)}`, options4.verbose);
254672
254879
  const { from, to, fromPort, sourceUrl, ssl, verbose } = options4;
254673
254880
  const httpPort = 80;
254674
254881
  const httpsPort = 443;
@@ -254677,72 +254884,72 @@ async function setupReverseProxy(options4) {
254677
254884
  if (ssl) {
254678
254885
  const isHttpPortBusy = await isPortInUse(httpPort, hostname, verbose);
254679
254886
  if (!isHttpPortBusy) {
254680
- debugLog2("setup", "Starting HTTP redirect server", verbose);
254887
+ debugLog("setup", "Starting HTTP redirect server", verbose);
254681
254888
  startHttpRedirectServer(verbose);
254682
254889
  } else {
254683
- debugLog2("setup", "Port 80 is in use, skipping HTTP redirect", verbose);
254890
+ debugLog("setup", "Port 80 is in use, skipping HTTP redirect", verbose);
254684
254891
  log.warn("Port 80 is in use, HTTP to HTTPS redirect will not be available");
254685
254892
  }
254686
254893
  }
254687
254894
  const targetPort = ssl ? httpsPort : httpPort;
254688
254895
  const isTargetPortBusy = await isPortInUse(targetPort, hostname, verbose);
254689
254896
  if (isTargetPortBusy) {
254690
- debugLog2("setup", `Port ${targetPort} is busy, finding alternative`, verbose);
254897
+ debugLog("setup", `Port ${targetPort} is busy, finding alternative`, verbose);
254691
254898
  const availablePort = await findAvailablePort(ssl ? 8443 : 8080, hostname, verbose);
254692
254899
  log.warn(`Port ${targetPort} is in use. Using port ${availablePort} instead.`);
254693
254900
  log.info(`You can use 'sudo lsof -i :${targetPort}' (Unix) or 'netstat -ano | findstr :${targetPort}' (Windows) to check what's using the port.`);
254694
254901
  await createProxyServer(from, to, fromPort, availablePort, hostname, sourceUrl, ssl, verbose);
254695
254902
  } else {
254696
- debugLog2("setup", `Using default port ${targetPort}`, verbose);
254903
+ debugLog("setup", `Using default port ${targetPort}`, verbose);
254697
254904
  await createProxyServer(from, to, fromPort, targetPort, hostname, sourceUrl, ssl, verbose);
254698
254905
  }
254699
254906
  } catch (err4) {
254700
- debugLog2("setup", `Setup failed: ${err4}`, verbose);
254907
+ debugLog("setup", `Setup failed: ${err4}`, verbose);
254701
254908
  log.error(`Failed to setup reverse proxy: ${err4.message}`);
254702
254909
  cleanup();
254703
254910
  }
254704
254911
  }
254705
254912
  function startHttpRedirectServer(verbose) {
254706
- debugLog2("redirect", "Starting HTTP redirect server", verbose);
254913
+ debugLog("redirect", "Starting HTTP redirect server", verbose);
254707
254914
  const server = http.createServer((req, res) => {
254708
254915
  const host = req.headers.host || "";
254709
- debugLog2("redirect", `Redirecting request from ${host}${req.url} to HTTPS`, verbose);
254916
+ debugLog("redirect", `Redirecting request from ${host}${req.url} to HTTPS`, verbose);
254710
254917
  res.writeHead(301, {
254711
254918
  Location: `https://${host}${req.url}`
254712
254919
  });
254713
254920
  res.end();
254714
254921
  }).listen(80);
254715
254922
  activeServers.add(server);
254716
- debugLog2("redirect", "HTTP redirect server started", verbose);
254923
+ debugLog("redirect", "HTTP redirect server started", verbose);
254717
254924
  }
254718
254925
  function startProxy(options4) {
254719
254926
  const finalOptions = {
254720
254927
  ...config6,
254721
254928
  ...options4
254722
254929
  };
254723
- debugLog2("proxy", `Starting proxy with options: ${JSON.stringify({
254930
+ debugLog("proxy", `Starting proxy with options: ${JSON.stringify({
254724
254931
  from: finalOptions.from,
254725
254932
  to: finalOptions.to,
254726
254933
  https: finalOptions.https
254727
254934
  })}`, finalOptions.verbose);
254728
254935
  startServer(finalOptions).catch((err4) => {
254729
- debugLog2("proxy", `Failed to start proxy: ${err4}`, finalOptions.verbose);
254936
+ debugLog("proxy", `Failed to start proxy: ${err4}`, finalOptions.verbose);
254730
254937
  log.error(`Failed to start proxy: ${err4.message}`);
254731
254938
  cleanup();
254732
254939
  });
254733
254940
  }
254734
254941
  function startProxies(options4) {
254735
254942
  if (Array.isArray(options4)) {
254736
- debugLog2("proxies", `Starting multiple proxies: ${options4.length}`, options4[0]?.verbose);
254943
+ debugLog("proxies", `Starting multiple proxies: ${options4.length}`, options4[0]?.verbose);
254737
254944
  Promise.all(options4.map((option) => startServer(option))).catch((err4) => {
254738
- debugLog2("proxies", `Failed to start proxies: ${err4}`, options4[0]?.verbose);
254945
+ debugLog("proxies", `Failed to start proxies: ${err4}`, options4[0]?.verbose);
254739
254946
  log.error("Failed to start proxies:", err4);
254740
254947
  cleanup();
254741
254948
  });
254742
254949
  } else if (options4) {
254743
- debugLog2("proxies", "Starting single proxy", options4.verbose);
254950
+ debugLog("proxies", "Starting single proxy", options4.verbose);
254744
254951
  startServer(options4).catch((err4) => {
254745
- debugLog2("proxies", `Failed to start proxy: ${err4}`, options4.verbose);
254952
+ debugLog("proxies", `Failed to start proxy: ${err4}`, options4.verbose);
254746
254953
  log.error("Failed to start proxy:", err4);
254747
254954
  cleanup();
254748
254955
  });
@@ -254762,12 +254969,12 @@ cli.command("start", "Start the Reverse Proxy Server").option("--from <from>", "
254762
254969
  });
254763
254970
  cli.command("update:etc-hosts", "Update the /etc/hosts file with the proxy domains. Please note, this command requires sudo/admin permissions.").alias("update-etc-hosts").example("sudo reverse-proxy update:etc-hosts").example("sudo reverse-proxy update-etc-hosts").action(async () => {
254764
254971
  log.info("Ensuring /etc/hosts file covers the proxy domain/s...");
254765
- const hostsFilePath = os9.platform() === "win32" ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : "/etc/hosts";
254972
+ const hostsFilePath2 = os10.platform() === "win32" ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : "/etc/hosts";
254766
254973
  if (config6 && typeof config6 === "object") {
254767
254974
  const entriesToAdd = Object.entries(config6).map(([from, to]) => `127.0.0.1 ${to} # reverse-proxy mapping for ${from}`);
254768
254975
  entriesToAdd.push("127.0.0.1 localhost # essential localhost mapping");
254769
254976
  try {
254770
- let currentHostsContent = export_readFileSync(hostsFilePath, "utf8");
254977
+ let currentHostsContent = export_readFileSync(hostsFilePath2, "utf8");
254771
254978
  let updated = false;
254772
254979
  for (const entry of entriesToAdd) {
254773
254980
  const [ip, host] = entry.split(" ", 2);
@@ -254780,7 +254987,7 @@ cli.command("update:etc-hosts", "Update the /etc/hosts file with the proxy domai
254780
254987
  }
254781
254988
  }
254782
254989
  if (updated) {
254783
- export_writeFileSync(hostsFilePath, currentHostsContent, "utf8");
254990
+ export_writeFileSync(hostsFilePath2, currentHostsContent, "utf8");
254784
254991
  log.success("Hosts file updated with latest proxy domains.");
254785
254992
  } else {
254786
254993
  log.info("No new entries were added to the hosts file.");