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.

177 lines (176 loc) • 6.17 kB
import { arrayPush, arraySlice, arraySort, isSafeInteger, bufferFrom, bufferAlloc, bufferConcat, bufferCompare, bufWriteUInt16BE, bufWriteUInt32BE, bufWriteBigUInt64BE, bufSubarray, bufferFromArrayBuffer, bufReadUInt16BE, bufReadUInt32BE, bufReadBigUInt64BE, bufToString, toBigInt, bigIntToNumber, MAX_SAFE_INTEGER, byteLength, taBuffer, taByteOffset, taByteLength, } from "../intrinsics.js"; import { inertArray } from "../inert.js"; export class CborError extends Error { constructor(m) { super(m); this.name = "CborError"; } } function head(major, n) { const mt = major << 5; if (n < 0) throw new CborError("negative length"); if (n < 24) return bufferFrom([mt | n]); if (n < 0x100) return bufferFrom([mt | 24, n]); if (n < 0x10000) { const b = bufferAlloc(3); b[0] = mt | 25; bufWriteUInt16BE(b, n, 1); return b; } if (n < 0x100000000) { const b = bufferAlloc(5); b[0] = mt | 26; bufWriteUInt32BE(b, n, 1); return b; } const b = bufferAlloc(9); b[0] = mt | 27; bufWriteBigUInt64BE(b, toBigInt(n), 1); return b; } export function encInt(n) { if (!isSafeInteger(n)) throw new CborError("non-safe-integer"); return n >= 0 ? head(0, n) : head(1, -n - 1); } export function encBstr(buf) { return bufferConcat([head(2, byteLength(buf)), buf]); } export function encTstr(s) { const u = bufferFrom(s, "utf8"); return bufferConcat([head(3, byteLength(u)), u]); } export function encArray(items) { const parts = [head(4, items.length)]; for (let i = 0; i < items.length; i++) arrayPush(parts, items[i]); return bufferConcat(parts); } export function encMap(entries) { const sorted = arraySort(arraySlice(entries), (a, b) => bufferCompare(a[0], b[0])); const parts = [head(5, sorted.length)]; for (let i = 0; i < sorted.length; i++) { const e = sorted[i]; arrayPush(parts, e[0]); arrayPush(parts, e[1]); } return bufferConcat(parts); } export function encTag(tag, content) { return bufferConcat([head(6, tag), content]); } function readHead(c) { if (c.i >= byteLength(c.buf)) throw new CborError("unexpected end"); const ib = c.buf[c.i]; c.i++; const major = ib >> 5; const ai = ib & 0x1f; let n; if (ai < 24) n = ai; else if (ai === 24) { if (c.i + 1 > byteLength(c.buf)) throw new CborError("unexpected end (1-byte head)"); n = c.buf[c.i]; c.i += 1; if (n < 24) throw new CborError("non-canonical: 1-byte head < 24"); } else if (ai === 25) { if (c.i + 2 > byteLength(c.buf)) throw new CborError("unexpected end (2-byte head)"); n = bufReadUInt16BE(c.buf, c.i); c.i += 2; if (n < 0x100) throw new CborError("non-canonical: 2-byte head < 256"); } else if (ai === 26) { if (c.i + 4 > byteLength(c.buf)) throw new CborError("unexpected end (4-byte head)"); n = bufReadUInt32BE(c.buf, c.i); c.i += 4; if (n < 0x10000) throw new CborError("non-canonical: 4-byte head < 65536"); } else if (ai === 27) { if (c.i + 8 > byteLength(c.buf)) throw new CborError("unexpected end (8-byte head)"); const big = bufReadBigUInt64BE(c.buf, c.i); c.i += 8; if (big < 0x100000000n) throw new CborError("non-canonical: 8-byte head < 2^32"); if (big > toBigInt(MAX_SAFE_INTEGER)) throw new CborError("integer too large"); n = bigIntToNumber(big); } else throw new CborError("indefinite/unsupported length"); return { major, n }; } function decodeAt(c, depth) { if (depth > c.maxDepth) throw new CborError("max depth"); const { major, n } = readHead(c); switch (major) { case 0: return { t: "int", v: n }; case 1: return { t: "int", v: -n - 1 }; case 2: { if (c.i + n > byteLength(c.buf)) throw new CborError("bstr overrun"); const v = bufSubarray(c.buf, c.i, c.i + n); c.i += n; return { t: "bstr", v: bufferFrom(v) }; } case 3: { if (c.i + n > byteLength(c.buf)) throw new CborError("tstr overrun"); const v = bufToString(bufSubarray(c.buf, c.i, c.i + n), "utf8"); c.i += n; return { t: "tstr", v }; } case 4: { const arr = []; for (let k = 0; k < n; k++) arrayPush(arr, decodeAt(c, depth + 1)); return { t: "array", v: inertArray(arr) }; } case 5: { const m = []; let prevKeyBytes = null; for (let k = 0; k < n; k++) { const keyStart = c.i; const key = decodeAt(c, depth + 1); const keyBytes = bufferFrom(bufSubarray(c.buf, keyStart, c.i)); if (prevKeyBytes !== null) { const cmp = bufferCompare(prevKeyBytes, keyBytes); if (cmp === 0) throw new CborError("non-canonical: duplicate map key"); if (cmp > 0) throw new CborError("non-canonical: map keys not in canonical order"); } prevKeyBytes = keyBytes; const val = decodeAt(c, depth + 1); arrayPush(m, inertArray([key, val])); } return { t: "map", v: inertArray(m) }; } case 6: return { t: "tag", tag: n, v: decodeAt(c, depth + 1) }; default: throw new CborError(`unsupported major type ${major}`); } } export function decode(bytes) { const buf = bufferFromArrayBuffer(taBuffer(bytes), taByteOffset(bytes), taByteLength(bytes)); const c = { buf, i: 0, maxDepth: 32 }; const v = decodeAt(c, 0); if (c.i !== byteLength(buf)) throw new CborError("trailing bytes"); return v; }