@oh-my-pi/pi-utils 16.2.2 → 16.2.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,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.2.3] - 2026-06-28
6
+
7
+ ### Added
8
+
9
+ - Added `escapeXmlAttribute` utility function for safe XML attribute value encoding.
10
+
11
+ ### Fixed
12
+
13
+ - Fixed a crash in `ptree.ChildProcess.bytes()` and the `ssh://` read path when handling large subprocess outputs (over 128 KB) under Bun by ensuring it consistently returns a `Uint8Array`.
14
+
5
15
  ## [16.2.0] - 2026-06-27
6
16
 
7
17
  ### Added
@@ -19,3 +19,11 @@ export declare function sanitizeText(text: string): string;
19
19
  * — use it for element text, not attribute values.
20
20
  */
21
21
  export declare function escapeXmlText(input: string): string;
22
+ /**
23
+ * Escape XML-significant characters for an attribute VALUE: the three body
24
+ * characters (`&`, `<`, `>`) plus the double quote (`"` → `&quot;`) that would
25
+ * otherwise close the attribute. Allocation-conscious: returns the input
26
+ * unchanged (same reference) when nothing needs escaping. Use it for attribute
27
+ * values; {@link escapeXmlText} is for element bodies and leaves `"` intact.
28
+ */
29
+ export declare function escapeXmlAttribute(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.2.2",
4
+ "version": "16.2.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.2.2",
34
+ "@oh-my-pi/pi-natives": "16.2.3",
35
35
  "handlebars": "^4.7.9",
36
36
  "winston": "^3.19.0",
37
37
  "winston-daily-rotate-file": "^5.0.0"
package/src/ptree.ts CHANGED
@@ -247,7 +247,12 @@ export class ChildProcess<In extends InMask = InMask> {
247
247
  }
248
248
 
249
249
  async bytes(): Promise<Uint8Array> {
250
- return new Response(this.stdout).bytes();
250
+ // Bun's `Response(stream).bytes()` returns the raw `ArrayBuffer` once the
251
+ // stream emits more than one chunk (subprocess stdout chunks past ~128 KB).
252
+ // Normalize at the contract boundary so every caller — SSH read,
253
+ // `decodeUtf8Text`, callers slicing with `.subarray` — sees a `Uint8Array`.
254
+ const body = (await new Response(this.stdout).bytes()) as Uint8Array | ArrayBuffer;
255
+ return body instanceof Uint8Array ? body : new Uint8Array(body);
251
256
  }
252
257
 
253
258
  // ── Wait ─────────────────────────────────────────────────────────────
@@ -64,3 +64,33 @@ export function escapeXmlText(input: string): string {
64
64
  }
65
65
  return output;
66
66
  }
67
+
68
+ /**
69
+ * Escape XML-significant characters for an attribute VALUE: the three body
70
+ * characters (`&`, `<`, `>`) plus the double quote (`"` → `&quot;`) that would
71
+ * otherwise close the attribute. Allocation-conscious: returns the input
72
+ * unchanged (same reference) when nothing needs escaping. Use it for attribute
73
+ * values; {@link escapeXmlText} is for element bodies and leaves `"` intact.
74
+ */
75
+ export function escapeXmlAttribute(input: string): string {
76
+ let firstEscapable = -1;
77
+ for (let index = 0; index < input.length; index++) {
78
+ const char = input.charCodeAt(index);
79
+ if (char === 38 || char === 60 || char === 62 || char === 34) {
80
+ firstEscapable = index;
81
+ break;
82
+ }
83
+ }
84
+ if (firstEscapable === -1) return input;
85
+
86
+ let output = input.slice(0, firstEscapable);
87
+ for (let index = firstEscapable; index < input.length; index++) {
88
+ const char = input[index];
89
+ if (char === "&") output += "&amp;";
90
+ else if (char === "<") output += "&lt;";
91
+ else if (char === ">") output += "&gt;";
92
+ else if (char === '"') output += "&quot;";
93
+ else output += char;
94
+ }
95
+ return output;
96
+ }