0.2.1 • Published 5 months ago

nexid v0.2.1

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

NeXID - Fast, Lexicographically Sortable Unique IDs

npm version TypeScript License: ISC

A fast, efficient TypeScript implementation of the XID specification - globally unique identifiers that are lexicographically sortable. This library provides a complete solution for generating and working with NeXIDs in both Node.js and browser environments.

Features

  • 🔤 Lexicographically Sortable: IDs sort naturally in databases and binary searches
  • 🕒 Time-Ordered: First component is timestamp, so IDs sort chronologically
  • 📦 Compact: 20 characters vs 36 for UUID
  • 🔗 URL-Safe: Uses only alphanumeric characters (0-9 and a-v)
  • 🌐 Universal: Works in any JavaScript environment (Node.js, browsers, Deno)
  • ⚡️ Fast: Generates ~3 million IDs per second
  • 🔒 Secure: Uses cryptographically strong random values
  • 🧩 TypeScript: Full type definitions and type safety

Installation

npm install nexid

Quick Start

import NeXID from 'nexid';

// Generate a new ID
const id = NeXID.newId();
console.log(id.toString()); // e.g. "cv37img5tppgl4002kb0"

// Extract the timestamp
const timestamp = id.getTime();
console.log(timestamp.toISOString()); // e.g. "2025-03-04T03:54:00.000Z"

// Parse from an existing string
const parsedId = NeXID.fromString('cv37img5tppgl4002kb0');

// Sort IDs chronologically (oldest to newest)
const sortedIds = NeXID.sortIds([id3, id1, id2]);

Why NeXID?

XIDs provide a perfect balance of features that make them ideal for many applications:

  1. vs. UUID - Significantly more compact (20 chars vs 36), with lexicographical and chronological sorting
  2. vs. auto-increment - Distributed generation without central coordination
  3. vs. timestamp+random - Structured format with guaranteed uniqueness
  4. vs. snowflake - No configuration or central server required

Structure

Each NeXID consists of 12 bytes (96 bits), encoded as 20 characters:

|--- 4 bytes ---||--- 3 bytes ---||--- 2 bytes ---||--- 3 bytes ---|
      time          machine ID        process ID        counter
  • Timestamp: 4 bytes (seconds since Unix epoch)
  • Machine ID: 3 bytes (derived from hostname or random)
  • Process ID: 2 bytes (process ID or random value)
  • Counter: 3 bytes (incremented for each ID, starts with random value)

This is then encoded using base32-hex (characters 0-9 and a-v), resulting in a 20-character string.

API Reference

Basic Functions

// Generate a new ID with the current timestamp
const id = NeXID.newId();

// Generate an ID with a specific timestamp
const pastId = NeXID.newId(new Date('2020-01-01'));

// Parse an ID from a string
const parsed = NeXID.fromString('cv37img5tppgl4002kb0');

// Create an ID from raw bytes
const bytes = new Uint8Array(12); // Must be exactly 12 bytes
const idFromBytes = NeXID.fromBytes(bytes);

// Get a nil (zero) ID
const nilID = NeXID.nilId;

// Sort an array of IDs lexicographically (which also sorts them chronologically)
const sorted = NeXID.sortIds(myIds);

Advanced Usage: Custom Generator

import { createXidGenerator } from 'xid';

// Create a custom generator with options
const generator = createXidGenerator({
  // Optional: Custom machine ID (3 bytes)
  machineId: new Uint8Array([1, 2, 3]),

  // Optional: Set a specific process ID
  processId: 12345,

  // Optional: Custom random source (for controlled environments)
  randomSource: (size) => {
    // Your custom secure random implementation
    const bytes = new Uint8Array(size);
    // Fill with random values...
    return bytes;
  },

  // Optional: Allow fallback to non-cryptographic random
  // NOT recommended for production!
  allowInsecureRandomFallback: false,
});

// Generate IDs with the custom generator
const id1 = generator.generate();
const id2 = generator.generateWithTime(new Date());

ID Object API

Each ID instance provides these methods:

// Get string representation
const str = id.toString();

// Get Date object from the timestamp portion
const date = id.getTime();

// Get the machine ID portion (3 bytes)
const machineId = id.getMachineId();

// Get the process ID number
const pid = id.getProcessId();

// Get the counter value
const counter = id.getCounter();

// Get a copy of the raw bytes
const bytes = id.toBytes();

// Compare two IDs lexicographically (which also compares them chronologically)
if (id1.compare(id2) > 0) {
  console.log('id1 is newer than id2');
}

// Check if two IDs are equal
if (id1.equals(id2)) {
  console.log('IDs are identical');
}

// Check if this is a nil (zero) ID
if (id.isNil()) {
  console.log('This is a nil ID');
}

Performance

XID achieves excellent performance while maintaining its feature set:

LibrarySpeed (IDs/sec)Time-basedURL-safeFixed lengthSize (chars)
hyperid55,436,37524
node randomUUID9,397,71436
uuid v49,317,44036
nanoid7,012,80021
uuid v13,326,12136
xid3,110,86120
shortid714,054variable
ksuid85,59727
ulid50,16726

Note: Benchmarks run on Node.js v22 on a modern machine. Results may vary.

Browser Support

XID works in all modern browsers and uses the native crypto.getRandomValues() API for secure random generation.

Error Handling

The library throws typed errors to help handle edge cases:

import { InvalidIDError, RandomSourceUnavailableError } from 'xid';

try {
  const id = fromString('invalid-id-string');
} catch (err) {
  if (err instanceof InvalidIDError) {
    console.error('Invalid ID format:', err.message);
  }
}

Development

# Install dependencies
npm install

# Build the library
npm run build

# Run tests
npm test

# Run benchmarks
npm run benchmark

Documentation

More extensive documentation is available in the docs directory:

Credits

Based on the XID specification by Olivier Poitrey.

License

MIT License

0.2.1

5 months ago

0.2.0

5 months ago

0.1.2

5 months ago

0.1.1

5 months ago

0.1.0

5 months ago