Release Helper Tools 0.1.0

This commit is contained in:
2026-09-01 02:33:45 +02:00
commit 5fffd48642
78 changed files with 11635 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
import { describe, expect, it } from "vitest";
import {
base64ToBytes,
base64UrlToBytes,
bytesToBase64,
bytesToBase64Url,
bytesToHex,
convertLineEndings,
decodeText,
decodeUrlComponent,
encodeText,
encodeUrlComponent,
fromCodePoints,
hexToBytes,
inspectCodePoints,
inspectUrl,
normalizeUnicode,
parseFormEncoded,
segmentGraphemes,
stringifyFormEncoded,
transformCase,
} from "../../src/helpers";
describe("binary and text encoding", () => {
it("round-trips canonical Base64, Base64URL, hex, and text encodings", () => {
const bytes = encodeText("Hello, 🌍", "utf-8");
expect(bytesToBase64(bytes)).toBe("SGVsbG8sIPCfjI0=");
expect(decodeText(base64ToBytes("SGVsbG8sIPCfjI0="))).toBe("Hello, 🌍");
expect(bytesToBase64Url(new Uint8Array([251, 255]), false)).toBe("-_8");
expect(base64UrlToBytes("-_8")).toEqual(new Uint8Array([251, 255]));
expect(base64UrlToBytes("Zg==")).toEqual(new Uint8Array([102]));
expect(bytesToHex(bytes).startsWith("48656c6c6f")).toBe(true);
expect(hexToBytes("0x00 ff", { allowWhitespace: true })).toEqual(
new Uint8Array([0, 255]),
);
expect(decodeText(encodeText("Aé", "latin1"), "latin1")).toBe("Aé");
expect(decodeText(encodeText("A🌍", "utf-16le"), "utf-16le")).toBe("A🌍");
expect(decodeText(encodeText("A🌍", "utf-16be"), "utf-16be")).toBe("A🌍");
});
it("rejects ambiguous, malformed, and oversized encodings", () => {
expect(() => base64ToBytes("Zg")).toThrow(/padding/u);
expect(() => base64ToBytes("Zh==")).toThrow(/unused|canonical/u);
expect(() => base64UrlToBytes("+w")).toThrow(/Invalid/u);
expect(() => base64UrlToBytes("Zg=")).toThrow(/padding/u);
expect(() => base64ToBytes("Z g==")).toThrow(/whitespace/u);
expect(() => hexToBytes("abc")).toThrow(/pairs/u);
expect(() => hexToBytes("ffff", { maxOutputBytes: 1 })).toThrow(/limit/u);
expect(() => encodeText("€", "latin1")).toThrow(/Latin-1/u);
});
});
describe("URL, Unicode, case, and line helpers", () => {
it("handles URL components and repeated form fields without object coercion", () => {
expect(decodeUrlComponent(encodeUrlComponent("a b/✓"))).toBe("a b/✓");
const entries = parseFormEncoded("tag=one&tag=two+words&empty=");
expect(entries).toEqual([
["tag", "one"],
["tag", "two words"],
["empty", ""],
]);
expect(stringifyFormEncoded(entries)).toBe("tag=one&tag=two+words&empty=");
expect(
inspectUrl("../a?q=1#x", "https://example.test/base/"),
).toMatchObject({
href: "https://example.test/a?q=1#x",
protocol: "https:",
hostname: "example.test",
passwordPresent: false,
});
});
it("inspects scalar values and user-perceived graphemes", () => {
const points = inspectCodePoints("A🌍");
expect(points).toEqual([
{
character: "A",
codePoint: 65,
hex: "U+0041",
utf16Index: 0,
utf16Length: 1,
},
{
character: "🌍",
codePoint: 0x1f30d,
hex: "U+1F30D",
utf16Index: 1,
utf16Length: 2,
},
]);
expect(fromCodePoints(points.map((point) => point.codePoint))).toBe("A🌍");
expect(segmentGraphemes("👨‍👩‍👧‍👦é")).toHaveLength(2);
expect(normalizeUnicode("e\u0301", "NFC")).toBe("é");
expect(() => fromCodePoints([0xd800])).toThrow(/scalar/u);
expect(fromCodePoints(new Array(100_000).fill(0x61))).toHaveLength(100_000);
});
it("transforms case and line endings deterministically", () => {
expect(transformCase("XML http value", "camel")).toBe("xmlHttpValue");
expect(transformCase("hello-world", "pascal")).toBe("HelloWorld");
expect(transformCase("helloWorld", "snake")).toBe("hello_world");
expect(convertLineEndings("a\r\nb\rc\n", "lf")).toBe("a\nb\nc\n");
expect(convertLineEndings("a\nb", "crlf")).toBe("a\r\nb");
});
});