Comparing version 2.2.1 to 2.2.2
@@ -117,2 +117,6 @@ var emitter = require('../emitter'); | ||
this.paused = false; | ||
// Internal counter that gets incremented every time doc.data is updated. | ||
// Used as a cheap way to check if doc.data has changed. | ||
this._dataStateVersion = 0; | ||
} | ||
@@ -160,3 +164,3 @@ emitter.mixin(Doc); | ||
// If we removed the type from the object, also remove its data | ||
this.data = undefined; | ||
this._setData(undefined); | ||
} else { | ||
@@ -168,2 +172,7 @@ var err = new ShareDBError(ERROR_CODE.ERR_DOC_TYPE_NOT_RECOGNIZED, 'Missing type ' + newType); | ||
Doc.prototype._setData = function(data) { | ||
this.data = data; | ||
this._dataStateVersion++; | ||
}; | ||
// Ingest snapshot data. This data must include a version, snapshot and type. | ||
@@ -224,5 +233,7 @@ // This is used both to ingest data that was exported with a webpage and data | ||
this._setType(type); | ||
this.data = (this.type && this.type.deserialize) ? | ||
this.type.deserialize(snapshot.data) : | ||
snapshot.data; | ||
this._setData( | ||
(this.type && this.type.deserialize) ? | ||
this.type.deserialize(snapshot.data) : | ||
snapshot.data | ||
); | ||
this.emit('load'); | ||
@@ -629,3 +640,3 @@ callback && callback(); | ||
this.emit('before op', componentOp.op, source, op.src); | ||
this.data = this.type.apply(this.data, componentOp.op); | ||
this._setData(this.type.apply(this.data, componentOp.op)); | ||
this.emit('op', componentOp.op, source, op.src); | ||
@@ -643,3 +654,3 @@ } | ||
// Apply the operation to the local data, mutating it in place | ||
this.data = this.type.apply(this.data, op.op); | ||
this._setData(this.type.apply(this.data, op.op)); | ||
// Emit an 'op' event once the local data includes the changes from the | ||
@@ -657,7 +668,11 @@ // op. For locally submitted ops, this will be synchronously with | ||
this._setType(op.create.type); | ||
this.data = (this.type.deserialize) ? | ||
(this.type.createDeserialized) ? | ||
this.type.createDeserialized(op.create.data) : | ||
this.type.deserialize(this.type.create(op.create.data)) : | ||
this.type.create(op.create.data); | ||
if (this.type.deserialize) { | ||
if (this.type.createDeserialized) { | ||
this._setData(this.type.createDeserialized(op.create.data)); | ||
} else { | ||
this._setData(this.type.deserialize(this.type.create(op.create.data))); | ||
} | ||
} else { | ||
this._setData(this.type.create(op.create.data)); | ||
} | ||
this.emit('create', source); | ||
@@ -664,0 +679,0 @@ return; |
@@ -15,2 +15,3 @@ var LocalPresence = require('./local-presence'); | ||
this._isSending = false; | ||
this._docDataVersionByPresenceVersion = {}; | ||
@@ -39,2 +40,5 @@ this._opHandler = this._transformAgainstOp.bind(this); | ||
// Record the current data state version to check if we need to transform | ||
// the presence later | ||
this._docDataVersionByPresenceVersion[this.presenceVersion] = this._doc._dataStateVersion; | ||
LocalPresence.prototype.submit.call(this, value, callback); | ||
@@ -68,2 +72,3 @@ }; | ||
presence._pendingMessages = []; | ||
presence._docDataVersionByPresenceVersion = {}; | ||
}); | ||
@@ -82,5 +87,14 @@ }; | ||
var presence = this; | ||
var docDataVersion = this._doc._dataStateVersion; | ||
this._pendingMessages.forEach(function(message) { | ||
// Check if the presence needs transforming against the op - this is to check against | ||
// edge cases where presence is submitted from an 'op' event | ||
var messageDocDataVersion = presence._docDataVersionByPresenceVersion[message.pv]; | ||
if (messageDocDataVersion >= docDataVersion) return; | ||
try { | ||
message.p = presence._transformPresence(message.p, op, source); | ||
// Ensure the presence's data version is kept consistent to deal with "deep" op | ||
// submissions | ||
presence._docDataVersionByPresenceVersion[message.pv] = docDataVersion; | ||
} catch (error) { | ||
@@ -110,2 +124,3 @@ var callback = presence._getCallback(message.pv); | ||
this._pendingMessages = []; | ||
this._docDataVersionByPresenceVersion = {}; | ||
}; | ||
@@ -112,0 +127,0 @@ |
{ | ||
"name": "sharedb", | ||
"version": "2.2.1", | ||
"version": "2.2.2", | ||
"description": "JSON OT database backend", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -1,3 +0,1 @@ | ||
_This README is for `sharedb@1.x`. For `sharedb@1.x-beta`, see [the 1.x-beta branch](https://github.com/share/sharedb/tree/1.x-beta). To upgrade, see [the upgrade guide](https://github.com/share/sharedb/wiki/Upgrading-to-sharedb@1.0.0-from-1.0.0-beta)._ | ||
# ShareDB | ||
@@ -4,0 +2,0 @@ |
@@ -982,2 +982,48 @@ var Backend = require('../../../lib/backend'); | ||
}); | ||
it('does not transform presence submitted in an op event when the presence was created late', function(done) { | ||
var localPresence1; | ||
doc1.on('op', function() { | ||
localPresence1.submit({index: 7}); | ||
}); | ||
localPresence1 = presence1.create('presence-1'); | ||
async.series([ | ||
presence2.subscribe.bind(presence2), | ||
function(next) { | ||
doc1.submitOp({index: 5, value: 'ern'}); | ||
presence2.on('receive', function(id, value) { | ||
expect(value).to.eql({index: 7}); | ||
next(); | ||
}); | ||
} | ||
], done); | ||
}); | ||
it('does transform late-created presence submitted in an op event by a deep second op', function(done) { | ||
var localPresence1; | ||
var submitted = false; | ||
doc1.on('op', function() { | ||
if (submitted) return; | ||
submitted = true; | ||
localPresence1.submit({index: 7}); | ||
doc1.submitOp({index: 5, value: 'akg'}); | ||
}); | ||
localPresence1 = presence1.create('presence-1'); | ||
async.series([ | ||
presence2.subscribe.bind(presence2), | ||
function(next) { | ||
doc1.submitOp({index: 5, value: 'ern'}); | ||
presence2.on('receive', function(id, value) { | ||
expect(value).to.eql({index: 10}); | ||
next(); | ||
}); | ||
} | ||
], done); | ||
}); | ||
}); |
589034
15632
64