Planet
Collaboratively edit JSON-style data in realtime using
Socket.IO by
synchronizing all operations on a planetary shared object.
Each operation will be received in exactly the same
order as they are incoming to the Planet server.
This includes the client that is emitting the operation.
This approach guarantees the exact same state on all clients
and has been proven to work reliably in other projects such as
netpd.
Planet is optimized to edit JSON style data and does not require
OT.
If you are looking for rich text editing have a look at
ShareJS.
Example
Server
var planet = require('planet'),
socket = require('socket.io').listen(8080);
planet(socket);
CLI
planet --host localhost --port 8080
Client
io.connect('//:8080')
.on('connect', function(){
this.emit('merge', {
'sugar': 1,
'milk': 0
});
this.on('set', function(key, value){
console.log(key, value);
});
this.emit('set', 'sugar', 2);
this.emit('set', 'milk', 100);
this.emit('get', function(data){
console.log(data);
});
});
Or run the example with:
node test/example
Operations
-
set - Sets a value at a specific location.
The value will be overwritten, not merged!
-
remove - Deletes a value at a specified location.
-
merge - Recursively merges data into the state.
-
delete - Deletes the state.
-
get - Asynchronously fetches values from the state,
optionally at a specified location. Returns the whole
state if no location is passed.
Terminology
-
operation - The custom events that is used to
modify the planetary shared object.
-
value - Can be of type string, number, object,
array, boolean or null.
-
location - Specifies a property of the shared object
by a key (string) or path (array).
-
path - A path is an array of strings or/and numbers
to specify a property in an object. Numbers refer to
element positions of arrays.
-
data - Refers always to an object.
-
state - The current content of the planet
that can be manipulated by the operations or
read with get.
Arrays
Arrays are not treated as objects and will not be merged
by merge operations. The elements of an array can be
set or fetched by get opernations. Removing single
elements from an array is not yet specified.
String
Single characters of a string value can be manipulated
with set or read with get.
Install
npm install planet
Include the Client
<script src="//localhost:8080/socket.io/socket.io.js"></script>
var io = require('socket.io-client');
Events
Planet Operations are fired as Socket.IO custom events.
The operations can be listened on both the server and the client
via on and once.
Event: set
client.on('set', function(location, value){ });
Event: remove
client.on('remove', function(location){ });
Event: merge
client.on('merge', function(data){ });
Event: delete
client.on('delete', function(){ });
Client API
Method: connect
var earth = io.connect('//:8004', options);
Method: disconnect
earth.disconnect();
Method: emit
Emits Planet operations.
Emit: set
earth.emit('set', 'bag', null);
earth.emit('set', 'bag', {'sugar': 20});
earth.emit('set', ['bag', 'eggs'], 12);
earth.emit('set', ['todo-list', 0], 'My first thing todo');
Emit: remove
earth.emit('remove', 'key');
earth.emit('remove', ['bag', 'eggs']);
Emit: merge
earth.emit('merge', {'bag': {'eggs': 6, 'milk': 100}});
earth.emit('merge', {'bag': {'sugar': 20}});
Emit: delete
earth.emit('delete');
Emit: get
earth.emit('get', function(data){ });
earth.emit('get', 'bag', function(value){ });
earth.emit('get', ['bag', 'eggs'], function(value){ });
earth.emit('get', ['todo-list', 0], function(value){ });
Server API
var Planet = require('planet'),
socket = require('socket.io').listen(8080, 'localhost');
Constructor: Planet
var earth = new Planet(socket, options);
The new keyword is optional.
Arguments
- Socket - Socket.IO socket server.
- Options (object) - the configuration object.
Options
limit - the maximum amount of concurrent client connections.
Defaults to 200.
Method: merge
earth.merge({'key': 'value'});
Method: set
earth.set('bag', {'sugar': 20});
earth.set(['bag', 'eggs'], 12);
earth.set(['todo-list', 0], 'My first thing todo');
Method: remove
earth.remove('key');
earth.remove(['bag', 'eggs']);
Method: delete
earth.delete();
Method: get
earth.get(function(data){ });
earth.get('bag', function(value){ });
earth.get(['bag', 'eggs'], function(value){ });
earth.get(['todo-list', 0], function(value){ });
CLI API
To run planet form a command-line interface install
Planet globally npm install planet -g
or cd bin && ./planet.
CLI Options
-
-p, --port [NUMBER] - The port to bind to (default: 8004).
-
-h, --host [STRING] - The host to connect to (default: 127.0.0.1).
-
-l, --limit [NUMBER] - Maximum concurrent client connections,
a number lower than your ulimit (default: 200).
-
--io.<configuration> -
Socket.IO configuration
for example:
--io.transports=websocket,htmlfile
--no-io.browser-client-cache
Note the dot notation and that dashes after --io. will
be replaced by whitespace to match Socket.IO configs.
-
-v, --version - Prints the current version.
-
--help - Shows this help message.
Tests
make test-server
make test-client
make test-browser
node test/server
Benchmarks
node test/benchmark
TODO
- Strict (option for disallowing auto-creation of setting keys at new location)
- Predefined model (option for predefining a data structure
and disalow merging/setting inexistent keys)
- Latency optimization
- Cleanup error messages
- Eventually provide a client side script for merge, get and set manipulation
Dependencies