CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ulid

A universally-unique, lexicographically-sortable, identifier generator

78

1.34x
Overview
Eval results
Files

base32-utilities.mddocs/

Base32 Utilities

Utilities for working with Crockford Base32 encoding, including error correction and string manipulation specifically designed for ULID components.

Capabilities

Fix ULID Base32

Corrects common Base32 encoding errors in ULID strings by replacing ambiguous characters with their intended equivalents.

/**
 * Fix a ULID's Base32 encoding
 * Replaces i/I and l/L with 1, o/O with 0, and removes hyphens
 * @param id - The ULID string to fix
 * @returns The corrected ULID string
 */
function fixULIDBase32(id: string): string;

Usage Examples:

import { fixULIDBase32, isValid } from "ulid";

// Fix common character confusions
const malformedId = "01HNZXIJGFACFAl6RBXDHEQN6o";
const fixed = fixULIDBase32(malformedId); // "01HNZX1JGFACFA16RBXDHEQN60"

// Verify the fix worked
console.log(isValid(malformedId)); // false
console.log(isValid(fixed)); // true

// Remove hyphens from formatted IDs
const hyphenatedId = "01HNZX8J-GFAC-FA36-RBXD-HEQN6E";
const clean = fixULIDBase32(hyphenatedId); // "01HNZX8JGFACFA36RBXDHEQN6E"

// Handle mixed case with corrections
const messyId = "01hnzxijgfacfal6rbxdheqno0";
const corrected = fixULIDBase32(messyId); // "01hnzx1jgfacfa16rbxdheqn00"

// Use in input validation pipeline
function sanitizeULID(input: string): string {
  const cleaned = fixULIDBase32(input);
  if (!isValid(cleaned)) {
    throw new Error(`Cannot fix malformed ULID: ${input}`);
  }
  return cleaned.toUpperCase();
}

// Batch processing
const userInputs = [
  "01HNZXIJGFACFAl6RBXDHEQN6o",
  "01-HNZX-8JGF-ACFA-36RB-XDHE-QN6E",
  "01hnzxijgfacfal6rbxdheqno0"
];
const cleanedIds = userInputs.map(fixULIDBase32);

Character Corrections:

  • i or I1 (one)
  • l or L1 (one)
  • o or O0 (zero)
  • - → `` (removed)

Common Error Sources:

  • Manual transcription: Humans often confuse similar-looking characters
  • OCR systems: Optical character recognition may misread characters
  • Font rendering: Some fonts make characters appear similar
  • Legacy formatting: Systems that add hyphens for readability

Increment Base32

Increments a Base32 string by one position, used internally by monotonic factories to ensure lexicographic ordering.

/**
 * Increment a Base32 string by one
 * @param str - The Base32 string to increment
 * @returns The incremented Base32 string
 * @throws ULIDError if the string contains invalid characters
 */
function incrementBase32(str: string): string;

Usage Examples:

import { incrementBase32 } from "ulid";

// Basic increment operations
console.log(incrementBase32("0000000000000000")); // "0000000000000001"
console.log(incrementBase32("0000000000000001")); // "0000000000000002"
console.log(incrementBase32("000000000000000Z")); // "0000000000000010"

// Handle carry operations
console.log(incrementBase32("ZZZZZZZZZZZZZZZZ")); // "10000000000000000"

// Real-world usage with ULID random portions
const randomPortion = "CEN5XA66EMZSRZW";
const incremented = incrementBase32(randomPortion); // "CEN5XA66EMZSRZX"

// Used internally by monotonicFactory
// (This is for educational purposes - normally handled automatically)
function simulateMonotonicBehavior(baseRandom: string, count: number) {
  let current = baseRandom;
  const results = [current];
  
  for (let i = 1; i < count; i++) {
    current = incrementBase32(current);
    results.push(current);
  }
  
  return results;
}

const sequence = simulateMonotonicBehavior("YYYYYYYYYYYYYYYY", 5);
// ["YYYYYYYYYYYYYYYY", "YYYYYYYYYYYYYYY Z", "YYYYYYYYYYYYYYZ0", ...]

Error Conditions:

  • Throws ULIDError with code Base32IncorrectEncoding if string contains invalid Base32 characters
  • Throws ULIDError with code Base32IncorrectEncoding if increment operation fails

Base32 Arithmetic:

The increment operation follows Crockford Base32 rules:

  • Alphabet: 0123456789ABCDEFGHJKMNPQRSTVWXYZ (32 characters)
  • Increment order: 0→1→2→...→9→A→B→...→Z→10→11→...
  • Carry handling: When a position reaches Z, it wraps to 0 and carries to the next position
  • String expansion: If all positions are Z, the result expands by one character

Performance Characteristics:

  • Time complexity: O(n) where n is string length (worst case: all carries)
  • Average case: O(1) for most increments (no carries required)
  • Memory usage: Creates new string, original unchanged

Integration with Monotonic Generation:

This function is used internally by monotonicFactory() to ensure that ULIDs generated within the same millisecond maintain lexicographic ordering:

// Conceptual example of monotonic factory behavior
let lastTimestamp = Date.now();
let lastRandom = "YYYYYYYYYYYYYYYY";

function generateMonotonic() {
  const currentTime = Date.now();
  
  if (currentTime === lastTimestamp) {
    // Same timestamp - increment random portion
    lastRandom = incrementBase32(lastRandom);
  } else {
    // New timestamp - generate new random portion
    lastTimestamp = currentTime;
    lastRandom = generateNewRandom();
  }
  
  return encodeTime(lastTimestamp) + lastRandom;
}

Install with Tessl CLI

npx tessl i tessl/npm-ulid

docs

base32-utilities.md

generation.md

index.md

time-utilities.md

uuid-conversion.md

validation.md

tile.json