Skip to content

Presence 2 #288

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 105 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
9121baf
Update tested nodejs versions in .travis.yml
gkubisa Apr 18, 2018
4dbefd1
Add .editorconfig
gkubisa Oct 17, 2017
2ef8181
Update mocha
gkubisa Apr 23, 2018
6b687db
Fix Doc.prototype.destroy
gkubisa Apr 18, 2018
1489e36
Fix hasWritePending in op's callback
gkubisa Apr 24, 2018
a4499a5
Implement ephemeral "presence" data sync
gkubisa Apr 16, 2018
33c7264
Execute some callbacks asynchronously
gkubisa Apr 27, 2018
8ff4b33
Don't send presence unnecessarily
gkubisa Apr 30, 2018
0ff380d
Re-sync presence after re-subscribe and re-connect
gkubisa Apr 30, 2018
d67dd6a
Emit presence asynchronously
gkubisa May 1, 2018
e8ec215
Add `submitted` param to `presence` event
gkubisa May 9, 2018
9c291b2
Merge branch 'share/master' into sync-presence
gkubisa Jun 5, 2018
173bf3a
Use the correct variable
gkubisa Jun 13, 2018
054d34d
Small test update
gkubisa Jun 21, 2018
642ded6
Merge branch 'fix-doc-destroy' into sync-presence
gkubisa Jul 12, 2018
56b726b
Make hasPending depend on inflightPresence and pendingPresence
gkubisa Jul 12, 2018
762496a
Remove cached ops without using setTimeout
gkubisa Jul 10, 2018
e4c5e6d
Remove --exit mocha option
gkubisa Jul 20, 2018
428c46a
Workaround for circular dependency
gkubisa Jul 20, 2018
33450ae
Resolve merge conflicts
curran Apr 17, 2019
f43b752
Restore tests to working order
curran Apr 17, 2019
9409429
Remove extraneous .editorconfig
curran Apr 17, 2019
c4cf1b8
Revert extraneous changes in .travis.yml and package.json
curran Apr 17, 2019
237d2ad
Use lolex to make 'expires cached ops' test more stable.
curran Apr 17, 2019
c8d35c5
Move doc.presence to doc.presence.current
curran Apr 17, 2019
3efb82c
Move doc.receivedPresence to doc.presence.received
curran Apr 17, 2019
f0451e3
Move doc.requestReplyPresence to doc.presence.requestReply
curran Apr 17, 2019
5217635
Move doc.cachedOps to doc.presence.cachedOps
curran Apr 17, 2019
ac26dae
Move doc.inflightPresenceSeq to doc.presence.inflightSeq
curran Apr 17, 2019
48acccc
Move doc.inflightPresence to doc.presence.inflight
curran Apr 17, 2019
cab69fb
Move doc.pendingPresence to doc.presence.pending
curran Apr 17, 2019
6a0ecc4
Refactor presence fields into object declaration.
curran Apr 17, 2019
d41c961
Simplify object creation; 'change Object.create(null)' to '{}'.
curran Apr 17, 2019
fc351fa
Introduce enablePresence option. Closes #128
curran Apr 17, 2019
6cd16f3
Misc cleanup, finishing touches.
curran Apr 17, 2019
ad6a528
Split out presence methods into separate module
curran Apr 17, 2019
eaafc98
Move more presence-related logic into presence methods module.
curran Apr 17, 2019
7259f7e
Move presence methods such that they are passed into Backend
curran Apr 18, 2019
09f6415
Migrate hardRollbackPresence to presence instance
curran Apr 18, 2019
23a06c3
Migrate _initializePresence
curran Apr 18, 2019
824346f
Migrate _handlePresence
curran Apr 18, 2019
d6e3e3d
Migrate _processReceivedPresence
curran Apr 18, 2019
0382c03
Migrate processAllReceivedPresence
curran Apr 18, 2019
46d8a1b
Migrate _transformPresence
curran Apr 18, 2019
fc16be7
Migrate pausePresence
curran Apr 18, 2019
9cb5564
Migrate cacheOp
curran Apr 18, 2019
6461a79
Migrate flushPresence
curran Apr 18, 2019
2358022
Migrate transformAllPresence
curran Apr 18, 2019
41a4743
Migrate emitPresence
curran Apr 18, 2019
ba7d880
Migrate submitPresence
curran Apr 18, 2019
3ffd1ab
Migrate _setPresence
curran Apr 18, 2019
6114bad
Clean up intermediate migration steps
curran Apr 18, 2019
19446cd
Convert StatelessPresence to a class
curran Apr 18, 2019
0089f80
Convert StatelessPresence into idiomatic JS class.
curran Apr 18, 2019
2054057
Add test case that doc invokes presence.destroy inside doc.destroy
curran Apr 18, 2019
1a64a06
Introduce DummyPresence, use it by default
curran Apr 18, 2019
47193da
Remove if(this.presence) guards.
curran Apr 18, 2019
b23661c
Optimize cacheOp
curran Apr 18, 2019
521f77b
Split out getPendingPresence logic from hardRollbackPresence.
curran Apr 18, 2019
bdb6424
Clean up DummyPresence
curran Apr 18, 2019
0f3084a
Introduce Presence base class inherited by DummyPresence and Stateles…
curran Apr 18, 2019
41cd2ea
Add Presence base class module
curran Apr 19, 2019
986a695
Start disentangling presence logic from Agent
curran Apr 19, 2019
ac55884
Migrate Agent._createPresence
curran Apr 19, 2019
9187b34
Migrate subscribeToStream
curran Apr 19, 2019
7507731
Migrate _subscribeToQuery
curran Apr 19, 2019
1a1f52a
Migrate handlePresenceMessage
curran Apr 19, 2019
49ff5c2
Use only flushPresence(), not flush(), _handleSubscribe
curran Apr 19, 2019
87aa90b
Disentangle doc internals from flushPresence
curran Apr 19, 2019
2198e8e
Begin disentangling presence logic from connection.js
curran Apr 19, 2019
cf0168d
Decouple sendPresence
curran Apr 19, 2019
4c46a05
Move Presence class to presence.DocPresence
curran Apr 19, 2019
f0b7b97
Rename doc.presence to doc._docPresence
curran Apr 19, 2019
db39b69
Move doc._docPresence.current back to original API doc.presence.
curran Apr 19, 2019
60a567b
Split out implementation of ConnectionPresence.
curran Apr 19, 2019
8b6872e
Refactor ConnectionPresence to idiomatic JS class.
curran Apr 19, 2019
55e1a4d
Split out implementation of AgentPresence.
curran Apr 19, 2019
7a79d98
Refactor AgentPresence to idiomatic JS class.
curran Apr 19, 2019
693492d
Unify isPresenceMessage between ConnectionPresence and AgentPresence
curran Apr 19, 2019
538c3c1
Update README to document presence API
curran Apr 19, 2019
963affa
Iterate README
curran Apr 19, 2019
810175e
Minor cleanup
curran Apr 19, 2019
0724fd7
Migrate backend presence logic to decoupled BackendPresence class.
curran Apr 19, 2019
7af8427
Finishing touches
curran Apr 19, 2019
910b384
fix: repair broken link
severo May 2, 2019
ca4816f
Merge pull request #289 from severo/master
ericyhwang May 3, 2019
406d4e0
Update logger.js
qinyang912 May 9, 2019
7c729d7
fix: change the default logger
qinyang912 May 10, 2019
b0d4277
Merge pull request #290 from qinyang912/master
ericyhwang May 15, 2019
65ce131
1.0.0-beta.23
ericyhwang May 15, 2019
f09ad62
Update rest of examples to @teamwork/websocket-json-stream
ericyhwang May 15, 2019
1ff8798
Merge pull request #291 from share/examples-ws-update
nateps May 15, 2019
2fb0637
Fix broken status indication in textarea example
hamoid May 19, 2019
d210133
Merge pull request #292 from hamoid/patch-1
ericyhwang May 20, 2019
95ae394
Allow options to be passed for `fetch` and `getOps`
May 23, 2019
0b65164
Merge pull request #215 from alecgibson/getops-options
alecgibson Jul 4, 2019
1a7bc3e
Move from `jshint` to `eslint`
Jul 4, 2019
8a05619
Fix indentation linting
Jul 4, 2019
a551de3
Extend Google's ESLint config
Jul 15, 2019
3dfc938
Use `.gitignore` for defining ESLint's ignore pattern
Jul 15, 2019
df5a466
Reset `no-unused-vars` linting rule to default
Jul 15, 2019
40abc17
Review markups
Jul 15, 2019
9851465
Merge pull request #302 from share/eslint
nateps Jul 17, 2019
5cc30e6
Merge: Update branch 'presence-continuation-2' of https://github.com/…
ericyhwang Jul 17, 2019
14b0d31
eslint --fix, plus a bit of manual line wrapping to get under 120 chars
ericyhwang Jul 17, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// The ESLint ecmaVersion argument is inconsistently used. Some rules will ignore it entirely, so if the rule has
// been set, it will still error even if it's not applicable to that version number. Since Google sets these
// rules, we have to turn them off ourselves.
const DISABLED_ES6_OPTIONS = {
'no-var': 'off',
'prefer-rest-params': 'off'
};

const SHAREDB_RULES = {
// Comma dangle is not supported in ES3
'comma-dangle': ['error', 'never'],
// We control our own objects and prototypes, so no need for this check
'guard-for-in': 'off',
// Google prescribes different indents for different cases. Let's just use 2 spaces everywhere. Note that we have
// to override ESLint's default of 0 indents for this.
'indent': ['error', 2, {
'SwitchCase': 1
}],
// Less aggressive line length than Google, which is especially useful when we have a lot of callbacks in our code
'max-len': ['error',
{
code: 120,
tabWidth: 2,
ignoreUrls: true,
}
],
// Google overrides the default ESLint behaviour here, which is slightly better for catching erroneously unused variables
'no-unused-vars': ['error', {vars: 'all', args: 'after-used'}],
// It's more readable to ensure we only have one statement per line
'max-statements-per-line': ['error', {max: 1}],
// as-needed quote props are easier to write
'quote-props': ['error', 'as-needed'],
'require-jsdoc': 'off',
'valid-jsdoc': 'off'
};

module.exports = {
extends: 'google',
parserOptions: {
ecmaVersion: 3
},
rules: Object.assign(
{},
DISABLED_ES6_OPTIONS,
SHAREDB_RULES
),
};
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ coverage
# Dependency directories
node_modules
package-lock.json
yarn.lock
jspm_packages
18 changes: 0 additions & 18 deletions .jshintrc

This file was deleted.

2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ node_js:
- "10"
- "8"
- "6"
script: "npm run jshint && npm run test-cover"
script: "npm run lint && npm run test-cover"
# Send coverage data to Coveralls
after_script: "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js"
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ tracker](https://github.com/share/sharedb/issues).

- Realtime synchronization of any JSON document
- Concurrent multi-user collaboration
- Realtime synchronization of any ephemeral "presence" data
- Synchronous editing API with asynchronous eventual consistency
- Realtime query subscriptions
- Simple integration with any database - [MongoDB](https://github.com/share/sharedb-mongo), [PostgresQL](https://github.com/share/sharedb-postgres) (experimental)
Expand Down Expand Up @@ -73,6 +74,22 @@ initial data. Then you can submit editing operations on the document (using
OT). Finally you can delete the document with a delete operation. By
default, ShareDB stores all operations forever - nothing is truly deleted.

## User Presence Synchronization

ShareDB supports synchronization of user presence data such as cursor positions and text selections. This feature is opt-in, not enabled by default. To enable this feature, pass a presence implementation as the `presence` option to the ShareDB constructor.

ShareDB includes an implementation of presence called `StatelessPresence`. This provides an implementation of presence that works out of the box, but it has some scalability problems. Each time a client joins a document, this implementation requests current presence information from all other clients, via the server. This approach may be problematic in terms of performance when a large number of users are present on the same document simultaneously. If you don't expect too many simultaneous users per document, `StatelessPresence` should work well. The server does not store any state at all regarding presence (it exists only in clients), hence the name "Stateless Presence".

In `StatelessPresence`, presence data represents a user and is automatically synchronized between all clients subscribed to the same document. Its format is defined by the document's [OT Type](https://github.com/ottypes/docs) (specifically, by [`transformPresence`, `createPresence`, and `comparePresence`](https://github.com/teamwork/ot-docs#optional-properties)). All clients can modify their own presence data and receive a read-only version of other client's data. Presence data is automatically cleared when a client unsubscribes from the document or disconnects. It is also automatically transformed against applied operations, so that it still makes sense in the context of a modified document, for example a cursor position may be automatically advanced when a user types at the beginning of a text document.

To use `StatelessPresence`, pass it into the ShareDB constructor like this:

```js
var ShareDB = require('sharedb');
var statelessPresence = require('sharedb/lib/presence/stateless');
var share = new ShareDB({ presence: statelessPresence })`).
```

## Server API

### Initialization
Expand All @@ -91,6 +108,8 @@ __Options__
* `options.pubsub` _(instance of `ShareDB.PubSub`)_
Notify other ShareDB processes when data changes
through this pub/sub adapter. Defaults to `ShareDB.MemoryPubSub()`.
* `options.presence` _(implementation of presence classes)_
Enable user presence synchronization. The value of `options.presence` option is expected to contain implementations of the classes `DocPresence`, `ConnectionPresence`, `AgentPresence`, and `BackendPresence`. Logic related to presence is encapsulated within these classes, so it is possible develop additional third party presence implementations external to ShareDB.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this API. It feels atypical both of normal JS, and also of this library. I think I'd rather pass in a single presence object which exposes API methods, much like the DB adapters.


#### Database Adapters
* `ShareDB.MemoryDB`, backed by a non-persistent database with no queries
Expand Down Expand Up @@ -308,6 +327,9 @@ Unique document ID
`doc.data` _(Object)_
Document contents. Available after document is fetched or subscribed to.

`doc.presence` _(Object)_
Each property under `doc.presence` contains presence data shared by a client subscribed to this document. The property name is an empty string for this client's data and connection IDs for other clients' data. The structure of the presence object is defined by the OT type of the document (for example, in [ot-rich-text](https://github.com/Teamwork/ot-rich-text#presence) and [@datavis-tech/json0](https://github.com/datavis-tech/json0#presence)).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I agree with handing this off to the types, because:

  • I'm not sure it's strictly part of "OT", and it feels like a violation of Single Responsibility
  • This relies on us being able to update the type libraries (can we?)
  • There are potentially multiple desired implementations for a given type. Especially if you consider - for example - json0, which can sub-type other arbitrary types. What is a "generic" shape for this presence?

To me, it feels more natural for us to define some sort of separate presence transformers, probably being passed in to the presence constructor option?


`doc.fetch(function(err) {...})`
Populate the fields on `doc` with a snapshot of the document from the server.

Expand Down Expand Up @@ -337,6 +359,9 @@ An operation was applied to the data. `source` will be `false` for ops received
`doc.on('del', function(data, source) {...})`
The document was deleted. Document contents before deletion are passed in as an argument. `source` will be `false` for ops received from the server and defaults to `true` for ops generated locally.

`doc.on('presence', function(srcList, submitted) {...})`
Presence data has changed. `srcList` is an Array of `doc.presence` property names for which values have changed. `submitted` is `true`, if the event is the result of new presence data being submitted by the local or remote user, otherwise it is `false` - eg if the presence data was transformed against an operation or was cleared on unsubscribe, disconnect or roll-back.

`doc.on('error', function(err) {...})`
There was an error fetching the document or applying an operation.

Expand Down Expand Up @@ -370,6 +395,11 @@ Invokes the given callback function after

Note that `whenNothingPending` does NOT wait for pending `model.query()` calls.

`doc.submitPresence(presenceData[, function(err) {...}])`
Set local presence data and publish it for other clients.
`presenceData` structure depends on the document type.
Presence is synchronized only when subscribed to the document.

### Class: `ShareDB.Query`

`query.ready` _(Boolean)_
Expand Down Expand Up @@ -467,6 +497,10 @@ Additional fields may be added to the error object for debugging context dependi
* 4022 - Database adapter does not support queries
* 4023 - Cannot project snapshots of this type
* 4024 - Invalid version
* 4025 - Passing options to subscribe has not been implemented
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(NB: We'll need to change these to string error codes: #309)

* 4026 - Not subscribed to document
* 4027 - Presence data superseded
* 4028 - OT Type does not support presence

### 5000 - Internal error

Expand Down
2 changes: 1 addition & 1 deletion examples/counter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"dependencies": {
"express": "^4.14.0",
"sharedb": "^1.0.0-beta",
"websocket-json-stream": "^0.0.1",
"@teamwork/websocket-json-stream": "^2.0.0",
"ws": "^1.1.0"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions examples/counter/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ var http = require('http');
var express = require('express');
var ShareDB = require('sharedb');
var WebSocket = require('ws');
var WebSocketJSONStream = require('websocket-json-stream');
var WebSocketJSONStream = require('@teamwork/websocket-json-stream');

var backend = new ShareDB();
createDoc(startServer);
Expand All @@ -29,7 +29,7 @@ function startServer() {

// Connect any incoming WebSocket connection to ShareDB
var wss = new WebSocket.Server({server: server});
wss.on('connection', function(ws, req) {
wss.on('connection', function(ws) {
var stream = new WebSocketJSONStream(ws);
backend.listen(stream);
});
Expand Down
2 changes: 1 addition & 1 deletion examples/leaderboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

![Demo](demo.gif)

This is a port of [https://github.com/percolatestudio/react-leaderboard](Leaderboard) to
This is a port of [Leaderboard](https://github.com/percolatestudio/react-leaderboard) to
ShareDB.

In this demo, data is not persisted. To persist data, run a Mongo
Expand Down
2 changes: 1 addition & 1 deletion examples/leaderboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"sharedb-mingo-memory": "^1.0.0-beta",
"through2": "^2.0.1",
"underscore": "^1.8.3",
"websocket-json-stream": "^0.0.3",
"@teamwork/websocket-json-stream": "^2.0.0",
"ws": "^1.1.0"
},
"devDependencies": {
Expand Down
23 changes: 12 additions & 11 deletions examples/leaderboard/server/index.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
var http = require("http");
var ShareDB = require("sharedb");
var connect = require("connect");
var http = require('http');
var ShareDB = require('sharedb');
var connect = require('connect');
var serveStatic = require('serve-static');
var ShareDBMingoMemory = require('sharedb-mingo-memory');
var WebSocketJSONStream = require('websocket-json-stream');
var WebSocketJSONStream = require('@teamwork/websocket-json-stream');
var WebSocket = require('ws');
var util = require('util');

// Start ShareDB
var share = ShareDB({db: new ShareDBMingoMemory()});
var share = new ShareDB({db: new ShareDBMingoMemory()});

// Create a WebSocket server
var app = connect();
app.use(serveStatic('.'));
var server = http.createServer(app);
var wss = new WebSocket.Server({server: server});
server.listen(8080);
console.log("Listening on http://localhost:8080");
console.log('Listening on http://localhost:8080');

// Connect any incoming WebSocket connection with ShareDB
wss.on('connection', function(ws, req) {
wss.on('connection', function(ws) {
var stream = new WebSocketJSONStream(ws);
share.listen(stream);
});

// Create initial documents
var connection = share.connect();
connection.createFetchQuery('players', {}, {}, function(err, results) {
if (err) { throw err; }
if (err) {
throw err;
}

if (results.length === 0) {
var names = ["Ada Lovelace", "Grace Hopper", "Marie Curie",
"Carl Friedrich Gauss", "Nikola Tesla", "Claude Shannon"];
var names = ['Ada Lovelace', 'Grace Hopper', 'Marie Curie',
'Carl Friedrich Gauss', 'Nikola Tesla', 'Claude Shannon'];

names.forEach(function(name, index) {
var doc = connection.get('players', ''+index);
Expand Down
2 changes: 1 addition & 1 deletion examples/rich-text/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"quill": "^1.0.0-beta.11",
"rich-text": "^3.0.1",
"sharedb": "^1.0.0-beta",
"websocket-json-stream": "^0.0.1",
"@teamwork/websocket-json-stream": "^2.0.0",
"ws": "^1.1.0"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions examples/rich-text/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ var express = require('express');
var ShareDB = require('sharedb');
var richText = require('rich-text');
var WebSocket = require('ws');
var WebSocketJSONStream = require('websocket-json-stream');
var WebSocketJSONStream = require('@teamwork/websocket-json-stream');

ShareDB.types.register(richText.type);
var backend = new ShareDB();
Expand Down Expand Up @@ -32,7 +32,7 @@ function startServer() {

// Connect any incoming WebSocket connection to ShareDB
var wss = new WebSocket.Server({server: server});
wss.on('connection', function(ws, req) {
wss.on('connection', function(ws) {
var stream = new WebSocketJSONStream(ws);
backend.listen(stream);
});
Expand Down
26 changes: 13 additions & 13 deletions examples/textarea/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,35 @@ var sharedb = require('sharedb/lib/client');
var StringBinding = require('sharedb-string-binding');

// Open WebSocket connection to ShareDB server
const WebSocket = require('reconnecting-websocket');
var WebSocket = require('reconnecting-websocket');
var socket = new WebSocket('ws://' + window.location.host);
var connection = new sharedb.Connection(socket);

var element = document.querySelector('textarea');
var statusSpan = document.getElementById('status-span');
status.innerHTML = "Not Connected"
statusSpan.innerHTML = 'Not Connected';

element.style.backgroundColor = "gray";
socket.onopen = function(){
status.innerHTML = "Connected"
element.style.backgroundColor = "white";
element.style.backgroundColor = 'gray';
socket.onopen = function() {
statusSpan.innerHTML = 'Connected';
element.style.backgroundColor = 'white';
};

socket.onclose = function(){
status.innerHTML = "Closed"
element.style.backgroundColor = "gray";
socket.onclose = function() {
statusSpan.innerHTML = 'Closed';
element.style.backgroundColor = 'gray';
};

socket.onerror = function() {
status.innerHTML = "Error"
element.style.backgroundColor = "red";
}
statusSpan.innerHTML = 'Error';
element.style.backgroundColor = 'red';
};

// Create local Doc instance mapped to 'examples' collection document with id 'textarea'
var doc = connection.get('examples', 'textarea');
doc.subscribe(function(err) {
if (err) throw err;

var binding = new StringBinding(element, doc, ['content']);
binding.setup();
});
4 changes: 2 additions & 2 deletions examples/textarea/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function createDoc(callback) {
doc.fetch(function(err) {
if (err) throw err;
if (doc.type === null) {
doc.create({ content: '' }, callback);
doc.create({content: ''}, callback);
return;
}
callback();
Expand All @@ -29,7 +29,7 @@ function startServer() {

// Connect any incoming WebSocket connection to ShareDB
var wss = new WebSocket.Server({server: server});
wss.on('connection', function(ws, req) {
wss.on('connection', function(ws) {
var stream = new WebSocketJSONStream(ws);
backend.listen(stream);
});
Expand Down
Loading