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
with chat
plugin and default options run:
$ yarn bouncer.js dist/your-app
port defaults to 4200
if process.env.PORT
not set
serve folder with plugin (chat)
port defaults to 4200
if process.env.PORT
not set
// require static files server
const serve = require('@jacekpietal/bouncer.js/server')
// require chat plugin
const chat = require('@jacekpietal/bouncer.js/plugins/chat')
// serve public folder with chat plugin debug and ssl
serve(
'dist/your-app',
{ chat },
{ // optional
debug: true, // optional
ssl: { // optional
key_file_name,
cert_file_name,
passphrase // optional
}
}
)
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
// concect to server at port 4200
const socket = new WebSocket('ws://localhost:4200')
let username,
messages = [],
message = ''
// on socket available to send
socket.onopen = (value) => {
// mandatory in this library
socket.send(JSON.stringify({ event: '/join', data: 'chat' }))
}
// on receive message from server
socket.onmessage = ({ data: string }) => {
const { id, event, data } = JSON.parse(string)
// first message is always join message
if (!username) {
// set own user id
username = id
}
// append to list of messages
messages.push(`<div>${id} > ${event} > ${data}</div>\n`)
}
// on demo form submit send message to server
function sendMessage(event) {
// dont send form anywhere :)
event.preventDefault()
const data = message.trim()
// dont send void data
if (!data) return
// after we get data value, empty chatbox
message = ''
// send message to socket
socket.send(JSON.stringify({ event: 'say', data }))
}
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: '~>',
// 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,
// for creating random unique socket id
idConfig: { lang: "english"|"japanese", len: 5 },
// 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: | 16 passed, 16 total |
Snapshots: | 0 total |
Time: | 1.303 s |
# to test run:
$ yarn test #
matic 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