Socket
Socket
Sign inDemoInstall

@evercoder/sharedb

Package Overview
Dependencies
131
Maintainers
5
Versions
4
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.1 to 1.0.2

lib/logger/index.js

9

lib/agent.js
var hat = require('hat');
var util = require('./util');
var types = require('./types');
var logger = require('./logger');

@@ -50,3 +51,3 @@ /**

if (err) {
console.warn('Agent closed due to error', this.clientId, err.stack || err);
logger.warn('Agent closed due to error', this.clientId, err.stack || err);
}

@@ -99,3 +100,3 @@ if (this.closed) return;

// direct request by the client
console.error('Doc subscription stream error', collection, id, data.error);
logger.error('Doc subscription stream error', collection, id, data.error);
return;

@@ -142,3 +143,3 @@ }

// direct request by the client
console.error('Query subscription stream error', collection, query, err);
logger.error('Query subscription stream error', collection, query, err);
};

@@ -201,3 +202,3 @@

if (err.stack) {
console.warn(err.stack);
logger.warn(err.stack);
}

@@ -204,0 +205,0 @@ request.error = {

@@ -8,2 +8,3 @@ var Doc = require('./doc');

var util = require('../util');
var logger = require('../logger');

@@ -120,7 +121,7 @@ function connectionState(socket) {

} catch (err) {
console.warn('Failed to parse message', event);
logger.warn('Failed to parse message', event);
return;
}
if (connection.debug) console.log('RECV', JSON.stringify(data));
if (connection.debug) logger.info('RECV', JSON.stringify(data));

@@ -257,3 +258,3 @@ var request = {data: data};

default:
console.warn('Ignoring unrecognized message', message);
logger.warn('Ignoring unrecognized message', message);
}

@@ -280,3 +281,3 @@ };

} else {
console.error('Invalid bulk message', message);
logger.error('Invalid bulk message', message);
}

@@ -433,3 +434,3 @@ };

Connection.prototype.send = function(message) {
if (this.debug) console.log('SEND', JSON.stringify(message));
if (this.debug) logger.info('SEND', JSON.stringify(message));

@@ -436,0 +437,0 @@ this.emit('send', message);

var emitter = require('../emitter');
var logger = require('../logger');
var ShareDBError = require('../error');

@@ -332,4 +333,7 @@ var types = require('../types');

this.version++;
this._otApply(message, false);
return;
try {
this._otApply(message, false);
} catch (error) {
return this._hardRollback(error);
}
};

@@ -518,4 +522,4 @@

if (!this.type) {
var err = new ShareDBError(4015, 'Cannot apply op to uncreated document. ' + this.collection + '.' + this.id);
return this.emit('error', err);
// Throw here, because all usage of _otApply should be wrapped with a try/catch
throw new ShareDBError(4015, 'Cannot apply op to uncreated document. ' + this.collection + '.' + this.id);
}

@@ -666,4 +670,8 @@

this._pushOp(op, callback);
this._otApply(op, source);
try {
this._pushOp(op, callback);
this._otApply(op, source);
} catch (error) {
return this._hardRollback(error);
}

@@ -845,3 +853,3 @@ // The call to flush is delayed so if submit() is called multiple times

// have sent all the ops that have happened before acknowledging our op
console.warn('Invalid version from server. Expected: ' + this.version + ' Received: ' + message.v, message);
logger.warn('Invalid version from server. Expected: ' + this.version + ' Received: ' + message.v, message);

@@ -880,3 +888,7 @@ // Fetching should get us back to a working document state

// by the server, the editor window should update to reflect the undo.
this._otApply(op, false);
try {
this._otApply(op, false);
} catch (error) {
return this._hardRollback(error);
}

@@ -891,5 +903,12 @@ this._clearInflightOp(err);

Doc.prototype._hardRollback = function(err) {
// Store pending ops so that we can notify their callbacks of the error.
// We combine the inflight op and the pending ops, because it's possible
// to hit a condition where we have no inflight op, but we do have pending
// ops. This can happen when an invalid op is submitted, which causes us
// to hard rollback before the pending op was flushed.
var pendingOps = [];
if (this.inflightOp) pendingOps.push(this.inflightOp);
pendingOps = pendingOps.concat(this.pendingOps);
// Cancel all pending ops and reset if we can't invert
var op = this.inflightOp;
var pending = this.pendingOps;
this._setType(null);

@@ -900,10 +919,15 @@ this.version = null;

// Fetch the latest from the server to get us back into a working state
// Fetch the latest version from the server to get us back into a working state
var doc = this;
this.fetch(function() {
var called = op && callEach(op.callbacks, err);
for (var i = 0; i < pending.length; i++) {
callEach(pending[i].callbacks, err);
// We want to check that no errors are swallowed, so we check that:
// - there are callbacks to call, and
// - that every single pending op called a callback
// If there are no ops queued, or one of them didn't handle the error,
// then we emit the error.
var allOpsHadCallbacks = !!pendingOps.length;
for (var i = 0; i < pendingOps.length; i++) {
allOpsHadCallbacks = callEach(pendingOps[i].callbacks, err) && allOpsHadCallbacks;
}
if (err && !called) return doc.emit('error', err);
if (err && !allOpsHadCallbacks) return doc.emit('error', err);
});

@@ -910,0 +934,0 @@ };

@@ -6,1 +6,2 @@ exports.Connection = require('./connection');

exports.types = require('../types');
exports.logger = require('../logger');

@@ -8,2 +8,3 @@ var Backend = require('./backend');

Backend.Error = require('./error');
Backend.logger = require('./logger');
Backend.MemoryDB = require('./db/memory');

@@ -10,0 +11,0 @@ Backend.MemoryMilestoneDB = require('./milestone-db/memory');

var Duplex = require('stream').Duplex;
var inherits = require('util').inherits;
var logger = require('./logger');
var util = require('./util');

@@ -39,3 +40,3 @@

this.on('error', function(error) {
console.warn('ShareDB client message stream error', error);
logger.warn('ShareDB client message stream error', error);
socket.close('stopped');

@@ -42,0 +43,0 @@ });

{
"name": "@evercoder/sharedb",
"version": "1.0.1",
"version": "1.0.2",
"description": "JSON OT database backend",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

# ShareDB
⚠️ __This is a fork of ShareDB published as:__
```bash
npm -i @evercoder/sharedb
```
---
[![NPM Version](https://img.shields.io/npm/v/sharedb.svg)](https://npmjs.org/package/sharedb)

@@ -45,3 +53,3 @@ [![Build Status](https://travis-ci.org/share/sharedb.svg?branch=master)](https://travis-ci.org/share/sharedb)

In the "textarea" example we show this off using a Reconnecting Websocket implementation from [https://github.com/pladaria/reconnecting-websocket](reconnecting-websocket).
In the "textarea" example we show this off using a Reconnecting Websocket implementation from [reconnecting-websocket](https://github.com/pladaria/reconnecting-websocket).

@@ -186,2 +194,23 @@

### Logging
By default, ShareDB logs to `console`. This can be overridden if you wish to silence logs, or to log to your own logging driver or alert service.
Methods can be overridden by passing a [`console`-like object](https://developer.mozilla.org/en-US/docs/Web/API/console) to `logger.setMethods`:
```javascript
var ShareDB = require('sharedb');
ShareDB.logger.setMethods({
info: () => {}, // Silence info
warn: () => alerts.warn(arguments), // Forward warnings to alerting service
error: () => alerts.critical(arguments) // Remap errors to critical alerts
});
```
ShareDB only supports the following logger methods:
- `info`
- `warn`
- `error`
### Shutdown

@@ -272,3 +301,3 @@

`doc.ingestSnapshot(snapshot, callback)`
Ingest snapshot data. This data must include a version, snapshot and type. This method is generally called interally as a result of fetch or subscribe and not directly. However, it may be called directly to pass data that was transferred to the client external to the client's ShareDB connection, such as snapshot data sent along with server rendering of a webpage.
Ingest snapshot data. The `snapshot` param must include the fields `v` (doc version), `data`, and `type` (OT type). This method is generally called interally as a result of fetch or subscribe and not directly from user code. However, it may still be called directly from user code to pass data that was transferred to the client external to the client's ShareDB connection, such as snapshot data sent along with server rendering of a webpage.

@@ -370,3 +399,24 @@ `doc.destroy()`

### Logging
By default, ShareDB logs to `console`. This can be overridden if you wish to silence logs, or to log to your own logging driver or alert service.
Methods can be overridden by passing a [`console`-like object](https://developer.mozilla.org/en-US/docs/Web/API/console) to `logger.setMethods`
```javascript
var ShareDB = require('sharedb/lib/client');
ShareDB.logger.setMethods({
info: () => {}, // Silence info
warn: () => alerts.warn(arguments), // Forward warnings to alerting service
error: () => alerts.critical(arguments) // Remap errors to critical alerts
});
```
ShareDB only supports the following logger methods:
- `info`
- `warn`
- `error`
## Error codes

@@ -373,0 +423,0 @@

var Backend = require('../../lib/backend');
var expect = require('expect.js');
var sinon = require('sinon');
var util = require('../util')

@@ -252,2 +253,131 @@ describe('client query subscribe', function() {

describe('submitting an invalid op', function () {
var doc;
var invalidOp;
var validOp;
beforeEach(function (done) {
// This op is invalid because we try to perform a list deletion
// on something that isn't a list
invalidOp = {p: ['name'], ld: 'Scooby'};
validOp = {p:['snacks'], oi: true};
doc = this.connection.get('dogs', 'scooby');
doc.create({ name: 'Scooby' }, function (error) {
if (error) return done(error);
doc.whenNothingPending(done);
});
});
it('returns an error to the submitOp callback', function (done) {
doc.submitOp(invalidOp, function (error) {
expect(error.message).to.equal('Referenced element not a list');
done();
});
});
it('rolls the doc back to a usable state', function (done) {
util.callInSeries([
function (next) {
doc.submitOp(invalidOp, function (error) {
expect(error).to.be.ok();
next();
});
},
function (next) {
doc.whenNothingPending(next);
},
function (next) {
expect(doc.data).to.eql({name: 'Scooby'});
doc.submitOp(validOp, next);
},
function (next) {
expect(doc.data).to.eql({name: 'Scooby', snacks: true});
next();
},
done
]);
});
it('rescues an irreversible op collision', function (done) {
// This test case attempts to reconstruct the following corner case, with
// two independent references to the same document. We submit two simultaneous, but
// incompatible operations (eg one of them changes the data structure the other op is
// attempting to manipulate).
//
// The second document to attempt to submit should have its op rejected, and its
// state successfully rolled back to a usable state.
var doc1 = this.backend.connect().get('dogs', 'snoopy');
var doc2 = this.backend.connect().get('dogs', 'snoopy');
var pauseSubmit = false;
var fireSubmit;
this.backend.use('submit', function (request, callback) {
if (pauseSubmit) {
fireSubmit = function () {
pauseSubmit = false;
callback();
};
} else {
fireSubmit = null;
callback();
}
});
util.callInSeries([
function (next) {
doc1.create({colours: ['white']}, next);
},
function (next) {
doc1.whenNothingPending(next);
},
function (next) {
doc2.fetch(next);
},
function (next) {
doc2.whenNothingPending(next);
},
// Both documents start off at the same v1 state, with colours as a list
function (next) {
expect(doc1.data).to.eql({colours: ['white']});
expect(doc2.data).to.eql({colours: ['white']});
next();
},
// doc1 successfully submits an op which changes our list into a string in v2
function (next) {
doc1.submitOp({p: ['colours'], oi: 'white,black'}, next);
},
// This next step is a little fiddly. We abuse the middleware to pause the op submission and
// ensure that we get this repeatable sequence of events:
// 1. doc2 is still on v1, where 'colours' is a list (but it's a string in v2)
// 2. doc2 submits an op that assumes 'colours' is still a list
// 3. doc2 fetches v2 before the op submission completes - 'colours' is no longer a list locally
// 4. doc2's op is rejected by the server, because 'colours' is not a list on the server
// 5. doc2 attempts to roll back the inflight op by turning a list insertion into a list deletion
// 6. doc2 applies this list deletion to a field that is no longer a list
// 7. type.apply throws, because this is an invalid op
function (next) {
pauseSubmit = true;
doc2.submitOp({p: ['colours', '0'], li: 'black'}, function (error) {
expect(error.message).to.equal('Referenced element not a list');
next();
});
doc2.fetch(function (error) {
if (error) return next(error);
fireSubmit();
});
},
// Validate that - despite the error in doc2.submitOp - doc2 has been returned to a
// workable state in v2
function (next) {
expect(doc1.data).to.eql({colours: 'white,black'});
expect(doc2.data).to.eql(doc1.data);
doc2.submitOp({p: ['colours'], oi: 'white,black,red'}, next);
},
done
]);
});
});
});

@@ -632,12 +632,7 @@ var async = require('async');

doc.pause();
var calledBack = false;
doc.on('error', function() {
expect(calledBack).equal(true);
done();
});
doc.submitOp({p: ['age'], na: 1}, function(err) {
expect(err).ok();
expect(err.code).to.equal(4017);
expect(doc.version).equal(2);
expect(doc.data).eql(undefined);
calledBack = true;
done();
});

@@ -662,12 +657,7 @@ doc.fetch();

doc.pause();
var calledBack = false;
doc.on('error', function() {
expect(calledBack).equal(true);
done();
});
doc.create({age: 9}, function(err) {
expect(err).ok();
expect(err.code).to.equal(4018);
expect(doc.version).equal(3);
expect(doc.data).eql({age: 5});
calledBack = true;
done();
});

@@ -674,0 +664,0 @@ doc.fetch();

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc