├── .gitignore ├── client.js ├── connection.js ├── package.json └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /client.js: -------------------------------------------------------------------------------- 1 | const Automerge = require('automerge') 2 | const Connection = require('./connection') 3 | const net = require('net') 4 | const HOST = '127.0.0.1' 5 | const PORT = 9876 6 | const docSet = new Automerge.DocSet() 7 | 8 | // Print out the document whenever it changes 9 | docSet.registerHandler((docId, doc) => { 10 | console.log(`[${docId}] ${JSON.stringify(doc)}`) 11 | }) 12 | 13 | // Make a change to the document every 3 seconds 14 | setInterval(() => { 15 | let doc = docSet.getDoc('example') 16 | if (doc) { 17 | doc = Automerge.change(doc, doc => { 18 | doc.clientNum = (doc.clientNum || 0) + 1 19 | }) 20 | docSet.setDoc('example', doc) 21 | } 22 | }, 3000) 23 | 24 | const socket = new net.Socket() 25 | let connection 26 | 27 | // Connecting to a TCP port 28 | socket.connect(PORT, HOST, () => { 29 | console.log(`[${HOST}:${PORT}] connected`) 30 | connection = new Connection(docSet, socket) 31 | }) 32 | 33 | // Receiving data from the server 34 | socket.on('data', (data) => { 35 | if (!(data instanceof Buffer)) { 36 | data = Buffer.from(data, 'utf8') 37 | } 38 | connection.receiveData(data) 39 | }) 40 | 41 | socket.on('close', () => { 42 | console.log(`[${HOST}:${PORT}] connection closed`) 43 | }) 44 | 45 | socket.on('error', (err) => { 46 | console.log(`[${socket.remoteAddress}:${socket.remotePort}] error: ${err}`) 47 | }) 48 | -------------------------------------------------------------------------------- /connection.js: -------------------------------------------------------------------------------- 1 | const Automerge = require('automerge') 2 | 3 | class Connection { 4 | constructor (docSet, socket) { 5 | this.automerge = new Automerge.Connection(docSet, msg => this.sendMsg(msg)) 6 | this.socket = socket 7 | this.buffer = Buffer.alloc(0) 8 | this.automerge.open() 9 | } 10 | 11 | receiveData (data) { 12 | this.buffer = Buffer.concat([this.buffer, data]) 13 | 14 | // If there is enough data in the buffer, decode it into messages 15 | while (true) { 16 | if (this.buffer.length < 4) break 17 | 18 | const msglen = this.buffer.readInt32BE(0) 19 | if (this.buffer.length < msglen + 4) break 20 | 21 | const msg = JSON.parse(this.buffer.toString('utf8', 4, msglen + 4)) 22 | this.buffer = this.buffer.slice(msglen + 4) 23 | //console.log('Received:', msg) 24 | this.automerge.receiveMsg(msg) 25 | } 26 | } 27 | 28 | sendMsg (msg) { 29 | if (!this.socket) return 30 | //console.log('Sending:', msg) 31 | const data = Buffer.from(JSON.stringify(msg), 'utf8') 32 | const header = Buffer.alloc(4) 33 | header.writeInt32BE(data.length, 0) 34 | this.socket.write(header) 35 | this.socket.write(data) 36 | } 37 | 38 | close () { 39 | if (!this.socket) return 40 | this.socket.end() 41 | this.socket = null 42 | } 43 | } 44 | 45 | module.exports = Connection 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "automerge": "0.9.1" 4 | }, 5 | "devDependencies": { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const Automerge = require('automerge') 2 | const Connection = require('./connection') 3 | const net = require('net') 4 | const HOST = '0.0.0.0' 5 | const PORT = 9876 6 | 7 | // Initialise an example document 8 | const docSet = new Automerge.DocSet() 9 | const initDoc = Automerge.change(Automerge.init(), doc => doc.hello = 'Hi!') 10 | docSet.setDoc('example', initDoc) 11 | 12 | // Print out the document whenever it changes 13 | docSet.registerHandler((docId, doc) => { 14 | console.log(`[${docId}] ${JSON.stringify(doc)}`) 15 | }) 16 | 17 | // Make a change to the document every 3 seconds 18 | setInterval(() => { 19 | let doc = docSet.getDoc('example') 20 | doc = Automerge.change(doc, doc => { 21 | doc.serverNum = (doc.serverNum || 0) + 1 22 | }) 23 | docSet.setDoc('example', doc) 24 | }, 3000) 25 | 26 | // This function is called every time a client connects to the server socket 27 | function handler(socket) { 28 | console.log(`[${socket.remoteAddress}:${socket.remotePort}] connected`) 29 | const connection = new Connection(docSet, socket) 30 | 31 | // Receiving data from the client 32 | socket.on('data', (data) => { 33 | if (!(data instanceof Buffer)) { 34 | data = Buffer.from(data, 'utf8') 35 | } 36 | connection.receiveData(data) 37 | }) 38 | 39 | socket.on('close', (data) => { 40 | console.log(`[${socket.remoteAddress}:${socket.remotePort}] connection closed`) 41 | connection.close() 42 | }) 43 | 44 | socket.on('error', (err) => { 45 | console.log(`[${socket.remoteAddress}:${socket.remotePort}] error: ${err}`) 46 | }) 47 | } 48 | 49 | // Listen on a TCP port 50 | net.createServer(handler).listen(PORT, HOST) 51 | console.log(`Listening on ${HOST}:${PORT}`) 52 | --------------------------------------------------------------------------------