vite-plugin-rpx 0.11.28 → 0.11.29

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.
Files changed (2) hide show
  1. package/dist/index.js +314 -238
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -22023,8 +22023,14 @@ function generateWildcardPatterns(domain) {
22023
22023
  const patterns = new Set;
22024
22024
  patterns.add(domain);
22025
22025
  const parts = domain.split(".");
22026
- if (parts.length >= 2)
22027
- patterns.add(`*.${parts.slice(1).join(".")}`);
22026
+ if (parts.length >= 3) {
22027
+ const tld = parts[parts.length - 1];
22028
+ const sld = parts[parts.length - 2];
22029
+ const secondLevelSuffixes = new Set(["co", "com", "org", "net", "ac", "gov", "edu"]);
22030
+ const baseIsPublicSuffix = parts.length === 3 && tld.length === 2 && secondLevelSuffixes.has(sld);
22031
+ if (!baseIsPublicSuffix)
22032
+ patterns.add(`*.${parts.slice(1).join(".")}`);
22033
+ }
22028
22034
  return Array.from(patterns);
22029
22035
  }
22030
22036
  function generateSSLPaths(options) {
@@ -22146,7 +22152,15 @@ async function generateCertificate(options) {
22146
22152
  const scriptPath = join12(sslDir, "trust-rpx-cert.sh");
22147
22153
  const scriptContent = `#!/bin/bash
22148
22154
  echo "Trusting RPX Root CA"
22149
- sudo security add-trusted-cert ${MACOS_CA_TRUST_FLAGS} -k ${MACOS_SYSTEM_KEYCHAIN} "${rootCAPaths.caCertPath}"
22155
+ # SUDO_PASSWORD (e.g. from a Stacks app's .env) makes the trust step
22156
+ # non-interactive. Without it, fall back to a normal sudo prompt.
22157
+ if [ -n "$SUDO_PASSWORD" ]
22158
+ then
22159
+ printf '%s
22160
+ ' "$SUDO_PASSWORD" | sudo -S -p '' security add-trusted-cert ${MACOS_CA_TRUST_FLAGS} -k ${MACOS_SYSTEM_KEYCHAIN} "${rootCAPaths.caCertPath}"
22161
+ else
22162
+ sudo security add-trusted-cert ${MACOS_CA_TRUST_FLAGS} -k ${MACOS_SYSTEM_KEYCHAIN} "${rootCAPaths.caCertPath}"
22163
+ fi
22150
22164
  echo "Root CA trusted! Please restart your browser."
22151
22165
  echo "If you still see certificate warnings, type 'thisisunsafe' on the warning page in Chrome/Arc browsers."
22152
22166
  `;
@@ -22157,7 +22171,15 @@ echo "If you still see certificate warnings, type 'thisisunsafe' on the warning
22157
22171
  const scriptPath = join12(sslDir, "trust-rpx-cert.sh");
22158
22172
  const scriptContent = `#!/bin/bash
22159
22173
  echo "Trusting RPX Root CA"
22160
- sudo security add-trusted-cert ${MACOS_CA_TRUST_FLAGS} -k ${MACOS_SYSTEM_KEYCHAIN} "${rootCAPaths.caCertPath}"
22174
+ # SUDO_PASSWORD (e.g. from a Stacks app's .env) makes the trust step
22175
+ # non-interactive. Without it, fall back to a normal sudo prompt.
22176
+ if [ -n "$SUDO_PASSWORD" ]
22177
+ then
22178
+ printf '%s
22179
+ ' "$SUDO_PASSWORD" | sudo -S -p '' security add-trusted-cert ${MACOS_CA_TRUST_FLAGS} -k ${MACOS_SYSTEM_KEYCHAIN} "${rootCAPaths.caCertPath}"
22180
+ else
22181
+ sudo security add-trusted-cert ${MACOS_CA_TRUST_FLAGS} -k ${MACOS_SYSTEM_KEYCHAIN} "${rootCAPaths.caCertPath}"
22182
+ fi
22161
22183
  echo "Root CA trusted! Please restart your browser."
22162
22184
  echo "If you still see certificate warnings, type 'thisisunsafe' on the warning page in Chrome/Arc browsers."
22163
22185
  `;
@@ -24871,15 +24893,262 @@ var init_site_supervisor = __esm(() => {
24871
24893
  init_utils();
24872
24894
  init_logger();
24873
24895
  });
24896
+ // ../rpx/src/hosts.ts
24897
+ import { exec } from "node:child_process";
24898
+ import fs4 from "node:fs";
24899
+ import os2 from "node:os";
24900
+ import path8 from "node:path";
24901
+ import * as process26 from "node:process";
24902
+ import { promisify } from "node:util";
24903
+ function isLoopbackDevelopmentHost(host) {
24904
+ const normalized = host.trim().toLowerCase();
24905
+ return normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".localhost.");
24906
+ }
24907
+ async function execSudo(command) {
24908
+ if (process26.platform === "win32")
24909
+ throw new Error("Administrator privileges required on Windows");
24910
+ if (isProcessElevated()) {
24911
+ const { stdout } = await execAsync(command);
24912
+ return stdout;
24913
+ }
24914
+ const sudoPassword = getSudoPassword();
24915
+ const escaped = command.replace(/'/g, `'\\''`);
24916
+ try {
24917
+ if (sudoPassword) {
24918
+ const { stdout } = await execAsync(`echo '${sudoPassword}' | sudo -S sh -c '${escaped}' 2>/dev/null`);
24919
+ sudoPrivilegesAcquired = true;
24920
+ return stdout;
24921
+ }
24922
+ if (sudoPrivilegesAcquired) {
24923
+ try {
24924
+ const { stdout } = await execAsync(`sudo -n sh -c '${escaped}'`);
24925
+ return stdout;
24926
+ } catch (error) {
24927
+ debugLog("hosts", "Cached sudo privileges expired, requesting again", true);
24928
+ }
24929
+ }
24930
+ try {
24931
+ const { stdout } = await execAsync(`sudo -n sh -c '${escaped}'`);
24932
+ sudoPrivilegesAcquired = true;
24933
+ return stdout;
24934
+ } catch {
24935
+ throw new Error("sudo required but no cached credentials (set SUDO_PASSWORD in .env or run sudo -v)");
24936
+ }
24937
+ } catch (error) {
24938
+ throw new Error(`Failed to execute sudo command: ${error.message}`);
24939
+ }
24940
+ }
24941
+ function parseHostsLine(line) {
24942
+ const hashIndex = line.indexOf("#");
24943
+ const body = (hashIndex === -1 ? line : line.slice(0, hashIndex)).trim();
24944
+ if (!body)
24945
+ return null;
24946
+ const comment = hashIndex === -1 ? "" : line.slice(hashIndex + 1).trim();
24947
+ const [address, ...names] = body.split(/\s+/);
24948
+ if (!address || names.length === 0)
24949
+ return null;
24950
+ const marker = /^rpx(?::pid=(\d+))?$/.exec(comment);
24951
+ return {
24952
+ address,
24953
+ names,
24954
+ comment,
24955
+ rpxPid: marker?.[1] ? Number.parseInt(marker[1], 10) : null,
24956
+ rpxManaged: marker !== null
24957
+ };
24958
+ }
24959
+ function hostsLineMapsHost(line, host) {
24960
+ const parsed = parseHostsLine(line);
24961
+ return parsed !== null && LOOPBACK_ADDRESSES.has(parsed.address) && parsed.names.includes(host);
24962
+ }
24963
+ function filterRpxHostsEntries(content, hostsToRemove) {
24964
+ const removal = new Set(hostsToRemove);
24965
+ const removed = new Set;
24966
+ const lines = content.split(`
24967
+ `);
24968
+ const out = [];
24969
+ let i2 = 0;
24970
+ while (i2 < lines.length) {
24971
+ const line = lines[i2];
24972
+ if (line.trim() === LEGACY_HOSTS_MARKER) {
24973
+ const block = [];
24974
+ let j2 = i2 + 1;
24975
+ while (j2 < lines.length) {
24976
+ const blockLine = lines[j2];
24977
+ if (blockLine.trim() === "" || blockLine.trim().startsWith("#"))
24978
+ break;
24979
+ block.push(blockLine);
24980
+ j2++;
24981
+ }
24982
+ const kept = block.filter((blockLine) => {
24983
+ const parsed2 = parseHostsLine(blockLine);
24984
+ const hit = parsed2 !== null && LOOPBACK_ADDRESSES.has(parsed2.address) && parsed2.names.some((n2) => removal.has(n2));
24985
+ if (hit) {
24986
+ for (const name of parsed2.names) {
24987
+ if (removal.has(name))
24988
+ removed.add(name);
24989
+ }
24990
+ }
24991
+ return !hit;
24992
+ });
24993
+ if (kept.length > 0 || block.length === 0)
24994
+ out.push(line, ...kept);
24995
+ i2 = j2;
24996
+ continue;
24997
+ }
24998
+ const parsed = parseHostsLine(line);
24999
+ if (parsed?.rpxManaged && LOOPBACK_ADDRESSES.has(parsed.address) && parsed.names.some((n2) => removal.has(n2))) {
25000
+ for (const name of parsed.names) {
25001
+ if (removal.has(name))
25002
+ removed.add(name);
25003
+ }
25004
+ i2++;
25005
+ continue;
25006
+ }
25007
+ out.push(line);
25008
+ i2++;
25009
+ }
25010
+ while (out.length > 0 && out[out.length - 1].trim() === "")
25011
+ out.pop();
25012
+ return { content: `${out.join(`
25013
+ `)}
25014
+ `, removed: [...removed] };
25015
+ }
25016
+ async function readHostsFile(verbose) {
25017
+ try {
25018
+ return await fs4.promises.readFile(hostsFilePath, "utf-8");
25019
+ } catch {
25020
+ debugLog("hosts", "Reading hosts file requires elevated permissions, using sudo", verbose);
25021
+ return execSudo(`cat "${hostsFilePath}"`);
25022
+ }
25023
+ }
25024
+ async function writeHostsFile(content, verbose) {
25025
+ const tmpFile = path8.join(os2.tmpdir(), `rpx-hosts-${Date.now()}.tmp`);
25026
+ try {
25027
+ await fs4.promises.writeFile(tmpFile, content, "utf8");
25028
+ await execSudo(`cat "${tmpFile}" | tee "${hostsFilePath}" > /dev/null`);
25029
+ } finally {
25030
+ await fs4.promises.unlink(tmpFile).catch((err) => {
25031
+ debugLog("hosts", `Failed to remove temporary file: ${err}`, verbose);
25032
+ });
25033
+ }
25034
+ }
25035
+ async function addHosts(hosts, verbose) {
25036
+ const needsHostsFile = hosts.filter((h2) => !isLoopbackDevelopmentHost(h2));
25037
+ const skipped = hosts.filter((h2) => isLoopbackDevelopmentHost(h2));
25038
+ if (skipped.length > 0) {
25039
+ debugLog("hosts", `Skipping /etc/hosts for loopback dev names: ${skipped.join(", ")}`, verbose);
25040
+ }
25041
+ if (needsHostsFile.length === 0)
25042
+ return;
25043
+ debugLog("hosts", `Adding hosts: ${needsHostsFile.join(", ")}`, verbose);
25044
+ debugLog("hosts", `Using hosts file at: ${hostsFilePath}`, verbose);
25045
+ try {
25046
+ let existingContent;
25047
+ try {
25048
+ existingContent = await readHostsFile(verbose);
25049
+ } catch (sudoErr) {
25050
+ console.log(" Could not read hosts file — skipping hosts setup");
25051
+ debugLog("hosts", `sudo read also failed: ${sudoErr}`, verbose);
25052
+ throw new Error(`Cannot read hosts file: ${sudoErr}`);
25053
+ }
25054
+ const existingLines = existingContent.split(`
25055
+ `);
25056
+ const newEntries = needsHostsFile.filter((host) => !existingLines.some((line) => hostsLineMapsHost(line, host)));
25057
+ if (newEntries.length === 0) {
25058
+ debugLog("hosts", "All hosts already exist in hosts file", verbose);
25059
+ return;
25060
+ }
25061
+ const pid3 = process26.pid;
25062
+ const hostEntries = newEntries.map((host) => `
25063
+ 127.0.0.1 ${host} ${RPX_HOSTS_MARKER}:pid=${pid3}
25064
+ ::1 ${host} ${RPX_HOSTS_MARKER}:pid=${pid3}`).join(`
25065
+ `);
25066
+ try {
25067
+ await writeHostsFile(existingContent + hostEntries, verbose);
25068
+ console.log(` Hosts updated: ${newEntries.join(", ")}`);
25069
+ } catch (error) {
25070
+ console.log(" Could not update hosts file automatically");
25071
+ console.log(" Add these entries to /etc/hosts:");
25072
+ newEntries.forEach((host) => {
25073
+ console.log(` 127.0.0.1 ${host}`);
25074
+ console.log(` ::1 ${host}`);
25075
+ });
25076
+ console.log(` Or run: sudo nano ${hostsFilePath}`);
25077
+ }
25078
+ } catch (err) {
25079
+ const error = err;
25080
+ debugLog("hosts", `Failed to manage hosts file: ${error.message}`, verbose);
25081
+ }
25082
+ }
25083
+ async function removeHosts(hosts, verbose) {
25084
+ debugLog("hosts", `Removing hosts: ${hosts.join(", ")}`, verbose);
25085
+ try {
25086
+ let content;
25087
+ try {
25088
+ content = await readHostsFile(verbose);
25089
+ } catch (sudoErr) {
25090
+ debugLog("hosts", `sudo read also failed: ${sudoErr}`, verbose);
25091
+ throw new Error(`Cannot read hosts file: ${sudoErr}`);
25092
+ }
25093
+ const { content: newContent, removed } = filterRpxHostsEntries(content, hosts);
25094
+ if (removed.length === 0) {
25095
+ debugLog("hosts", "No matching rpx-managed hosts found to remove", verbose);
25096
+ return;
25097
+ }
25098
+ try {
25099
+ await writeHostsFile(newContent, verbose);
25100
+ debugLog("hosts", `Hosts removed successfully: ${removed.join(", ")}`, verbose);
25101
+ } catch (error) {
25102
+ debugLog("hosts", "Could not clean up hosts file automatically", verbose);
25103
+ }
25104
+ } catch (err) {
25105
+ debugLog("hosts", `Failed to clean up hosts file: ${err.message}`, verbose);
25106
+ }
25107
+ }
25108
+ async function checkHosts(hosts, verbose) {
25109
+ debugLog("hosts", `Checking hosts: ${hosts}`, verbose);
25110
+ let content;
25111
+ try {
25112
+ content = await fs4.promises.readFile(hostsFilePath, "utf-8");
25113
+ } catch (readErr) {
25114
+ debugLog("hosts", `Error reading hosts file: ${readErr}`, verbose);
25115
+ try {
25116
+ const sudoPassword = getSudoPassword();
25117
+ let cmd;
25118
+ if (sudoPassword) {
25119
+ cmd = `echo '${sudoPassword}' | sudo -S cat "${hostsFilePath}" 2>/dev/null`;
25120
+ } else {
25121
+ cmd = `sudo -n cat "${hostsFilePath}" 2>/dev/null || cat "${hostsFilePath}" 2>/dev/null || echo ""`;
25122
+ }
25123
+ const { stdout } = await execAsync(cmd);
25124
+ content = stdout;
25125
+ } catch (sudoErr) {
25126
+ debugLog("hosts", `Cannot read hosts file, assuming entries don't exist: ${sudoErr}`, verbose);
25127
+ return hosts.map(() => false);
25128
+ }
25129
+ }
25130
+ const lines = content.split(`
25131
+ `);
25132
+ return hosts.map((host) => lines.some((line) => hostsLineMapsHost(line, host)));
25133
+ }
25134
+ var execAsync, hostsFilePath, RPX_HOSTS_MARKER = "# rpx", LEGACY_HOSTS_MARKER = "# Added by rpx", LOOPBACK_ADDRESSES, sudoPrivilegesAcquired = false;
25135
+ var init_hosts = __esm(() => {
25136
+ init_registry();
25137
+ init_utils();
25138
+ execAsync = promisify(exec);
25139
+ hostsFilePath = process26.platform === "win32" ? path8.join(process26.env.windir || "C:\\Windows", "System32", "drivers", "etc", "hosts") : "/etc/hosts";
25140
+ LOOPBACK_ADDRESSES = new Set(["127.0.0.1", "::1"]);
25141
+ });
25142
+
24874
25143
  // ../rpx/src/dns-state.ts
24875
25144
  import * as fsp5 from "node:fs/promises";
24876
25145
  import { homedir as homedir11 } from "node:os";
24877
- import * as path8 from "node:path";
25146
+ import * as path9 from "node:path";
24878
25147
  function defaultRpxDir() {
24879
- return path8.join(homedir11(), ".stacks", "rpx");
25148
+ return path9.join(homedir11(), ".stacks", "rpx");
24880
25149
  }
24881
25150
  function getDnsStatePath(rpxDir = defaultRpxDir()) {
24882
- return path8.join(rpxDir, RPX_DNS_STATE_FILE);
25151
+ return path9.join(rpxDir, RPX_DNS_STATE_FILE);
24883
25152
  }
24884
25153
  async function loadDnsState(rpxDir = defaultRpxDir()) {
24885
25154
  try {
@@ -24983,8 +25252,8 @@ __export(exports_dns, {
24983
25252
  });
24984
25253
  import dgram from "node:dgram";
24985
25254
  import * as fsp6 from "node:fs/promises";
24986
- import * as path9 from "node:path";
24987
- import * as process26 from "node:process";
25255
+ import * as path10 from "node:path";
25256
+ import * as process27 from "node:process";
24988
25257
  function parseHeader(buffer) {
24989
25258
  return {
24990
25259
  id: buffer.readUInt16BE(0),
@@ -25098,7 +25367,7 @@ function buildNxdomainResponse(queryId, question) {
25098
25367
  return Buffer.concat(parts);
25099
25368
  }
25100
25369
  async function startDnsServer(domains, verbose) {
25101
- if (process26.platform !== "darwin")
25370
+ if (process27.platform !== "darwin")
25102
25371
  return false;
25103
25372
  const devDomains = devDomainsFromHosts(domains);
25104
25373
  if (devDomains.length === 0)
@@ -25218,7 +25487,7 @@ port ${DNS_PORT}
25218
25487
  `;
25219
25488
  }
25220
25489
  function resolverFilePath(basename) {
25221
- return path9.join(MACOS_RESOLVER_DIR, basename);
25490
+ return path10.join(MACOS_RESOLVER_DIR, basename);
25222
25491
  }
25223
25492
  function contentLooksLikeRpxResolver(content) {
25224
25493
  return content.includes("127.0.0.1") && content.includes(String(DNS_PORT));
@@ -25233,7 +25502,7 @@ async function readResolverFile(basename) {
25233
25502
  }
25234
25503
  }
25235
25504
  async function flushDnsCache(verbose) {
25236
- if (process26.platform !== "darwin")
25505
+ if (process27.platform !== "darwin")
25237
25506
  return;
25238
25507
  const { execSudoSync: execSudoSync2, getSudoPassword: getSudoPassword2, isProcessElevated: isProcessElevated2 } = await Promise.resolve().then(() => (init_utils(), exports_utils));
25239
25508
  if (!isProcessElevated2() && !getSudoPassword2()) {
@@ -25270,7 +25539,7 @@ async function setupResolver(verbose, domains) {
25270
25539
  return setupDevelopmentDns({ domains: domains ?? [], verbose });
25271
25540
  }
25272
25541
  async function installResolvers(basenames, verbose) {
25273
- if (process26.platform !== "darwin")
25542
+ if (process27.platform !== "darwin")
25274
25543
  return true;
25275
25544
  const { getSudoPassword: getSudoPassword2, isProcessElevated: isProcessElevated2 } = await Promise.resolve().then(() => (init_utils(), exports_utils));
25276
25545
  if (!isProcessElevated2() && !getSudoPassword2()) {
@@ -25288,7 +25557,7 @@ async function installResolvers(basenames, verbose) {
25288
25557
  }
25289
25558
  }
25290
25559
  async function uninstallResolvers(basenames, verbose) {
25291
- if (process26.platform !== "darwin")
25560
+ if (process27.platform !== "darwin")
25292
25561
  return;
25293
25562
  const { getSudoPassword: getSudoPassword2, isProcessElevated: isProcessElevated2 } = await Promise.resolve().then(() => (init_utils(), exports_utils));
25294
25563
  if (!isProcessElevated2() && !getSudoPassword2())
@@ -25302,7 +25571,7 @@ async function uninstallResolvers(basenames, verbose) {
25302
25571
  }
25303
25572
  }
25304
25573
  async function removeLegacyTldResolvers(verbose) {
25305
- if (process26.platform !== "darwin")
25574
+ if (process27.platform !== "darwin")
25306
25575
  return [];
25307
25576
  const removed = [];
25308
25577
  for (const label of LEGACY_TLD_RESOLVER_LABELS) {
@@ -25337,7 +25606,7 @@ async function setupDevelopmentDns(opts) {
25337
25606
  version: DNS_STATE_VERSION,
25338
25607
  resolvers: basenames,
25339
25608
  domains,
25340
- ownerPid: opts.ownerPid ?? process26.pid,
25609
+ ownerPid: opts.ownerPid ?? process27.pid,
25341
25610
  updatedAt: new Date().toISOString()
25342
25611
  };
25343
25612
  await saveDnsState(rpxDir, state);
@@ -25361,7 +25630,7 @@ async function syncDevelopmentDnsFromRegistry(entries, opts = {}) {
25361
25630
  domains,
25362
25631
  rpxDir,
25363
25632
  verbose: opts.verbose,
25364
- ownerPid: opts.ownerPid ?? process26.pid
25633
+ ownerPid: opts.ownerPid ?? process27.pid
25365
25634
  });
25366
25635
  }
25367
25636
  async function tearDownDevelopmentDns(opts = {}) {
@@ -25403,13 +25672,13 @@ var init_dns = __esm(() => {
25403
25672
  import { spawn as nodeSpawn } from "node:child_process";
25404
25673
  import * as fsp7 from "node:fs/promises";
25405
25674
  import { homedir as homedir12 } from "node:os";
25406
- import * as path10 from "node:path";
25407
- import * as process27 from "node:process";
25675
+ import * as path11 from "node:path";
25676
+ import * as process28 from "node:process";
25408
25677
  function getDaemonRpxDir() {
25409
- return path10.join(homedir12(), ".stacks", "rpx");
25678
+ return path11.join(homedir12(), ".stacks", "rpx");
25410
25679
  }
25411
25680
  function getDaemonPidPath(rpxDir = getDaemonRpxDir()) {
25412
- return path10.join(rpxDir, "daemon.pid");
25681
+ return path11.join(rpxDir, "daemon.pid");
25413
25682
  }
25414
25683
  async function readDaemonPid(rpxDir = getDaemonRpxDir()) {
25415
25684
  try {
@@ -25428,12 +25697,12 @@ async function releaseDaemonLock(rpxDir = getDaemonRpxDir()) {
25428
25697
  await fsp7.unlink(getDaemonPidPath(rpxDir)).catch(() => {});
25429
25698
  }
25430
25699
  function defaultDaemonSpawnCommand() {
25431
- const exec = process27.execPath;
25432
- const interpName = path10.basename(exec).toLowerCase();
25700
+ const exec2 = process28.execPath;
25701
+ const interpName = path11.basename(exec2).toLowerCase();
25433
25702
  const isInterpreter = interpName === "bun" || interpName === "node" || interpName.startsWith("bun-");
25434
- if (isInterpreter && process27.argv[1])
25435
- return [exec, process27.argv[1], "daemon:start"];
25436
- return [exec, "daemon:start"];
25703
+ if (isInterpreter && process28.argv[1])
25704
+ return [exec2, process28.argv[1], "daemon:start"];
25705
+ return [exec2, "daemon:start"];
25437
25706
  }
25438
25707
  async function ensureDaemonRunning(opts = {}) {
25439
25708
  const rpxDir = opts.rpxDir ?? getDaemonRpxDir();
@@ -25458,8 +25727,8 @@ async function ensureDaemonRunning(opts = {}) {
25458
25727
  const child = nodeSpawn(command[0], command.slice(1), {
25459
25728
  detached: true,
25460
25729
  stdio: "ignore",
25461
- cwd: opts.spawnCwd ?? process27.cwd(),
25462
- env: opts.spawnEnv ? { ...process27.env, ...opts.spawnEnv } : process27.env
25730
+ cwd: opts.spawnCwd ?? process28.cwd(),
25731
+ env: opts.spawnEnv ? { ...process28.env, ...opts.spawnEnv } : process28.env
25463
25732
  });
25464
25733
  child.unref();
25465
25734
  let spawnError = null;
@@ -25472,10 +25741,10 @@ async function ensureDaemonRunning(opts = {}) {
25472
25741
  while (Date.now() < deadline) {
25473
25742
  if (spawnError)
25474
25743
  throw spawnError;
25475
- const pid4 = await readDaemonPid(rpxDir);
25476
- if (pid4 !== null && isPidAlive(pid4)) {
25477
- debugLog("daemon", `daemon registered with pid=${pid4}`, verbose);
25478
- return { pid: pid4, spawned: true };
25744
+ const pid5 = await readDaemonPid(rpxDir);
25745
+ if (pid5 !== null && isPidAlive(pid5)) {
25746
+ debugLog("daemon", `daemon registered with pid=${pid5}`, verbose);
25747
+ return { pid: pid5, spawned: true };
25479
25748
  }
25480
25749
  await new Promise((resolve14) => setTimeout(resolve14, pollMs));
25481
25750
  }
@@ -25497,6 +25766,7 @@ var init_daemon = __esm(() => {
25497
25766
  init_static_files();
25498
25767
  init_auth();
25499
25768
  init_registry();
25769
+ init_hosts();
25500
25770
  init_dns();
25501
25771
  init_utils();
25502
25772
  registryUpstreamPools = new Map;
@@ -25531,9 +25801,9 @@ init_daemon();
25531
25801
  init_logger();
25532
25802
  init_registry();
25533
25803
  init_utils();
25534
- import * as fs4 from "node:fs";
25535
- import * as path11 from "node:path";
25536
- import * as process28 from "node:process";
25804
+ import * as fs5 from "node:fs";
25805
+ import * as path12 from "node:path";
25806
+ import * as process29 from "node:process";
25537
25807
  function deriveIdFromTarget(to, routePath) {
25538
25808
  const base = routePath && routePath !== "/" ? `${to}${routePath}` : to;
25539
25809
  const cleaned = base.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 128);
@@ -25561,8 +25831,8 @@ async function runViaDaemon(opts) {
25561
25831
  from: p2.from,
25562
25832
  to: p2.to,
25563
25833
  path: p2.path,
25564
- pid: opts.persistent ? undefined : process28.pid,
25565
- cwd: process28.cwd(),
25834
+ pid: opts.persistent ? undefined : process29.pid,
25835
+ cwd: process29.cwd(),
25566
25836
  createdAt,
25567
25837
  cleanUrls: p2.cleanUrls,
25568
25838
  changeOrigin: p2.changeOrigin,
@@ -25600,219 +25870,24 @@ async function runViaDaemon(opts) {
25600
25870
  };
25601
25871
  const onSignal = (sig) => {
25602
25872
  debugLog("runner", `received ${sig}, unregistering ${idsForCleanup.length} entries`, verbose);
25603
- cleanup().finally(() => process28.exit(0));
25873
+ cleanup().finally(() => process29.exit(0));
25604
25874
  };
25605
- process28.once("SIGINT", onSignal);
25606
- process28.once("SIGTERM", onSignal);
25607
- process28.once("exit", () => {
25875
+ process29.once("SIGINT", onSignal);
25876
+ process29.once("SIGTERM", onSignal);
25877
+ process29.once("exit", () => {
25608
25878
  if (cleaned)
25609
25879
  return;
25610
25880
  for (const id of idsForCleanup) {
25611
25881
  try {
25612
- fs4.unlinkSync(path11.join(dirForCleanup, `${id}.json`));
25882
+ fs5.unlinkSync(path12.join(dirForCleanup, `${id}.json`));
25613
25883
  } catch {}
25614
25884
  }
25615
25885
  });
25616
25886
  await new Promise(() => {});
25617
25887
  }
25618
25888
 
25619
- // ../rpx/src/hosts.ts
25620
- init_utils();
25621
- import { exec } from "node:child_process";
25622
- import fs5 from "node:fs";
25623
- import os2 from "node:os";
25624
- import path12 from "node:path";
25625
- import * as process29 from "node:process";
25626
- import { promisify } from "node:util";
25627
- var execAsync = promisify(exec);
25628
- function isLoopbackDevelopmentHost(host) {
25629
- const normalized = host.trim().toLowerCase();
25630
- return normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".localhost.");
25631
- }
25632
- var hostsFilePath = process29.platform === "win32" ? path12.join(process29.env.windir || "C:\\Windows", "System32", "drivers", "etc", "hosts") : "/etc/hosts";
25633
- var sudoPrivilegesAcquired = false;
25634
- async function execSudo(command) {
25635
- if (process29.platform === "win32")
25636
- throw new Error("Administrator privileges required on Windows");
25637
- if (isProcessElevated()) {
25638
- const { stdout } = await execAsync(command);
25639
- return stdout;
25640
- }
25641
- const sudoPassword = getSudoPassword();
25642
- const escaped = command.replace(/'/g, `'\\''`);
25643
- try {
25644
- if (sudoPassword) {
25645
- const { stdout } = await execAsync(`echo '${sudoPassword}' | sudo -S sh -c '${escaped}' 2>/dev/null`);
25646
- sudoPrivilegesAcquired = true;
25647
- return stdout;
25648
- }
25649
- if (sudoPrivilegesAcquired) {
25650
- try {
25651
- const { stdout } = await execAsync(`sudo -n sh -c '${escaped}'`);
25652
- return stdout;
25653
- } catch (error) {
25654
- debugLog("hosts", "Cached sudo privileges expired, requesting again", true);
25655
- }
25656
- }
25657
- try {
25658
- const { stdout } = await execAsync(`sudo -n sh -c '${escaped}'`);
25659
- sudoPrivilegesAcquired = true;
25660
- return stdout;
25661
- } catch {
25662
- throw new Error("sudo required but no cached credentials (set SUDO_PASSWORD in .env or run sudo -v)");
25663
- }
25664
- } catch (error) {
25665
- throw new Error(`Failed to execute sudo command: ${error.message}`);
25666
- }
25667
- }
25668
- async function addHosts(hosts, verbose) {
25669
- const needsHostsFile = hosts.filter((h2) => !isLoopbackDevelopmentHost(h2));
25670
- const skipped = hosts.filter((h2) => isLoopbackDevelopmentHost(h2));
25671
- if (skipped.length > 0) {
25672
- debugLog("hosts", `Skipping /etc/hosts for loopback dev names: ${skipped.join(", ")}`, verbose);
25673
- }
25674
- if (needsHostsFile.length === 0)
25675
- return;
25676
- debugLog("hosts", `Adding hosts: ${needsHostsFile.join(", ")}`, verbose);
25677
- debugLog("hosts", `Using hosts file at: ${hostsFilePath}`, verbose);
25678
- try {
25679
- let existingContent;
25680
- try {
25681
- existingContent = await fs5.promises.readFile(hostsFilePath, "utf-8");
25682
- } catch {
25683
- debugLog("hosts", "Reading hosts file requires elevated permissions, using sudo", verbose);
25684
- try {
25685
- existingContent = await execSudo(`cat "${hostsFilePath}"`);
25686
- } catch (sudoErr) {
25687
- console.log(" Could not read hosts file — skipping hosts setup");
25688
- debugLog("hosts", `sudo read also failed: ${sudoErr}`, verbose);
25689
- throw new Error(`Cannot read hosts file: ${sudoErr}`);
25690
- }
25691
- }
25692
- const newEntries = needsHostsFile.filter((host) => {
25693
- const ipv4Entry = `127.0.0.1 ${host}`;
25694
- const ipv6Entry = `::1 ${host}`;
25695
- return !existingContent.includes(ipv4Entry) && !existingContent.includes(ipv6Entry);
25696
- });
25697
- if (newEntries.length === 0) {
25698
- debugLog("hosts", "All hosts already exist in hosts file", verbose);
25699
- return;
25700
- }
25701
- const hostEntries = newEntries.map((host) => `
25702
- # Added by rpx
25703
- 127.0.0.1 ${host}
25704
- ::1 ${host}`).join(`
25705
- `);
25706
- const tmpFile = path12.join(os2.tmpdir(), `rpx-hosts-${Date.now()}.tmp`);
25707
- try {
25708
- await fs5.promises.writeFile(tmpFile, existingContent + hostEntries, "utf8");
25709
- await execSudo(`cat "${tmpFile}" | tee "${hostsFilePath}" > /dev/null`);
25710
- console.log(` Hosts updated: ${newEntries.join(", ")}`);
25711
- } catch (error) {
25712
- console.log(" Could not update hosts file automatically");
25713
- console.log(" Add these entries to /etc/hosts:");
25714
- newEntries.forEach((host) => {
25715
- console.log(` 127.0.0.1 ${host}`);
25716
- console.log(` ::1 ${host}`);
25717
- });
25718
- console.log(` Or run: sudo nano ${hostsFilePath}`);
25719
- } finally {
25720
- try {
25721
- await fs5.promises.unlink(tmpFile);
25722
- } catch {}
25723
- }
25724
- } catch (err) {
25725
- const error = err;
25726
- debugLog("hosts", `Failed to manage hosts file: ${error.message}`, verbose);
25727
- }
25728
- }
25729
- async function removeHosts(hosts, verbose) {
25730
- debugLog("hosts", `Removing hosts: ${hosts.join(", ")}`, verbose);
25731
- try {
25732
- let content;
25733
- try {
25734
- content = await fs5.promises.readFile(hostsFilePath, "utf-8");
25735
- } catch {
25736
- debugLog("hosts", "Reading hosts file requires elevated permissions, using sudo", verbose);
25737
- try {
25738
- content = await execSudo(`cat "${hostsFilePath}"`);
25739
- } catch (sudoErr) {
25740
- debugLog("hosts", `sudo read also failed: ${sudoErr}`, verbose);
25741
- throw new Error(`Cannot read hosts file: ${sudoErr}`);
25742
- }
25743
- }
25744
- const lines = content.split(`
25745
- `);
25746
- let modified = false;
25747
- const filteredLines = lines.filter((line) => {
25748
- const isHostLine = hosts.some((host) => line.includes(` ${host}`) && (line.includes("127.0.0.1") || line.includes("::1")));
25749
- if (isHostLine) {
25750
- modified = true;
25751
- return false;
25752
- }
25753
- if (line.trim() === "# Added by rpx") {
25754
- modified = true;
25755
- return false;
25756
- }
25757
- return true;
25758
- });
25759
- if (!modified) {
25760
- debugLog("hosts", "No matching hosts found to remove", verbose);
25761
- return;
25762
- }
25763
- while (filteredLines[filteredLines.length - 1]?.trim() === "")
25764
- filteredLines.pop();
25765
- const newContent = `${filteredLines.join(`
25766
- `)}
25767
- `;
25768
- const tmpFile = path12.join(os2.tmpdir(), `rpx-hosts-${Date.now()}.tmp`);
25769
- try {
25770
- await fs5.promises.writeFile(tmpFile, newContent, "utf8");
25771
- await execSudo(`cat "${tmpFile}" | tee "${hostsFilePath}" > /dev/null`);
25772
- debugLog("hosts", "Hosts removed successfully", verbose);
25773
- } catch (error) {
25774
- debugLog("hosts", "Could not clean up hosts file automatically", verbose);
25775
- } finally {
25776
- try {
25777
- await fs5.promises.unlink(tmpFile);
25778
- } catch (unlinkErr) {
25779
- debugLog("hosts", `Failed to remove temporary file: ${unlinkErr}`, verbose);
25780
- }
25781
- }
25782
- } catch (err) {
25783
- debugLog("hosts", `Failed to clean up hosts file: ${err.message}`, verbose);
25784
- }
25785
- }
25786
- async function checkHosts(hosts, verbose) {
25787
- debugLog("hosts", `Checking hosts: ${hosts}`, verbose);
25788
- let content;
25789
- try {
25790
- content = await fs5.promises.readFile(hostsFilePath, "utf-8");
25791
- } catch (readErr) {
25792
- debugLog("hosts", `Error reading hosts file: ${readErr}`, verbose);
25793
- try {
25794
- const sudoPassword = getSudoPassword();
25795
- let cmd;
25796
- if (sudoPassword) {
25797
- cmd = `echo '${sudoPassword}' | sudo -S cat "${hostsFilePath}" 2>/dev/null`;
25798
- } else {
25799
- cmd = `sudo -n cat "${hostsFilePath}" 2>/dev/null || cat "${hostsFilePath}" 2>/dev/null || echo ""`;
25800
- }
25801
- const { stdout } = await execAsync(cmd);
25802
- content = stdout;
25803
- } catch (sudoErr) {
25804
- debugLog("hosts", `Cannot read hosts file, assuming entries don't exist: ${sudoErr}`, verbose);
25805
- return hosts.map(() => false);
25806
- }
25807
- }
25808
- return hosts.map((host) => {
25809
- const ipv4Entry = `127.0.0.1 ${host}`;
25810
- const ipv6Entry = `::1 ${host}`;
25811
- return content.includes(ipv4Entry) || content.includes(ipv6Entry);
25812
- });
25813
- }
25814
-
25815
25889
  // ../rpx/src/start.ts
25890
+ init_hosts();
25816
25891
  init_https();
25817
25892
  init_port_manager();
25818
25893
 
@@ -26846,6 +26921,7 @@ function logToConsole(options) {
26846
26921
 
26847
26922
  // ../rpx/src/index.ts
26848
26923
  init_config();
26924
+ init_hosts();
26849
26925
  init_https();
26850
26926
  init_macos_trust();
26851
26927
  init_cert_inspect();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vite-plugin-rpx",
3
3
  "type": "module",
4
- "version": "0.11.28",
4
+ "version": "0.11.29",
5
5
  "description": "A modern and smart reverse proxy. Vite plugin.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",
@@ -47,7 +47,7 @@
47
47
  "typecheck": "bunx tsc --noEmit"
48
48
  },
49
49
  "dependencies": {
50
- "@stacksjs/rpx": "0.11.28",
50
+ "@stacksjs/rpx": "0.11.29",
51
51
  "@stacksjs/tlsx": "^0.13.9"
52
52
  },
53
53
  "simple-git-hooks": {