UNPKG

noa-receipt

Version:

NOA Agent Action Receipt — open, offline-verifiable provenance for AI-agent actions. The governance/receipt organ only; the NOA brain is separate and proprietary.

92 lines (91 loc) • 3.08 kB
import { safeParse, isSafeJsonError } from "./safe-json.js"; export const MAX_INPUT_BYTES = 16 * 1024 * 1024; const _apply = Reflect.apply; const _taProto = Object.getPrototypeOf(Uint8Array.prototype); const _taTag = Reflect.getOwnPropertyDescriptor(_taProto, Symbol.toStringTag).get; const _taByteLength = Reflect.getOwnPropertyDescriptor(_taProto, "byteLength").get; const _decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }); const _decode = TextDecoder.prototype.decode; const _strCharCodeAt = String.prototype.charCodeAt; function typedArrayTag(value) { return _apply(_taTag, value, []); } export function isUint8Array(value) { return typedArrayTag(value) === "Uint8Array"; } function utf8Length(text) { let total = 0; const n = text.length; let i = 0; while (i < n) { const c = _apply(_strCharCodeAt, text, [i]); if (c < 0x80) { total += 1; i += 1; } else if (c < 0x800) { total += 2; i += 1; } else if (c >= 0xd800 && c <= 0xdbff) { const next = i + 1 < n ? _apply(_strCharCodeAt, text, [i + 1]) : -1; if (next < 0xdc00 || next > 0xdfff) return -1; total += 4; i += 2; } else if (c >= 0xdc00 && c <= 0xdfff) { return -1; } else { total += 3; i += 1; } if (total > MAX_INPUT_BYTES) return total; } return total; } export function decodeDocument(input, what) { if (typeof input === "string") { const len = utf8Length(input); if (len < 0) { return { ok: false, reason: `${what}: text is not well-formed Unicode (unpaired surrogate)` }; } if (len > MAX_INPUT_BYTES) { return { ok: false, reason: `${what}: input exceeds the ${MAX_INPUT_BYTES}-byte ceiling` }; } return { ok: true, text: input }; } if (!isUint8Array(input)) { return { ok: false, reason: `${what}: expected Uint8Array or string — a security-sensitive document is bytes, never a caller-owned object (ADR §3.1)`, }; } const byteLength = _apply(_taByteLength, input, []); if (byteLength > MAX_INPUT_BYTES) { return { ok: false, reason: `${what}: input exceeds the ${MAX_INPUT_BYTES}-byte ceiling` }; } let text; try { text = _apply(_decode, _decoder, [input]); } catch { return { ok: false, reason: `${what}: input is not valid UTF-8` }; } return { ok: true, text }; } export function parseDocument(input, what) { const decoded = decodeDocument(input, what); if (!decoded.ok) return decoded; try { return { ok: true, value: safeParse(decoded.text, { maxLength: MAX_INPUT_BYTES }) }; } catch (e) { if (isSafeJsonError(e)) return { ok: false, reason: `${what}: ${e.message}` }; return { ok: false, reason: `${what}: input could not be parsed` }; } }