A `bouncer` is a guy who works outside the night club checking did you pay for the entrance to that particular club. This library is a plug-and-play static files server + uWebSockets plugin manager with chat example and angular integration. One client (socket) may be subscribed to many topics (rooms) at the same time (since v2.18.0).
- you want a static files server
- you want to build a websocket chat
- you want to build any websocket plugin
- you want it on a single process
- you want easy angular integration
- you want easy vanilla js integration
- Common use cases when you might want to use this library:
- Installation
- Usage
- The Flow (!)
- The Plugins (!)
- Configuration
- Front End Client (socket.io-ish) extension
- Example imports
- Tests
- Compatibility
- License
- Author
It's hosted as an npm
package so installation is of course as simple as:
$ yarn add @jacekpietal/bouncer.js
# or
$ npm i @jacekpietal/bouncer.js --save
to start static server of folder dist/your-app
run
$ [PORT=4200] yarn bouncer.js dist/your-app [--debug] [--chat]
-
the
--chat
flag starts websockets chat plugin -
the
--debug
flag starts debug mode with lots of logs -
alter port by setting
PORT env var
serve folder with plugin (chat)
port defaults to 4200
if process.env.PORT
not set
const serve = require('@jacekpietal/bouncer.js/server')
const chat = require('@jacekpietal/bouncer.js/plugins/chat')
// serve public folder with chat plugin
serve('dist/your-app', { chat })
alternative
const BouncerJs = require('@jacekpietal/bouncer.js')
const chat = require('@jacekpietal/bouncer.js/plugins/chat')
const plugins = { chat }
// serve public folder with chat plugin
new BouncerJs({ plugins }).serve('dist/your-app')
frontend for above backend
// app.module.ts
+ import { ChatService } from '@jacekpietal/bouncer.js/build/plugins/chat/ng/chat.service';
+ function chatFactory(window: Window) {
+ return new ChatService(window);
+ }
+ { provide: 'Window', useValue: window },
+ { provide: 'Chat', useFactory: chatFactory, deps: ['Window'] }
// your-component.ts
+ constructor(@Inject('Chat') chat: ChatService) {
+ chat.connect() // if argument address not specified connects to location.origin
+ }
# add minimal typings
$ mkdir -p src/types
$ cp node_modules/@jacekpietal/bouncer.js/bouncer-js.d.ts src/types
const UWebSocket = require("@jacekpietal/bouncer.js/client.js");
const socket = new UWebSocket('ws://localhost:4200');
const refs = getHTMLElements();
socket.onopen = (value) => {
// step 1 ~> 2 of flow - send handshake of plugin name
socket.emitEvent("/join", "chat");
}
socket.on("*", ({ id, event, data }) => {
// append output
refs.messages.innerHTML += `<div>${id} > ${event} > ${data}</div>\n`;
});
socket.on("/join", ({ id, event, data }) => {
// first join is your join, set your server named id
if (!refs.username.innerText) {
refs.username.innerText = id;
}
});
refs.chat.addEventListener('submit', (event) => {
event.preventDefault();
const payload = refs.message.value.trim();
if (!payload) return;
socket.emitEvent("say", payload);
refs.message.value = "";
});
function getHTMLElements() {
return ['username', 'messages', 'message', 'chat'].reduce(
(obj, id) => ({
...obj,
[id]: document.querySelector(`#${id}`)
}),
{}
);
}
STEP 1: Before Connection
- client -> connects websocket to bouncer server on ws:// or wss:// protocol
- server -> waits for handshake / join event (which is defined in config.join)
STEP 2: Connection
- client -> sends handshake / join event with topic aka room name aka plugin name
- server -> plugin associated with that room joins client to room and starts to listen
- server -> broadcasts to all the people of that room that mentioned client joined
STEP 3: After Connection
- client -> does some actions (emits, receives)
- server -> plugin responds to the actions
STEP 4: Finish Connection
- client -> disconnects after some time
- server -> broadcasts to all other people from the room that client left (config.leave)
- To handshake a plugin in bouncer you need to send from your connected client something with similar payload:
{ "event": "/join", "data": "pluginName" }
-
A plugin is a function (ws, { id, event, data }) that is called each time the frontend websocket emits something to the server. context (this) of each plugin is bouncer instance.
-
The plugins receive (and send) the data in the format of:
{
id, // WebSocket id - this is automatically added
event, // event name as string
data, // any data accompanying the event
}
- Read more (with types and parameters) in the API Documentation
A call to new BouncerJs(userConfig)
creates a bouncer instance
It is ready to receive any number of the following props if any as constructor parameters:
{
plugins: {
// any number of plugins with this format
[plugin]: function (ws, { event, id, data }) {
// user implementation
// `this` context is bound to the bouncer instance
}
},
// logo for discriminating lib's messages
logo: 'ʕ•ᴥ•ʔ bouncer.js',
// default port is read from ENV
port: process.env.PORT | 4200,
// this event joins a topic / room
join: '/join',
// this event leaves a topic / room
leave: '/leave',
// a lot more logs
debug: false,
// defaults to undefined
ssl: {
key: '/path/to/key_file_name.key',
cert: '/path/to/cert_file_name.crt',
passphrase: ''
}
}
{
onEvent(ws, event, data),
join(ws, topic),
leave(ws, topic),
broadcast({ topic }, { id, event, data }),
send(ws, { id, event, data }),
router: uws.SSLApp|uws.App,
rooms: Map(),
config: {
// read above section in readme, also:
// after the client config is applied to default config
// the resulting startup config reference is here
}
}
If you can use a bundler for frontend, see:
- see client.js
- see client.spec.js
to improve above frontend code yourself with it
// uWebSocket api is extended in @jacekpietal/bouncer.js/client by
{
emitEvent(eventName, objectOrString),
emit(objectOrString),
on(eventName, callback),
on('*', callback), // on any event
}
// require static files server
const serve = require('@jacekpietal/bouncer.js/server')
// for frontend use this is a websocket enchanced,
// but you can still use normal websocket on frontend
const UWebSocketClient = require('@jacekpietal/bouncer.js/client')
// chat plugin ready to be used with bouncer
// chat === createEcho("chat");
const chat = require('@jacekpietal/bouncer.js/plugins/chat')
const createEcho = require('@jacekpietal/bouncer.js/lib/echo')
// this creates a simple plugin with echo broadcast back to others
// with topic named joystick
const joystick = createEcho('joystick')
// the heart of the library
const BouncerJs = require('@jacekpietal/bouncer.js')
// allows to use older plugins with 2 functions
// deprecated, backwards compatibility to older versions
const shim = require('@jacekpietal/bouncer.js/lib/shim')
- Test Suites: 5 passed, 5 total
- Tests: 17 passed, 17 total
https://circleci.com/gh/Prozi/bouncer.js
# to test run:
$ yarn test # automatic tests in jest
$ yarn start # manual test/example: chat
For the few users to have somewhat of a bridge between the socket-starter library that this library deprecates:
- see shim.js
- see shim.spec.js
If you do shim(plugin)
then your plugin may be in the format of:
{
initialize(io)
handshake(socket, data),
}
- Do what you want, fork, etc.
- I am not responsible for any problem this free application causes :P
- Have fun, please open any issues, etc.
- © 2020-2021 Jacek Pietal