Package Exports
- mudb
- mudb/client
- mudb/server
- mudb/socket
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 (mudb) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
mudb
A database for real-time server-client applications.
A mudb application has one MuServer and several MuClients. Each node consists of one or more protocols which define different behaviors.
A protocol is essentially a collection of event handlers. To define a protocol, typically you need to specify
- a protocol schema
- a server protocol
- a client protocol
example
Here is a minimal chat room demo, heavily documented to show how to define a protocol using mudb.
schema.js
The first step of creating any applications with mudb is to specify a protocol schema using muschema, defining the data structures of different messages that all parties agree on.
var muschema = require('muschema')
var MuStruct = muschema.MuStruct
var MuString = muschema.MuString
// define a protocol schema, which always contains a `server` and a `client` property
exports.ChatSchema = {
// define data structures of messages sent to client
client: {
// each `chat` message contains a record with
// a `name` property of string type
// a `text` property of string type
// both should always be present
chat: new MuStruct({
name: new MuString(),
text: new MuString(),
}),
},
// define data structures of messages sent to server
server: {
// each `say` message contains a string
say: new MuString(),
},
}server.js
function randomName () {
return Math.random().toString(36).substr(2, 11)
}
// `server` should be a `MuServer`
module.exports = function (server) {
// create a protocol and add it to `server`
var serverProtocol = server.protocol(require('./schema').ChatSchema)
// a dictionary of clients' displayed names
var clientNames = {}
// configure protocol by specifying handlers
serverProtocol.configure({
// handlers for client messages
message: {
// whenever a client sends a `say` message (i.e. clientProtocol.server.message.say()),
// server should broadcast the content to everyone, along with sender's displayed name
say: function (client, content) {
// fist argument of every client message handler is a reference
// to client who sent the message
// broadcast sender's name and text content, both of which are strings contained in a record,
// as described in `ChatSchema.client.chat`, to all connected clients in a `chat` message
serverProtocol.broadcast.chat({
name: clientNames[client.sessionId],
text: content,
})
},
},
// when a client connects, server should inform everyone
connect: function (client) {
// set client's displayed name to a random string
clientNames[client.sessionId] = randomName()
// also broadcast a `chat` message to all connected clients
// except this time the message is from server
serverProtocol.broadcast.chat({
name: 'server',
text: clientNames[client.sessionId] + ' joined the channel',
})
},
// when a client disconnects, server should also inform everyone
disconnect: function (client) {
serverProtocol.broadcast.chat({
name: 'server',
text: clientNames[client.sessionId] + ' left',
})
// stop tracking client's displayed name
delete clientNames[client.sessionId]
},
})
// launch server after creating and configuring all protocols
server.start()
}client.js
// `client` should be a `MuClient`
module.exports = function (client) {
var messageDiv = document.createElement('div')
var textLabel = document.createElement('label')
var textInput = document.createElement('input')
// create a protocol and add it to `client`
var clientProtocol = client.protocol(require('./schema').ChatSchema)
messageDiv.style.overflow = 'auto'
messageDiv.style.width = '400px'
messageDiv.style.height = '300px'
textLabel.textContent = 'message: '
textInput.type = 'text'
textInput.style.width = '400px'
textInput.style.padding = '0px'
textInput.style.margin = '0px'
document.body.appendChild(messageDiv)
document.body.appendChild(document.createElement('br'))
document.body.appendChild(textLabel)
document.body.appendChild(textInput)
// configure protocol by specifying handlers
clientProtocol.configure({
// when client is ready to handle messages
ready: function () {
textInput.addEventListener('keydown', function (ev) {
// when pressing `enter` on the input box
if (ev.keyCode === 13) {
// send a `say` message to server
// the message being sent contains the text in the input box,
// a string, as described by `ChatSchema.server.say`
clientProtocol.server.message.say(textInput.value)
// empty input box
textInput.value = ''
}
})
},
// handlers for server messages
message: {
// whenever server sends a `chat` message (e.g. serverProtocol.broadcast.chat()),
chat: function (data) {
// which contains name of whoever said something and the content
var name = data.name
var text = data.text
// print name and content out in message box
var textNode = document.createTextNode(name + ": " + text)
messageDiv.appendChild(textNode)
messageDiv.appendChild(document.createElement('br'))
}
}
})
// open client after creating and configuring all protocols
client.start()
}To run the demo,
- cd into the directory containing the three files above
npm i mudomudo --socket websocket --open
table of contents
- 1 install
- 2 api
- 3 usage tips
- 4 helpful modules
- 5 more examples
- 6 TODO
1 install
npm i mudb muweb-socket mulocal-socket2 api
2.1 interfaces
Purely instructive types used to describe the API:
TableOf<T>:{ [messageName:string]:T } | {}ProtocolSchema:{ server:TableOf<AnyMuSchema>, client:TableOf<AnyMuSchema> }DispatchFn:(data, unreliable?:boolean) => undefinedSendRawFn:(data:Uint8Array|string, unreliable?:boolean) => undefinedClientMessageHandler:(client:MuRemoteClient, data, unreliable?:boolean) => undefinedServerMessageHandler:(data, unreliable:boolean) => undefined
To create a mudb application, a socket (i.e. a MuSocket instance) and a corresponding socket server (i.e. a MuSocketServer instance) are required. We try to make mudb extensible by allowing you to use a customized socket-server implementation.
A few socket modules are provided out of the box alongside mudb (e.g. muweb-socket). Make sure that you have checked those out before trying to create your own socket modules.
2.1.1 MuSocket
MuSockets are bidirectional sockets. They support both reliable, ordered streams and unreliable optimistic packet transfer. Any mudb sockets should implement the MuSocket interface as described below.
2.1.1.1 sessionId:string
Required property, a unique session id identifying the socket
2.1.1.2 open:boolean
Required property, a flag determining whether the socket is open
2.1.1.3 start(spec)
Required method, can be used to establish a connection to the server from the client side
spec:{ ready, message, close }ready()should be called when the connection is readymessage(data:Uint8Array|string, unreliable:boolean)should be called when receiving messagesclose(error?)should be called when the connection is closed
2.1.1.4 send(data:Uint8Array|string, unreliable?:boolean)
Required method, used to send messages to the other end of the connection
2.1.1.5 close()
Required method, used to close the connection
2.1.2 MuSocketServer
A MuSocketServer handles client communications coming through the corresponding MuSockets. Any mudb socket servers should implement the MuSocketServer interface as described below.
2.1.2.1 clients:MuSocket[]
Required property, server-side mocks of connected clients
2.1.2.2 open:boolean
Required property, a flag determining whether the socket server is running
2.1.2.3 start(spec)
Required method, used to launch the socket server
spec:{ ready, connection, close }ready()should be called when the socket server is launchedconnection(client:MuSocket)should be called when a client connectsclose(error?)should be called when the socket server is closed
2.1.2.4 close()
Required method, used to shut down the socket server
2.2 MuServer(socketServer:MuSocketServer)
var httpServer = require('http').createServer(/* ... */)
var socketServer = new require('muweb-socket').MuWebSocketServer(httpServer)
var muServer = new require('mudb').MuServer(socketServer) 2.2.1 protocol(schema:ProtocolSchema) : MuServerProtocol
Adds a server protocol, and returns it to be configured.
A MuServer can have multiple protocols. Note that you cannot add any new protocols after the server is started.
2.2.2 start(spec?)
Launches server.
spec:{ ready?, close? }ready(): called when the underlying socket server is launchedclose(error?): called when the underlying socket server is closed
2.2.3 destroy()
Shuts down the underlying socket server and terminates all clients. Useful when having multiple instances of mudb.
2.3 MuServerProtocol
2.3.1 broadcast:TableOf<DispatchFn>
An object of methods, each of which broadcasts to all connected clients. Each message (delta encoded) will be handled by a specific handler. For example, the message sent by protocol.broadcast.shower(shampoo, true) will be handled by the shower method on the corresponding client protocol, as shower(shampoo, true). So this is effectively dispatching method calls to a remote object.
broadcastRaw:SendRawFn
A method that broadcasts to all connected clients with "raw" (not processed, contrary to delta) messages. The messages will be handled by the raw message handler of the corresponding client protocol.
2.3.2 configure(spec)
Each protocol should be configured before the server is started and can be configured only once, by specifying the event handlers in spec.
spec:{ message, ready?, connect?, raw?, disconnect?, close? }message:TableOf<ClientMessageHandler>requiredready()called when the underlying socket server is launchedconnect(client:MuRemoteClient)called when a client connectsraw(client:MuRemoteClient, data:Uint8Array|string, unreliable:boolean)called when a "raw" message is receiveddisconnect(client:MuRemoteClient)called when a client disconnectsclose()called when the underlying socket server is closed
2.4 MuRemoteClient
A MuRemoteClient is the server-side representation of a client, used in the event handlers.
2.4.1 sessionId
A string representing a unique session id identifying the client.
2.4.2 message:TableOf<DispatchFn>
An object of methods, each of which sends messages (delta encoded) to the corresponding client.
2.4.3 sendRaw:SendRawFn
A method that sends "raw" messages to the corresponding client.
2.4.4 close()
Closes the reliable socket.
2.5 MuClient(socket:MuSocket)
var socket = new require('muweb-socket/socket').MuWebSocket(spec)
var muClient = new require('mudb').MuClient(socket) 2.5.1 protocol(schema:ProtocolSchema) : MuClientProtocol
Adds a client protocol, and returns it to be configured.
A MuClient can have multiple protocols. Note that you cannot add any new protocols after the client is started.
2.5.2 start(spec?)
Runs client.
spec:{ ready?, close? }ready(error?:string)called when the client is ready to handle messagesclose(error?:string)called when all sockets are closed
2.5.3 destroy()
Closes all sockets.
2.6 MuClientProtocol
2.6.1 server:MuRemoteServer
The client-side representation of the server.
2.6.2 configure(spec)
Each protocol should be configured before the client is started and can be configured only once, by specifying the event handlers in spec.
spec:{ message, ready?, raw?, close? }message:TableOf<ServerMessageHandler>requiredready()called when the client is ready to handle messagesraw(data:Uint8Array|string, unreliable:boolean)called when receiving a "raw" messageclose()called when all sockets are closed
2.7 MuRemoteServer
2.7.1 message:TableOf<DispatchFn>
An object of methods, each of which sends specific messages (delta encoded) to the server.
sendRaw:SendRawFn
A method that sends "raw" messages to the server.
3 usage tips
4 helpful modules
5 more examples
6 TODO
- more test cases
credits
Development supported by Shenzhen DianMao Digital Technology Co., Ltd.
Written in Shenzhen, China.
(c) 2017 Mikola Lysenko, Shenzhen DianMao Digital Technology Co., Ltd.