[READ-ONLY] Mirror of https://github.com/bombshell-dev/tty. Platform independent 2D layout engine for terminal applications based on Clay
5

Configure Feed

Select the types of activity you want to include in your feed.

Merge PR #33 descriptive pack errors

Ryan Rauh (May 10, 2026, 7:38 AM EDT) 4d09e0c8 bb508c75

+79 -4
+20 -4
ops.ts
··· 56 56 return o; 57 57 } 58 58 59 - function packString(view: DataView, bytes: Uint8Array, o: number): number { 59 + function packString( 60 + view: DataView, 61 + bytes: Uint8Array, 62 + o: number, 63 + end: number, 64 + context: string, 65 + ): number { 66 + let paddedLength = Math.ceil(bytes.length / 4) * 4; 67 + let next = o + 4 + paddedLength; 68 + if (next > end) { 69 + throw new RangeError( 70 + `clayterm transfer buffer capacity exceeded while packing ${context} ` + 71 + `(${next} byte offset, ${end} byte limit). ` + 72 + `Render a smaller visible slice or reduce frame content.`, 73 + ); 74 + } 75 + 60 76 view.setUint32(o, bytes.length, true); 61 77 o += 4; 62 78 new Uint8Array(view.buffer).set(bytes, o); 63 - o += Math.ceil(bytes.length / 4) * 4; 79 + o += paddedLength; 64 80 return o; 65 81 } 66 82 ··· 86 102 o += 4; 87 103 88 104 let bytes = encoder.encode(op.id); 89 - o = packString(view, bytes, o); 105 + o = packString(view, bytes, o, end, "element id"); 90 106 91 107 let mask = 0; 92 108 if (op.layout) mask |= PROP_LAYOUT; ··· 224 240 o += 4; 225 241 226 242 let str = encoder.encode(op.content); 227 - o = packString(view, str, o); 243 + o = packString(view, str, o, end, "text content"); 228 244 break; 229 245 } 230 246 }
+7
specs/renderer-spec.md
··· 469 469 the renderer and is not an operation the caller performs or observes. The 470 470 transfer mechanism is an implementation detail described in Section 12.1. 471 471 472 + If a frame exceeds transfer-buffer capacity while packing string content, the 473 + renderer MUST throw a descriptive `RangeError` that identifies the condition as 474 + a transfer-buffer, frame-capacity, or packing overflow. The renderer MUST NOT 475 + expose only the raw host-level TypedArray message `"offset is out of bounds"` 476 + for this condition. The error message SHOULD direct callers to render a smaller 477 + visible slice or reduce frame content. 478 + 472 479 ### 9.3 Directive identity 473 480 474 481 Each element directive carries an `id` provided by the caller via `open()`.
+52
test/pack.test.ts
··· 1 + import { describe, expect, it } from "./suite.ts"; 2 + import { close, open, pack, text } from "../ops.ts"; 3 + 4 + describe("pack", () => { 5 + it("throws a descriptive RangeError when text exceeds the transfer buffer", () => { 6 + let memory = new ArrayBuffer(64); 7 + let error: unknown; 8 + 9 + try { 10 + pack( 11 + [ 12 + open("root"), 13 + text("x".repeat(128)), 14 + close(), 15 + ], 16 + memory, 17 + 0, 18 + memory.byteLength, 19 + ); 20 + } catch (caught) { 21 + error = caught; 22 + } 23 + 24 + expect(error).toBeInstanceOf(RangeError); 25 + expect((error as Error).message).toMatch( 26 + /transfer buffer|capacity|packing/, 27 + ); 28 + expect((error as Error).message).toContain("text content"); 29 + expect((error as Error).message).not.toBe("offset is out of bounds"); 30 + expect((error as Error).message).toMatch( 31 + /smaller visible slice|reduce frame content/, 32 + ); 33 + }); 34 + 35 + it("throws a descriptive RangeError when an element id exceeds the transfer buffer", () => { 36 + let memory = new ArrayBuffer(16); 37 + let error: unknown; 38 + 39 + try { 40 + pack([open("x".repeat(64)), close()], memory, 0, memory.byteLength); 41 + } catch (caught) { 42 + error = caught; 43 + } 44 + 45 + expect(error).toBeInstanceOf(RangeError); 46 + expect((error as Error).message).toMatch( 47 + /transfer buffer|capacity|packing/, 48 + ); 49 + expect((error as Error).message).toContain("element id"); 50 + expect((error as Error).message).not.toBe("offset is out of bounds"); 51 + }); 52 + });