Skip to content

Commit

Permalink
Merge branch 'share:master' into ci-sanity-check
Browse files Browse the repository at this point in the history
  • Loading branch information
curran committed Mar 29, 2023
2 parents dfb2fc2 + d21c26a commit 77eecf3
Show file tree
Hide file tree
Showing 26 changed files with 1,420 additions and 615 deletions.
8 changes: 6 additions & 2 deletions docs/middleware/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,13 @@ This action has these additional `context` properties:

> The query's projection [fields]({{ site.baseurl }}{% link api/backend.md %}#addprojection)
`channel` -- string
`channel` -- string (deprecated)

> The [Pub/Sub]({{ site.baseurl }}{% link adapters/pub-sub.md %}) channel the query will subscribe to. Defaults to its collection channel.
> This property is deprecated use `channels` instead. The [Pub/Sub]({{ site.baseurl }}{% link adapters/pub-sub.md %}) channels the query will subscribe to. Defaults to its collection channel.
`channels` -- string[]

> The [Pub/Sub]({{ site.baseurl }}{% link adapters/pub-sub.md %}) channels the query will subscribe to. Defaults to its collection channel.
`query` -- Object

Expand Down
18 changes: 13 additions & 5 deletions docs/middleware/op-submission.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,20 @@ Since `'submitRequestEnd'` is an event -- not a middleware hook -- it provides n

## Mutating ops

{: .warn :}
Mutating ops in middleware is generally a **bad idea**, and should be avoided.
Ops may be amended in the `apply` middleware using the special `request.$fixup()` method:

The main reason for avoiding op mutation is that the client who submitted the op will not be informed of the mutation, so the client's doc will never receive the mutation.
```js
backend.use('apply', (request, next) => {
let error;
try {
request.$fixup([{p: ['meta'], oi: {timestamp: Date.now()}}]);
} catch (e) {
error = e;
}

The general workaround is to trigger a second op submission, rather than mutate the provided op. This obviously has the downside of op submissions being unatomic, but is the safest way to get the original client to receive the update.
next(error);
});
```

{: .warn :}
When submitting ops from the middleware, set careful conditions under which you submit ops in order to avoid infinite op submission loops, where submitting an op recursively triggers infinite op submissions.
The `request.$fixup()` method may throw an error, which should be handled appropriately, usually by passing directly to the `next()` callback.
2 changes: 1 addition & 1 deletion docs/queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ backend.use('commit', (context, next) => {

backend.use('query', (context, next) => {
// Set our query to only listen for changes on our user-specific channel
context.channel = userChannel(context)
context.channels = [userChannel(context)]
next()
})

Expand Down
12 changes: 6 additions & 6 deletions examples/counter-json1/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
"license": "MIT",
"dependencies": {
"@teamwork/websocket-json-stream": "^2.0.0",
"express": "^4.14.0",
"ot-json1": "^1.0.0",
"reconnecting-websocket": "^4.2.0",
"sharedb": "^1.0.0-beta",
"ws": "^7.2.0"
"express": "^4.18.2",
"ot-json1": "^1.0.2",
"reconnecting-websocket": "^4.4.0",
"sharedb": "^3.3.0",
"ws": "^8.12.1"
},
"devDependencies": {
"browserify": "^16.5.0"
"browserify": "^17.0.0"
}
}
10 changes: 5 additions & 5 deletions examples/counter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
"license": "MIT",
"dependencies": {
"@teamwork/websocket-json-stream": "^2.0.0",
"express": "^4.14.0",
"reconnecting-websocket": "^4.2.0",
"sharedb": "^1.0.0-beta",
"ws": "^7.2.0"
"express": "^4.18.2",
"reconnecting-websocket": "^4.4.0",
"sharedb": "^3.3.0",
"ws": "^8.12.1"
},
"devDependencies": {
"browserify": "^16.5.0"
"browserify": "^17.0.0"
}
}
6 changes: 4 additions & 2 deletions examples/leaderboard/client/index.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
var Body = require('./Body.jsx');
var React = require('react');
var ReactDOM = require('react-dom');
var ReactDOM = require('react-dom/client');

ReactDOM.render(<Body />, document.querySelector('#main'));
var container = document.getElementById('main');
var root = ReactDOM.createRoot(container);
root.render(<Body />);
28 changes: 14 additions & 14 deletions examples/leaderboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "React Leaderboard backed by ShareDB",
"main": "server.js",
"scripts": {
"build": "browserify -t [ babelify --presets [ react ] ] client/index.jsx -o static/dist/bundle.js",
"build": "browserify -t [ babelify --presets [ @babel/react ] ] client/index.jsx -o static/dist/bundle.js",
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server/index.js"
},
Expand All @@ -16,20 +16,20 @@
"license": "MIT",
"dependencies": {
"@teamwork/websocket-json-stream": "^2.0.0",
"classnames": "^2.2.5",
"express": "^4.17.1",
"prop-types": "^15.7.2",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"reconnecting-websocket": "^4.2.0",
"sharedb": "^1.0.0-beta",
"sharedb-mingo-memory": "^1.0.0-beta",
"underscore": "^1.8.3",
"ws": "^7.2.0"
"classnames": "^2.3.2",
"express": "^4.18.2",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"reconnecting-websocket": "^4.4.0",
"sharedb": "^3.3.0",
"sharedb-mingo-memory": "^2.1.2",
"underscore": "^1.13.6",
"ws": "^8.12.1"
},
"devDependencies": {
"babel-preset-react": "^6.5.0",
"babelify": "^7.3.0",
"browserify": "^16.5.0"
"@babel/preset-react": "^7.18.6",
"babelify": "^10.0.0",
"browserify": "^17.0.0"
}
}
16 changes: 8 additions & 8 deletions examples/rich-text-presence/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@
"license": "MIT",
"dependencies": {
"@teamwork/websocket-json-stream": "^2.0.0",
"bson-objectid": "^1.3.0",
"express": "^4.17.1",
"bson-objectid": "^2.0.4",
"express": "^4.18.2",
"quill": "^1.3.7",
"quill-cursors": "^2.2.1",
"reconnecting-websocket": "^4.2.0",
"quill-cursors": "^4.0.2",
"reconnecting-websocket": "^4.4.0",
"rich-text": "^4.1.0",
"sharedb": "file:../../",
"tinycolor2": "^1.4.1",
"ws": "^7.2.0"
"sharedb": "^3.3.0",
"tinycolor2": "^1.6.0",
"ws": "^8.12.1"
},
"devDependencies": {
"browserify": "^16.5.0"
"browserify": "^17.0.0"
}
}
5 changes: 4 additions & 1 deletion examples/rich-text-presence/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ var WebSocket = require('ws');
var WebSocketJSONStream = require('@teamwork/websocket-json-stream');

ShareDB.types.register(richText.type);
var backend = new ShareDB({presence: true});
var backend = new ShareDB({
presence: true,
doNotForwardSendPresenceErrorsToClient: true
});
createDoc(startServer);

// Create initial document then fire callback
Expand Down
12 changes: 6 additions & 6 deletions examples/rich-text/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
"license": "MIT",
"dependencies": {
"@teamwork/websocket-json-stream": "^2.0.0",
"express": "^4.17.1",
"express": "^4.18.2",
"quill": "^1.3.7",
"reconnecting-websocket": "^4.2.0",
"rich-text": "^4.0.0",
"sharedb": "^1.0.0-beta",
"ws": "^7.2.0"
"reconnecting-websocket": "^4.4.0",
"rich-text": "^4.1.0",
"sharedb": "^3.3.0",
"ws": "^8.12.1"
},
"devDependencies": {
"browserify": "^16.5.0"
"browserify": "^17.0.0"
}
}
10 changes: 5 additions & 5 deletions examples/textarea/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
"license": "MIT",
"dependencies": {
"@teamwork/websocket-json-stream": "^2.0.0",
"express": "^4.17.1",
"reconnecting-websocket": "^4.2.0",
"sharedb": "^1.0.0-beta",
"express": "^4.18.2",
"reconnecting-websocket": "^4.4.0",
"sharedb": "^3.3.0",
"sharedb-string-binding": "^1.0.0",
"ws": "^7.2.0"
"ws": "^8.12.1"
},
"devDependencies": {
"browserify": "^16.5.0"
"browserify": "^17.0.0"
}
}
3 changes: 2 additions & 1 deletion lib/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -718,9 +718,10 @@ Agent.prototype._unsubscribeBulk = function(collection, ids, callback) {

Agent.prototype._submit = function(collection, id, op, callback) {
var agent = this;
this.backend.submit(this, collection, id, op, null, function(err, ops) {
this.backend.submit(this, collection, id, op, null, function(err, ops, request) {
// Message to acknowledge the op was successfully submitted
var ack = {src: op.src, seq: op.seq, v: op.v};
if (request._fixupOps.length) ack[ACTIONS.fixup] = request._fixupOps;
if (err) {
// Occasional 'Op already submitted' errors are expected to happen as
// part of normal operation, since inflight ops need to be resent after
Expand Down
51 changes: 44 additions & 7 deletions lib/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ Backend.prototype.submit = function(agent, index, id, op, options, originalCallb

var callback = function(error, ops) {
backend.emit('submitRequestEnd', error, request);
originalCallback(error, ops);
originalCallback(error, ops, request);
};

var err = ot.checkOp(op);
Expand Down Expand Up @@ -661,25 +661,62 @@ Backend.prototype.querySubscribe = function(agent, index, query, options, callba
'DB does not support subscribe'
));
}
backend.pubsub.subscribe(request.channel, function(err, stream) {
if (err) return callback(err);

var channels = request.channels;

if (request.channel) {
logger.warn(
'[DEPRECATED] "query" middleware\'s context.channel is deprecated, use context.channels instead. ' +
'Read more: https://share.github.io/sharedb/middleware/actions#query'
);
channels = [request.channel];
}

if (!channels || !channels.length) {
return callback(new ShareDBError(ERROR_CODE.ERR_QUERY_CHANNEL_MISSING, 'Required minimum one query channel.'));
}

var streams = [];

function destroyStreams() {
streams.forEach(function(stream) {
stream.destroy();
});
}

function createQueryEmitter() {
if (options.ids) {
var queryEmitter = new QueryEmitter(request, stream, options.ids);
var queryEmitter = new QueryEmitter(request, streams, options.ids);
backend.emit('timing', 'querySubscribe.reconnect', Date.now() - start, request);
callback(null, queryEmitter);
return;
}
// Issue query on db to get our initial results
backend._query(agent, request, function(err, snapshots, extra) {
if (err) {
stream.destroy();
destroyStreams();
return callback(err);
}
var ids = pluckIds(snapshots);
var queryEmitter = new QueryEmitter(request, stream, ids, extra);
var queryEmitter = new QueryEmitter(request, streams, ids, extra);
backend.emit('timing', 'querySubscribe.initial', Date.now() - start, request);
callback(null, queryEmitter, snapshots, extra);
});
}

channels.forEach(function(channel) {
backend.pubsub.subscribe(channel, function(err, stream) {
if (err) {
destroyStreams();
return callback(err);
}
streams.push(stream);

var subscribedToAllChannels = streams.length === channels.length;
if (subscribedToAllChannels) {
createQueryEmitter();
}
});
});
});
};
Expand All @@ -693,7 +730,7 @@ Backend.prototype._triggerQuery = function(agent, index, query, options, callbac
collection: collection,
projection: projection,
fields: fields,
channel: this.getCollectionChannel(collection),
channels: [this.getCollectionChannel(collection)],
query: query,
options: options,
db: null,
Expand Down
18 changes: 18 additions & 0 deletions lib/client/doc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var types = require('../types');
var util = require('../util');
var clone = util.clone;
var deepEqual = require('fast-deep-equal');
var ACTIONS = require('../message-actions').ACTIONS;

var ERROR_CODE = ShareDBError.CODES;

Expand Down Expand Up @@ -960,6 +961,23 @@ Doc.prototype._opAcknowledged = function(message) {
return this.fetch();
}

if (message[ACTIONS.fixup]) {
for (var i = 0; i < message[ACTIONS.fixup].length; i++) {
var fixupOp = message[ACTIONS.fixup][i];

for (var j = 0; j < this.pendingOps.length; j++) {
var transformErr = transformX(this.pendingOps[i], fixupOp);
if (transformErr) return this._hardRollback(transformErr);
}

try {
this._otApply(fixupOp, false);
} catch (error) {
return this._hardRollback(error);
}
}
}

// The op was committed successfully. Increment the version number
this.version++;

Expand Down
4 changes: 4 additions & 0 deletions lib/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ ShareDBError.prototype.name = 'ShareDBError';
ShareDBError.CODES = {
ERR_APPLY_OP_VERSION_DOES_NOT_MATCH_SNAPSHOT: 'ERR_APPLY_OP_VERSION_DOES_NOT_MATCH_SNAPSHOT',
ERR_APPLY_SNAPSHOT_NOT_PROVIDED: 'ERR_APPLY_SNAPSHOT_NOT_PROVIDED',
ERR_FIXUP_IS_ONLY_VALID_ON_APPLY: 'ERR_FIXUP_IS_ONLY_VALID_ON_APPLY',
ERR_CANNOT_FIXUP_DELETION: 'ERR_CANNOT_FIXUP_DELETION',
ERR_CLIENT_ID_BADLY_FORMED: 'ERR_CLIENT_ID_BADLY_FORMED',
ERR_CANNOT_PING_OFFLINE: 'ERR_CANNOT_PING_OFFLINE',
ERR_CONNECTION_SEQ_INTEGER_OVERFLOW: 'ERR_CONNECTION_SEQ_INTEGER_OVERFLOW',
Expand Down Expand Up @@ -45,6 +47,7 @@ ShareDBError.CODES = {
ERR_OT_OP_NOT_PROVIDED: 'ERR_OT_OP_NOT_PROVIDED',
ERR_PRESENCE_TRANSFORM_FAILED: 'ERR_PRESENCE_TRANSFORM_FAILED',
ERR_PROTOCOL_VERSION_NOT_SUPPORTED: 'ERR_PROTOCOL_VERSION_NOT_SUPPORTED',
ERR_QUERY_CHANNEL_MISSING: 'ERR_QUERY_CHANNEL_MISSING',
ERR_QUERY_EMITTER_LISTENER_NOT_ASSIGNED: 'ERR_QUERY_EMITTER_LISTENER_NOT_ASSIGNED',
/**
* A special error that a "readSnapshots" middleware implementation can use to indicate that it
Expand All @@ -67,6 +70,7 @@ ShareDBError.CODES = {
ERR_SNAPSHOT_READS_REJECTED: 'ERR_SNAPSHOT_READS_REJECTED',
ERR_SUBMIT_TRANSFORM_OPS_NOT_FOUND: 'ERR_SUBMIT_TRANSFORM_OPS_NOT_FOUND',
ERR_TYPE_CANNOT_BE_PROJECTED: 'ERR_TYPE_CANNOT_BE_PROJECTED',
ERR_TYPE_DOES_NOT_SUPPORT_COMPOSE: 'ERR_TYPE_DOES_NOT_SUPPORT_COMPOSE',
ERR_TYPE_DOES_NOT_SUPPORT_PRESENCE: 'ERR_TYPE_DOES_NOT_SUPPORT_PRESENCE',
ERR_UNKNOWN_ERROR: 'ERR_UNKNOWN_ERROR'
};
Expand Down
1 change: 1 addition & 0 deletions lib/message-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ exports.ACTIONS = {
bulkSubscribe: 'bs',
bulkUnsubscribe: 'bu',
fetch: 'f',
fixup: 'fixup',
subscribe: 's',
unsubscribe: 'u',
op: 'op',
Expand Down

0 comments on commit 77eecf3

Please sign in to comment.