Comparing version 0.2.0 to 0.3.0
@@ -5,6 +5,10 @@ { | ||
"description": "OT managing library", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"dependencies": { | ||
"marcelklehr/browser-stream": "*" | ||
, "timoxley/next-tick": "*" | ||
}, | ||
"devDependencies": { | ||
"marcelklehr/ottypes": "*" | ||
}, | ||
"license": "MIT", | ||
@@ -11,0 +15,0 @@ "main": "index.js", |
@@ -80,2 +80,5 @@ var Link = require('./Link') | ||
link.on('link:requestInit', function() { | ||
if(null === this.content) { | ||
return // I don't know either! | ||
} | ||
link.send('init', {content: this.content, initialEdit: this.history.latest().pack()}) | ||
@@ -119,2 +122,7 @@ }.bind(this)) | ||
this.history.pushEdit(initialEdit) | ||
// I got an init, so my slaves get one, too | ||
this.slaves.forEach(function(slave) { | ||
slave.send('init', {content: this.content, initialEdit: this.history.latest().pack()}) | ||
}.bind(this)) | ||
} | ||
@@ -129,8 +137,9 @@ | ||
Document.prototype.receiveEdit = function(edit, fromLink) { | ||
edit = Edit.unpack(edit, this.ottype) | ||
if (!this.master || fromLink === this.master) { | ||
// Edit comes from master, or even better: we are master, yea baby! | ||
this.dispatchEdit(Edit.unpack(edit, this.ottype), fromLink) | ||
this.dispatchEdit(edit, fromLink) | ||
}else { | ||
// check with master first | ||
this.master.sendEdit(edit, function onack() { | ||
this.master.sendEdit(edit, function onack(err, edit) { | ||
this.dispatchEdit(edit, fromLink) | ||
@@ -183,2 +192,6 @@ }.bind(this)) | ||
// so we don't need to transform anything coming from master. | ||
// add to history + set id | ||
this.history.pushEdit(edit) | ||
return edit | ||
@@ -194,3 +207,3 @@ }else { | ||
// add to history | ||
// add to history + set id | ||
this.history.pushEdit(edit) | ||
@@ -217,5 +230,6 @@ | ||
if(link === fromLink) return | ||
if(link === this.master) return | ||
link.sendEdit(edit) | ||
}) | ||
}.bind(this)) | ||
} |
@@ -17,5 +17,3 @@ function Edit(ottype) { | ||
if(json.cs) { | ||
edit.changeset = ottype.deserialize? | ||
ottype.deserialize(json.cs) | ||
: JSON.parse(json.cs) | ||
edit.changeset = json.cs | ||
} | ||
@@ -31,3 +29,2 @@ edit.parent = json.parent | ||
var edit = new Edit(ottype) | ||
edit.id = randString() | ||
return edit | ||
@@ -41,3 +38,2 @@ } | ||
var edit = new Edit(ottype) | ||
edit.id = randString() | ||
edit.changeset = cs | ||
@@ -73,2 +69,6 @@ return edit | ||
Edit.prototype.merge = function(edit) { | ||
return Edit.newFromChangeset(this.ottype.compose(this.changeset, edit.changeset), this.ottype) | ||
} | ||
Edit.prototype.pack = function() { | ||
@@ -80,5 +80,3 @@ var o = { | ||
if(this.changeset) { | ||
o.cs = this.ottype.serialize? | ||
this.ottype.serialize(this.changeset) | ||
: JSON.stringify(this.changeset) | ||
o.cs = this.changeset | ||
} | ||
@@ -85,0 +83,0 @@ return JSON.stringify(o) |
@@ -37,4 +37,18 @@ var Document = require('./Document') | ||
this.master.sendEdit(edit, function onack() { // XXX: We could also merge into the queue | ||
this.applyEdit(edit) | ||
// Merge into the queue for increased collab speed | ||
if(this.master.queue.length == 1) { | ||
var parent = this.master.queue[0].parent | ||
, callback =this.master.queue[0].callback | ||
this.master.queue[0] = this.master.queue[0].merge(edit) | ||
this.master.queue[0].callback = callback | ||
this.master.queue[0].parent = parent | ||
return | ||
} | ||
this.master.sendEdit(edit, function onack(err, edit) { | ||
// Update queue | ||
this.master.queue.forEach(function(queuedEdit) { | ||
queuedEdit.parent = edit.id | ||
}) | ||
this.applyEdit(edit, true) | ||
//this.distributeEdit(edit) // Unnecessary round trip | ||
@@ -58,3 +72,3 @@ this.history.pushEdit(edit) | ||
incoming.transformAgainst(this.master.sentEdit) | ||
this.master.sentEdit.follow(incomingOriginal) | ||
this.master.sentEdit.follow(incomingOriginal) // Why!? | ||
} | ||
@@ -81,2 +95,5 @@ | ||
// add edit to history | ||
this.history.pushEdit(incoming) | ||
return incoming | ||
@@ -86,3 +103,3 @@ } | ||
// overrides Document#applyEdit | ||
EditableDocument.prototype.applyEdit = function(edit) { | ||
EditableDocument.prototype.applyEdit = function(edit, ownEdit) { | ||
// apply changes | ||
@@ -92,3 +109,3 @@ console.log('EditableDocument: apply edit', edit) | ||
this.content = edit.apply(this.content) | ||
this._change(this.content, edit.changeset) | ||
if(!ownEdit) this._change(this.content, edit.changeset) | ||
}catch(e) { | ||
@@ -95,0 +112,0 @@ throw new Error('Applying edit "'+edit.id+'" failed: '+e.message) |
@@ -18,3 +18,5 @@ // Stores revisions that are synced with the server | ||
History.prototype.pushEdit = function(edit) { | ||
if(this.remembers(edit.id)) return | ||
// Only Master Document may set ids | ||
if(!edit.id) edit.id = ++this.idCounter | ||
if(this.latest() && this.latest().id != edit.parent) throw new Error('This edit\'s parent is not the latest edit in history: '+JSON.stringify(edit), console.log(this.history)) | ||
@@ -28,2 +30,3 @@ this.history.push(edit.id) | ||
this.history = [] | ||
this.idCounter = 0 | ||
} | ||
@@ -30,0 +33,0 @@ |
var Duplex = require('stream').Duplex | ||
var nextTick = 'undefined' == typeof setImmediate? require('next-tick') : setImmediate | ||
/** | ||
@@ -14,3 +17,3 @@ * This is a Link | ||
this.on('error', function(er) { | ||
console.warn('Error in link', er.stack || er) | ||
console.warn('Error in link', 'undefined'==typeof process? er : er.stack || er) | ||
this.end() | ||
@@ -50,3 +53,3 @@ // i dunno what to do here... | ||
Link.prototype.sendEdit = function(edit, cb) { | ||
if(cb) this.callbacks[edit.id] = cb | ||
if(cb) edit.callback = cb | ||
@@ -58,3 +61,5 @@ if(this.queue.length || this.sentEdit) { | ||
this.sentEdit = edit | ||
this.send('edit', edit.pack()) | ||
nextTick(function() { | ||
this.send('edit', edit.pack()) | ||
}.bind(this)) | ||
} | ||
@@ -69,3 +74,3 @@ } | ||
this.sentEdit = this.queue.unshift() | ||
this.sentEdit = this.queue.shift() | ||
this.send('edit', this.sentEdit.pack()) | ||
@@ -80,10 +85,22 @@ } | ||
if(args[0] == 'ack') { | ||
var id = args[1] | ||
if(args[1] != this.sentEdit.id) return cb() // XXX: Well that'd be embarrassing, what do we do here? | ||
if(this.sentEdit && this.sentEdit.callback) { | ||
// Callback | ||
this.sentEdit.id = id | ||
// The nextTick shim for browsers doesn't seem to enforce the call order | ||
// (_read is called below and they must be in that order), so we call directly | ||
//nextTick(this.sentEdit.callback.bind(null, null, this.sentEdit)) | ||
try { | ||
this.sentEdit.callback(null, this.sentEdit) | ||
}catch(e) { | ||
this.emit('error', e) | ||
} | ||
delete this.sentEdit.callback | ||
} | ||
this.sentEdit = null | ||
this.sentEdit = null | ||
if(this.callbacks[args[1]]) { | ||
setImmediate(this.callbacks[args[1]]) // XXX: Use a shim here... | ||
delete this.callbacks[args[1]] | ||
} | ||
nextTick(function() { | ||
this._read(0) | ||
}.bind(this)) | ||
} | ||
@@ -90,0 +107,0 @@ |
{ | ||
"name": "gulf", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "Sync anything!", | ||
@@ -5,0 +5,0 @@ "repository": { |
# Gulf [![Build Status](https://travis-ci.org/marcelklehr/gulf.png)](https://travis-ci.org/marcelklehr/gulf) | ||
The Gulf stream will sync your documents. Anywhere in the world, in node.js and the browser! | ||
OT is too hard on you? The Gulf stream will sync your documents in real-time. Anywhere in the world, in node.js and the browser! | ||
![Gulf stream (Public domain)](https://upload.wikimedia.org/wikipedia/commons/1/19/Golfstrom.jpg) | ||
### How? | ||
You choose an [OT](https://en.wikipedia.org/wiki/Operational_transformation) [type](https://github.com/marcelklehr/gulf#operational-transform-bling) algorithm, gulf will sync your documents. | ||
## Show me! | ||
@@ -10,13 +16,7 @@ | ||
*/ | ||
var gulf = require('gulf') | ||
, net = require('net') | ||
, textOT = require('ottypes').text | ||
var textOT = require('ottypes').text | ||
// Create a new master document | ||
var doc = gulf.Document.create(textOT, 'abc') | ||
doc.content // 'abc' | ||
// Set up a server | ||
@@ -39,13 +39,7 @@ net.createServer(function(socket) { | ||
*/ | ||
var gulf = require('gulf') | ||
, net = require('net') | ||
, textOT = require('ottypes').text | ||
var textOT = require('ottypes').text | ||
// Create a new slave document (empty by default) | ||
var doc = new gulf.Document(textOT) | ||
doc.content // null -- it's really empty. | ||
// Connect to alice's server | ||
@@ -52,0 +46,0 @@ net.connect(7453, function(socket) { |
@@ -7,6 +7,7 @@ /* global xdescribe, describe, it, xit */ | ||
try { | ||
gulf = require('gulf') | ||
}catch(e) { | ||
console.log(e) | ||
gulf = require('../') | ||
expect = require('expect.js') | ||
}catch(e) { | ||
gulf = require('gulf') | ||
} | ||
@@ -35,3 +36,2 @@ | ||
var initialContent = 'abc' | ||
, cs = [3, 'd'] | ||
var docA = gulf.Document.create(ottype, initialContent) | ||
@@ -49,6 +49,6 @@ , docB = new gulf.EditableDocument(ottype) | ||
linkA.on('link:edit', console.log.bind(console, 'edit in linkA')) | ||
/*linkA.on('link:edit', console.log.bind(console, 'edit in linkA')) | ||
linkA.on('link:ack', console.log.bind(console, 'ack in linkA')) | ||
linkB.on('link:edit', console.log.bind(console, 'edit in linkB')) | ||
linkB.on('link:ack', console.log.bind(console, 'ack in linkB')) | ||
linkB.on('link:ack', console.log.bind(console, 'ack in linkB'))*/ | ||
@@ -69,3 +69,4 @@ it('should adopt the current document state correctly', function(done) { | ||
it('should replicate insertions across links', function(done) { | ||
docB.update(cs) | ||
content = 'abcd' // We mimick some edit->cs algo here | ||
docB.update([3, 'd']) // *bling* | ||
@@ -77,5 +78,19 @@ setTimeout(function() { | ||
done() | ||
}, 10) | ||
}, 100) | ||
}) | ||
it('should replicate multiple insertions across links', function(done) { | ||
content = 'abcdefg' // We mimick some edit->cs algo here | ||
docB.update([4, 'e']) // *bling* | ||
docB.update([5, 'f']) // *bling* | ||
docB.update([6, 'g']) // *bling* | ||
setTimeout(function() { | ||
console.log('DocB:', docB.content, 'DocA', docA.content) | ||
expect(docB.content).to.eql(docA.content) | ||
expect(content).to.eql(docA.content) | ||
done() | ||
}, 100) | ||
}) | ||
}) | ||
}) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
28985
14
0
603
0
154