Files
skills/docs/foundation/data/bytes/src/encoding/encoding.md
2026-05-29 08:20:56 +01:00

5.4 KiB

encoding

(no submodules)

Purpose

The encoding module provides lossless, bidirectional conversion between raw Uint8Array byte sequences and three ubiquitous text representations: lowercase hexadecimal, standard base64, and UTF-8. It is the lowest-level I/O surface in @suaveplan/bytes and is intentionally free of any dependency beyond @opentelemetry/api and the @suaveplan/error error substrate.

Every conversion function validates its input strictly and throws a typed BytesError on malformed data, making it safe to use directly in protocol parsers, cryptographic pipelines, and data serialisation layers without additional guard code at the call site.

Features

  • Hex encoding/decoding — bytesToHex emits lowercase two-character hex pairs; hexToBytes accepts both upper- and lowercase and rejects odd-length or non-hex inputs with BYTES.ENCODE.INVALID_HEX.
  • Base64 encoding/decoding — bytesToBase64 uses the standard (non-URL-safe) alphabet; base64ToBytes validates the input against the standard alphabet before delegating to atob, rethrowing any decode error as BYTES.ENCODE.INVALID_BASE64.
  • UTF-8 encoding/decoding — utf8ToBytes uses TextEncoder and bytesToUtf8 uses TextDecoder, both handling multi-byte Unicode code points correctly.
  • OTel tracing — every exported function wraps its body in a named active span under the @suaveplan/bytes tracer so encoding operations appear in distributed traces.
  • Zod schemas — HexStringSchema and Base64StringSchema are exported from encoding.schemas.ts for use in Zod-based validation pipelines.
  • Property-tested — fast-check verifies lossless round-trips for arbitrary byte sequences for all three codecs.

Usage: Basic Example

import { bytesToHex, hexToBytes, bytesToBase64, base64ToBytes, utf8ToBytes, bytesToUtf8 } from "@suaveplan/bytes/encoding";

const raw = new Uint8Array([72, 101, 108, 108, 111]);

// Hex
const hex = bytesToHex(raw);        // "48656c6c6f"
const back = hexToBytes(hex);       // Uint8Array [72, 101, 108, 108, 111]

// Base64
const b64 = bytesToBase64(raw);     // "SGVsbG8="
const back2 = base64ToBytes(b64);  // Uint8Array [72, 101, 108, 108, 111]

// UTF-8
const text = bytesToUtf8(raw);      // "Hello"
const back3 = utf8ToBytes(text);   // Uint8Array [72, 101, 108, 108, 111]

Usage: Advanced Example

import { hexToBytes, bytesToBase64, BytesError } from "@suaveplan/bytes";

// Safely parse an untrusted hex payload from an HTTP header
function parseHexHeader(value: string): Uint8Array {
    try {
        return hexToBytes(value.trim().toLowerCase());
    } catch (err) {
        if (err instanceof BytesError) {
            // err.code === "BYTES.ENCODE.INVALID_HEX"
            throw new Error(`Malformed X-Signature header: ${err.message}`);
        }
        throw err;
    }
}

// Convert the raw bytes to base64 for JSON transport
const signature = parseHexHeader("deadbeef");
const forJson = bytesToBase64(signature); // "3q2+7w=="

API Reference

bytesToHex(bytes: Uint8Array): string

Converts bytes to a lowercase hexadecimal string. Each byte becomes exactly two hex characters. Returns "" for an empty array.

hexToBytes(hex: string): Uint8Array

Converts a hex string to a Uint8Array. Accepts upper- and lowercase characters. Returns an empty array for an empty string.

Throws BytesError(BYTES.ENCODE.INVALID_HEX) when:

  • hex.length is odd.
  • hex contains characters outside [0-9a-fA-F].

bytesToBase64(bytes: Uint8Array): string

Converts bytes to a standard base64 string (with = padding). Returns "" for an empty array.

base64ToBytes(base64: string): Uint8Array

Converts a standard base64 string to a Uint8Array. Returns an empty array for an empty string.

Throws BytesError(BYTES.ENCODE.INVALID_BASE64) when base64 contains characters outside the standard base64 alphabet or the decode otherwise fails.

bytesToUtf8(bytes: Uint8Array): string

Decodes bytes as a UTF-8 string using TextDecoder. Invalid sequences are replaced with the Unicode replacement character (U+FFFD) per platform behaviour.

utf8ToBytes(str: string): Uint8Array

Encodes str as UTF-8 bytes using TextEncoder. Every Unicode code point, including surrogate pairs and multi-byte characters, is encoded correctly.

Implementation Notes

The hex codec avoids regular-expression replacement in the hot encode path in favour of a simple loop over byte values with toString(16).padStart(2, "0"), which is both readable and cache-friendly on V8/JavaScriptCore. The decode path uses a single HEX_RE.test() validation pass before looping — this avoids per-character error checking while still catching all invalid inputs in one branch.

The base64 codec delegates to the platform btoa/atob globals (available natively in Node 18+ and Bun), avoiding any third-party encoding library. The encode path materialises a binary string from the byte array; the decode path pre-validates with a regex before calling atob and wraps any thrown exception in a BytesError so callers never see a raw DOMException.

OTel spans are started synchronously and ended in finally blocks, ensuring spans are always closed even when an exception propagates. The tracer is module-level so instrumentation is zero-cost when no SDK is installed (the @opentelemetry/api noop tracer is used automatically).