@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/cli.js CHANGED
@@ -17,7 +17,7 @@ var __toESM = (mod, isNodeMode, target) => {
17
17
  };
18
18
 
19
19
  // bin/cli.ts
20
- import os7 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.1.1";
217848
+ var version = "0.3.0";
217849
217849
 
217850
217850
  // src/config.ts
217851
217851
  import os4 from "os";
@@ -217908,16 +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 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
+ }
218089
+
218090
+ // src/https.ts
218091
+ import os8 from "os";
218092
+ import path9 from "path";
217921
218093
 
217922
218094
  // node_modules/@stacksjs/tlsx/dist/index.js
217923
218095
  import fs22 from "fs";
@@ -217956,7 +218128,7 @@ import process82 from "process";
217956
218128
  import process102 from "process";
217957
218129
  import process182 from "process";
217958
218130
  import process112 from "process";
217959
- import os6 from "os";
218131
+ import os7 from "os";
217960
218132
  import tty33 from "tty";
217961
218133
  import process142 from "process";
217962
218134
  import process132 from "process";
@@ -217965,10 +218137,10 @@ import process162 from "process";
217965
218137
  import process172 from "process";
217966
218138
  import process192 from "process";
217967
218139
  import os23 from "os";
217968
- import path7 from "path";
218140
+ import path8 from "path";
217969
218141
  import { resolve as resolve6 } from "path";
217970
- import process22 from "process";
217971
- import fs4 from "fs";
218142
+ import process23 from "process";
218143
+ import fs6 from "fs";
217972
218144
  import path22 from "path";
217973
218145
  var __create4 = Object.create;
217974
218146
  var __getProtoOf4 = Object.getPrototypeOf;
@@ -251459,7 +251631,7 @@ function _supportsColor3(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
251459
251631
  return min;
251460
251632
  }
251461
251633
  if (process112.platform === "win32") {
251462
- const osRelease = os6.release().split(".");
251634
+ const osRelease = os7.release().split(".");
251463
251635
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
251464
251636
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
251465
251637
  }
@@ -254012,7 +254184,7 @@ function isObject33(item) {
254012
254184
  return item && typeof item === "object" && !Array.isArray(item);
254013
254185
  }
254014
254186
  async function loadConfig3({ name, cwd, defaultConfig }) {
254015
- const configPath2 = resolve6(cwd || process22.cwd(), `${name}.config`);
254187
+ const configPath2 = resolve6(cwd || process23.cwd(), `${name}.config`);
254016
254188
  try {
254017
254189
  const importedConfig = await import(configPath2);
254018
254190
  const loadedConfig = importedConfig.default || importedConfig;
@@ -254026,18 +254198,18 @@ var config42 = await loadConfig3({
254026
254198
  defaultConfig: {
254027
254199
  altNameIPs: ["127.0.0.1"],
254028
254200
  altNameURIs: ["localhost"],
254029
- organizationName: "stacksjs.org",
254201
+ organizationName: "Local Development",
254030
254202
  countryName: "US",
254031
254203
  stateName: "California",
254032
254204
  localityName: "Playa Vista",
254033
254205
  commonName: "stacks.localhost",
254034
254206
  validityDays: 180,
254035
254207
  hostCertCN: "stacks.localhost",
254036
- domain: "localhost",
254208
+ domain: "stacks.localhost",
254037
254209
  rootCAObject: { certificate: "", privateKey: "" },
254038
- caCertPath: path7.join(os23.homedir(), ".stacks", "ssl", `tlsx.localhost.ca.crt`),
254039
- certPath: path7.join(os23.homedir(), ".stacks", "ssl", `tlsx.localhost.crt`),
254040
- 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`),
254041
254213
  verbose: false
254042
254214
  }
254043
254215
  });
@@ -254053,10 +254225,10 @@ function findFoldersWithFile(rootDir, fileName) {
254053
254225
  const result2 = [];
254054
254226
  function search(dir) {
254055
254227
  try {
254056
- const files2 = fs4.readdirSync(dir);
254228
+ const files2 = fs6.readdirSync(dir);
254057
254229
  for (const file of files2) {
254058
254230
  const filePath = path22.join(dir, file);
254059
- const stats = fs4.lstatSync(filePath);
254231
+ const stats = fs6.lstatSync(filePath);
254060
254232
  if (stats.isDirectory()) {
254061
254233
  search(filePath);
254062
254234
  } else if (file === fileName) {
@@ -254070,29 +254242,29 @@ function findFoldersWithFile(rootDir, fileName) {
254070
254242
  search(rootDir);
254071
254243
  return result2;
254072
254244
  }
254073
- function debugLog(category, message, verbose) {
254245
+ function debugLog2(category, message, verbose) {
254074
254246
  if (verbose || config42.verbose) {
254075
254247
  console.debug(`[tlsx:${category}] ${message}`);
254076
254248
  }
254077
254249
  }
254078
254250
  function randomSerialNumber(verbose) {
254079
- debugLog("cert", "Generating random serial number", verbose);
254251
+ debugLog2("cert", "Generating random serial number", verbose);
254080
254252
  const serialNumber = makeNumberPositive(import_node_forge2.default.util.bytesToHex(import_node_forge2.default.random.getBytesSync(20)));
254081
- debugLog("cert", `Generated serial number: ${serialNumber}`, verbose);
254253
+ debugLog2("cert", `Generated serial number: ${serialNumber}`, verbose);
254082
254254
  return serialNumber;
254083
254255
  }
254084
254256
  function getCertNotBefore(verbose) {
254085
- debugLog("cert", "Calculating certificate not-before date", verbose);
254257
+ debugLog2("cert", "Calculating certificate not-before date", verbose);
254086
254258
  const twoDaysAgo = new Date(Date.now() - 172800000);
254087
254259
  const year = twoDaysAgo.getFullYear();
254088
254260
  const month = (twoDaysAgo.getMonth() + 1).toString().padStart(2, "0");
254089
254261
  const day = twoDaysAgo.getDate().toString().padStart(2, "0");
254090
254262
  const date = new Date(`${year}-${month}-${day}T23:59:59Z`);
254091
- debugLog("cert", `Certificate not-before date: ${date.toISOString()}`, verbose);
254263
+ debugLog2("cert", `Certificate not-before date: ${date.toISOString()}`, verbose);
254092
254264
  return date;
254093
254265
  }
254094
254266
  function getCertNotAfter(notBefore, verbose) {
254095
- debugLog("cert", "Calculating certificate not-after date", verbose);
254267
+ debugLog2("cert", "Calculating certificate not-after date", verbose);
254096
254268
  const validityDays = config42.validityDays;
254097
254269
  const daysInMillis = validityDays * 60 * 60 * 24 * 1000;
254098
254270
  const notAfterDate = new Date(notBefore.getTime() + daysInMillis);
@@ -254100,27 +254272,27 @@ function getCertNotAfter(notBefore, verbose) {
254100
254272
  const month = (notAfterDate.getMonth() + 1).toString().padStart(2, "0");
254101
254273
  const day = notAfterDate.getDate().toString().padStart(2, "0");
254102
254274
  const date = new Date(`${year}-${month}-${day}T23:59:59Z`);
254103
- 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);
254104
254276
  return date;
254105
254277
  }
254106
254278
  function getCANotAfter(notBefore, verbose) {
254107
- debugLog("cert", "Calculating CA not-after date", verbose);
254279
+ debugLog2("cert", "Calculating CA not-after date", verbose);
254108
254280
  const year = notBefore.getFullYear() + 100;
254109
254281
  const month = (notBefore.getMonth() + 1).toString().padStart(2, "0");
254110
254282
  const day = notBefore.getDate().toString().padStart(2, "0");
254111
254283
  const date = new Date(`${year}-${month}-${day}T23:59:59Z`);
254112
- 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);
254113
254285
  return date;
254114
254286
  }
254115
254287
  async function createRootCA(options23) {
254116
- debugLog("ca", "Creating new Root CA Certificate", options23?.verbose);
254117
- 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);
254118
254290
  const { privateKey, publicKey } = import_node_forge2.pki.rsa.generateKeyPair(2048);
254119
254291
  const mergedOptions = {
254120
254292
  ...config42,
254121
254293
  ...options23 || {}
254122
254294
  };
254123
- debugLog("ca", "Setting certificate attributes", options23?.verbose);
254295
+ debugLog2("ca", "Setting certificate attributes", options23?.verbose);
254124
254296
  const attributes = [
254125
254297
  { shortName: "C", value: mergedOptions.countryName },
254126
254298
  { shortName: "ST", value: mergedOptions.stateName },
@@ -254128,7 +254300,7 @@ async function createRootCA(options23) {
254128
254300
  { shortName: "O", value: "Local Development CA" },
254129
254301
  { shortName: "CN", value: "Local Development Root CA" }
254130
254302
  ];
254131
- debugLog("ca", "Setting certificate extensions", options23?.verbose);
254303
+ debugLog2("ca", "Setting certificate extensions", options23?.verbose);
254132
254304
  const extensions = [
254133
254305
  {
254134
254306
  name: "basicConstraints",
@@ -254145,7 +254317,7 @@ async function createRootCA(options23) {
254145
254317
  name: "subjectKeyIdentifier"
254146
254318
  }
254147
254319
  ];
254148
- debugLog("ca", "Creating CA certificate", options23?.verbose);
254320
+ debugLog2("ca", "Creating CA certificate", options23?.verbose);
254149
254321
  const caCert = import_node_forge2.pki.createCertificate();
254150
254322
  caCert.publicKey = publicKey;
254151
254323
  caCert.serialNumber = randomSerialNumber(options23?.verbose);
@@ -254154,11 +254326,11 @@ async function createRootCA(options23) {
254154
254326
  caCert.setSubject(attributes);
254155
254327
  caCert.setIssuer(attributes);
254156
254328
  caCert.setExtensions(extensions);
254157
- debugLog("ca", "Signing certificate with SHA-256", options23?.verbose);
254329
+ debugLog2("ca", "Signing certificate with SHA-256", options23?.verbose);
254158
254330
  caCert.sign(privateKey, import_node_forge2.default.md.sha256.create());
254159
254331
  const pemCert = import_node_forge2.pki.certificateToPem(caCert);
254160
254332
  const pemKey = import_node_forge2.pki.privateKeyToPem(privateKey);
254161
- debugLog("ca", "Root CA Certificate created successfully", options23?.verbose);
254333
+ debugLog2("ca", "Root CA Certificate created successfully", options23?.verbose);
254162
254334
  return {
254163
254335
  certificate: pemCert,
254164
254336
  privateKey: pemKey,
@@ -254167,34 +254339,41 @@ async function createRootCA(options23) {
254167
254339
  };
254168
254340
  }
254169
254341
  async function generateCert(options23) {
254170
- debugLog("cert", "Generating new host certificate", options23?.verbose);
254171
- 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);
254172
254344
  if (!options23?.hostCertCN?.trim()) {
254173
- debugLog("cert", "Error: hostCertCN is required", options23?.verbose);
254345
+ debugLog2("cert", "Error: hostCertCN is required", options23?.verbose);
254174
254346
  throw new Error('"hostCertCN" must be a String');
254175
254347
  }
254176
254348
  if (!options23.domain?.trim()) {
254177
- debugLog("cert", "Error: domain is required", options23?.verbose);
254349
+ debugLog2("cert", "Error: domain is required", options23?.verbose);
254178
254350
  throw new Error('"domain" must be a String');
254179
254351
  }
254180
254352
  if (!options23.rootCAObject || !options23.rootCAObject.certificate || !options23.rootCAObject.privateKey) {
254181
- debugLog("cert", "Error: rootCAObject is invalid or missing", options23?.verbose);
254353
+ debugLog2("cert", "Error: rootCAObject is invalid or missing", options23?.verbose);
254182
254354
  throw new Error('"rootCAObject" must be an Object with the properties "certificate" & "privateKey"');
254183
254355
  }
254184
- debugLog("cert", "Converting Root CA PEM to forge objects", options23?.verbose);
254356
+ debugLog2("cert", "Converting Root CA PEM to forge objects", options23?.verbose);
254185
254357
  const caCert = import_node_forge2.pki.certificateFromPem(options23.rootCAObject.certificate);
254186
254358
  const caKey = import_node_forge2.pki.privateKeyFromPem(options23.rootCAObject.privateKey);
254187
- 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);
254188
254360
  const hostKeys = import_node_forge2.pki.rsa.generateKeyPair(2048);
254189
- debugLog("cert", "Setting certificate attributes", options23?.verbose);
254361
+ debugLog2("cert", "Setting certificate attributes", options23?.verbose);
254190
254362
  const attributes = [
254191
- { shortName: "C", value: config42.countryName },
254192
- { shortName: "ST", value: config42.stateName },
254193
- { shortName: "L", value: config42.localityName },
254194
- { shortName: "O", value: "Local Development" },
254195
- { 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 }
254196
254368
  ];
254197
- 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);
254198
254377
  const extensions = [
254199
254378
  {
254200
254379
  name: "basicConstraints",
@@ -254214,30 +254393,25 @@ async function generateCert(options23) {
254214
254393
  },
254215
254394
  {
254216
254395
  name: "subjectAltName",
254217
- altNames: [
254218
- { type: 2, value: "*.localhost" },
254219
- { type: 2, value: "localhost" },
254220
- { type: 2, value: "stacks.localhost" },
254221
- { type: 2, value: options23.domain }
254222
- ]
254396
+ altNames
254223
254397
  }
254224
254398
  ];
254225
- debugLog("cert", "Creating new host certificate", options23?.verbose);
254399
+ debugLog2("cert", "Creating new host certificate", options23?.verbose);
254226
254400
  const newHostCert = import_node_forge2.pki.createCertificate();
254227
254401
  newHostCert.publicKey = hostKeys.publicKey;
254228
- debugLog("cert", "Setting certificate properties", options23?.verbose);
254402
+ debugLog2("cert", "Setting certificate properties", options23?.verbose);
254229
254403
  newHostCert.serialNumber = randomSerialNumber(options23?.verbose);
254230
254404
  newHostCert.validity.notBefore = getCertNotBefore(options23?.verbose);
254231
254405
  newHostCert.validity.notAfter = getCertNotAfter(newHostCert.validity.notBefore, options23?.verbose);
254232
254406
  newHostCert.setSubject(attributes);
254233
254407
  newHostCert.setIssuer(caCert.subject.attributes);
254234
254408
  newHostCert.setExtensions(extensions);
254235
- debugLog("cert", "Signing certificate with SHA-256", options23?.verbose);
254409
+ debugLog2("cert", "Signing certificate with SHA-256", options23?.verbose);
254236
254410
  newHostCert.sign(caKey, import_node_forge2.default.md.sha256.create());
254237
- debugLog("cert", "Converting certificate to PEM format", options23?.verbose);
254411
+ debugLog2("cert", "Converting certificate to PEM format", options23?.verbose);
254238
254412
  const pemHostCert = import_node_forge2.pki.certificateToPem(newHostCert);
254239
254413
  const pemHostKey = import_node_forge2.pki.privateKeyToPem(hostKeys.privateKey);
254240
- debugLog("cert", "Host certificate generated successfully", options23?.verbose);
254414
+ debugLog2("cert", "Host certificate generated successfully", options23?.verbose);
254241
254415
  return {
254242
254416
  certificate: pemHostCert,
254243
254417
  privateKey: pemHostKey,
@@ -254246,81 +254420,81 @@ async function generateCert(options23) {
254246
254420
  };
254247
254421
  }
254248
254422
  async function addCertToSystemTrustStoreAndSaveCert(cert, caCert, options23) {
254249
- debugLog("trust", `Adding certificate to system trust store with options: ${JSON.stringify(options23)}`, options23?.verbose);
254250
- 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);
254251
254425
  const certPath = storeCert(cert, options23);
254252
- debugLog("trust", "Storing CA certificate", options23?.verbose);
254426
+ debugLog2("trust", "Storing CA certificate", options23?.verbose);
254253
254427
  const caCertPath = storeCACert(caCert, options23);
254254
254428
  const platform23 = os42.platform();
254255
- debugLog("trust", `Detected platform: ${platform23}`, options23?.verbose);
254429
+ debugLog2("trust", `Detected platform: ${platform23}`, options23?.verbose);
254256
254430
  const args = "TC, C, C";
254257
254431
  if (platform23 === "darwin") {
254258
- debugLog("trust", "Adding certificate to macOS keychain", options23?.verbose);
254432
+ debugLog2("trust", "Adding certificate to macOS keychain", options23?.verbose);
254259
254433
  await runCommand2(`sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${caCertPath}`);
254260
254434
  } else if (platform23 === "win32") {
254261
- debugLog("trust", "Adding certificate to Windows certificate store", options23?.verbose);
254435
+ debugLog2("trust", "Adding certificate to Windows certificate store", options23?.verbose);
254262
254436
  await runCommand2(`certutil -f -v -addstore -enterprise Root ${caCertPath}`);
254263
254437
  } else if (platform23 === "linux") {
254264
- debugLog("trust", "Adding certificate to Linux certificate store", options23?.verbose);
254438
+ debugLog2("trust", "Adding certificate to Linux certificate store", options23?.verbose);
254265
254439
  const rootDirectory = os42.homedir();
254266
254440
  const targetFileName = "cert9.db";
254267
- debugLog("trust", `Searching for certificate databases in ${rootDirectory}`, options23?.verbose);
254441
+ debugLog2("trust", `Searching for certificate databases in ${rootDirectory}`, options23?.verbose);
254268
254442
  const foldersWithFile = findFoldersWithFile(rootDirectory, targetFileName);
254269
254443
  for (const folder of foldersWithFile) {
254270
- debugLog("trust", `Processing certificate database in ${folder}`, options23?.verbose);
254444
+ debugLog2("trust", `Processing certificate database in ${folder}`, options23?.verbose);
254271
254445
  try {
254272
- 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);
254273
254447
  await runCommand2(`certutil -d sql:${folder} -D -n ${config42.commonName}`);
254274
254448
  } catch (error) {
254275
- debugLog("trust", `Warning: Error deleting existing cert: ${error}`, options23?.verbose);
254449
+ debugLog2("trust", `Warning: Error deleting existing cert: ${error}`, options23?.verbose);
254276
254450
  console.warn(`Error deleting existing cert: ${error}`);
254277
254451
  }
254278
- debugLog("trust", `Adding new certificate to ${folder}`, options23?.verbose);
254452
+ debugLog2("trust", `Adding new certificate to ${folder}`, options23?.verbose);
254279
254453
  await runCommand2(`certutil -d sql:${folder} -A -t ${args} -n ${config42.commonName} -i ${caCertPath}`);
254280
254454
  log3.info(`Cert added to ${folder}`);
254281
254455
  }
254282
254456
  } else {
254283
- debugLog("trust", `Error: Unsupported platform ${platform23}`, options23?.verbose);
254457
+ debugLog2("trust", `Error: Unsupported platform ${platform23}`, options23?.verbose);
254284
254458
  throw new Error(`Unsupported platform: ${platform23}`);
254285
254459
  }
254286
- debugLog("trust", "Certificate successfully added to system trust store", options23?.verbose);
254460
+ debugLog2("trust", "Certificate successfully added to system trust store", options23?.verbose);
254287
254461
  return certPath;
254288
254462
  }
254289
254463
  function storeCert(cert, options23) {
254290
- 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);
254291
254465
  const certPath = options23?.certPath || config42.certPath;
254292
254466
  const certKeyPath = options23?.keyPath || config42.keyPath;
254293
- debugLog("storage", `Certificate path: ${certPath}`, options23?.verbose);
254294
- 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);
254295
254469
  const certDir = path32.dirname(certPath);
254296
254470
  if (!fs22.existsSync(certDir)) {
254297
- debugLog("storage", `Creating certificate directory: ${certDir}`, options23?.verbose);
254471
+ debugLog2("storage", `Creating certificate directory: ${certDir}`, options23?.verbose);
254298
254472
  fs22.mkdirSync(certDir, { recursive: true });
254299
254473
  }
254300
- debugLog("storage", "Writing certificate file", options23?.verbose);
254474
+ debugLog2("storage", "Writing certificate file", options23?.verbose);
254301
254475
  fs22.writeFileSync(certPath, cert.certificate);
254302
254476
  const certKeyDir = path32.dirname(certKeyPath);
254303
254477
  if (!fs22.existsSync(certKeyDir)) {
254304
- debugLog("storage", `Creating private key directory: ${certKeyDir}`, options23?.verbose);
254478
+ debugLog2("storage", `Creating private key directory: ${certKeyDir}`, options23?.verbose);
254305
254479
  fs22.mkdirSync(certKeyDir, { recursive: true });
254306
254480
  }
254307
- debugLog("storage", "Writing private key file", options23?.verbose);
254481
+ debugLog2("storage", "Writing private key file", options23?.verbose);
254308
254482
  fs22.writeFileSync(certKeyPath, cert.privateKey);
254309
- debugLog("storage", "Certificate and private key stored successfully", options23?.verbose);
254483
+ debugLog2("storage", "Certificate and private key stored successfully", options23?.verbose);
254310
254484
  return certPath;
254311
254485
  }
254312
254486
  function storeCACert(caCert, options23) {
254313
- debugLog("storage", "Storing CA certificate", options23?.verbose);
254487
+ debugLog2("storage", "Storing CA certificate", options23?.verbose);
254314
254488
  const caCertPath = options23?.caCertPath || config42.caCertPath;
254315
- debugLog("storage", `CA certificate path: ${caCertPath}`, options23?.verbose);
254489
+ debugLog2("storage", `CA certificate path: ${caCertPath}`, options23?.verbose);
254316
254490
  const caCertDir = path32.dirname(caCertPath);
254317
254491
  if (!fs22.existsSync(caCertDir)) {
254318
- debugLog("storage", `Creating CA certificate directory: ${caCertDir}`, options23?.verbose);
254492
+ debugLog2("storage", `Creating CA certificate directory: ${caCertDir}`, options23?.verbose);
254319
254493
  fs22.mkdirSync(caCertDir, { recursive: true });
254320
254494
  }
254321
- debugLog("storage", "Writing CA certificate file", options23?.verbose);
254495
+ debugLog2("storage", "Writing CA certificate file", options23?.verbose);
254322
254496
  fs22.writeFileSync(caCertPath, caCert);
254323
- debugLog("storage", "CA certificate stored successfully", options23?.verbose);
254497
+ debugLog2("storage", "CA certificate stored successfully", options23?.verbose);
254324
254498
  return caCertPath;
254325
254499
  }
254326
254500
  var export_tls = import_node_forge2.tls;
@@ -254328,8 +254502,37 @@ var export_pki = import_node_forge2.pki;
254328
254502
  var export_forge = import_node_forge2.default;
254329
254503
 
254330
254504
  // src/https.ts
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
+ }
254331
254530
  async function generateCertificate(domain) {
254332
- domain = domain ?? config6.https.altNameURIs[0];
254531
+ if (config6.https === true)
254532
+ config6.https = httpsConfig();
254533
+ else if (config6.https === false)
254534
+ return;
254535
+ domain = domain ?? config6.https.domain;
254333
254536
  log.info(`Generating a self-signed SSL certificate for: ${domain}`);
254334
254537
  const caCert = await createRootCA(config6.https);
254335
254538
  const hostCert = await generateCert({
@@ -254352,70 +254555,89 @@ async function generateCertificate(domain) {
254352
254555
  log.success("Certificate generated");
254353
254556
  }
254354
254557
 
254355
- // src/utils.ts
254356
- function debugLog2(category, message, verbose) {
254357
- if (verbose || config6.verbose) {
254358
- console.debug(`[rpx:${category}] ${message}`);
254359
- }
254360
- }
254361
-
254362
254558
  // src/start.ts
254363
254559
  var activeServers = new Set;
254364
- function cleanup() {
254365
- debugLog2("cleanup", "Starting cleanup process", config6.verbose);
254560
+ async function cleanup() {
254561
+ debugLog("cleanup", "Starting cleanup process", config6.verbose);
254366
254562
  console.log(`\n`);
254367
254563
  log.info("Shutting down proxy servers...");
254368
- const closePromises = Array.from(activeServers).map((server) => new Promise((resolve5) => {
254564
+ const cleanupPromises = [];
254565
+ const serverClosePromises = Array.from(activeServers).map((server) => new Promise((resolve5) => {
254369
254566
  server.close(() => {
254370
- debugLog2("cleanup", "Server closed successfully", config6.verbose);
254567
+ debugLog("cleanup", "Server closed successfully", config6.verbose);
254371
254568
  resolve5();
254372
254569
  });
254373
254570
  }));
254374
- Promise.all(closePromises).then(() => {
254375
- debugLog2("cleanup", "All servers closed successfully", config6.verbose);
254376
- log.success("All proxy servers shut down successfully");
254377
- process23.exit(0);
254378
- }).catch((err4) => {
254379
- debugLog2("cleanup", `Error during shutdown: ${err4}`, config6.verbose);
254380
- log.error("Error during shutdown:", err4);
254381
- process23.exit(1);
254382
- });
254383
- }
254384
- process23.on("SIGINT", cleanup);
254385
- process23.on("SIGTERM", cleanup);
254386
- process23.on("uncaughtException", (err4) => {
254387
- 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);
254388
254606
  log.error("Uncaught exception:", err4);
254389
254607
  cleanup();
254390
254608
  });
254391
254609
  async function loadSSLConfig(options4) {
254392
- debugLog2("ssl", "Loading SSL configuration", options4.verbose);
254610
+ debugLog("ssl", "Loading SSL configuration", options4.verbose);
254611
+ if (options4.https === true)
254612
+ options4.https = httpsConfig();
254613
+ else if (options4.https === false)
254614
+ return null;
254393
254615
  if (!options4.https?.keyPath && !options4.https?.certPath) {
254394
- debugLog2("ssl", "No SSL configuration provided", options4.verbose);
254616
+ debugLog("ssl", "No SSL configuration provided", options4.verbose);
254395
254617
  return null;
254396
254618
  }
254397
254619
  if (options4.https?.keyPath && !options4.https?.certPath || !options4.https?.keyPath && options4.https?.certPath) {
254398
254620
  const missing = !options4.https?.keyPath ? "keyPath" : "certPath";
254399
- debugLog2("ssl", `Invalid SSL configuration - missing ${missing}`, options4.verbose);
254621
+ debugLog("ssl", `Invalid SSL configuration - missing ${missing}`, options4.verbose);
254400
254622
  throw new Error(`SSL Configuration requires both keyPath and certPath. Missing: ${missing}`);
254401
254623
  }
254402
254624
  try {
254403
254625
  if (!options4.https?.keyPath || !options4.https?.certPath)
254404
254626
  return null;
254405
254627
  try {
254406
- debugLog2("ssl", "Reading SSL certificate files", options4.verbose);
254407
- const key = await fs6.promises.readFile(options4.https?.keyPath, "utf8");
254408
- const cert = await fs6.promises.readFile(options4.https?.certPath, "utf8");
254409
- 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);
254410
254632
  return { key, cert };
254411
254633
  } catch (error) {
254412
254634
  if (error.code === "ENOENT") {
254413
- debugLog2("ssl", "Certificates not found, generating new ones", options4.verbose);
254414
- await generateCertificate();
254415
- debugLog2("ssl", "Reading newly generated certificates", options4.verbose);
254416
- const key = await fs6.promises.readFile(options4.https?.keyPath, "utf8");
254417
- const cert = await fs6.promises.readFile(options4.https?.certPath, "utf8");
254418
- 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);
254419
254641
  return { key, cert };
254420
254642
  }
254421
254643
  throw error;
@@ -254423,22 +254645,22 @@ async function loadSSLConfig(options4) {
254423
254645
  } catch (err4) {
254424
254646
  const error = err4;
254425
254647
  const detail = error.code === "ENOENT" ? `File not found: ${error.path}` : error.message;
254426
- debugLog2("ssl", `SSL configuration error: ${error}`, options4.verbose);
254648
+ debugLog("ssl", `SSL configuration error: ${error}`, options4.verbose);
254427
254649
  throw new Error(`SSL Configuration Error: ${detail}`);
254428
254650
  }
254429
254651
  }
254430
254652
  function isPortInUse(port, hostname, verbose) {
254431
- 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);
254432
254654
  return new Promise((resolve5) => {
254433
254655
  const server = net.createServer();
254434
254656
  server.once("error", (err4) => {
254435
254657
  if (err4.code === "EADDRINUSE") {
254436
- debugLog2("port", `Port ${port} is in use`, verbose);
254658
+ debugLog("port", `Port ${port} is in use`, verbose);
254437
254659
  resolve5(true);
254438
254660
  }
254439
254661
  });
254440
254662
  server.once("listening", () => {
254441
- debugLog2("port", `Port ${port} is available`, verbose);
254663
+ debugLog("port", `Port ${port} is available`, verbose);
254442
254664
  server.close();
254443
254665
  resolve5(false);
254444
254666
  });
@@ -254446,17 +254668,17 @@ function isPortInUse(port, hostname, verbose) {
254446
254668
  });
254447
254669
  }
254448
254670
  async function findAvailablePort(startPort, hostname, verbose) {
254449
- debugLog2("port", `Finding available port starting from ${startPort}`, verbose);
254671
+ debugLog("port", `Finding available port starting from ${startPort}`, verbose);
254450
254672
  let port = startPort;
254451
254673
  while (await isPortInUse(port, hostname, verbose)) {
254452
- debugLog2("port", `Port ${port} is in use, trying ${port + 1}`, verbose);
254674
+ debugLog("port", `Port ${port} is in use, trying ${port + 1}`, verbose);
254453
254675
  port++;
254454
254676
  }
254455
- debugLog2("port", `Found available port: ${port}`, verbose);
254677
+ debugLog("port", `Found available port: ${port}`, verbose);
254456
254678
  return port;
254457
254679
  }
254458
254680
  async function testConnection(hostname, port, verbose) {
254459
- debugLog2("connection", `Testing connection to ${hostname}:${port}`, verbose);
254681
+ debugLog("connection", `Testing connection to ${hostname}:${port}`, verbose);
254460
254682
  return new Promise((resolve5, reject) => {
254461
254683
  const socket = net.connect({
254462
254684
  host: hostname,
@@ -254464,54 +254686,89 @@ async function testConnection(hostname, port, verbose) {
254464
254686
  timeout: 5000
254465
254687
  });
254466
254688
  socket.once("connect", () => {
254467
- debugLog2("connection", `Successfully connected to ${hostname}:${port}`, verbose);
254689
+ debugLog("connection", `Successfully connected to ${hostname}:${port}`, verbose);
254468
254690
  socket.end();
254469
254691
  resolve5();
254470
254692
  });
254471
254693
  socket.once("timeout", () => {
254472
- debugLog2("connection", `Connection to ${hostname}:${port} timed out`, verbose);
254694
+ debugLog("connection", `Connection to ${hostname}:${port} timed out`, verbose);
254473
254695
  socket.destroy();
254474
254696
  reject(new Error(`Connection to ${hostname}:${port} timed out`));
254475
254697
  });
254476
254698
  socket.once("error", (err4) => {
254477
- debugLog2("connection", `Failed to connect to ${hostname}:${port}: ${err4}`, verbose);
254699
+ debugLog("connection", `Failed to connect to ${hostname}:${port}: ${err4}`, verbose);
254478
254700
  socket.destroy();
254479
254701
  reject(new Error(`Failed to connect to ${hostname}:${port}: ${err4.message}`));
254480
254702
  });
254481
254703
  });
254482
254704
  }
254483
254705
  async function startServer(options4) {
254484
- debugLog2("server", `Starting server with options: ${JSON.stringify(options4)}`, options4?.verbose);
254706
+ debugLog("server", `Starting server with options: ${JSON.stringify(options4)}`, options4?.verbose);
254485
254707
  if (!options4)
254486
254708
  options4 = config6;
254487
254709
  if (!options4.from)
254488
254710
  options4.from = config6.from;
254489
254711
  if (!options4.to)
254490
254712
  options4.to = config6.to;
254491
- if (config6.https) {
254492
- const domain = config6.https.altNameURIs?.[0] || new URL(options4.to).hostname;
254493
- options4.keyPath = config6.https.keyPath || `/Users/${process23.env.USER}/.stacks/ssl/${domain}.crt.key`;
254494
- options4.certPath = config6.https.certPath || `/Users/${process23.env.USER}/.stacks/ssl/${domain}.crt`;
254495
- debugLog2("server", `HTTPS enabled, using cert paths: ${options4.keyPath}, ${options4.certPath}`, options4.verbose);
254496
- }
254497
254713
  const fromUrl = new URL(options4.from.startsWith("http") ? options4.from : `http://${options4.from}`);
254498
254714
  const toUrl = new URL(options4.to.startsWith("http") ? options4.to : `http://${options4.to}`);
254499
254715
  const fromPort = Number.parseInt(fromUrl.port) || (fromUrl.protocol.includes("https:") ? 443 : 80);
254500
- debugLog2("server", `Parsed URLs - from: ${fromUrl}, to: ${toUrl}`, options4.verbose);
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
+ }
254747
+ if (config6.https) {
254748
+ if (config6.https === true)
254749
+ config6.https = httpsConfig();
254750
+ const domain = toUrl.hostname;
254751
+ if (typeof options4.https !== "boolean" && options4.https) {
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);
254755
+ }
254756
+ }
254757
+ debugLog("server", `Parsed URLs - from: ${fromUrl}, to: ${toUrl}`, options4.verbose);
254501
254758
  try {
254502
254759
  await testConnection(fromUrl.hostname, fromPort, options4.verbose);
254503
254760
  } catch (err4) {
254504
- debugLog2("server", `Connection test failed: ${err4}`, options4.verbose);
254761
+ debugLog("server", `Connection test failed: ${err4}`, options4.verbose);
254505
254762
  log.error(err4.message);
254506
- process23.exit(1);
254763
+ process24.exit(1);
254507
254764
  }
254508
254765
  let sslConfig = null;
254509
254766
  if (config6.https) {
254510
254767
  try {
254511
254768
  sslConfig = await loadSSLConfig(options4);
254512
254769
  } catch (err4) {
254513
- debugLog2("server", `SSL config failed, attempting to generate certificates: ${err4}`, options4.verbose);
254514
- await generateCertificate();
254770
+ debugLog("server", `SSL config failed, attempting to generate certificates: ${err4}`, options4.verbose);
254771
+ await generateCertificate(options4.to);
254515
254772
  sslConfig = await loadSSLConfig(options4);
254516
254773
  }
254517
254774
  }
@@ -254528,9 +254785,9 @@ async function startServer(options4) {
254528
254785
  });
254529
254786
  }
254530
254787
  async function createProxyServer(from, to, fromPort, listenPort, hostname, sourceUrl, ssl, verbose) {
254531
- debugLog2("proxy", `Creating proxy server ${from} -> ${to}`, verbose);
254788
+ debugLog("proxy", `Creating proxy server ${from} -> ${to}`, verbose);
254532
254789
  const requestHandler = (req, res) => {
254533
- debugLog2("request", `Incoming request: ${req.method} ${req.url}`, verbose);
254790
+ debugLog("request", `Incoming request: ${req.method} ${req.url}`, verbose);
254534
254791
  const proxyOptions = {
254535
254792
  hostname: sourceUrl.hostname,
254536
254793
  port: fromPort,
@@ -254541,9 +254798,9 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
254541
254798
  host: sourceUrl.host
254542
254799
  }
254543
254800
  };
254544
- debugLog2("request", `Proxy request options: ${JSON.stringify(proxyOptions)}`, verbose);
254801
+ debugLog("request", `Proxy request options: ${JSON.stringify(proxyOptions)}`, verbose);
254545
254802
  const proxyReq = http.request(proxyOptions, (proxyRes) => {
254546
- debugLog2("response", `Proxy response received with status ${proxyRes.statusCode}`, verbose);
254803
+ debugLog("response", `Proxy response received with status ${proxyRes.statusCode}`, verbose);
254547
254804
  const headers = {
254548
254805
  ...proxyRes.headers,
254549
254806
  "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
@@ -254553,7 +254810,7 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
254553
254810
  proxyRes.pipe(res);
254554
254811
  });
254555
254812
  proxyReq.on("error", (err4) => {
254556
- debugLog2("request", `Proxy request failed: ${err4}`, verbose);
254813
+ debugLog("request", `Proxy request failed: ${err4}`, verbose);
254557
254814
  log.error("Proxy request failed:", err4);
254558
254815
  res.writeHead(502);
254559
254816
  res.end(`Proxy Error: ${err4.message}`);
@@ -254580,11 +254837,11 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
254580
254837
  allowHTTP1: true,
254581
254838
  ALPNProtocols: ["h2", "http/1.1"]
254582
254839
  } : undefined;
254583
- debugLog2("server", `Creating server with SSL config: ${!!ssl}`, verbose);
254840
+ debugLog("server", `Creating server with SSL config: ${!!ssl}`, verbose);
254584
254841
  const server = ssl && serverOptions ? https.createServer(serverOptions, requestHandler) : http.createServer(requestHandler);
254585
254842
  if (ssl) {
254586
254843
  server.on("secureConnection", (tlsSocket) => {
254587
- debugLog2("tls", `TLS Connection established: ${JSON.stringify({
254844
+ debugLog("tls", `TLS Connection established: ${JSON.stringify({
254588
254845
  protocol: tlsSocket.getProtocol?.(),
254589
254846
  cipher: tlsSocket.getCipher?.(),
254590
254847
  authorized: tlsSocket.authorized,
@@ -254595,7 +254852,7 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
254595
254852
  activeServers.add(server);
254596
254853
  return new Promise((resolve5, reject) => {
254597
254854
  server.listen(listenPort, hostname, () => {
254598
- debugLog2("server", `Server listening on port ${listenPort}`, verbose);
254855
+ debugLog("server", `Server listening on port ${listenPort}`, verbose);
254599
254856
  console.log("");
254600
254857
  console.log(` ${green(bold("reverse-proxy"))} ${green(`v${version}`)}`);
254601
254858
  console.log("");
@@ -254612,13 +254869,13 @@ async function createProxyServer(from, to, fromPort, listenPort, hostname, sourc
254612
254869
  resolve5();
254613
254870
  });
254614
254871
  server.on("error", (err4) => {
254615
- debugLog2("server", `Server error: ${err4}`, verbose);
254872
+ debugLog("server", `Server error: ${err4}`, verbose);
254616
254873
  reject(err4);
254617
254874
  });
254618
254875
  });
254619
254876
  }
254620
254877
  async function setupReverseProxy(options4) {
254621
- debugLog2("setup", `Setting up reverse proxy: ${JSON.stringify(options4)}`, options4.verbose);
254878
+ debugLog("setup", `Setting up reverse proxy: ${JSON.stringify(options4)}`, options4.verbose);
254622
254879
  const { from, to, fromPort, sourceUrl, ssl, verbose } = options4;
254623
254880
  const httpPort = 80;
254624
254881
  const httpsPort = 443;
@@ -254627,73 +254884,72 @@ async function setupReverseProxy(options4) {
254627
254884
  if (ssl) {
254628
254885
  const isHttpPortBusy = await isPortInUse(httpPort, hostname, verbose);
254629
254886
  if (!isHttpPortBusy) {
254630
- debugLog2("setup", "Starting HTTP redirect server", verbose);
254887
+ debugLog("setup", "Starting HTTP redirect server", verbose);
254631
254888
  startHttpRedirectServer(verbose);
254632
254889
  } else {
254633
- debugLog2("setup", "Port 80 is in use, skipping HTTP redirect", verbose);
254890
+ debugLog("setup", "Port 80 is in use, skipping HTTP redirect", verbose);
254634
254891
  log.warn("Port 80 is in use, HTTP to HTTPS redirect will not be available");
254635
254892
  }
254636
254893
  }
254637
254894
  const targetPort = ssl ? httpsPort : httpPort;
254638
254895
  const isTargetPortBusy = await isPortInUse(targetPort, hostname, verbose);
254639
254896
  if (isTargetPortBusy) {
254640
- debugLog2("setup", `Port ${targetPort} is busy, finding alternative`, verbose);
254897
+ debugLog("setup", `Port ${targetPort} is busy, finding alternative`, verbose);
254641
254898
  const availablePort = await findAvailablePort(ssl ? 8443 : 8080, hostname, verbose);
254642
254899
  log.warn(`Port ${targetPort} is in use. Using port ${availablePort} instead.`);
254643
254900
  log.info(`You can use 'sudo lsof -i :${targetPort}' (Unix) or 'netstat -ano | findstr :${targetPort}' (Windows) to check what's using the port.`);
254644
254901
  await createProxyServer(from, to, fromPort, availablePort, hostname, sourceUrl, ssl, verbose);
254645
254902
  } else {
254646
- debugLog2("setup", `Using default port ${targetPort}`, verbose);
254903
+ debugLog("setup", `Using default port ${targetPort}`, verbose);
254647
254904
  await createProxyServer(from, to, fromPort, targetPort, hostname, sourceUrl, ssl, verbose);
254648
254905
  }
254649
254906
  } catch (err4) {
254650
- debugLog2("setup", `Setup failed: ${err4}`, verbose);
254907
+ debugLog("setup", `Setup failed: ${err4}`, verbose);
254651
254908
  log.error(`Failed to setup reverse proxy: ${err4.message}`);
254652
254909
  cleanup();
254653
254910
  }
254654
254911
  }
254655
254912
  function startHttpRedirectServer(verbose) {
254656
- debugLog2("redirect", "Starting HTTP redirect server", verbose);
254913
+ debugLog("redirect", "Starting HTTP redirect server", verbose);
254657
254914
  const server = http.createServer((req, res) => {
254658
254915
  const host = req.headers.host || "";
254659
- debugLog2("redirect", `Redirecting request from ${host}${req.url} to HTTPS`, verbose);
254916
+ debugLog("redirect", `Redirecting request from ${host}${req.url} to HTTPS`, verbose);
254660
254917
  res.writeHead(301, {
254661
254918
  Location: `https://${host}${req.url}`
254662
254919
  });
254663
254920
  res.end();
254664
254921
  }).listen(80);
254665
254922
  activeServers.add(server);
254666
- debugLog2("redirect", "HTTP redirect server started", verbose);
254923
+ debugLog("redirect", "HTTP redirect server started", verbose);
254667
254924
  }
254668
254925
  function startProxy(options4) {
254669
254926
  const finalOptions = {
254670
254927
  ...config6,
254671
254928
  ...options4
254672
254929
  };
254673
- debugLog2("proxy", `Starting proxy with options: ${JSON.stringify({
254930
+ debugLog("proxy", `Starting proxy with options: ${JSON.stringify({
254674
254931
  from: finalOptions.from,
254675
254932
  to: finalOptions.to,
254676
- keyPath: finalOptions.https.keyPath,
254677
- certPath: finalOptions.https.certPath
254933
+ https: finalOptions.https
254678
254934
  })}`, finalOptions.verbose);
254679
254935
  startServer(finalOptions).catch((err4) => {
254680
- debugLog2("proxy", `Failed to start proxy: ${err4}`, finalOptions.verbose);
254936
+ debugLog("proxy", `Failed to start proxy: ${err4}`, finalOptions.verbose);
254681
254937
  log.error(`Failed to start proxy: ${err4.message}`);
254682
254938
  cleanup();
254683
254939
  });
254684
254940
  }
254685
254941
  function startProxies(options4) {
254686
254942
  if (Array.isArray(options4)) {
254687
- debugLog2("proxies", `Starting multiple proxies: ${options4.length}`, options4[0]?.verbose);
254943
+ debugLog("proxies", `Starting multiple proxies: ${options4.length}`, options4[0]?.verbose);
254688
254944
  Promise.all(options4.map((option) => startServer(option))).catch((err4) => {
254689
- debugLog2("proxies", `Failed to start proxies: ${err4}`, options4[0]?.verbose);
254945
+ debugLog("proxies", `Failed to start proxies: ${err4}`, options4[0]?.verbose);
254690
254946
  log.error("Failed to start proxies:", err4);
254691
254947
  cleanup();
254692
254948
  });
254693
254949
  } else if (options4) {
254694
- debugLog2("proxies", "Starting single proxy", options4.verbose);
254950
+ debugLog("proxies", "Starting single proxy", options4.verbose);
254695
254951
  startServer(options4).catch((err4) => {
254696
- debugLog2("proxies", `Failed to start proxy: ${err4}`, options4.verbose);
254952
+ debugLog("proxies", `Failed to start proxy: ${err4}`, options4.verbose);
254697
254953
  log.error("Failed to start proxy:", err4);
254698
254954
  cleanup();
254699
254955
  });
@@ -254713,12 +254969,12 @@ cli.command("start", "Start the Reverse Proxy Server").option("--from <from>", "
254713
254969
  });
254714
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 () => {
254715
254971
  log.info("Ensuring /etc/hosts file covers the proxy domain/s...");
254716
- const hostsFilePath = os7.platform() === "win32" ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : "/etc/hosts";
254972
+ const hostsFilePath2 = os10.platform() === "win32" ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : "/etc/hosts";
254717
254973
  if (config6 && typeof config6 === "object") {
254718
254974
  const entriesToAdd = Object.entries(config6).map(([from, to]) => `127.0.0.1 ${to} # reverse-proxy mapping for ${from}`);
254719
254975
  entriesToAdd.push("127.0.0.1 localhost # essential localhost mapping");
254720
254976
  try {
254721
- let currentHostsContent = export_readFileSync(hostsFilePath, "utf8");
254977
+ let currentHostsContent = export_readFileSync(hostsFilePath2, "utf8");
254722
254978
  let updated = false;
254723
254979
  for (const entry of entriesToAdd) {
254724
254980
  const [ip, host] = entry.split(" ", 2);
@@ -254731,7 +254987,7 @@ cli.command("update:etc-hosts", "Update the /etc/hosts file with the proxy domai
254731
254987
  }
254732
254988
  }
254733
254989
  if (updated) {
254734
- export_writeFileSync(hostsFilePath, currentHostsContent, "utf8");
254990
+ export_writeFileSync(hostsFilePath2, currentHostsContent, "utf8");
254735
254991
  log.success("Hosts file updated with latest proxy domains.");
254736
254992
  } else {
254737
254993
  log.info("No new entries were added to the hosts file.");