qunitx-cli 0.23.1 → 0.23.3

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +44 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -115,7 +115,7 @@ What it does: cold-start cost dominates a single `qunitx` run — launching Chro
115
115
 
116
116
  A single one-off run won't get faster from spinning the daemon up; it's an opt-in optimization aimed at two situations:
117
117
 
118
- - **Local TDD loops.** Export `QUNITX_DAEMON=1` in your shell profile (or run `qunitx daemon start` once at the top of your session) and forget about it — subsequent runs reuse the daemon automatically until you `daemon stop` or 30 idle minutes pass.
118
+ - **Local TDD loops.** Export `QUNITX_DAEMON=1` in your shell profile (or run `qunitx daemon start` once at the top of your session) and forget about it — subsequent runs reuse the daemon automatically until you `daemon stop` or 30 idle minutes pass. Override the idle window with `QUNITX_DAEMON_IDLE_TIMEOUT` (`1h`, `45s`, `500ms`; bare numbers are minutes), or set it to `false` to disable auto-shutdown entirely.
119
119
  - **Monorepo CI** where each package shells out to `qunitx` separately. Set `QUNITX_DAEMON=1` for the job and a single daemon is auto-spawned and reused across every package's invocation.
120
120
 
121
121
  ```sh
package/dist/cli.js CHANGED
@@ -374,7 +374,7 @@ var init_package = __esm({
374
374
  package_default = {
375
375
  name: "qunitx-cli",
376
376
  type: "module",
377
- version: "0.23.1",
377
+ version: "0.23.3",
378
378
  description: "Browser runner for QUnitx: run your qunitx tests in google-chrome",
379
379
  author: "Izel Nakri",
380
380
  license: "MIT",
@@ -748,6 +748,31 @@ var init_init = __esm({
748
748
  }
749
749
  });
750
750
 
751
+ // lib/utils/parse-daemon-idle-timeout.ts
752
+ function parseDaemonIdleTimeout(value) {
753
+ if (!value) return { ms: DEFAULT_DAEMON_IDLE_TIMEOUT_MS, warning: null };
754
+ if (/^\s*false\s*$/i.test(value)) return { ms: Infinity, warning: null };
755
+ const match = /^\s*(\d+(?:\.\d+)?)\s*(ms|s|m|h)?\s*$/i.exec(value);
756
+ if (!match) return invalid(value);
757
+ const n = Number(match[1]);
758
+ if (!(n > 0)) return invalid(value);
759
+ const unit = (match[2] ?? "m").toLowerCase();
760
+ return { ms: Math.round(n * UNIT_TO_MS[unit]), warning: null };
761
+ }
762
+ function invalid(value) {
763
+ return {
764
+ ms: DEFAULT_DAEMON_IDLE_TIMEOUT_MS,
765
+ warning: `\u26A0 qunitx: QUNITX_DAEMON_IDLE_TIMEOUT=${JSON.stringify(value)} is not a valid duration; using 30-minute default. Accepted: bare number = minutes, "30m", "1h", "45s", "500ms", "false" (no auto-shutdown).`
766
+ };
767
+ }
768
+ var UNIT_TO_MS, DEFAULT_DAEMON_IDLE_TIMEOUT_MS;
769
+ var init_parse_daemon_idle_timeout = __esm({
770
+ "lib/utils/parse-daemon-idle-timeout.ts"() {
771
+ UNIT_TO_MS = { ms: 1, s: 1e3, m: 6e4, h: 36e5 };
772
+ DEFAULT_DAEMON_IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
773
+ }
774
+ });
775
+
751
776
  // lib/utils/close-with-grace.ts
752
777
  function closeWithGrace(closes, graceMs = CLEANUP_GRACE_MS) {
753
778
  return new Promise((resolve) => {
@@ -4461,6 +4486,7 @@ async function shutdownDaemon2(state, reason) {
4461
4486
  }
4462
4487
  function resetIdleTimer(state) {
4463
4488
  if (state.idleTimer) clearTimeout(state.idleTimer);
4489
+ if (!Number.isFinite(IDLE_TIMEOUT_MS)) return;
4464
4490
  state.idleTimer = setTimeout(() => void shutdownDaemon2(state, "idle timeout"), IDLE_TIMEOUT_MS);
4465
4491
  state.idleTimer.unref();
4466
4492
  }
@@ -4634,12 +4660,13 @@ var IDLE_TIMEOUT_MS, LIVENESS_PROBE_TIMEOUT_MS, MAX_CONSECUTIVE_CRASHES;
4634
4660
  var init_server = __esm({
4635
4661
  "lib/commands/daemon/server.ts"() {
4636
4662
  init_daemon_socket_path();
4663
+ init_parse_daemon_idle_timeout();
4637
4664
  init_socket_utils();
4638
4665
  init_config();
4639
4666
  init_browser();
4640
4667
  init_tests_in_browser();
4641
4668
  init_run();
4642
- IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
4669
+ IDLE_TIMEOUT_MS = parseDaemonIdleTimeout(process.env.QUNITX_DAEMON_IDLE_TIMEOUT).ms;
4643
4670
  LIVENESS_PROBE_TIMEOUT_MS = 500;
4644
4671
  MAX_CONSECUTIVE_CRASHES = 2;
4645
4672
  }
@@ -4654,7 +4681,11 @@ __export(daemon_exports, {
4654
4681
  import { spawn as spawn3 } from "node:child_process";
4655
4682
  import fs16, { existsSync as existsSync3 } from "node:fs";
4656
4683
  import path13 from "node:path";
4657
- import { fileURLToPath as fileURLToPath2 } from "node:url";
4684
+ async function buildDaemonSpawn() {
4685
+ const sea = await import("node:sea").catch(() => null);
4686
+ if (sea?.isSea()) return { bin: process.execPath, args: ["daemon", "_serve"] };
4687
+ return { bin: process.execPath, args: [process.argv[1], "daemon", "_serve"] };
4688
+ }
4658
4689
  function runDaemonCommand() {
4659
4690
  const sub = process.argv[3];
4660
4691
  if (sub === "_serve") return runServeMode();
@@ -4690,7 +4721,10 @@ function waitForFile(filePath, timeoutMs) {
4690
4721
  });
4691
4722
  }
4692
4723
  async function spawnAndWaitForDaemon() {
4693
- spawn3(process.execPath, [CLI_ENTRY, "daemon", "_serve"], {
4724
+ const parsed = parseDaemonIdleTimeout(process.env.QUNITX_DAEMON_IDLE_TIMEOUT);
4725
+ if (parsed.warning) process.stderr.write(parsed.warning + "\n");
4726
+ const { bin, args } = await buildDaemonSpawn();
4727
+ spawn3(bin, args, {
4694
4728
  detached: true,
4695
4729
  stdio: "ignore",
4696
4730
  env: { ...process.env, QUNITX_DAEMON_CWD: process.cwd() }
@@ -4743,11 +4777,12 @@ async function statusDaemon() {
4743
4777
  );
4744
4778
  return 0;
4745
4779
  }
4746
- var SPAWN_TIMEOUT_MS, highlight2, color2, USAGE, __filename, CLI_ENTRY;
4780
+ var SPAWN_TIMEOUT_MS, highlight2, color2, USAGE;
4747
4781
  var init_daemon = __esm({
4748
4782
  "lib/commands/daemon/index.ts"() {
4749
4783
  init_color();
4750
4784
  init_daemon_socket_path();
4785
+ init_parse_daemon_idle_timeout();
4751
4786
  init_client();
4752
4787
  init_package();
4753
4788
  SPAWN_TIMEOUT_MS = 3e4;
@@ -4761,13 +4796,13 @@ ${color2("$ qunitx daemon stop")} # Stop the running daemon
4761
4796
  ${color2("$ qunitx daemon status")} # Print pid, socket, and uptime
4762
4797
 
4763
4798
  ${highlight2("Environment:")}
4764
- ${color2("QUNITX_DAEMON=1")} : auto-spawn the daemon on the first qunitx run; reuse it on every run after (overrides the CI=1 bypass)
4765
- ${color2("QUNITX_NO_DAEMON=1")} : never use the daemon for this run
4799
+ ${color2("QUNITX_DAEMON=1")} : auto-spawn the daemon on the first qunitx run; reuse it on every run after (overrides the CI=1 bypass)
4800
+ ${color2("QUNITX_NO_DAEMON=1")} : never use the daemon for this run
4801
+ ${color2("QUNITX_DAEMON_IDLE_TIMEOUT")} : idle window before self-shutdown (default 30m). Bare number = minutes; ${color2("ms")} / ${color2("s")} / ${color2("m")} / ${color2("h")} suffixes accepted (${color2("1h")}, ${color2("45s")}, ${color2("500ms")}). Set to ${color2("false")} to disable auto-shutdown. Read at daemon spawn; invalid values warn and fall back to the default.
4802
+ ${color2("QUNITX_DAEMON_LOG=<path>")} : redirect the daemon's stdout + stderr to a file (otherwise lost to the detached spawn)
4766
4803
 
4767
4804
  ${highlight2("Tip:")} set ${color2("QUNITX_DAEMON=1")} to auto-spawn the daemon on the first qunitx run; ${color2("$ qunitx --help")} for top-level options.
4768
4805
  `;
4769
- __filename = fileURLToPath2(import.meta.url);
4770
- CLI_ENTRY = path13.resolve(path13.dirname(__filename), "..", "..", "..", "cli.ts");
4771
4806
  }
4772
4807
  });
4773
4808
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "qunitx-cli",
3
3
  "type": "module",
4
- "version": "0.23.1",
4
+ "version": "0.23.3",
5
5
  "description": "Browser runner for QUnitx: run your qunitx tests in google-chrome",
6
6
  "author": "Izel Nakri",
7
7
  "license": "MIT",