npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@dreamystify/kestrel

v1.3.0

Published

A distributed, k-sortable unique ID generation system using Redis and Lua, inspired by Icicle.

Readme

Getting started

Prerequisites

  • Node.js v20+ (ES modules support required)
  • Redis v7+ (or a Redis Cluster for distributed mode)
# Initialize the repo (if applicable)
./.scripts/init.sh

Installation

npm install @dreamystify/kestrel

To build the package locally with esbuild, run:

ahoy build

ES Modules Support

This package is built as an ES module and requires Node.js v18+ with ES modules support. When using this package:

  • Use import statements instead of require()
  • Ensure your package.json has "type": "module" or use .mjs file extensions
  • The package exports are optimized for ES module resolution

Usage

The Kestrel library supports three connection modes: standalone, Sentinel, and Cluster. Below are examples for each mode.

Standalone Mode

import { Kestrel } from '@dreamystify/kestrel';

(async () => {
  // Initialize Kestrel with standalone Redis
  const kestrel = await Kestrel.initialize({
    host: 'localhost',
    port: 6379,
    username: 'yourUsername', // if using authentication
    password: 'yourPassword', // if using authentication
    database: 0,
  });

  // Generate a single unique ID
  const id = await kestrel.getId();
  console.log('Generated ID:', id);

  // Generate a batch of 3 IDs
  const ids = await kestrel.getIds(3);
  console.log('Generated IDs:', ids);
})();

Sentinel Mode

import { Kestrel } from '@dreamystify/kestrel';

(async () => {
  // Initialize Kestrel using Redis Sentinel
  const kestrel = await Kestrel.initialize({
    sentinels: [
      { host: 'sentinel1', port: 26379 },
      { host: 'sentinel2', port: 26379 },
    ],
    name: 'mymaster', // name of your master instance
    username: 'yourUsername', // for the master
    password: 'yourPassword', // for the master
    sentinelUsername: 'sentinelUser', // if your Sentinel requires authentication
    sentinelPassword: 'sentinelPassword', // if your Sentinel requires authentication
    database: 0,
  });

  const id = await kestrel.getId();
  console.log('Generated ID:', id);
})();

Cluster Mode

import { Kestrel } from '@dreamystify/kestrel';

(async () => {
  // Initialize Kestrel using Redis Cluster
  const kestrel = await Kestrel.initialize({
    clusterNodes: [
      { host: 'redis-cluster-node1', port: 6379 },
      { host: 'redis-cluster-node2', port: 6379 },
      { host: 'redis-cluster-node3', port: 6379 },
    ],
    username: 'yourUsername', // for cluster authentication
    password: 'yourPassword', // for cluster authentication
    database: 0,
  });

  const id = await kestrel.getId();
  console.log('Generated ID:', id);
})();

Sharding Configuration

import { Kestrel } from '@dreamystify/kestrel';

(async () => {
  // Initialize Kestrel with a custom shard configuration (for standalone mode)
  const kestrel = await Kestrel.initialize({
    host: 'localhost',
    port: 6379,
    username: 'yourUsername',
    password: 'yourPassword',
    database: 0,
    // Optionally set a fixed shard ID via environment variable:
    // KESTREL_SHARD_ID_KEY: '{kestrel}-shard-id',
    // KESTREL_SHARD_ID: '1',
  });

  const id = await kestrel.getId();
  console.log('Generated ID:', id);
})();

In Cluster mode, the library automatically assigns shard IDs to master nodes based on the cluster topology. You can later query each node (using redis-cli) to see the assigned shard IDs:

docker run -it --rm --network redis_cluster redis:7.0.2 redis-cli -a yourPassword --cluster call redis-cluster-node1:6379 GET '{kestrel}-shard-id'

Decoding IDs

Kestrel IDs can be decoded back into their component parts, allowing you to extract information like when the ID was created, which shard generated it, and the sequence number.

import { Kestrel } from '@dreamystify/kestrel';

(async () => {
  const kestrel = await Kestrel.initialize({
    host: 'localhost',
    port: 6379,
    username: 'yourUsername',
    password: 'yourPassword',
  });

  // Generate an ID
  const id = await kestrel.getId();

  // Decode a single ID
  const decoded = Kestrel.decodeId(id);
  console.log('Timestamp (ms since Unix epoch):', decoded.timestampMs);
  console.log('Timestamp (ms since custom epoch):', decoded.timestamp);
  console.log('Logical Shard ID:', decoded.logicalShardId); // 0-1023
  console.log('Sequence:', decoded.sequence); // 0-4095
  console.log('Created At:', decoded.createdAt); // JavaScript Date object

  // Decode multiple IDs
  const ids = await kestrel.getIds(5);
  const decodedIds = Kestrel.decodeIds(ids);
  decodedIds.forEach((d, index) => {
    console.log(`ID ${index + 1}:`);
    console.log(`  Created: ${d.createdAt.toISOString()}`);
    console.log(`  Shard: ${d.logicalShardId}, Sequence: ${d.sequence}`);
  });
})();

The decodeId method accepts IDs as bigint, string, or number, making it easy to decode IDs from various sources (databases, APIs, etc.).

Error Handling

Kestrel emits events for errors. It is designed to let your application handle logging and error processing in a way that suits your needs. For example:

kestrel.on('error', error => {
  console.error('Kestrel error:', error.error);
});

kestrel.on('connect', () => {
  console.log('Kestrel connected to Redis');
});

Events

| Event Constant | Event String | Description | | ------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------- | | CLIENT_CREATED | clientCreated | Emitted when the Redis client instance is successfully created. | | CONNECTED | connected | Emitted when the client has successfully connected to Redis. | | SCRIPT_LOADED | scriptLoaded | Emitted when a Lua script is successfully loaded on a Redis node. | | NODE_ADDED | nodeAdded | Emitted when a new node is detected in a cluster. | | NODE_REMOVED | nodeRemoved | Emitted when a node is removed from a cluster. | | CLUSTER_NODE_ADDED | +node | Emitted when a new cluster node is added (internal cluster topology event). | | CLUSTER_NODE_REMOVED | -node | Emitted when a cluster node is removed (internal cluster topology event). | | ERROR | error | Emitted when an error occurs within the Kestrel library. | | Redis Connection Events | | | | CONNECT | connect | Emitted when a connection is established. | | CONNECTING | connecting | Emitted when the client is attempting to establish a connection. | | RECONNECTING | reconnecting | Emitted when the client is attempting to reconnect after a disconnect or error. | | DISCONNECTED | disconnected | Emitted when the client has been disconnected. | | WAIT | wait | Emitted when the client is waiting (typically during retry/backoff). | | READY | ready | Emitted when the client is ready to accept commands. | | CLOSE | close | Emitted when the connection is closed. | | END | end | Emitted when the connection has ended. | | RECONNECTED | reconnected | Emitted when the client has successfully reconnected. | | RECONNECTION_ATTEMPTS_REACHED | reconnectionAttemptsReached | Emitted when the maximum number of reconnection attempts is reached and no further retries occur. |

Testing

# Start the testing environment
ahoy start

# Run the tests with coverage
ahoy test

# Run tests with coverage reporting
ahoy coverage

# Stop the testing environment
ahoy stop

Testing Sentinel and Cluster

# Start the testing environment
ahoy start

# Check docker logs
ahoy logs

# Stop the testing environment
ahoy stop

Kudos

The project was inspired by Icicle, just setup for node.js.