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

redweb

v0.7.3

Published

A way to quickly set up an express server

Readme

RedWeb

RedWeb is a flexible Node.js framework built on top of Express.js and WebSocket. It enables quick setup of HTTP(S) and WebSocket servers with a modular route and handler system.


📦 Installation

npm install redweb

🚀 Quick Start

const { HttpServer, SocketServer } = require('redweb');

new HttpServer(); // serves public/ by default
new SocketServer(); // starts WS on :3000

🌐 HTTP Server Example (HTMX Support)

const { HttpServer, METHODS } = require('redweb');

new HttpServer({
  port: 3000,
  publicPaths: ['./public'],
  enableHtmxRendering: true,
  services: [
    {
      serviceName: '/submit',
      method: METHODS.POST,
      function: (req, res) => {
        if (!req.body.name) return res.status(400).json({ error: 'Missing name' });
        res.status(200).json({ message: `Thanks, ${req.body.name}!` });
      }
    }
  ]
});

.htmx files under public/ will render server-side. Example:

<!-- public/hello.htmx -->
<@>
  <h1>Hello, {{name}}!</h1>
<@/>

🔌 WebSocket Broadcast Chat (🔥 Instant Testing)

1. ChatHandler.js

const { BaseHandler } = require('redweb');

class ChatHandler extends BaseHandler {
  constructor() {
    super('chat');
  }

  onMessage(socket, message) {
    const text = message.text;
    socket.broadcast({ type: 'chat', text });
  }
}

module.exports = ChatHandler;

2. ChatRoute.js

const { SocketRoute } = require('redweb');
const ChatHandler = require('./ChatHandler');

class ChatRoute extends SocketRoute {
  constructor() {
    super({
      path: '/chat',
      handlers: [ChatHandler],
      allowDuplicateConnections: true
    });
  }
}

module.exports = ChatRoute;

3. server.js

const { SocketServer } = require('redweb');
const ChatRoute = require('./ChatRoute');

new SocketServer({
  port: 3000,
  routes: [ChatRoute]
});

4. client.html

<!DOCTYPE html>
<html>
  <body>
    <h1>Broadcast Chat</h1>
    <input id="msg" placeholder="Type message..." />
    <button onclick="send()">Send</button>
    <pre id="log"></pre>

    <script>
      const log = document.getElementById('log');
      const ws = new WebSocket('ws://localhost:3000/chat');

      ws.onmessage = (e) => {
        const msg = JSON.parse(e.data);
        log.textContent += `\n${msg.text}`;
      };

      function send() {
        const text = document.getElementById('msg').value;
        ws.send(JSON.stringify({ type: 'chat', text }));
      }
    </script>
  </body>
</html>

Open multiple tabs to test.


🧩 Socket Architecture

SocketRoute

Defines a WebSocket path, handlers, and optional route-scoped services:

new SocketRoute({
  path: '/game',
  handlers: [ChatHandler, MoveHandler],
  services: [MatchService], // ✅ Scoped only to this route
  allowDuplicateConnections: true
});

BaseHandler

Handlers are message-type keyed classes:

class MoveHandler extends BaseHandler {
  constructor() {
    super('move');
  }

  onMessage(socket, message) {
    // handle movement logic
  }
}

SocketService (NEW)

Socket services run alongside handlers on a route. Use for timers, logic, cleanup.

class MatchService extends SocketService {
  constructor() {
    super('match', 1000); // tick every 1s
  }

  onInit(route) {
    route.registry.on('maxPlayersReached', () => this.startMatch());
  }

  onTick() {
    // tick logic
  }

  onShutdown() {
    // cleanup
  }
}

📦 SocketRegistry (NEW) – Event-Driven Socket Object Store

SocketRegistry is a lightweight, extendable class for managing WebSocket-connected clients (or any socket-bound object). It provides add/remove/get/broadcast utilities with full EventEmitter support.

Useful for managing players, NPCs, chat members, rooms, etc.


🔧 Basic Usage


class Player {
    constructor(socket, id) {
        this.socket = socket;
        this.id = id;
    }

    send(type, payload) {
        this.socket.send(JSON.stringify({ type, ...payload }));
    }

    getSanitized() {
        return { id: this.id };
    }
}

🚀 Extending SocketRegistry to Create a Player Registry

class PlayerRegistry extends SocketRegistry {
    create(socket, id) {
        return new Player(socket, id);
    }

    addPlayer(socket, id) {
        const player = this.create(socket, id);
        const success = this.add(player);
        if (success) this.emit('playerJoined', player);
        return success;
    }

    removePlayer(id) {
        const success = this.remove(id);
        if (success) this.emit('playerLeft', id);
        return success;
    }

    broadcastToAll(message) {
        this.items.forEach(player => player.send(message.type, message));
    }
}

📣 Built-in Events

You can listen to events:

const registry = new PlayerRegistry();

registry.on('playerJoined', player => {
    console.log('New player:', player.id);
});

registry.on('playerLeft', id => {
    console.log('Player left:', id);
});

🔄 Built-in Methods

  • add(player)
  • remove(id)
  • getById(id)
  • getBySocket(socket)
  • all()
  • count()
  • broadcast(message, excludeSocket?)
  • getSanitizedList()

🔧 Configuration

HTTP / HTTPS Options

| Option | Type | Default | Description | | --------------------- | --------- | -------------- | ------------------------------ | | port | number | 80 | Port to listen on | | bind | string | '0.0.0.0' | Bind address | | publicPaths | string[] | ['./public'] | Serve static and .htmx files | | services | object[] | [] | REST endpoints | | enableHtmxRendering | boolean | false | Enables .htmx file rendering | | ssl | object | undefined | Used in HttpsServer |


WebSocket Server Options

| Option | Type | Default | Description | | -------- | --------------- | ------- | ---------------------------- | | port | number | 3000 | WebSocket port | | routes | SocketRoute[] | [] | List of custom route classes |

 

Changelog

Here’s the updated CHANGELOG.md entry for RedWeb v0.7.1, written professionally and focused only on the framework-level additions:


📦 RedWeb v0.7.1 – Socket Services & Registries

✨ Added

  • SocketService: A new class for running autonomous, lifecycle-aware logic alongside a SocketRoute. Ideal for game loops, timers, state machines, or server-side AI.

    • Hooks: onInit(route), onTick(), onShutdown()
    • Optional tickRateMs support for periodic execution
  • SocketRegistry: A generic, event-driven registry for managing WebSocket-bound entities

    • Includes .add(), .remove(), .getById(), .broadcast()
    • Fully compatible with custom socket wrappers and EventEmitter

0.7.0 Update Highlights

  • allowDuplicateConnections for multi-tab testing
  • Robust message validation
  • socket.broadcast() now excludes sender
  • Better error handling

🪪 License

MIT