@waelio/messaging
v2.3.0
Published
A real-time messaging application with direct & broadcast messages, user lists, and optional MongoDB persistence.
Maintainers
Readme
waelio-messaging
Real-time messaging hub powered by FeathersJS channels and Socket.io — direct messages, broadcasts, private rooms, user lists, history, and optional MongoDB persistence.
Live Demo: https://waelio-messaging.onrender.com/
What is this?
waelio-messaging gives you:
- a real-time Socket.io server built on FeathersJS channels (no REST)
- a ready-to-use chat UI (
public/index.html) - direct message + broadcast + private rooms + message history
- optional MongoDB persistence (falls back to in-memory automatically)
The original MessagingHub (raw ws) is kept as a standalone library export. The server now uses a Feathers app layered on top, replacing the hand-rolled broadcast loops with declarative channel routing.
If you just want to run and chat locally, use the 5-minute guide below.
5-minute start (recommended)
1) Install dependencies
npm install2) Start the app
npm run dev3) Open the app
Open your browser at:
- http://localhost:8080
4) Test chat quickly
Open 2 browser tabs to the same URL and:
- send a direct message
- click Broadcast for all users
- click History to load previous messages
How to use the UI
- Users Online (left): pick a user for direct messages
- Send: sends to selected user
- Broadcast: sends to everyone
- History: loads saved/in-memory message history
- Room Start/Leave: optional focused room messaging
- Install (when shown): install the PWA
Background activity indicators
When the tab is in background and a new message arrives, the app shows:
- unread count in the tab title
- a favicon badge
- pulsing activity dot + count in the header
The indicator clears when you return to the tab/window.
PWA (Installable Client)
The bundled UI under public/ is a Progressive Web App:
- Installable on desktop and mobile (manifest + service worker)
- Basic offline support (core assets cached, navigation fallback)
- Install prompt via
beforeinstallprompt
Troubleshooting
App doesn’t open
- Ensure server is running (
npm run dev) - Ensure port
8080is free
No messages between tabs
- Make sure both tabs use the same server URL
- Hard refresh both tabs after restarting server
MongoDB persistence not working
- Set
MONGO_URIto a valid Mongo connection string - Without
MONGO_URI, app uses in-memory storage by design
Package install
If you want to use this as a library in your own project:
npm install @waelio/messagingLibrary usage (server)
npm run dev # start in dev (ts-node)
npm run build # compile TypeScript
npm start # run compiled serverWeb APIs used (client activity indicators)
- Page Visibility API (
document.hidden,visibilitychange) - Window focus event (
window.addEventListener('focus', ...)) - Document title updates (
document.title) - Favicon updates via
<link rel="icon">+ Canvas API - PWA install events (
beforeinstallprompt,appinstalled)
Advanced: create your own server
import http from "http";
import express from "express";
import { createFeathersApp } from "./src/feathers/app.js";
const app = express();
const server = http.createServer(app);
// Socket.io + Feathers channels, no REST
// Optional: pass a MongoDB URI as second argument
await createFeathersApp(server, process.env.MONGO_URI);
server.listen(8080, () => console.log("ready"));If you prefer the original raw-ws hub as a library:
import http from "http";
import express from "express";
import { MessagingHub } from "@waelio/messaging";
const app = express();
const server = http.createServer(app);
const hub = new MessagingHub(server, { mongoURI: process.env.MONGO_URI });
await hub.ready;
server.listen(8080, () => console.log("ready"));Web Component
Auto-send on connect:
<waelio-message target="USER_ID" message="hello"></waelio-message>Broadcast:
<waelio-message message="hello everyone" broadcast></waelio-message>Manual:
<waelio-message id="msg" send-on="manual" target="USER_ID"></waelio-message>
<script>
msg.addEventListener("connected", () => msg.send("hi again"));
</script>Attributes: target, message, broadcast, ws-url, send-on (connect|manual|click), reconnect. Events: connected, disconnected, sent, error.
Architecture
src/
feathers/
app.ts ← Feathers app, Socket.io transport (no REST)
channels.ts ← Declarative channel routing rules
services/
messages.ts ← create (send) + find (history)
rooms.ts ← create (join private room)
MessagingHub.ts ← Original raw-ws hub (library export, unchanged)
server.ts ← HTTP server entry point
public/
index.html ← p5.js canvas UI using socket.io-clientHow Feathers channels replace the old broadcast loops
| Scenario | Channel used |
| ---------------------- | --------------------------------------------- |
| Direct message | direct/<recipientId> |
| Broadcast | all (filtered to exclude sender) |
| Room message | rooms/<roomId> (filtered to exclude sender) |
| Room join notification | direct/<userId> + direct/<partnerId> |
Each connected socket is placed in all and direct/<id> on connect. When two clients join a room their sockets are also added to rooms/<roomId>. Feathers service.publish() in channels.ts decides which channel receives each service event — no manual looping required.
Protocol (Summary)
Client → Server (Socket.io emit)
| Old ws message type | New Feathers call |
| --------------------- | ------------------------------------------------------------------- |
| route | socket.emit('messages::create', { type:'route', to, payload }) |
| broadcast | socket.emit('messages::create', { type:'broadcast', payload }) |
| get-history | socket.emit('messages::find', {}, callback) |
| join-room | socket.emit('rooms::create', { with: partnerId }) |
| room-message | socket.emit('messages::create', { type:'room-message', payload }) |
| start-typing | socket.emit('start-typing') |
| stop-typing | socket.emit('stop-typing') |
Server → Client (Socket.io events)
register-success{ id }— your assigned client IDuser-list{ users[] }— full list of connected IDsuser-joined{ id, ts }/user-left{ id, ts }user-typing{ id }/user-stopped-typing{ id }messages created{ senderId, recipientId, payload, isBroadcast, roomId, timestamp }— Feathers service eventrooms created{ roomId, userId, partnerId }— Feathers service event
Persistence (Optional)
Pass a MongoDB URI to createFeathersApp (or MessagingHub) to persist messages across restarts. Without it, an in-memory store is used automatically (last 100 messages).
await createFeathersApp(server, process.env.MONGO_URI);GitHub webhook sync
The server can synchronize portalRequests records from GitHub webhook events through:
POST /api/github/webhook- HMAC verification via
GITHUB_WEBHOOK_SECRETand GitHub'sX-Hub-Signature-256header
Supported event types:
issuespull_requestrepository_dispatch
How a GitHub event finds a portal request
For issues and pull_request events, include the request ID in either:
- the issue/PR body as
Portal-Request-Id: <requestId> - a label such as
portal-request:<requestId>
Optional metadata can also be supplied:
Session-Code: <code>in the body, or a label likesession:<code>- labels like
startedorin-progressto promote the request status tostarted
Status mapping:
issues→importedby default, orstartedwhen a started-style label is presentpull_request→startedrepository_dispatch→ explicitclient_payload.status
Explicit sync via repository_dispatch
Send a repository_dispatch payload with:
{
"action": "portal-request-sync",
"client_payload": {
"requestId": "<requestId>",
"status": "imported",
"hostDisplayName": "octocat",
"sessionCode": "ROOM-42"
}
}This is handy when you want GitHub Actions to drive the sync directly instead of inferring it from labels or issue bodies.
Built-in GitHub Actions helper
This repository now includes .github/workflows/portal-request-sync.yml.
It can help in two ways:
- Manual sync from the GitHub Actions UI with
requestId,status,hostDisplayName, andsessionCode - Automatic sync dispatch for matching
issuesandpull_requestactivity by emitting arepository_dispatchevent on the same repo
Recommended setup modes:
- Direct webhook mode
- Configure your GitHub webhook to send
issuesandpull_requestdirectly to/api/github/webhook - Use this when you want the fewest moving parts
- Workflow-assisted mode
- Enable the included
Portal Request Syncworkflow - Configure your GitHub webhook to send
repository_dispatchto/api/github/webhook - Use this when you want GitHub Actions to normalize issue/PR activity before your server receives it
To avoid duplicate updates, pick one mode per repository instead of enabling both issue/PR webhooks and workflow-generated repository_dispatch events for the same sync flow.
Release Scripts
Patch / Minor / Major then publish:
npm run release:patch && npm run release:publish
npm run release:minor && npm run release:publish
npm run release:major && npm run release:publishLicense
This project is licensed under the MIT License - see the LICENSE file for details.
