# 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 ```ts 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 ```ts 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).