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

dialogue-ts

v0.0.4

Published

An event-based realtime communication library based on Socket.IO and Hono, supporting Bun and Node.js runtimes.

Readme

Dialogue Ts

An event-based realtime communication library based on Socket.IO, Hono, and Bun.

Dialogue is basically asking how do we scale to different real time use cases using same mental model and api.

📚 Documentation

📖 View Full Documentation →

Complete guides, API references, and examples available at: https://hussseinkizz.github.io/dialogue/

Prerequisites

  • Bun - Fast all-in-one JavaScript runtime (required for server)
  • Zod - TypeScript-first schema validation

Installation

bun add dialogue-ts zod

Key Concepts

Events

Events are the core building blocks. Each event has a name and an optional Zod schema for validation:

import { defineEvent } from "dialogue-ts";
import { z } from "zod";

const Message = defineEvent("message", {
  schema: z.object({
    text: z.string().min(1).max(1000),
    senderId: z.string(),
  }),
});

const Typing = defineEvent("typing", {
  schema: z.object({ isTyping: z.boolean() }),
});

Rooms

Rooms are channels where events are broadcast. Clients join rooms to send and receive events:

const dialogue = createDialogue({
  port: 3000,
  rooms: {
    chat: {
      name: "Chat Room",
      events: [Message, Typing],
      defaultSubscriptions: ["message"],
      maxSize: 100, // Optional capacity limit
    },
  },
});

Subscriptions

Clients can subscribe to specific event types within a room. Only subscribed events are received, reducing unnecessary traffic.

Quick Start: Chat Example

Server (Bun)

// server.ts
import { createDialogue, defineEvent } from "dialogue-ts";
import { z } from "zod";

// Define events
const Message = defineEvent("message", {
  schema: z.object({
    text: z.string().min(1),
    username: z.string(),
  }),
});

const UserJoined = defineEvent("user-joined", {
  schema: z.object({ username: z.string() }),
});

// Create dialogue instance
const dialogue = createDialogue({
  port: 3000,
  rooms: {
    general: {
      name: "General Chat",
      events: [Message, UserJoined],
      defaultSubscriptions: ["message", "user-joined"],
    },
  },
  hooks: {
    clients: {
      onConnected: (client) => {
        console.log(`Client connected: ${client.userId}`);
        client.join("general");

        // Notify others
        dialogue.trigger("general", UserJoined, {
          username: client.userId,
        });
      },
      onDisconnected: (client) => {
        console.log(`Client disconnected: ${client.userId}`);
      },
    },
  },
});

// Start server
await dialogue.start();
console.log("Chat server running on http://localhost:3000");

Run with:

bun run server.ts

Client (Browser/Node)

// client.ts
import { createDialogueClient } from "dialogue-ts/client";

const client = createDialogueClient({
  url: "ws://localhost:3000",
  auth: { userId: "alice" },
});

// Connect and join room
await client.connect();
const chat = await client.join("general");

// Listen for messages
chat.on("message", (msg) => {
  console.log(`${msg.data.username}: ${msg.data.text}`);
});

// Listen for users joining
chat.on("user-joined", (msg) => {
  console.log(`${msg.data.username} joined the chat`);
});

// Send a message
chat.trigger("message", {
  text: "Hello everyone!",
  username: "alice",
});

Server API

createDialogue(config)

Creates a Dialogue server instance.

const dialogue = createDialogue({
  port: 3000,
  rooms: { /* room configs */ },
  hooks: {
    clients: {
      onConnected: (client) => { /* handle connection */ },
      onDisconnected: (client) => { /* handle disconnection */ },
    },
  },
  logger: createDefaultLogger(), // Optional custom logger
});

dialogue.start()

Starts the server. Returns a promise.

dialogue.trigger(roomId, event, data, from?)

Triggers an event from the server to all subscribers in a room.

dialogue.trigger("general", Message, { text: "Hello!", username: "system" });

dialogue.getRoom(roomId)

Gets a room instance for direct manipulation.

dialogue.createRoom(id, config, createdById?)

Creates a new room at runtime.

const room = dialogue.createRoom("project-123", {
  name: "Project Discussion",
  events: [Message, UserJoined],
  defaultSubscriptions: ["message"],
  maxSize: 50,
}, "user-456");

dialogue.deleteRoom(id)

Deletes a room. Returns true if successful.

dialogue.deleteRoom("project-123");

User Management

// Get all connections for a user
const clients = dialogue.getClients("user-123");

// Get rooms a user is in
const rooms = dialogue.getClientRooms("user-123");

// Check if user is in a room
if (dialogue.isInRoom("user-123", "vip-room")) { ... }

// Remove user from all rooms (with notifications)
dialogue.getClientRooms("user-123").leaveAll((roomId) => {
  dialogue.trigger(roomId, UserLeft, { username: "user-123" });
});

Client API

DialogueClient

import { createDialogueClient } from "dialogue-ts/client";

const client = createDialogueClient({
  url: "ws://localhost:3000",
  auth: { userId: "user-123", token: "jwt-token" },
});

client.connect() / client.disconnect()

Connect to or disconnect from the server.

client.join(roomId)

Joins a room and returns a RoomContext.

client.createRoom(options)

Creates a new room on the server.

const roomInfo = await client.createRoom({
  id: "tech-talk",
  name: "Tech Talk",
  description: "Discuss technology",
  maxSize: 100,
});

client.deleteRoom(roomId)

Deletes a room (only creator can delete).

client.onRoomCreated(handler) / client.onRoomDeleted(handler)

Listen for room lifecycle events.

client.onRoomCreated((room) => console.log("New room:", room.name));
client.onRoomDeleted((roomId) => console.log("Deleted:", roomId));

RoomContext

const room = await client.join("chat");

// Listen for events
const unsubscribe = room.on("message", (msg) => {
  console.log(msg.data);
});

// Send events
room.trigger("message", { text: "Hello!" });

// Subscribe/unsubscribe from event types
room.subscribe("typing");
room.unsubscribe("typing");

// Leave room
room.leave();

Hooks

Hooks provide lifecycle callbacks for clients, rooms, and events.

Client Hooks

const dialogue = createDialogue({
  rooms: { /* ... */ },
  hooks: {
    clients: {
      onConnected: (client) => {
        console.log(`${client.userId} connected`);
        client.join("general");
      },
      onDisconnected: (client) => {
        console.log(`${client.userId} disconnected`);
      },
      onJoined: (client, roomId) => {
        dialogue.trigger(roomId, UserJoined, { username: client.userId });
      },
      onLeft: (client, roomId) => {
        dialogue.trigger(roomId, UserLeft, { username: client.userId });
      },
    },
  },
});

Room Hooks

hooks: {
  rooms: {
    onCreated: (room) => {
      console.log(`Room ${room.name} created`);
    },
    onDeleted: (roomId) => {
      console.log(`Room ${roomId} deleted`);
    },
  },
}

Event Hooks (for persistence)

hooks: {
  events: {
    onTriggered: (roomId, event) => {
      // Called for every event
      analytics.track(event.event, event.data);
    },
    onCleanup: async (roomId, eventName, events) => {
      // Called when events are evicted from memory
      await db.events.insertMany(events);
    },
    onLoad: async (roomId, eventName, start, end) => {
      // Load historical events from database for pagination
      return db.events.find({ roomId, eventName }).skip(start).limit(end - start);
    },
  },
}

Event History

Store and retrieve historical events per room. Useful for chat history, activity feeds, etc.

Enabling History

Enable history per event type:

const Message = defineEvent("message", {
  schema: z.object({ text: z.string(), username: z.string() }),
  history: { enabled: true, limit: 100 }, // Keep last 100 messages
});

Auto-sync on Join

Automatically send history when clients join a room:

const dialogue = createDialogue({
  rooms: {
    chat: {
      name: "Chat",
      events: [Message],
      syncHistoryOnJoin: true, // Send all history
      // syncHistoryOnJoin: 50, // Or limit to 50 events
    },
  },
});

Client-side History

// Automatic history on join
client.onHistory((roomId, events) => {
  events.forEach((event) => renderMessage(event));
});

// Manual pagination
const room = await client.join("chat");
const olderMessages = await room.getHistory("message", 50, 100); // Skip 50, get 50

Server-side History Access

const room = dialogue.room("chat");
const recentMessages = await room.history("message", 0, 20); // Last 20 messages

Documentation

For complete documentation, visit hussseinkizz.github.io/dialogue

Quick links:

Example App

A working chat application is included in the example/ folder. See the example README for setup instructions.

Built With

  • Socket.IO - Real-time bidirectional event-based communication
  • Slang Ts - Pattern Matching And Functional Utilities
  • Hono - Ultrafast web framework for the Edge
  • Bun - Fast all-in-one JavaScript runtime
  • Zod - TypeScript-first schema validation

License

MIT