From dcd1836d51928d899bf815ed7309802ff70a83a4 Mon Sep 17 00:00:00 2001 From: can1357 Date: Wed, 17 Jun 2026 01:20:37 +0200 Subject: [PATCH] feat(utils): added isTerminalHeadless/setTerminalHeadless terminal-suppression flag - Added `isTerminalHeadless()` / `setTerminalHeadless()` to `env`, a process-wide switch that suppresses real-terminal side effects (stdout escape/frame writes, stdin raw mode, CSI/OSC probes, SIGWINCH, window-title changes, emergency restore). - Defaulted the flag to `isBunTestRuntime()` so it engages when `bun test` sets `NODE_ENV=test`, and made `setTerminalHeadless` return the previous value so callers can restore exact prior state. --- packages/utils/CHANGELOG.md | 1 + packages/utils/src/env.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index ed155fd8d..b8330ef29 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added - Added `escapeXmlText` utility to escape XML-significant characters `&`, `<`, and `>` in element body text +- Added `isTerminalHeadless()` / `setTerminalHeadless()` to centrally suppress real-terminal side effects (stdout escape/frame writes, stdin raw mode, CSI/OSC capability probes, SIGWINCH, window-title changes, emergency restore) under the test runtime. Defaults on when `bun test` sets `NODE_ENV=test`; terminal-contract tests opt out via `setTerminalHeadless(false)` ## [15.13.3] - 2026-06-15 diff --git a/packages/utils/src/env.ts b/packages/utils/src/env.ts index 14d984d7f..b3337f92e 100644 --- a/packages/utils/src/env.ts +++ b/packages/utils/src/env.ts @@ -167,6 +167,33 @@ export function isBunTestRuntime(): boolean { return Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test"; } +let terminalHeadless = isBunTestRuntime(); + +/** + * True when real-terminal side effects must be suppressed: stdout escape/frame + * writes, stdin raw-mode + resume, CSI/OSC capability probes, SIGWINCH, window + * title changes, and emergency restore. Defaults to {@link isBunTestRuntime} so + * `bun test` launched inside a real TTY never paints the TUI, leaks probe + * queries, or hijacks the developer's stdin; production runtimes stay + * interactive. + * + * Terminal-contract tests that must exercise the real I/O path opt out with + * `setTerminalHeadless(false)` and restore it afterwards. + */ +export function isTerminalHeadless(): boolean { + return terminalHeadless; +} + +/** + * Override the {@link isTerminalHeadless} default and return the previous value + * so callers can restore exact prior state (`const prev = setTerminalHeadless(false); … setTerminalHeadless(prev);`). + */ +export function setTerminalHeadless(headless: boolean): boolean { + const previous = terminalHeadless; + terminalHeadless = headless; + return previous; +} + /** * True when this code is running inside a `bun build --compile` standalone * binary. Detects via the embedded virtual-filesystem path markers