Guide

Encoding and Decoding

Two calls by name, a codec object per encoding and a subpath per family. Strings are UTF-8 and bytes stay bytes

By name

ts
import { decode, encode } from "@agntn/encodings";

encode("hex", "gm");             // "676d"
encode("hex", "gm", { upper: true });
decode("hex", "0x67 6D").bytes;  // Uint8Array [103, 109]

The name is forgiving. Base 64, base_64 and b64 all find base64. qp finds quoted-printable, btoa finds ascii85. A name nobody knows throws UnknownEncodingError. The message lists every name, so you don't guess twice.

Options are checked against what the encoding declares. Pass upper to base64 and you get InvalidOptionError, not a silent nothing. Only six encodings take options at all.

EncodingOptions
hexupper
binaryseparate
ascii85delimiters
bech32, bech32mprefix, limit
uuencodename, mode

What comes back

decode returns { bytes, details }. The bytes are a Uint8Array, always. Want text? Run them through TextDecoder yourself. The library won't guess that your bytes are UTF-8.

And details? That's the part of the text that isn't data.

ts
decode("bech32", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4").details;
// { prefix: "bc", witnessVersion: 0 }

decode("uuencode", "begin 644 cat.txt\n#0V%T\n`\nend\n").details;
// { mode: "644", name: "cat.txt" }

Codec objects

Every encoding is also a plain object with encode and decode. No registry involved.

ts
import { base58, base58check, hex } from "@agntn/encodings";

base58.encode(new TextEncoder().encode("hello world"));  // "StV1DL6CwTryKyV"
base58check.decode("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa");
// 21 bytes: version 0x00, then the HASH160

These work on bytes. hex.decode returns a Uint8Array, base58.encode wants one. The registry just wraps them.

Subpaths

Each family has its own entry. Pulling base58 into a bundle doesn't drag the registry along. Or identify. Or sixteen other alphabets.

ts
import { base58, base58check } from "@agntn/encodings/base58";
import { bech32, segwit } from "@agntn/encodings/bech32";

Eleven families, one subpath each. A test bundles every one of them and fails if another family's code sneaks in.

Decoding is strict, mostly

Where a spec says something is wrong, decoding says so. It names the offending character and its index.

text
base58: "0" (U+0030) at index 0 is not in the alphabet
base64: 1 padding characters where the length needs 2

Real text often comes wrapped, though. So decoding reads through the wrapping. Base64 and base32 skip line breaks and spaces, and a MIME body decodes as it is. Hex takes either case, spaces and a 0x. Crockford reads O as 0 and I or L as 1. That's the whole point of Crockford. Want the rules per encoding? Every encoding page lists them.

An invisible character never reaches the message raw. A U+2028 shows up as U+2028. Not as a line break that rewrites the error under your feet.