/** * peachScript.ts — PeachScript encoding/decoding. * * PeachScript is the device-to-device export format. Each byte of an * encrypted vault payload maps to one Unicode Private Use Area code point * in U+E000..U+E0FF. The bundled `PeachScript-Regular.otf` renders those * 256 code points as the visual script. Outside Peach the string renders * as boxes — that is the desired property: PeachScript is meant to be * read by Peach, not by humans. * * The encoding is trivially reversible: byte 0 → U+E000, byte 255 → * U+E0FF. No padding, no base conversion, no alignment fix-ups. The font * does the visual work; this module is the codec. */ const BASE = 0xe000; /** Encode a binary payload to a PeachScript string (one byte per code point). */ export function encodePeachScript(bytes: Uint8Array): string { const out: string[] = []; for (let i = 0; i < bytes.length; i++) { out.push(String.fromCodePoint(BASE + bytes[i])); } return out.join(""); } /** * Decode a PeachScript string back to bytes. Throws * `INVALID_PEACHSCRIPT_CHARACTER` when any character is outside the * allotted PUA range — i.e. the string was edited, truncated mid-pair, * or is not PeachScript at all. */ export function decodePeachScript(str: string): Uint8Array { const codePoints = Array.from(str); const bytes = new Uint8Array(codePoints.length); for (let i = 0; i < codePoints.length; i++) { const cp = codePoints[i].codePointAt(0)!; if (cp < BASE || cp > BASE + 0xff) { throw new Error("INVALID_PEACHSCRIPT_CHARACTER"); } bytes[i] = cp - BASE; } return bytes; } /** Strip whitespace from a PeachScript string before decoding. */ export function normalisePeachScript(str: string): string { return str.replace(/\s+/g, ""); } /** * Cheap pre-flight: every non-whitespace char must be in the PUA range and * the string must be non-empty. Used by the import UI to gate the decode * button without throwing. */ export function isPeachScript(str: string): boolean { const trimmed = normalisePeachScript(str); if (trimmed.length === 0) return false; for (const ch of trimmed) { const cp = ch.codePointAt(0)!; if (cp < BASE || cp > BASE + 0xff) return false; } return true; }