Release Binary Tools 0.1.0

This commit is contained in:
2026-09-01 13:04:49 +02:00
commit 60768466c1
59 changed files with 10424 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
import { describe, expect, it } from "vitest";
import {
decodeBase32,
decodeBase58,
decodeInput,
encodeBase32,
encodeBase58,
formatHexRows,
} from "../../src/core/encoding";
describe("binary encodings", () => {
it("round-trips RFC 4648 Base32", () => {
const bytes = new TextEncoder().encode("foobar");
expect(encodeBase32(bytes)).toBe("MZXW6YTBOI======");
expect(new TextDecoder().decode(decodeBase32("MZXW6YTBOI======"))).toBe(
"foobar",
);
});
it("round-trips Base58 including leading zeros", () => {
const bytes = Uint8Array.of(0, 0, 1, 2, 255);
expect(decodeBase58(encodeBase58(bytes))).toEqual(bytes);
});
it("rejects malformed hex", () => {
expect(() => decodeInput("abc", "hex")).toThrow(/byte pairs/u);
});
it("retains absolute offsets for paged byte inspection", () => {
expect(formatHexRows(Uint8Array.of(1, 2, 3), 4_096)[0]?.offset).toBe(4_096);
});
});
+111
View File
@@ -0,0 +1,111 @@
import { describe, expect, it } from "vitest";
import {
decodeStructured,
encodeStructured,
parseDer,
parseProtobuf,
parseSchema,
} from "../../src/core/inspect";
describe("structured binary inspection", () => {
it.each(["cbor", "msgpack"] as const)("round-trips %s", (format) => {
const encoded = encodeStructured(format, '{"name":"Ada","active":true}');
expect(JSON.parse(decodeStructured(format, encoded))).toEqual({
name: "Ada",
active: true,
});
});
it("builds a DER tree with exact source ranges", () => {
const tree = parseDer(
Uint8Array.of(0x30, 0x06, 0x02, 0x01, 0x2a, 0x01, 0x01, 0xff),
);
expect(tree[0]).toMatchObject({ label: "SEQUENCE", start: 0, end: 8 });
expect(tree[0]?.children?.[0]).toMatchObject({
label: "INTEGER",
value: "42",
start: 2,
end: 5,
});
});
it("decodes protobuf wire fields using a schema", () => {
const tree = parseProtobuf(
Uint8Array.of(0x08, 0x96, 0x01, 0x12, 0x03, 0x41, 0x64, 0x61),
{
"1": { name: "id", type: "uint" },
"2": { name: "name", type: "string" },
},
);
expect(tree.map((node) => node.value)).toEqual(["150", "Ada"]);
expect(tree[1]).toMatchObject({ start: 3, end: 8 });
});
it("rejects indefinite DER and unsupported protobuf groups", () => {
expect(() => parseDer(Uint8Array.of(0x30, 0x80, 0, 0))).toThrow(
/indefinite/u,
);
expect(() => parseProtobuf(Uint8Array.of(0x0b))).toThrow(/Unsupported/u);
});
it.each([
["non-minimal high tag", Uint8Array.of(0x1f, 0x1e, 0x00)],
["leading-zero high tag", Uint8Array.of(0x1f, 0x80, 0x1f, 0x00)],
["non-canonical boolean", Uint8Array.of(0x01, 0x01, 0x01)],
["empty integer", Uint8Array.of(0x02, 0x00)],
["redundant positive integer", Uint8Array.of(0x02, 0x02, 0x00, 0x7f)],
["redundant negative integer", Uint8Array.of(0x02, 0x02, 0xff, 0x80)],
["non-empty null", Uint8Array.of(0x05, 0x01, 0x00)],
["primitive sequence", Uint8Array.of(0x10, 0x00)],
])("rejects %s DER", (_label, input) => {
expect(() => parseDer(input)).toThrow();
});
it("rejects a leading zero in a long-form DER length", () => {
expect(() =>
parseDer(
Uint8Array.from([0x04, 0x82, 0x00, 0x80, ...new Uint8Array(128)]),
),
).toThrow(/leading zero/u);
});
it("validates schema records and their wire compatibility", () => {
expect(() => parseSchema('{"name":{"type":"uint"}}')).toThrow(
/field number/u,
);
expect(() => parseSchema('{"1":{"type":"made-up"}}')).toThrow(
/not supported/u,
);
expect(() => parseSchema('{"1":{"type":"uint","fields":{}}}')).toThrow(
/requires type message/u,
);
expect(() =>
parseProtobuf(Uint8Array.of(0x0a, 0x01, 0x41), {
"1": { type: "uint" },
}),
).toThrow(/expects wire 0/u);
});
it("rejects out-of-range protobuf keys and varints", () => {
expect(() =>
parseProtobuf(Uint8Array.of(0x80, 0x80, 0x80, 0x80, 0x10)),
).toThrow(/Unsupported wire key/u);
expect(() =>
parseProtobuf(
Uint8Array.of(
0x08,
0xff,
0xff,
0xff,
0xff,
0xff,
0xff,
0xff,
0xff,
0xff,
0x02,
),
),
).toThrow(/unsigned 64-bit/u);
});
});