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
JavaScript
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` };
}
}