@oh-my-pi/pi-utils 16.0.1 → 16.0.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.0.3] - 2026-06-16
6
+
7
+ ### Added
8
+
9
+ - Added `escapeXmlText` utility to escape XML-significant characters `&`, `<`, and `>` in element body text
10
+ - 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)`
11
+
5
12
  ## [15.13.3] - 2026-06-15
6
13
 
7
14
  ### Added
@@ -45,6 +45,23 @@ export declare function $pickenv(...keys: string[]): string | undefined;
45
45
  export declare function $envpos(name: string, defaultValue: number): number;
46
46
  /** True when `BUN_ENV` or `NODE_ENV` is the string `test`. */
47
47
  export declare function isBunTestRuntime(): boolean;
48
+ /**
49
+ * True when real-terminal side effects must be suppressed: stdout escape/frame
50
+ * writes, stdin raw-mode + resume, CSI/OSC capability probes, SIGWINCH, window
51
+ * title changes, and emergency restore. Defaults to {@link isBunTestRuntime} so
52
+ * `bun test` launched inside a real TTY never paints the TUI, leaks probe
53
+ * queries, or hijacks the developer's stdin; production runtimes stay
54
+ * interactive.
55
+ *
56
+ * Terminal-contract tests that must exercise the real I/O path opt out with
57
+ * `setTerminalHeadless(false)` and restore it afterwards.
58
+ */
59
+ export declare function isTerminalHeadless(): boolean;
60
+ /**
61
+ * Override the {@link isTerminalHeadless} default and return the previous value
62
+ * so callers can restore exact prior state (`const prev = setTerminalHeadless(false); … setTerminalHeadless(prev);`).
63
+ */
64
+ export declare function setTerminalHeadless(headless: boolean): boolean;
48
65
  /**
49
66
  * True when this code is running inside a `bun build --compile` standalone
50
67
  * binary. Detects via the embedded virtual-filesystem path markers
@@ -12,3 +12,10 @@
12
12
  * string after the control probe.
13
13
  */
14
14
  export declare function sanitizeText(text: string): string;
15
+ /**
16
+ * Escape the three XML-significant characters (`&`, `<`, `>`) in text destined
17
+ * for an XML/markup element body. Allocation-conscious: returns the input
18
+ * unchanged (same reference) when nothing needs escaping. Quotes are left as-is
19
+ * — use it for element text, not attribute values.
20
+ */
21
+ export declare function escapeXmlText(input: string): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-utils",
4
- "version": "16.0.1",
4
+ "version": "16.0.3",
5
5
  "description": "Shared utilities for pi packages",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -31,7 +31,7 @@
31
31
  "fmt": "biome format --write ."
32
32
  },
33
33
  "dependencies": {
34
- "@oh-my-pi/pi-natives": "16.0.1",
34
+ "@oh-my-pi/pi-natives": "16.0.3",
35
35
  "beautiful-mermaid": "^1.1.3",
36
36
  "handlebars": "^4.7.9",
37
37
  "winston": "^3.19.0",
package/src/env.ts CHANGED
@@ -167,6 +167,33 @@ export function isBunTestRuntime(): boolean {
167
167
  return Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test";
168
168
  }
169
169
 
170
+ let terminalHeadless = isBunTestRuntime();
171
+
172
+ /**
173
+ * True when real-terminal side effects must be suppressed: stdout escape/frame
174
+ * writes, stdin raw-mode + resume, CSI/OSC capability probes, SIGWINCH, window
175
+ * title changes, and emergency restore. Defaults to {@link isBunTestRuntime} so
176
+ * `bun test` launched inside a real TTY never paints the TUI, leaks probe
177
+ * queries, or hijacks the developer's stdin; production runtimes stay
178
+ * interactive.
179
+ *
180
+ * Terminal-contract tests that must exercise the real I/O path opt out with
181
+ * `setTerminalHeadless(false)` and restore it afterwards.
182
+ */
183
+ export function isTerminalHeadless(): boolean {
184
+ return terminalHeadless;
185
+ }
186
+
187
+ /**
188
+ * Override the {@link isTerminalHeadless} default and return the previous value
189
+ * so callers can restore exact prior state (`const prev = setTerminalHeadless(false); … setTerminalHeadless(prev);`).
190
+ */
191
+ export function setTerminalHeadless(headless: boolean): boolean {
192
+ const previous = terminalHeadless;
193
+ terminalHeadless = headless;
194
+ return previous;
195
+ }
196
+
170
197
  /**
171
198
  * True when this code is running inside a `bun build --compile` standalone
172
199
  * binary. Detects via the embedded virtual-filesystem path markers
@@ -36,3 +36,31 @@ function sanitizeWellFormedText(text: string): string {
36
36
  CONTROL_RE.lastIndex = 0;
37
37
  return stripped.replace(CONTROL_RE, "");
38
38
  }
39
+
40
+ /**
41
+ * Escape the three XML-significant characters (`&`, `<`, `>`) in text destined
42
+ * for an XML/markup element body. Allocation-conscious: returns the input
43
+ * unchanged (same reference) when nothing needs escaping. Quotes are left as-is
44
+ * — use it for element text, not attribute values.
45
+ */
46
+ export function escapeXmlText(input: string): string {
47
+ let firstEscapable = -1;
48
+ for (let index = 0; index < input.length; index++) {
49
+ const char = input.charCodeAt(index);
50
+ if (char === 38 || char === 60 || char === 62) {
51
+ firstEscapable = index;
52
+ break;
53
+ }
54
+ }
55
+ if (firstEscapable === -1) return input;
56
+
57
+ let output = input.slice(0, firstEscapable);
58
+ for (let index = firstEscapable; index < input.length; index++) {
59
+ const char = input[index];
60
+ if (char === "&") output += "&amp;";
61
+ else if (char === "<") output += "&lt;";
62
+ else if (char === ">") output += "&gt;";
63
+ else output += char;
64
+ }
65
+ return output;
66
+ }