Package Exports
- paul-revere
- paul-revere/dist/websocket-client.js
- paul-revere/dist/websocket.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (paul-revere) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Paul Revere
Paul Revere is a lightweight wrapper for server-client WebSocket communication. It uses schemapack to encode buffers from JavaScript objects and delivers a smaller payload and faster parsing than traditional JSON messaging. On the browser, it wraps a vanilla WebSocket object, and on the server it wraps the ultra-fast µWebSockets package.
Paul Revere also accepts pub/sub adapters to allows for horizontal server scaling.
Server Usage
Paul Revere can be used with any Node server, though this example shows Express.
const express = require('express'),
PaulRevere = require('paul-revere'),
schemas = require('../shared/schemas'); // same module as client
/**
* Example schemas.js
* Follow schema rules found at https://github.com/phretaddin/schemapack
exports.chat = {
payload: {
message: 'string',
user: 'string'
},
meta: {
timestamp: 'string'
}
};
*/
const app = express();
// Get Node server instance
const server = app.listen(3000, () => app.log.info('Paul Revere App listening on port 3000'));
// Pass your schemas and a Node server to start a Paul Revere WebSocket server
const paul = new PaulRevere(schemas, {server});
// Bind a client connection handler
paul.onConnection(client => {
// The server exposes your schemas to broadcast messages to all clients
// Pass a client as the second argument to exclude it from the broadcast
paul.chat.broadcast({
payload: {
message: 'New user joined!',
user: 'ChatBot'
},
meta: {
timestamp: String(Date.now())
}
}, client); // Don't send this message to the client that just joined
// Each client has its own schema instance to send messages directly to that client...
client.chat.send({
payload: {
message: 'Welcome to Chat!',
user: 'ChatBot'
},
meta: {
timestamp: String(Date.now())
}
});
// And also listen for messages from that client
client.chat.onMessage(message => {
// Broadcasting the message to all clients simplifies front end rendering and listeners
// and is fast enough for non-optimistic updates
paul.chat.broadcast(message);
});
});Client Usage
Paul Revere is supported on all browsers that support native WebSockets. http://caniuse.com/#feat=websockets
import PaulRevere from 'paul-revere';
import schemas from '../shared/schemas'; // same module as server
// Pass your schemas and a WebSocket address to connect to a Paul Revere server
const paul = new PaulRevere(schemas, {url: 'ws://localhost:3000'}),
// The client exposes your schemas to send messages to the server
paul.chat.send({
payload: {
message: 'Hello!',
user: 'ClientBot'
},
meta: {
timestamp: String(Date.now())
}
});
// And also listen to messages from the server
paul.chat.onMessage(m => {
console.log(m);
});Server Adapters
In order to support horizontal server scaling, Paul Revere servers internally run off a pub/sub model for broadcasting messages. The default is nothing more than a stub function firing callbacks (see below). Custom adapters can be written and passed as the option pubSub when instantiating a server, and Paul Revere will use that instead. There is an official NATS adapter at paul-revere-nats-adapter.
Adapter API
Paul Revere servers need publicly accessible publish(subject, msg, exclude) and subscribe(subject, cb) methods. Nothing else is required to be exposed.
const pubSubAdapter = {
// subject will always be an integer unique to a schema type, msg will always be a plain object, and exclude may be undefined or a string id
publish(subject, msg, exclude) {
// listeners are namespaced with 'paulrevere' just to ensure string keys
// Your subject can be anything, so long as it is unique for every schema
if(!this.listeners[`paulrevere.${subject}`]) return;
this.listeners[`paulrevere.${subject}`].forEach(cb => cb(msg, exclude));
},
// subscriber callbacks expect a plain object msg, and also an exclude string id if passed in the schema.broadcast() method
subscribe(subject, cb) {
if(!this.listeners[`paulrevere.${subject}`]) this.listeners[`paulrevere.${subject}`] = [];
this.listeners[`paulrevere.${subject}`].push(cb);
},
listeners: {}
};
const paul = new PaulRevere(schemas, {server, pubSub: pubSubAdapter});