unplugin-https-reverse-proxy 4.1.0-beta.6 → 4.1.0-beta.7

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.
@@ -13532,7 +13532,7 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13532
13532
  return { ok: true };
13533
13533
  if (spawnSync(sudoCommand, ["-n", "true"], { stdio: "ignore" }).status === 0)
13534
13534
  return { ok: true };
13535
- if (options.target) {
13535
+ if (options.target && !options.quiet) {
13536
13536
  for (const line of describePasswordReason(options.target, options.https ?? false))
13537
13537
  process6.stderr.write(`${line}
13538
13538
  `);
@@ -13559,11 +13559,37 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13559
13559
  }
13560
13560
  function runPrompt(sudoCommand) {
13561
13561
  return new Promise((resolve2) => {
13562
+ const restore = takeBackTerminal();
13562
13563
  const child = spawn2(sudoCommand, ["-v"], { stdio: "inherit" });
13563
- child.on("error", () => resolve2({ status: 1, signal: null }));
13564
- child.on("exit", (status, signal) => resolve2({ status, signal }));
13564
+ const finish = (result) => {
13565
+ restore();
13566
+ resolve2(result);
13567
+ };
13568
+ child.on("error", () => finish({ status: 1, signal: null }));
13569
+ child.on("exit", (status, signal) => finish({ status, signal }));
13565
13570
  });
13566
13571
  }
13572
+ function takeBackTerminal() {
13573
+ const stdin = process6.stdin;
13574
+ if (!stdin.isTTY || typeof stdin.setRawMode !== "function")
13575
+ return () => {
13576
+ };
13577
+ const wasRaw = stdin.isRaw === true;
13578
+ try {
13579
+ stdin.setRawMode(false);
13580
+ } catch {
13581
+ return () => {
13582
+ };
13583
+ }
13584
+ return () => {
13585
+ if (!wasRaw)
13586
+ return;
13587
+ try {
13588
+ stdin.setRawMode(true);
13589
+ } catch {
13590
+ }
13591
+ };
13592
+ }
13567
13593
  var PrivilegeDeniedError = class extends Error {
13568
13594
  constructor(message) {
13569
13595
  super(message);
package/dist/esbuild.cjs CHANGED
@@ -13631,7 +13631,7 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13631
13631
  return { ok: true };
13632
13632
  if ((0, import_node_child_process2.spawnSync)(sudoCommand, ["-n", "true"], { stdio: "ignore" }).status === 0)
13633
13633
  return { ok: true };
13634
- if (options.target) {
13634
+ if (options.target && !options.quiet) {
13635
13635
  for (const line of describePasswordReason(options.target, options.https ?? false))
13636
13636
  import_node_process6.default.stderr.write(`${line}
13637
13637
  `);
@@ -13658,11 +13658,37 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13658
13658
  }
13659
13659
  function runPrompt(sudoCommand) {
13660
13660
  return new Promise((resolve2) => {
13661
+ const restore = takeBackTerminal();
13661
13662
  const child = (0, import_node_child_process2.spawn)(sudoCommand, ["-v"], { stdio: "inherit" });
13662
- child.on("error", () => resolve2({ status: 1, signal: null }));
13663
- child.on("exit", (status, signal) => resolve2({ status, signal }));
13663
+ const finish = (result) => {
13664
+ restore();
13665
+ resolve2(result);
13666
+ };
13667
+ child.on("error", () => finish({ status: 1, signal: null }));
13668
+ child.on("exit", (status, signal) => finish({ status, signal }));
13664
13669
  });
13665
13670
  }
13671
+ function takeBackTerminal() {
13672
+ const stdin = import_node_process6.default.stdin;
13673
+ if (!stdin.isTTY || typeof stdin.setRawMode !== "function")
13674
+ return () => {
13675
+ };
13676
+ const wasRaw = stdin.isRaw === true;
13677
+ try {
13678
+ stdin.setRawMode(false);
13679
+ } catch {
13680
+ return () => {
13681
+ };
13682
+ }
13683
+ return () => {
13684
+ if (!wasRaw)
13685
+ return;
13686
+ try {
13687
+ stdin.setRawMode(true);
13688
+ } catch {
13689
+ }
13690
+ };
13691
+ }
13666
13692
  var PrivilegeDeniedError = class extends Error {
13667
13693
  constructor(message) {
13668
13694
  super(message);
package/dist/esbuild.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  unpluginFactory
3
- } from "./chunk-Y4TJZ6XZ.js";
3
+ } from "./chunk-FNF6HQJB.js";
4
4
  import "./chunk-EY3HXBJ4.js";
5
5
  import "./chunk-C5A7XLKL.js";
6
6
  import "./chunk-WOMR3ZLM.js";
package/dist/index.cjs CHANGED
@@ -13631,7 +13631,7 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13631
13631
  return { ok: true };
13632
13632
  if ((0, import_node_child_process2.spawnSync)(sudoCommand, ["-n", "true"], { stdio: "ignore" }).status === 0)
13633
13633
  return { ok: true };
13634
- if (options.target) {
13634
+ if (options.target && !options.quiet) {
13635
13635
  for (const line of describePasswordReason(options.target, options.https ?? false))
13636
13636
  import_node_process6.default.stderr.write(`${line}
13637
13637
  `);
@@ -13658,11 +13658,37 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13658
13658
  }
13659
13659
  function runPrompt(sudoCommand) {
13660
13660
  return new Promise((resolve2) => {
13661
+ const restore = takeBackTerminal();
13661
13662
  const child = (0, import_node_child_process2.spawn)(sudoCommand, ["-v"], { stdio: "inherit" });
13662
- child.on("error", () => resolve2({ status: 1, signal: null }));
13663
- child.on("exit", (status, signal) => resolve2({ status, signal }));
13663
+ const finish = (result) => {
13664
+ restore();
13665
+ resolve2(result);
13666
+ };
13667
+ child.on("error", () => finish({ status: 1, signal: null }));
13668
+ child.on("exit", (status, signal) => finish({ status, signal }));
13664
13669
  });
13665
13670
  }
13671
+ function takeBackTerminal() {
13672
+ const stdin = import_node_process6.default.stdin;
13673
+ if (!stdin.isTTY || typeof stdin.setRawMode !== "function")
13674
+ return () => {
13675
+ };
13676
+ const wasRaw = stdin.isRaw === true;
13677
+ try {
13678
+ stdin.setRawMode(false);
13679
+ } catch {
13680
+ return () => {
13681
+ };
13682
+ }
13683
+ return () => {
13684
+ if (!wasRaw)
13685
+ return;
13686
+ try {
13687
+ stdin.setRawMode(true);
13688
+ } catch {
13689
+ }
13690
+ };
13691
+ }
13666
13692
  var PrivilegeDeniedError = class extends Error {
13667
13693
  constructor(message) {
13668
13694
  super(message);
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  unplugin,
4
4
  unpluginFactory,
5
5
  vitePrintUrls
6
- } from "./chunk-Y4TJZ6XZ.js";
6
+ } from "./chunk-FNF6HQJB.js";
7
7
  import "./chunk-EY3HXBJ4.js";
8
8
  import "./chunk-C5A7XLKL.js";
9
9
  import "./chunk-WOMR3ZLM.js";
package/dist/nuxt.cjs CHANGED
@@ -13632,7 +13632,7 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13632
13632
  return { ok: true };
13633
13633
  if ((0, import_node_child_process2.spawnSync)(sudoCommand, ["-n", "true"], { stdio: "ignore" }).status === 0)
13634
13634
  return { ok: true };
13635
- if (options.target) {
13635
+ if (options.target && !options.quiet) {
13636
13636
  for (const line of describePasswordReason(options.target, options.https ?? false))
13637
13637
  import_node_process6.default.stderr.write(`${line}
13638
13638
  `);
@@ -13659,11 +13659,37 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13659
13659
  }
13660
13660
  function runPrompt(sudoCommand) {
13661
13661
  return new Promise((resolve2) => {
13662
+ const restore = takeBackTerminal();
13662
13663
  const child = (0, import_node_child_process2.spawn)(sudoCommand, ["-v"], { stdio: "inherit" });
13663
- child.on("error", () => resolve2({ status: 1, signal: null }));
13664
- child.on("exit", (status, signal) => resolve2({ status, signal }));
13664
+ const finish = (result) => {
13665
+ restore();
13666
+ resolve2(result);
13667
+ };
13668
+ child.on("error", () => finish({ status: 1, signal: null }));
13669
+ child.on("exit", (status, signal) => finish({ status, signal }));
13665
13670
  });
13666
13671
  }
13672
+ function takeBackTerminal() {
13673
+ const stdin = import_node_process6.default.stdin;
13674
+ if (!stdin.isTTY || typeof stdin.setRawMode !== "function")
13675
+ return () => {
13676
+ };
13677
+ const wasRaw = stdin.isRaw === true;
13678
+ try {
13679
+ stdin.setRawMode(false);
13680
+ } catch {
13681
+ return () => {
13682
+ };
13683
+ }
13684
+ return () => {
13685
+ if (!wasRaw)
13686
+ return;
13687
+ try {
13688
+ stdin.setRawMode(true);
13689
+ } catch {
13690
+ }
13691
+ };
13692
+ }
13667
13693
  var PrivilegeDeniedError = class extends Error {
13668
13694
  constructor(message) {
13669
13695
  super(message);
package/dist/nuxt.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  vitePrintUrls
3
- } from "./chunk-Y4TJZ6XZ.js";
3
+ } from "./chunk-FNF6HQJB.js";
4
4
  import {
5
5
  consola
6
6
  } from "./chunk-EY3HXBJ4.js";
@@ -564,6 +564,7 @@ async function startHelper(options) {
564
564
  }
565
565
  const connections = /* @__PURE__ */ new Set();
566
566
  const idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
567
+ const exitOnIdle = options.exitOnIdle ?? true;
567
568
  let idleTimer;
568
569
  const stopIdleTimer = () => {
569
570
  if (idleTimer) {
@@ -634,6 +635,10 @@ async function startHelper(options) {
634
635
  return;
635
636
  log("no clients left; shutting down");
636
637
  removeOwnSocket(socketPath, token);
638
+ if (!exitOnIdle) {
639
+ server.close();
640
+ return;
641
+ }
637
642
  server.close(() => import_node_process2.default.exit(0));
638
643
  (_b = (_a2 = setTimeout(() => import_node_process2.default.exit(0), 1e3)).unref) == null ? void 0 : _b.call(_a2);
639
644
  }, idleTimeoutMs);
@@ -538,6 +538,7 @@ async function startHelper(options) {
538
538
  }
539
539
  const connections = /* @__PURE__ */ new Set();
540
540
  const idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
541
+ const exitOnIdle = options.exitOnIdle ?? true;
541
542
  let idleTimer;
542
543
  const stopIdleTimer = () => {
543
544
  if (idleTimer) {
@@ -608,6 +609,10 @@ async function startHelper(options) {
608
609
  return;
609
610
  log("no clients left; shutting down");
610
611
  removeOwnSocket(socketPath, token);
612
+ if (!exitOnIdle) {
613
+ server.close();
614
+ return;
615
+ }
611
616
  server.close(() => process2.exit(0));
612
617
  (_b = (_a2 = setTimeout(() => process2.exit(0), 1e3)).unref) == null ? void 0 : _b.call(_a2);
613
618
  }, idleTimeoutMs);
package/dist/rollup.cjs CHANGED
@@ -13631,7 +13631,7 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13631
13631
  return { ok: true };
13632
13632
  if ((0, import_node_child_process2.spawnSync)(sudoCommand, ["-n", "true"], { stdio: "ignore" }).status === 0)
13633
13633
  return { ok: true };
13634
- if (options.target) {
13634
+ if (options.target && !options.quiet) {
13635
13635
  for (const line of describePasswordReason(options.target, options.https ?? false))
13636
13636
  import_node_process6.default.stderr.write(`${line}
13637
13637
  `);
@@ -13658,11 +13658,37 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13658
13658
  }
13659
13659
  function runPrompt(sudoCommand) {
13660
13660
  return new Promise((resolve2) => {
13661
+ const restore = takeBackTerminal();
13661
13662
  const child = (0, import_node_child_process2.spawn)(sudoCommand, ["-v"], { stdio: "inherit" });
13662
- child.on("error", () => resolve2({ status: 1, signal: null }));
13663
- child.on("exit", (status, signal) => resolve2({ status, signal }));
13663
+ const finish = (result) => {
13664
+ restore();
13665
+ resolve2(result);
13666
+ };
13667
+ child.on("error", () => finish({ status: 1, signal: null }));
13668
+ child.on("exit", (status, signal) => finish({ status, signal }));
13664
13669
  });
13665
13670
  }
13671
+ function takeBackTerminal() {
13672
+ const stdin = import_node_process6.default.stdin;
13673
+ if (!stdin.isTTY || typeof stdin.setRawMode !== "function")
13674
+ return () => {
13675
+ };
13676
+ const wasRaw = stdin.isRaw === true;
13677
+ try {
13678
+ stdin.setRawMode(false);
13679
+ } catch {
13680
+ return () => {
13681
+ };
13682
+ }
13683
+ return () => {
13684
+ if (!wasRaw)
13685
+ return;
13686
+ try {
13687
+ stdin.setRawMode(true);
13688
+ } catch {
13689
+ }
13690
+ };
13691
+ }
13666
13692
  var PrivilegeDeniedError = class extends Error {
13667
13693
  constructor(message) {
13668
13694
  super(message);
package/dist/rollup.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  unpluginFactory
3
- } from "./chunk-Y4TJZ6XZ.js";
3
+ } from "./chunk-FNF6HQJB.js";
4
4
  import "./chunk-EY3HXBJ4.js";
5
5
  import "./chunk-C5A7XLKL.js";
6
6
  import "./chunk-WOMR3ZLM.js";
package/dist/rspack.cjs CHANGED
@@ -13630,7 +13630,7 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13630
13630
  return { ok: true };
13631
13631
  if ((0, import_node_child_process2.spawnSync)(sudoCommand, ["-n", "true"], { stdio: "ignore" }).status === 0)
13632
13632
  return { ok: true };
13633
- if (options.target) {
13633
+ if (options.target && !options.quiet) {
13634
13634
  for (const line of describePasswordReason(options.target, options.https ?? false))
13635
13635
  import_node_process6.default.stderr.write(`${line}
13636
13636
  `);
@@ -13657,11 +13657,37 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13657
13657
  }
13658
13658
  function runPrompt(sudoCommand) {
13659
13659
  return new Promise((resolve2) => {
13660
+ const restore = takeBackTerminal();
13660
13661
  const child = (0, import_node_child_process2.spawn)(sudoCommand, ["-v"], { stdio: "inherit" });
13661
- child.on("error", () => resolve2({ status: 1, signal: null }));
13662
- child.on("exit", (status, signal) => resolve2({ status, signal }));
13662
+ const finish = (result) => {
13663
+ restore();
13664
+ resolve2(result);
13665
+ };
13666
+ child.on("error", () => finish({ status: 1, signal: null }));
13667
+ child.on("exit", (status, signal) => finish({ status, signal }));
13663
13668
  });
13664
13669
  }
13670
+ function takeBackTerminal() {
13671
+ const stdin = import_node_process6.default.stdin;
13672
+ if (!stdin.isTTY || typeof stdin.setRawMode !== "function")
13673
+ return () => {
13674
+ };
13675
+ const wasRaw = stdin.isRaw === true;
13676
+ try {
13677
+ stdin.setRawMode(false);
13678
+ } catch {
13679
+ return () => {
13680
+ };
13681
+ }
13682
+ return () => {
13683
+ if (!wasRaw)
13684
+ return;
13685
+ try {
13686
+ stdin.setRawMode(true);
13687
+ } catch {
13688
+ }
13689
+ };
13690
+ }
13665
13691
  var PrivilegeDeniedError = class extends Error {
13666
13692
  constructor(message) {
13667
13693
  super(message);
package/dist/rspack.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  src_default
3
- } from "./chunk-Y4TJZ6XZ.js";
3
+ } from "./chunk-FNF6HQJB.js";
4
4
  import "./chunk-EY3HXBJ4.js";
5
5
  import "./chunk-C5A7XLKL.js";
6
6
  import "./chunk-WOMR3ZLM.js";
package/dist/vite.cjs CHANGED
@@ -13631,7 +13631,7 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13631
13631
  return { ok: true };
13632
13632
  if ((0, import_node_child_process2.spawnSync)(sudoCommand, ["-n", "true"], { stdio: "ignore" }).status === 0)
13633
13633
  return { ok: true };
13634
- if (options.target) {
13634
+ if (options.target && !options.quiet) {
13635
13635
  for (const line of describePasswordReason(options.target, options.https ?? false))
13636
13636
  import_node_process6.default.stderr.write(`${line}
13637
13637
  `);
@@ -13658,11 +13658,37 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13658
13658
  }
13659
13659
  function runPrompt(sudoCommand) {
13660
13660
  return new Promise((resolve2) => {
13661
+ const restore = takeBackTerminal();
13661
13662
  const child = (0, import_node_child_process2.spawn)(sudoCommand, ["-v"], { stdio: "inherit" });
13662
- child.on("error", () => resolve2({ status: 1, signal: null }));
13663
- child.on("exit", (status, signal) => resolve2({ status, signal }));
13663
+ const finish = (result) => {
13664
+ restore();
13665
+ resolve2(result);
13666
+ };
13667
+ child.on("error", () => finish({ status: 1, signal: null }));
13668
+ child.on("exit", (status, signal) => finish({ status, signal }));
13664
13669
  });
13665
13670
  }
13671
+ function takeBackTerminal() {
13672
+ const stdin = import_node_process6.default.stdin;
13673
+ if (!stdin.isTTY || typeof stdin.setRawMode !== "function")
13674
+ return () => {
13675
+ };
13676
+ const wasRaw = stdin.isRaw === true;
13677
+ try {
13678
+ stdin.setRawMode(false);
13679
+ } catch {
13680
+ return () => {
13681
+ };
13682
+ }
13683
+ return () => {
13684
+ if (!wasRaw)
13685
+ return;
13686
+ try {
13687
+ stdin.setRawMode(true);
13688
+ } catch {
13689
+ }
13690
+ };
13691
+ }
13666
13692
  var PrivilegeDeniedError = class extends Error {
13667
13693
  constructor(message) {
13668
13694
  super(message);
package/dist/vite.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  unpluginFactory
3
- } from "./chunk-Y4TJZ6XZ.js";
3
+ } from "./chunk-FNF6HQJB.js";
4
4
  import "./chunk-EY3HXBJ4.js";
5
5
  import "./chunk-C5A7XLKL.js";
6
6
  import "./chunk-WOMR3ZLM.js";
package/dist/webpack.cjs CHANGED
@@ -13631,7 +13631,7 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13631
13631
  return { ok: true };
13632
13632
  if ((0, import_node_child_process2.spawnSync)(sudoCommand, ["-n", "true"], { stdio: "ignore" }).status === 0)
13633
13633
  return { ok: true };
13634
- if (options.target) {
13634
+ if (options.target && !options.quiet) {
13635
13635
  for (const line of describePasswordReason(options.target, options.https ?? false))
13636
13636
  import_node_process6.default.stderr.write(`${line}
13637
13637
  `);
@@ -13658,11 +13658,37 @@ async function authenticateSudo(sudoCommand = "sudo", options = {}) {
13658
13658
  }
13659
13659
  function runPrompt(sudoCommand) {
13660
13660
  return new Promise((resolve2) => {
13661
+ const restore = takeBackTerminal();
13661
13662
  const child = (0, import_node_child_process2.spawn)(sudoCommand, ["-v"], { stdio: "inherit" });
13662
- child.on("error", () => resolve2({ status: 1, signal: null }));
13663
- child.on("exit", (status, signal) => resolve2({ status, signal }));
13663
+ const finish = (result) => {
13664
+ restore();
13665
+ resolve2(result);
13666
+ };
13667
+ child.on("error", () => finish({ status: 1, signal: null }));
13668
+ child.on("exit", (status, signal) => finish({ status, signal }));
13664
13669
  });
13665
13670
  }
13671
+ function takeBackTerminal() {
13672
+ const stdin = import_node_process6.default.stdin;
13673
+ if (!stdin.isTTY || typeof stdin.setRawMode !== "function")
13674
+ return () => {
13675
+ };
13676
+ const wasRaw = stdin.isRaw === true;
13677
+ try {
13678
+ stdin.setRawMode(false);
13679
+ } catch {
13680
+ return () => {
13681
+ };
13682
+ }
13683
+ return () => {
13684
+ if (!wasRaw)
13685
+ return;
13686
+ try {
13687
+ stdin.setRawMode(true);
13688
+ } catch {
13689
+ }
13690
+ };
13691
+ }
13666
13692
  var PrivilegeDeniedError = class extends Error {
13667
13693
  constructor(message) {
13668
13694
  super(message);
package/dist/webpack.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  unpluginFactory
3
- } from "./chunk-Y4TJZ6XZ.js";
3
+ } from "./chunk-FNF6HQJB.js";
4
4
  import "./chunk-EY3HXBJ4.js";
5
5
  import "./chunk-C5A7XLKL.js";
6
6
  import "./chunk-WOMR3ZLM.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "unplugin-https-reverse-proxy",
3
3
  "type": "module",
4
- "version": "4.1.0-beta.6",
4
+ "version": "4.1.0-beta.7",
5
5
  "description": "",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/zcf0508/unplugin-https-reverse-proxy#readme",
@@ -35,6 +35,14 @@ export interface HelperBootstrapOptions {
35
35
  * sooner than this and still reuses the running helper.
36
36
  */
37
37
  idleTimeoutMs?: number
38
+ /**
39
+ * Whether the idle timer may end this process.
40
+ *
41
+ * True for the real helper, which owns its process. False when `startHelper`
42
+ * is called in-process (tests): exiting there would take the caller down
43
+ * with it.
44
+ */
45
+ exitOnIdle?: boolean
38
46
  deps?: HelperDeps
39
47
  log?: (message: string) => void
40
48
  }
@@ -83,6 +91,9 @@ export async function startHelper(options: HelperBootstrapOptions): Promise<Runn
83
91
 
84
92
  const connections = new Set<Socket>()
85
93
  const idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS
94
+ // The real helper is a process of its own and may end itself. When this runs
95
+ // inside a host process (tests), only the socket is released.
96
+ const exitOnIdle = options.exitOnIdle ?? true
86
97
  let idleTimer: ReturnType<typeof setTimeout> | undefined
87
98
 
88
99
  const stopIdleTimer = (): void => {
@@ -171,6 +182,12 @@ export async function startHelper(options: HelperBootstrapOptions): Promise<Runn
171
182
 
172
183
  log('no clients left; shutting down')
173
184
  removeOwnSocket(socketPath, token)
185
+
186
+ if (!exitOnIdle) {
187
+ server.close()
188
+ return
189
+ }
190
+
174
191
  server.close(() => process.exit(0))
175
192
  // A listener with no clients closes synchronously, but keep a backstop so
176
193
  // the process cannot linger if a close callback never arrives.
@@ -72,7 +72,7 @@ export function describePasswordReason(target: string, https: boolean): string[]
72
72
  */
73
73
  export async function authenticateSudo(
74
74
  sudoCommand = 'sudo',
75
- options: { target?: string, https?: boolean } = {},
75
+ options: { target?: string, https?: boolean, quiet?: boolean } = {},
76
76
  ): Promise<{ ok: boolean, denied?: boolean, message?: string }> {
77
77
  if (platform() === 'win32')
78
78
  return { ok: true }
@@ -80,7 +80,9 @@ export async function authenticateSudo(
80
80
  if (spawnSync(sudoCommand, ['-n', 'true'], { stdio: 'ignore' }).status === 0)
81
81
  return { ok: true }
82
82
 
83
- if (options.target) {
83
+ // Tests drive this without a terminal; writing the notice there would print
84
+ // password instructions into the middle of the test output.
85
+ if (options.target && !options.quiet) {
84
86
  for (const line of describePasswordReason(options.target, options.https ?? false))
85
87
  process.stderr.write(`${line}\n`)
86
88
  process.stderr.write('\n')
@@ -123,16 +125,58 @@ export async function authenticateSudo(
123
125
  * Spawned asynchronously rather than with `spawnSync`: the synchronous form
124
126
  * blocks the event loop, so the SIGINT handler above never runs and the exit
125
127
  * looks like a plain failure instead of a cancellation.
128
+ *
129
+ * The terminal is put back into cooked mode first. Vite turns on raw mode for
130
+ * its `h + enter` shortcuts, and in that mode Enter no longer terminates the
131
+ * line sudo is reading, so the password appears to need a second Enter.
126
132
  */
127
133
  function runPrompt(sudoCommand: string): Promise<{ status: number | null, signal: NodeJS.Signals | null }> {
128
134
  return new Promise((resolve) => {
135
+ const restore = takeBackTerminal()
129
136
  const child = spawn(sudoCommand, ['-v'], { stdio: 'inherit' })
130
137
 
131
- child.on('error', () => resolve({ status: 1, signal: null }))
132
- child.on('exit', (status, signal) => resolve({ status, signal }))
138
+ const finish = (result: { status: number | null, signal: NodeJS.Signals | null }): void => {
139
+ restore()
140
+ resolve(result)
141
+ }
142
+
143
+ child.on('error', () => finish({ status: 1, signal: null }))
144
+ child.on('exit', (status, signal) => finish({ status, signal }))
133
145
  })
134
146
  }
135
147
 
148
+ /**
149
+ * Returns the terminal to cooked mode for the duration of the prompt.
150
+ *
151
+ * Vite leaves stdin in raw mode while it listens for its own shortcuts. In raw
152
+ * mode the tty does no line editing, so the Enter that should submit the
153
+ * password only reaches sudo as a bare carriage return and the read does not
154
+ * complete. The returned function puts the previous state back, so Vite's
155
+ * shortcuts keep working afterwards.
156
+ */
157
+ function takeBackTerminal(): () => void {
158
+ const stdin = process.stdin
159
+ if (!stdin.isTTY || typeof stdin.setRawMode !== 'function')
160
+ return () => {}
161
+
162
+ const wasRaw = stdin.isRaw === true
163
+ try {
164
+ stdin.setRawMode(false)
165
+ }
166
+ catch {
167
+ return () => {}
168
+ }
169
+
170
+ return () => {
171
+ if (!wasRaw)
172
+ return
173
+ try {
174
+ stdin.setRawMode(true)
175
+ }
176
+ catch {}
177
+ }
178
+ }
179
+
136
180
  /** The user dismissed the password prompt; this is a choice, not a failure. */
137
181
  export class PrivilegeDeniedError extends Error {
138
182
  constructor(message: string) {