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 🙏

© 2026 – Pkg Stats / Ryan Hefner

redblink

v1.0.0

Published

Lightweight Node.js library combining Publisher/Subscriber patterns with Async Local Storage for context-aware applications

Readme

RedBlink

npm version License: MIT

RedBlink is a lightweight Node.js library that combines Publisher/Subscriber patterns with Async Local Storage for building scalable, context-aware applications. It enables flat backend architectures where you can easily import files and test endpoints without complex dependency injection.

Features

  • Publisher/Subscriber Pattern: Create named channels for async communication
  • Async Local Storage: Share context data across async operations
  • Zero Dependencies: Built on native Node.js APIs
  • Proxy-based API: Intuitive syntax with dynamic property access
  • Event-driven Architecture: Based on Node.js EventEmitter
  • TypeScript Ready: Full type support included

Installation

npm install redblink

Quick Start

import RedBlink from 'redblink';

const redblink = new RedBlink();

// Create workers/handlers
redblink.worker('add', ({ a, b }) => {
    return a + b * redblink.context.add.multiplier;
});

redblink.api('getUserById', async ({ id }) => {
    const user = await database.findUser(id);
    return { user, requestId: redblink.context.request.id };
});

// Use with context
async function handleRequest() {
    redblink.context.add = { multiplier: 3 };
    redblink.context.request = { id: 'req-123' };
    
    const sum = await redblink.worker.add({ a: 1, b: 2 }); // Result: 7
    const userData = await redblink.api.getUserById({ id: 42 });
    
    console.log('Sum:', sum);
    console.log('User data:', userData);
}

handleRequest();

Core Concepts

Publisher/Subscriber

RedBlink creates named channels dynamically. Any property access (except context) becomes a pub/sub channel:

// Define a subscriber (handler)
redblink.notifications('email', async (data) => {
    await sendEmail(data.to, data.subject, data.body);
    return { sent: true, timestamp: Date.now() };
});

// Publish to the channel
const result = await redblink.notifications.email({
    to: '[email protected]',
    subject: 'Welcome!',
    body: 'Thanks for signing up!'
});

console.log(result); // { sent: true, timestamp: 1234567890 }

Async Local Storage

The context property provides isolated storage that persists across async operations:

redblink.database('getUser', async ({ id }) => {
    const traceId = redblink.context.trace.id;
    console.log(`[${traceId}] Fetching user ${id}`);
    
    const user = await db.users.findById(id);
    return user;
});

async function handleUserRequest(userId) {
    // Set context for this async flow
    redblink.context.trace = { id: `trace-${Date.now()}` };
    
    const user = await redblink.database.getUser({ id: userId });
    // The trace ID is automatically available in the handler
}

Advanced Usage

Multiple Handlers

You can register multiple handlers for the same channel:

redblink.hooks('beforeSave', (data) => {
    console.log('Validation hook');
    return data;
});

redblink.hooks('beforeSave', (data) => {
    console.log('Audit hook');
    return data;
});

// Both handlers will be called
await redblink.hooks.beforeSave({ name: 'John' });

Error Handling

RedBlink is built on EventEmitter, so you can listen for errors:

redblink.on('error', (error) => {
    console.error('RedBlink error:', error);
});

redblink.worker('failingTask', () => {
    throw new Error('Something went wrong');
});

try {
    await redblink.worker.failingTask();
} catch (error) {
    console.log('Caught error:', error.message);
}

Testing

RedBlink makes testing easy by providing a flat, importable structure:

// workers/math.js
import RedBlink from 'redblink';

const redblink = new RedBlink();

redblink.math('add', ({ a, b }) => a + b);
redblink.math('multiply', ({ a, b }) => a * b);

// test/math.test.js
import './workers/math.js';
import RedBlink from 'redblink';

const redblink = new RedBlink();

test('addition works', async () => {
    const result = await redblink.math.add({ a: 2, b: 3 });
    expect(result).toBe(5);
});

test('multiplication with context', async () => {
    redblink.context.multiplier = { factor: 2 };
    const result = await redblink.math.multiply({ a: 3, b: 4 });
    expect(result).toBe(12);
});

API Reference

new RedBlink()

Create a new RedBlink instance.

redblink[channel](topic, handler)

Register a handler for a specific topic on a channel.

  • channel: String - The channel name (any property except 'context')
  • topic: String - The topic name within the channel
  • handler: Function - Async or sync function to handle the message

redblink[channel].topic(payload)

Publish a message to a specific topic on a channel.

  • channel: String - The channel name
  • topic: String - The topic name
  • payload: Any - Data to send to the handler

Returns a Promise that resolves with the handler's return value.

redblink.context

Proxy object for accessing async local storage. Set and get context data that persists across async operations.

// Set context
redblink.context.user = { id: 123, role: 'admin' };
redblink.context.request = { traceId: 'abc-123' };

// Get context (available in any async operation)
const userId = redblink.context.user.id;
const traceId = redblink.context.request.traceId;

Use Cases

  • Microservices Communication: Easy pub/sub between service components
  • Request Context: Share request data (user info, trace IDs) across handlers
  • Event-driven Architecture: Decouple components with async messaging
  • Testing: Simple, flat structure for unit testing backend logic
  • Hooks & Middleware: Implement before/after hooks for business logic

Comparison with Other Solutions

| Feature | RedBlink | EventEmitter | Message Queues | |---------|---------|--------------|----------------| | Async Local Storage | ✅ | ❌ | ❌ | | Zero Dependencies | ✅ | ✅ | ❌ | | Promise-based | ✅ | ❌ | Varies | | Dynamic Channels | ✅ | ❌ | ❌ | | Built-in Context | ✅ | ❌ | ❌ |

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Author

  • Yarflam - Initial work

Inspired by the need for simple, context-aware pub/sub patterns in Node.js applications.