first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
# encoding
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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).