- Multi-platform Support: Works in browsers, Node.js, Service Workers, and Edge Workers (Cloudflare Workers, etc.)
- Edge Mode (Entropy): Optional
mode: 'edge'generates IDs with cryptographic randomness β no worker-id assignment needed for serverless/edge runtimes - Zero Dependencies: Lightweight with no external dependencies
- Functional Style: Pure functions without classes
- Time-sortable: IDs are chronologically sortable
- High Performance: Fast ID generation with built-in sequence handling
- TypeScript: Full type support with comprehensive type definitions
- Service Worker Support: Generate IDs in background contexts without blocking the main thread
npm install @tknf/snowflakeimport { createSnowflake, generateSnowflakeId } from '@tknf/snowflake';
// Create a generator function
const generateId = createSnowflake();
const id1 = generateId(); // "1234567890123456789"
const id2 = generateId(); // "1234567890123456790"
// Or generate a single ID
const id = generateSnowflakeId(); // "1234567890123456791"import { createSnowflake } from '@tknf/snowflake';
const generateId = createSnowflake({
epoch: 1640995200000, // Custom epoch (2022-01-01)
datacenterId: 1, // Datacenter ID (0-31)
workerId: 5 // Worker ID (0-31)
});
const id = generateId();For serverless / edge runtimes (e.g. Cloudflare Workers) where you cannot assign a stable
workerId to each ephemeral isolate, use mode: 'edge'. Instead of an assigned
(datacenterId, workerId) + sequence, the 22 non-timestamp bits are filled with
cryptographic randomness (crypto.getRandomValues). This removes the need for any
worker-id assignment and ensures uniqueness probabilistically via "timestamp + 22-bit entropy".
import { createSnowflake } from '@tknf/snowflake';
// No datacenterId / workerId required
const generateId = createSnowflake({ mode: 'edge', epoch: 1640995200000 });
const id = generateId(); // "1843927461029384756" (numeric string, chronologically sortable)modedefaults to'default'(the existing assigned worker-id behavior).'edge'is opt-in.- In edge mode
datacenterId/workerIdare ignored (even if provided). - The output is unchanged: a numeric string that fits a 63-bit positive integer (storable in a
SQLite / Turso
INTEGERcolumn) and remains chronologically sortable at millisecond granularity.
Uniqueness is probabilistic. 22 bits = 4,194,304 possibilities per millisecond. When generating
kIDs within the same millisecond the collision probability (birthday bound) is approximatelykΒ² / (2Β·2Β²Β²)(e.g. ~0.12% at 100 IDs/ms). Because of this, consumers should add a DBUNIQUEconstraint and retry-on-collision as a backstop so that even a theoretical collision causes no actual harm.
// src/node.ts - Node.js specific utilities
import { createSnowflakeFromEnv, generateFromEnv } from '@tknf/snowflake/node';
// Set environment variables
process.env.SNOWFLAKE_EPOCH = '1640995200000';
process.env.SNOWFLAKE_DATACENTER_ID = '1';
process.env.SNOWFLAKE_WORKER_ID = '5';
// Create generator from environment
const generateId = createSnowflakeFromEnv();
const id1 = generateId();
// Or generate single ID from environment
const id2 = generateFromEnv();
// Override environment with custom config
const id3 = generateFromEnv({ workerId: 10 });// src/browser.ts - Browser specific utilities
import {
createSnowflakeFromStorage,
generateFromStorage,
initializeBrowserConfig,
saveConfigToStorage
} from '@tknf/snowflake/browser';
// Initialize browser-specific config (auto-generated from browser characteristics)
const config = initializeBrowserConfig({
save: true, // Save to localStorage
config: { epoch: 1640995200000 } // Optional custom config
});
// Create generator using localStorage + browser fingerprint
const generateId = createSnowflakeFromStorage();
const id1 = generateId();
// Or generate single ID from storage
const id2 = generateFromStorage();
// Manual config management
saveConfigToStorage({ datacenterId: 5, workerId: 10 });
const id3 = generateFromStorage();Creates a Snowflake ID generator function.
Parameters:
config(optional): Configuration objectepoch(number): Custom epoch timestamp in milliseconds (default: 2020-01-01)datacenterId(number): Datacenter ID (0-31, default: 0). Ignored whenmodeis'edge'.workerId(number): Worker ID (0-31, default: 0). Ignored whenmodeis'edge'.mode('default' | 'edge'): Generation mode (default:'default'). See Edge Mode (Entropy).
Returns: Function that generates Snowflake IDs as strings
const generator = createSnowflake({
epoch: 1577836800000,
datacenterId: 1,
workerId: 2
});
const id = generator(); // "1234567890123456789"Generates a single Snowflake ID.
Parameters:
config(optional): Same ascreateSnowflake
Returns: Snowflake ID as string
const id = generateSnowflakeId({
datacenterId: 1,
workerId: 2
});Parses a Snowflake ID into its components.
Parameters:
id(string): Snowflake ID to parseepoch(number, optional): Epoch used for generation (default: 2020-01-01)mode('default' | 'edge', optional): Mode the ID was generated with (default:'default'). Edge IDs are bit-indistinguishable from default IDs, so you must pass the mode you generated with.
Returns (default mode): Object containing:
timestamp(number): Generation timestampdatacenterId(number): Datacenter IDworkerId(number): Worker IDsequence(number): Sequence numberentropy(null): Alwaysnullin default modedate(Date): Generation date
Returns (edge mode): In edge mode the non-timestamp bits are random, so the worker fields are
meaningless. They are returned as null and the raw 22-bit value is returned as entropy:
timestamp(number): Generation timestampdatacenterId/workerId/sequence(null): Not meaningful in edge modeentropy(number): The 22-bit random value (0β4,194,303)date(Date): Generation date
// default mode
const parsed = parseSnowflakeId("1234567890123456789");
// { timestamp: 1640995200123, datacenterId: 1, workerId: 2, sequence: 0, entropy: null, date: ... }
// edge mode
const edgeParsed = parseSnowflakeId("1843927461029384756", 1640995200000, "edge");
// { timestamp: ..., datacenterId: null, workerId: null, sequence: null, entropy: 4194301, date: ... }
getSnowflakeTimestampandsnowflakeToDateread only the timestamp bits, so they work identically for both default and edge IDs (nomodeargument needed).
Extracts timestamp from a Snowflake ID.
Parameters:
id(string): Snowflake IDepoch(number, optional): Epoch used for generation
Returns: Timestamp in milliseconds
const timestamp = getSnowflakeTimestamp("1234567890123456789");
console.log(timestamp); // 1640995200123Converts a Snowflake ID to a Date object.
Parameters:
id(string): Snowflake IDepoch(number, optional): Epoch used for generation
Returns: Date object
const date = snowflakeToDate("1234567890123456789");
console.log(date); // 2022-01-01T00:00:00.123ZImport from @tknf/snowflake/node for Node.js-specific features:
SNOWFLAKE_EPOCH: Custom epoch timestampSNOWFLAKE_DATACENTER_ID: Datacenter ID (0-31)SNOWFLAKE_WORKER_ID: Worker ID (0-31)
Loads configuration from environment variables.
const config = loadConfigFromEnv();
// Returns SnowflakeConfig object based on environment variablesCreates a generator with environment configuration.
const generator = createSnowflakeFromEnv({ workerId: 5 });Generates a single ID with environment configuration.
const id = generateFromEnv({ datacenterId: 2 });Import from @tknf/snowflake/browser for browser-specific features:
snowflake.epoch: Custom epoch timestampsnowflake.datacenterId: Datacenter ID (0-31)snowflake.workerId: Worker ID (0-31)
Generates browser-specific configuration based on browser characteristics (user agent, timezone, screen properties, etc.).
const config = generateBrowserConfig();
// Returns consistent config for the same browser environmentLoads configuration from localStorage.
const config = loadConfigFromStorage();
// Returns SnowflakeConfig object from localStorageSaves configuration to localStorage.
saveConfigToStorage({ datacenterId: 5, workerId: 10 });Creates a generator with localStorage configuration and browser fingerprinting fallback.
const generator = createSnowflakeFromStorage({ epoch: 1640995200000 });Generates a single ID with localStorage configuration.
const id = generateFromStorage({ workerId: 15 });Initializes browser-specific configuration with optional localStorage persistence.
Parameters:
options.save(boolean): Whether to save to localStorage (default: false)options.config(SnowflakeConfig): Custom configuration to merge
const config = initializeBrowserConfig({
save: true,
config: { epoch: 1640995200000 }
});Clears all stored configuration from localStorage.
clearStoredConfig();Gets detailed browser fingerprint for debugging purposes.
const fingerprint = getBrowserFingerprint();
// Returns object with datacenterId, workerId, timezone, userAgent, screen info, etc.Snowflake IDs are 64-bit integers composed of:
1 bit | 41 bits | 5 bits | 5 bits | 12 bits
unused | timestamp | datacenter| worker | sequence
- Timestamp (41 bits): Milliseconds since custom epoch
- Datacenter ID (5 bits): Identifies the datacenter (0-31)
- Worker ID (5 bits): Identifies the worker process (0-31)
- Sequence (12 bits): Counter for same-millisecond generation (0-4095)
In edge mode (mode: 'edge'), the datacenter | worker | sequence block is replaced by 22 bits
of cryptographic randomness:
1 bit | 41 bits | 22 bits
unused | timestamp | random entropy (crypto)
0 | ms-epoch | getRandomValues
- Timestamp (41 bits): Milliseconds since custom epoch (same position as default mode)
- Entropy (22 bits):
crypto.getRandomValues(0-4,194,303)
const generator = createSnowflake({ workerId: 1 });
// Generate 1000 IDs quickly
const ids = [];
for (let i = 0; i < 1000; i++) {
ids.push(generator());
}
// All IDs are unique and sortable
console.log(ids.length === new Set(ids).size); // trueconst worker1 = createSnowflake({ workerId: 1 });
const worker2 = createSnowflake({ workerId: 2 });
const id1 = worker1(); // From worker 1
const id2 = worker2(); // From worker 2
// IDs from different workers are still unique
console.log(id1 !== id2); // true// React/Vue/etc. application
import { initializeBrowserConfig, createSnowflakeFromStorage } from '@tknf/snowflake/browser';
// Initialize on app startup
const config = initializeBrowserConfig({
save: true, // Persist across browser sessions
config: { epoch: 1640995200000 }
});
const generator = createSnowflakeFromStorage();
// Use in your app
function createNewRecord() {
const id = generator();
console.log('New record ID:', id);
return id;
}// Service Worker example
// sw.js
importScripts('https://unpkg.com/@tknf/snowflake/dist/snowflake.min.js');
self.addEventListener('message', (event) => {
if (event.data.type === 'GENERATE_ID') {
const generator = Snowflake.createSnowflake({
datacenterId: 1,
workerId: 5
});
const id = generator();
// Send back to main thread
self.clients.matchAll().then(clients => {
for (const client of clients) {
client.postMessage({
type: 'ID_GENERATED',
data: { id }
});
}
});
}
});
// Main thread
navigator.serviceWorker.controller.postMessage({
type: 'GENERATE_ID'
});In Workers your code runs across many ephemeral isolates, so you cannot assign a stable workerId
(reading env.WORKER_ID gives every isolate the same value, which risks collisions). Use
mode: 'edge' so uniqueness comes from cryptographic entropy instead of an assigned worker number:
import { createSnowflake } from '@tknf/snowflake';
const generator = createSnowflake({ mode: 'edge', epoch: 1640995200000 });
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const id = generator();
return new Response(JSON.stringify({ id }), {
headers: { 'Content-Type': 'application/json' }
});
}
};Edge mode is probabilistic β pair it with a DB
UNIQUEconstraint and retry-on-collision (e.g. when using a single-writer Turso/SQLite). See Edge Mode (Entropy).
import { getBrowserFingerprint, generateBrowserConfig } from '@tknf/snowflake/browser';
// Get detailed browser information
const fingerprint = getBrowserFingerprint();
console.log('Browser fingerprint:', fingerprint);
// Consistent config for same browser
const config1 = generateBrowserConfig();
const config2 = generateBrowserConfig();
console.log(config1.workerId === config2.workerId); // true - same browser = same workerIdThe library includes comprehensive examples for different environments:
examples/iife/: IIFE browser example with localStorage integrationexamples/node/: Node.js example with environment variablesexamples/worker/: Service Worker example with background generation
Each example includes:
- Complete working code
- Performance testing
- Error handling
- Detailed README with setup instructions
# IIFE Browser Example
cd examples/iife
pnpm install
pnpm run start
# Node.js Example
cd examples/node
pnpm install
pnpm run start
# Service Worker Example
cd examples/worker
pnpm install
pnpm run startThis project uses Vite+ as its unified toolchain β bundling with tsdown, linting with Oxlint, formatting with Oxfmt, and testing with Vitest, all driven by the vp CLI.
git clone git@github.com:tknf/snowflake.git
cd snowflake
vp install# Build the library
vp pack
# Run tests
vp test run
# Run tests with coverage
vp test run --coverage
# Format + lint + type-check in one pass
vp check
# Lint only / format only
vp lint
vp fmtsrc/
βββ index.ts # Core Snowflake logic (platform-agnostic)
βββ node.ts # Node.js-specific utilities
βββ browser.ts # Browser-specific utilities
βββ iife.ts # IIFE build for browser usage
βββ index.test.ts # Core functionality tests
βββ node.test.ts # Node.js utilities tests
βββ browser.test.ts # Browser utilities tests
examples/
βββ iife/ # IIFE browser example
βββ node/ # Node.js example
βββ worker/ # Service Worker example
- Runtime: No runtime dependencies
- Development: Node.js 18+ for building and testing
- TypeScript: Full type support included
MIT License - see LICENSE file for details.
- Fork the repository
- Create a feature branch
- Make your changes
- Ensure checks pass:
vp check - Ensure tests pass:
vp test - Submit a pull request
This implementation follows the distributed ID generation pattern, designed for high-performance unique ID generation in distributed systems.
