CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-scure--base

Secure, audited & 0-dep implementation of base encoding algorithms

Pending
Overview
Eval results
Files

utils.mddocs/

Utility Functions

Low-level utility functions for building custom encoders using functional composition. These utilities enable creating domain-specific encoders by chaining simple operations together, following the same patterns used internally by @scure/base.

Capabilities

Chain Function

Composes multiple coders together in functional style, ensuring encode/decode operations maintain proper order.

/**
 * Chains multiple coders together in functional composition
 * Automatically handles proper order for encode/decode operations
 * @param args - Sequence of coders to chain together
 * @returns Single coder combining all operations
 */
function chain<T extends Chain & AsChain<T>>(
  ...args: T
): Coder<Input<First<T>>, Output<Last<T>>>;

type Chain = [Coder<any, any>, ...Coder<any, any>[]];

Usage Examples:

import { utils } from "@scure/base";

// Create custom base32 encoder by chaining operations
const customBase32 = utils.chain(
  utils.radix2(5),                                      // Convert bytes to 5-bit values
  utils.alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'),   // Map to base32 alphabet
  utils.padding(5),                                     // Add padding
  utils.join('')                                        // Join to string
);

const data = new Uint8Array([0x12, 0x34]);
const encoded = customBase32.encode(data);    // Works like base32.encode()
const decoded = customBase32.decode(encoded); // Works like base32.decode()

Alphabet Function

Maps between number arrays and string arrays using a custom alphabet.

/**
 * Creates encoder for number array to string array mapping using custom alphabet
 * @param letters - Alphabet as string or array of strings
 * @returns Coder mapping number indices to alphabet characters
 */
function alphabet(letters: string | string[]): Coder<number[], string[]>;

Usage Examples:

import { utils } from "@scure/base";

// Create alphabet mapper
const hexAlphabet = utils.alphabet('0123456789abcdef');

const digits = [15, 14, 13, 12]; // Indices into alphabet
const letters = hexAlphabet.encode(digits); // ['f', 'e', 'd', 'c']
const restored = hexAlphabet.decode(letters); // [15, 14, 13, 12]

// Can also use string array
const customAlphabet = utils.alphabet(['zero', 'one', 'two']);
const words = customAlphabet.encode([0, 2, 1]); // ['zero', 'two', 'one']

Radix Conversion

Convert between arbitrary numeric bases. Note: O(n^2) complexity for non-power-of-2 bases.

/**
 * Converts bytes to arbitrary radix representation
 * @param num - Target radix/base number  
 * @returns Coder converting bytes to number array in specified radix
 */
function radix(num: number): Coder<Uint8Array, number[]>;

/**
 * Optimized radix conversion for power-of-2 bases
 * @param bits - Number of bits per output digit (must be ≤32)
 * @param revPadding - Reverse padding direction
 * @returns Coder for efficient power-of-2 base conversion
 */
function radix2(bits: number, revPadding?: boolean): Coder<Uint8Array, number[]>;

Usage Examples:

import { utils } from "@scure/base";

// Convert to base 58 (O(n^2) - use sparingly)
const base58Radix = utils.radix(58);
const data = new Uint8Array([0x12, 0x34]);
const base58Digits = base58Radix.encode(data); // Array of base58 digit values

// Convert to base 32 (power of 2 - efficient)
const base32Radix = utils.radix2(5); // 2^5 = 32
const base32Digits = base32Radix.encode(data); // Array of 5-bit values

String Operations

Join and split string arrays with configurable separators.

/**
 * Joins string arrays into single strings and splits them back
 * @param separator - String to use for joining (default: empty string)
 * @returns Coder for joining/splitting string arrays
 */
function join(separator?: string): Coder<string[], string>;

Usage Examples:

import { utils } from "@scure/base";

// Join without separator (default)
const joiner = utils.join();
const joined = joiner.encode(['a', 'b', 'c']); // "abc"
const split = joiner.decode('abc'); // ['a', 'b', 'c'] - splits into chars

// Join with separator
const dashedJoiner = utils.join('-');
const dashed = dashedJoiner.encode(['hello', 'world']); // "hello-world"
const unDashed = dashedJoiner.decode('hello-world'); // ['hello', 'world']

Padding Operations

Add and remove padding from string arrays to ensure proper bit alignment.

/**
 * Adds/removes padding to ensure string array represents whole number of bits
 * @param bits - Number of bits per character in the encoding
 * @param chr - Padding character (default: '=')
 * @returns Coder for adding/removing padding
 */
function padding(bits: number, chr?: string): Coder<string[], string[]>;

Usage Examples:

import { utils } from "@scure/base";

// Base64 uses 6 bits per character, padded with '='
const base64Padding = utils.padding(6, '=');
const data = ['Q', 'W']; // Incomplete group
const padded = base64Padding.encode(data); // ['Q', 'W', '=', '=']
const unpadded = base64Padding.decode(padded); // ['Q', 'W']

// Base32 uses 5 bits per character
const base32Padding = utils.padding(5);
const base32Data = ['A', 'B', 'C'];
const paddedB32 = base32Padding.encode(base32Data); // Adds padding if needed

Checksum Validation

Add checksum validation to any encoder for error detection.

/**
 * Adds checksum validation using provided hash function
 * @param len - Number of checksum bytes to append
 * @param fn - Hash function for generating checksum
 * @returns Coder that appends/validates checksum
 */
function checksum(
  len: number,
  fn: (data: Uint8Array) => Uint8Array
): Coder<Uint8Array, Uint8Array>;

Usage Examples:

import { utils } from "@scure/base";
import { sha256 } from "@noble/hashes/sha2";

// Create checksum validator using SHA-256 (like base58check)
const sha256Checksum = utils.checksum(4, (data) => sha256(sha256(data)));

const originalData = new Uint8Array([0x12, 0x34, 0x56]);
const withChecksum = sha256Checksum.encode(originalData); // Appends 4-byte checksum
const verified = sha256Checksum.decode(withChecksum); // Validates and removes checksum

// Error on invalid checksum
try {
  const corrupted = new Uint8Array([...withChecksum]);
  corrupted[corrupted.length - 1] = 0; // Corrupt checksum
  sha256Checksum.decode(corrupted); // Throws "Invalid checksum"
} catch (error) {
  console.log("Checksum validation failed");
}

Normalization

Apply transformation functions during decode operations for input sanitization.

/**
 * Applies normalization function during decode operation
 * @param fn - Function to transform input during decode
 * @returns Coder that applies transformation on decode
 */
function normalize<T>(fn: (val: T) => T): Coder<T, T>;

Usage Examples:

import { utils } from "@scure/base";

// Create case-insensitive decoder (like base32crockford)
const caseNormalizer = utils.normalize((s: string) => s.toUpperCase());
const encoded = caseNormalizer.encode("Hello"); // "Hello" (no change on encode)
const decoded = caseNormalizer.decode("hello"); // "HELLO" (normalized on decode)

// Create character substitution (like base32crockford)
const charNormalizer = utils.normalize((s: string) => 
  s.toUpperCase().replace(/O/g, '0').replace(/[IL]/g, '1')
);
const normalized = charNormalizer.decode("hel1O"); // "HEL10" (O->0, I/L->1)

Direct Radix Conversion

Low-level radix conversion functions for direct use.

/**
 * Direct radix conversion between arbitrary bases (O(n^2) complexity)
 * @param data - Input digits in source base
 * @param from - Source radix
 * @param to - Target radix  
 * @returns Digits in target radix
 */
function convertRadix(data: number[], from: number, to: number): number[];

/**
 * Optimized radix conversion for power-of-2 bases
 * @param data - Input digits
 * @param from - Source radix (power of 2, ≤32)
 * @param to - Target radix (power of 2, ≤32) 
 * @param padding - Whether to add padding
 * @returns Converted digits
 */
function convertRadix2(
  data: number[], 
  from: number, 
  to: number, 
  padding: boolean
): number[];

Usage Examples:

import { utils } from "@scure/base";

// Convert from base 10 to base 16
const decimal = [1, 2, 3]; // Represents 123 in base 10
const hex = utils.convertRadix(decimal, 10, 16); // Convert to base 16

// Efficient binary to base32 conversion
const binary = [1, 0, 1, 1, 0]; // 5 bits
const base32 = utils.convertRadix2(binary, 2, 32, true); // Convert 2^1 to 2^5

Building Custom Encoders

Combine utilities to create domain-specific encoders:

import { utils } from "@scure/base";
import { sha256 } from "@noble/hashes/sha2";

// Example: Custom cryptocurrency address encoder
const cryptoAddress = utils.chain(
  utils.checksum(4, (data) => sha256(sha256(data))), // Double SHA-256 checksum
  utils.radix(58),                                   // Convert to base58 digits
  utils.alphabet('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'),
  utils.join('')                                     // Join to string
);

// Example: Custom data format with metadata
const dataFormat = utils.chain(
  utils.radix2(6),                                   // 6-bit encoding like base64
  utils.alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'),
  utils.padding(6, '='),                            // Base64-style padding
  utils.join('-'),                                   // Use dashes as separators
  utils.normalize((s: string) => s.toLowerCase())    // Normalize to lowercase
);

// Example: Compact binary encoding
const compactBinary = utils.chain(
  utils.radix2(8),                                   // Keep as bytes
  utils.alphabet('0123456789ABCDEF'),               // Hex alphabet
  utils.join('')                                     // Standard hex string
);

Performance Notes

  • radix() has O(n^2) complexity - use only with small inputs
  • radix2() has O(n) complexity - preferred for power-of-2 bases
  • convertRadix() and convertRadix2() are the underlying conversion functions
  • Built-in encoders (base64, hex) use optimized implementations when available

Error Handling

All utility functions validate inputs and provide descriptive errors:

import { utils } from "@scure/base";

try {
  utils.radix(1); // Base must be ≥ 2
} catch (error) {
  console.log(error.message); // "convertRadix: invalid from=1, base cannot be less than 2"
}

try {
  utils.alphabet('ABC').decode(['D']); // Invalid character
} catch (error) {
  console.log(error.message); // "Unknown letter: D. Allowed: ABC"
}

try {
  utils.padding(6).decode(['A', 'B', '=', '=', '=']); // Too much padding
} catch (error) {
  console.log(error.message); // "padding: invalid, string has too much padding"
}

Install with Tessl CLI

npx tessl i tessl/npm-scure--base

docs

base-encodings.md

base58-encodings.md

bech32-encodings.md

index.md

utils.md

tile.json