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