Skip to content

WIP Presence continuation #3

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

Open
wants to merge 225 commits into
base: sync-presence
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
225 commits
Select commit Hold shift + click to select a range
78306cc
adding reconnecting websocket example in text area and updating readme
mbalex99 Jan 26, 2017
e1f7ab0
Updating Readme
mbalex99 Jan 26, 2017
24cae9c
Add note about projectsSnapshots property
zmillman Feb 23, 2018
927b4eb
Allow snapshot and op to be a non-object
gkubisa Jun 25, 2018
90b46d6
Remove package-lock.json
gkubisa Jul 12, 2018
3569a63
Make whenNothingPending always async
gkubisa Jul 12, 2018
7c84621
Update tested nodejs versions
gkubisa Jul 12, 2018
762e05d
Merge pull request #138 from mbalex99/reconnectingwebsocket
nateps Jul 14, 2018
14e5180
Update mocha and fix 2 tests
gkubisa Jul 19, 2018
e45dcc9
Fix sharedb does not exist
gkubisa Jul 19, 2018
d5a03f3
Remove unused variable
gkubisa Jul 19, 2018
0518ab7
Fix flaky maxSubmitRetries test
Jul 19, 2018
032eb98
Work around circular sharedb and sharedb-mingo-memory dependency
Jul 19, 2018
e9ff681
Document race condition test fix
Jul 20, 2018
ec4e647
Warn about using deprecated features
gkubisa Jul 20, 2018
f157131
Add workaround for circular dependency
gkubisa Jul 20, 2018
01fa1b5
Merge pull request #196 from zmillman/patch-1
nateps Jul 23, 2018
6ba106b
Merge pull request #226 from Teamwork/update-mocha
nateps Jul 23, 2018
08eb790
Merge branch 'master' into fix-flaky-test
nateps Jul 23, 2018
632fad3
Merge pull request #227 from alecgibson/fix-flaky-test
nateps Jul 23, 2018
be3f161
Merge pull request #217 from Teamwork/fix-op-validation
nateps Jul 23, 2018
c26c79b
Merge branch 'master' into fix-doc-destroy
nateps Jul 23, 2018
d255048
Merge pull request #204 from Teamwork/fix-doc-destroy
nateps Jul 23, 2018
f36fc81
add package-lock.json to gitignore
nateps Jul 23, 2018
5f4bd90
Doc::destroy: fix call to `this` inside error case of async callback
nateps Jul 23, 2018
7913314
Remove sharedb-mingo-memory circular dependency
ericyhwang Jul 25, 2018
721a0ab
Merge pull request #230 from share/remove-mingo-dep
nateps Jul 25, 2018
39a95c8
Merge branch 'master' into warn-about-deprecated-features
nateps Aug 1, 2018
ac9fcd4
Merge pull request #228 from Teamwork/warn-about-deprecated-features
nateps Aug 1, 2018
08f3339
1.0.0-beta.10
ericyhwang Aug 1, 2018
f691f48
Add support for fetching a particular version of a snapshot
Jun 29, 2018
d29b26d
Only call `done` once
Jul 3, 2018
b7b3abe
Correctly parse epoch timestamp when getting a snapshot
Jul 4, 2018
0f22355
Replace Snapshot `deleted` flag with `type`
Jul 10, 2018
cf9bd25
Change snapshot version (sv) action to snapshot fetch (sf)
Jul 10, 2018
7cfc134
Create snapshots with `type.create`
Jul 10, 2018
c94ae65
Keep snapshot fetching op local to loop
Jul 10, 2018
c9141b0
Make `getSnapshot` version an optional parameter
Jul 10, 2018
5edd003
Update `getSnapshot` falsiness check to avoid empty strings, etc.
Jul 10, 2018
1c934a1
Store `getSnapshot` `typeof` check in a local variable
Jul 10, 2018
d25ee6e
Set SnapshotRequest `sent` flag to false if connection cannot send
Jul 10, 2018
dc2d022
Check if snapshot version is finite
Jul 10, 2018
4a90170
Error on getSnapshot with version -1
Jul 10, 2018
9640d73
Review markups
Jul 11, 2018
fff922c
Split `getSnapshot` into two methods
Jul 11, 2018
b030e27
Initialise empty snapshots with version 0, timestamp 0
Jul 12, 2018
ce64e8a
Remove pending and ready from Snapshot Request
Jul 12, 2018
dd83d07
Update getSnapshot JSDocs
Jul 12, 2018
c855933
Increment fetched snapshot version
Jul 12, 2018
afb7784
Update SnapshotRequest version variable names to match version
Jul 12, 2018
16d6eaa
Ensure snapshot fetch timestamp is finite
Jul 12, 2018
12e1bcc
Add Snapshot class
Jul 19, 2018
b57273d
Remove snapshot fetch at time
Jul 26, 2018
1ae5680
Review markups
Aug 2, 2018
0f3d526
Remove VS Code config
Aug 2, 2018
a39c141
Simplify reconnection test case
Aug 2, 2018
b4764ac
Update fetchSnapshot documentation
Aug 8, 2018
e80fe46
Review markups
Aug 9, 2018
8705abd
Merge pull request #220 from alecgibson/get-snapshot
ericyhwang Aug 13, 2018
be70a31
1.0.0-beta.11
ericyhwang Aug 13, 2018
1adbe74
Add Milestone Snapshots
Aug 14, 2018
8f72522
Move milestone snapshots into their own database adapter
Aug 16, 2018
10902b7
Allow milestone snapshot saving logic to be overridden in the middleware
Aug 16, 2018
e85d59e
Export milestone db classes
Aug 16, 2018
37c0797
Review markups
Aug 17, 2018
c95639b
Merge pull request #236 from reedsy/milestone-snapshots
ericyhwang Aug 17, 2018
0249c91
1.0.0-beta.12
ericyhwang Aug 17, 2018
c60d7a9
Expose `fetchSnapshot` requested version in `readSnapshots` middleware
Aug 20, 2018
c1f58a5
Close milestone database when calling `Backend.close`
Aug 23, 2018
a676d5e
Add a `NoOpMilestoneDB` implementation
Aug 29, 2018
f2b30f1
Add argument checking tests for MilestoneDB
Aug 29, 2018
cb94f33
Commonise valid version logic
Aug 30, 2018
f7dcb37
Improve test coverage for `SnapshotRequest.onConnectionStateChanged`
Aug 30, 2018
41f2395
Add a snapshot type to the `readSnapshots` middleware
Aug 31, 2018
11d982d
Rename Snapshot middleware "historical" flag to "byVersion"
Sep 5, 2018
76dc6de
Merge pull request #237 from share/fetch-snapshot-middleware
ericyhwang Sep 5, 2018
fb906ef
Merge pull request #242 from share/close-milestone-database
ericyhwang Sep 5, 2018
61bf178
Merge pull request #244 from share/null-milestone-db
ericyhwang Sep 5, 2018
c23ab1a
1.0.0-beta.13
ericyhwang Sep 5, 2018
70cb681
Document complicated snapshot request testing logic
Sep 6, 2018
e45010c
Merge pull request #245 from share/snapshot-request-test
ericyhwang Sep 6, 2018
82386db
1.0.0-beta.14
ericyhwang Sep 19, 2018
674e07f
Fix: correct a link of README
lwyj123 Oct 21, 2018
abffed5
Merge pull request #251 from lwyj123/patch-1
ericyhwang Oct 31, 2018
a28b7a3
Merge remote-tracking branch 'upstream/master' into fix-whenNothingPe…
gkubisa Nov 5, 2018
065ed9d
Remove some unnecessary code
gkubisa Nov 5, 2018
a67ad42
Clarify README for `doc.ingestSnapshot(snapshot, callback)`
ericyhwang Nov 6, 2018
002c4c2
Allow custom logger overrides
Nov 15, 2018
d41e034
Catch synchronous errors in `Doc._otApply`
Nov 15, 2018
1992b47
Move logger config out of `Backend` constructor
Nov 22, 2018
84a70af
Update hard rollback callback logic
Nov 22, 2018
4f81761
Merge pull request #258 from share/logger-override
ericyhwang Nov 23, 2018
b46cc6d
1.0.0-beta.15
ericyhwang Nov 23, 2018
70b8815
Merge pull request #259 from share/otapply-rollback
ericyhwang Nov 28, 2018
88bb142
1.0.0-beta.16
ericyhwang Nov 28, 2018
41c40f6
fix leak on duplicate subscription - #260
dcharbonnier Nov 30, 2018
713a8ff
Add test for re-subscribe memory leak
ericyhwang Dec 1, 2018
7827b91
Merge pull request #261 from dcharbonnier/sharedb/memory-leak
ericyhwang Dec 1, 2018
95c81b8
1.0.0-beta.17
ericyhwang Dec 1, 2018
4a7a178
Fetch snapshot by time
Nov 30, 2018
5dd070b
For readSnapshots middleware, add requestMethod and requestParams pro…
ericyhwang Jan 16, 2019
58e29e6
Code review changes for readSnapshots middleware
ericyhwang Jan 16, 2019
579b22c
Simplify `shouldBreak` calls for fetching snapshots by timestamp
Dec 19, 2018
8e46271
Move snapshot building function into `ot`
Jan 23, 2019
78f5e7f
Making it more clear to access the property of the document and assig…
Jan 24, 2019
494d1a5
Merge pull request #264 from urbanspr1nter/master
nateps Jan 30, 2019
f507ca1
Merge pull request #262 from share/snapshot-by-timestamp
ericyhwang Jan 30, 2019
7938f7f
1.0.0-beta.18
ericyhwang Jan 30, 2019
57c8598
Switch readSnapshots middleware context to use named class/constants
ericyhwang Feb 6, 2019
15a438f
Merge branch 'master' into readSnapshots-context
ericyhwang Feb 7, 2019
7e12683
Support new readSnapshots context info in fetchSnapshotByTimestamp
ericyhwang Feb 7, 2019
36e40e0
Update snapshot fetch documentation
Feb 14, 2019
66c7e00
Merge pull request #263 from share/readSnapshots-context
ericyhwang Feb 14, 2019
8c4f979
1.0.0-beta.19: For readSnapshots middleware, add request method and p…
ericyhwang Feb 14, 2019
57c1242
Revert "For readSnapshots middleware, add method and parameter proper…
ericyhwang Feb 15, 2019
9c33e41
Merge pull request #270 from share/revert-263-readSnapshots-context
ericyhwang Feb 15, 2019
cb79636
In Doc._clearInflightOp, clear inflightOp before calling callbacks
ericyhwang Feb 28, 2019
a077121
Merge pull request #273 from share/fix-272
ericyhwang Feb 28, 2019
02187ca
1.0.0-beta.20
ericyhwang Feb 28, 2019
31be02e
Update README.md
gkubisa Mar 5, 2019
17de0fd
Merge pull request #276 from Teamwork/teamwork-websocket-json-stream
ericyhwang Mar 7, 2019
1c4bb6d
Add new "reply" middleware action
ericyhwang Mar 27, 2019
46734e2
Fix jshint errors
ericyhwang Mar 27, 2019
ceef172
Remove error case from "reply" middleware
ericyhwang Mar 27, 2019
f5dbd37
Add test coverage for "reply" middleware producing error
ericyhwang Mar 27, 2019
a596911
Merge pull request #278 from share/reply-middleware
ericyhwang Mar 27, 2019
504a1f4
1.0.0-beta.21
ericyhwang Mar 27, 2019
8a47c18
Update middleware docs: add "reply" action, other tweaks
ericyhwang Mar 28, 2019
d130700
Merge pull request #268 from share/update-snapshot-docs
ericyhwang Mar 29, 2019
5394fa4
Merge pull request #279 from share/middleware-docs-reply
ericyhwang Mar 29, 2019
e6dd9b8
Use @teamwork/websocket-json-stream in textarea example
curran Apr 6, 2019
1ad5938
Clean up closed connections. Closes #282
curran Apr 9, 2019
186e2ab
Add test coverage for emitting 'close'.
curran Apr 9, 2019
ef236fa
Handle case of emitting both 'end' and 'close'. Closes #282
curran Apr 9, 2019
fe1b7c0
Merge branch 'master' into fix-whenNothingPending
ericyhwang Apr 10, 2019
588167c
Merge pull request #222 from Teamwork/fix-whenNothingPending
ericyhwang Apr 10, 2019
70ee4ea
Merge pull request #283 from curran/patch-12
ericyhwang Apr 10, 2019
abcea41
1.0.0-beta.22
ericyhwang Apr 12, 2019
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
e4c55d5
Merge pull request #281 from curran/update-example
ericyhwang 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
9 changes: 0 additions & 9 deletions .editorconfig

This file was deleted.

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
),
};
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
# Emacs
\#*\#

# VS Code
.vscode/

# Logs
logs
*.log
Expand All @@ -30,4 +33,6 @@ 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: "ln -s .. node_modules/sharedb; 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"
166 changes: 146 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,26 @@ tracker](https://github.com/share/sharedb/issues).
- Projections to select desired fields from documents and operations
- Middleware for implementing access control and custom extensions
- Ideal for use in browsers or on the server
- Reconnection of document and query subscriptions
- Offline change syncing upon reconnection
- In-memory implementations of database and pub/sub for unit testing

### Reconnection

**TLDR**
```javascript
const WebSocket = require('reconnecting-websocket');
var socket = new WebSocket('ws://' + window.location.host);
var connection = new sharedb.Connection(socket);
```

The native Websocket object that you feed to ShareDB's `Connection` constructor **does not** handle reconnections.

The easiest way is to give it a WebSocket object that does reconnect. There are plenty of example on the web. The most important thing is that the custom reconnecting websocket, must have the same API as the native rfc6455 version.

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



## Example apps

[<img src="examples/counter/demo.gif" height="300">
Expand Down Expand Up @@ -58,9 +74,21 @@ 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
## 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.

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), for example it may contain a user ID and a cursor position in a text document. 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.
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

Expand All @@ -80,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.

#### Database Adapters
* `ShareDB.MemoryDB`, backed by a non-persistent database with no queries
Expand All @@ -101,7 +131,7 @@ Community Provided Pub/Sub Adapters
### Listening to WebSocket connections

```js
var WebSocketJSONStream = require('websocket-json-stream');
var WebSocketJSONStream = require('@teamwork/websocket-json-stream');

// 'ws' is a websocket server connection, as passed into
// new (require('ws').Server).on('connection', ...)
Expand All @@ -128,7 +158,6 @@ Register a new middleware.
One of:
* `'connect'`: A new client connected to the server.
* `'op'`: An operation was loaded from the database.
* `'doc'`: DEPRECATED: A snapshot was loaded from the database. Please use 'readSnapshots'
* `'readSnapshots'`: Snapshot(s) were loaded from the database for a fetch or subscribe of a query or document
* `'query'`: A query is about to be sent to the database
* `'submit'`: An operation is about to be submitted to the database
Expand All @@ -139,17 +168,24 @@ Register a new middleware.
* `'afterSubmit'`: An operation was successfully submitted to
the database.
* `'receive'`: Received a message from a client
* `fn` _(Function(request, callback))_
* `'reply'`: About to send a non-error reply to a client message
* `fn` _(Function(context, callback))_
Call this function at the time specified by `action`.
`request` contains a subset of the following properties, as relevant for the action:
* `action`: The action this middleware is handing
* `agent`: An object corresponding to the server agent handing this client
* `req`: The HTTP request being handled
* `collection`: The collection name being handled
* `id`: The document id being handled
* `snapshots`: The retrieved snapshots for the `readSnapshots` action
* `query`: The query object being handled
* `op`: The op being handled
* `context` will always have the following properties:
* `action`: The action this middleware is hanlding
* `agent`: A reference to the server agent handling this client
* `backend`: A reference to this ShareDB backend instance
* `context` can also have additional properties, as relevant for the action:
* `collection`: The collection name being handled
* `id`: The document id being handled
* `op`: The op being handled
* `req`: HTTP request being handled, if provided to `share.listen` (for 'connect')
* `stream`: The duplex Stream provided to `share.listen` (for 'connect')
* `query`: The query object being handled (for 'query')
* `snapshots`: Array of retrieved snapshots (for 'readSnapshots')
* `data`: Received client message (for 'receive')
* `request`: Client message being replied to (for 'reply')
* `reply`: Reply to be sent to the client (for 'reply')

### Projections

Expand All @@ -172,6 +208,27 @@ share.addProjection('users_limited', 'users', { name:true, profileUrl:true });

Note that only the [JSON0 OT type](https://github.com/ottypes/json0) is supported for projections.

### 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

`share.close(callback)`
Expand Down Expand Up @@ -217,6 +274,48 @@ changes. Returns a [`ShareDB.Query`](#class-sharedbquery) instance.
* `options.*`
All other options are passed through to the database adapter.

`connection.fetchSnapshot(collection, id, version, callback): void;`
Get a read-only snapshot of a document at the requested version.

* `collection` _(String)_
Collection name of the snapshot
* `id` _(String)_
ID of the snapshot
* `version` _(number) [optional]_
The version number of the desired snapshot. If `null`, the latest version is fetched.
* `callback` _(Function)_
Called with `(error, snapshot)`, where `snapshot` takes the following form:

```javascript
{
id: string; // ID of the snapshot
v: number; // version number of the snapshot
type: string; // the OT type of the snapshot, or null if it doesn't exist or is deleted
data: any; // the snapshot
}
```

`connection.fetchSnapshotByTimestamp(collection, id, timestamp, callback): void;`
Get a read-only snapshot of a document at the requested version.

* `collection` _(String)_
Collection name of the snapshot
* `id` _(String)_
ID of the snapshot
* `timestamp` _(number) [optional]_
The timestamp of the desired snapshot. The returned snapshot will be the latest snapshot before the provided timestamp. If `null`, the latest version is fetched.
* `callback` _(Function)_
Called with `(error, snapshot)`, where `snapshot` takes the following form:

```javascript
{
id: string; // ID of the snapshot
v: number; // version number of the snapshot
type: string; // the OT type of the snapshot, or null if it doesn't exist or is deleted
data: any; // the snapshot
}
```

### Class: `ShareDB.Doc`

`doc.type` _(String_)
Expand All @@ -229,7 +328,7 @@ Unique document ID
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.
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)).

`doc.fetch(function(err) {...})`
Populate the fields on `doc` with a snapshot of the document from the server.
Expand All @@ -239,7 +338,7 @@ Populate the fields on `doc` with a snapshot of the document from the server, an
fire events on subsequent changes.

`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.

`doc.destroy()`
Unsubscribe and stop firing events.
Expand Down Expand Up @@ -338,6 +437,27 @@ after a sequence of diffs are handled.
`query.on('extra', function() {...}))`
(Only fires on subscription queries) `query.extra` changed.

### 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

Expand Down Expand Up @@ -376,9 +496,11 @@ Additional fields may be added to the error object for debugging context dependi
* 4021 - Invalid client id
* 4022 - Database adapter does not support queries
* 4023 - Cannot project snapshots of this type
* 4024 - OT Type does not support presence
* 4025 - Not subscribed to document
* 4026 - Presence data superseded
* 4024 - Invalid version
* 4025 - Passing options to subscribe has not been implemented
* 4026 - Not subscribed to document
* 4027 - Presence data superseded
* 4028 - OT Type does not support presence

### 5000 - Internal error

Expand All @@ -402,3 +524,7 @@ The `41xx` and `51xx` codes are reserved for use by ShareDB DB adapters, and the
* 5016 - _unsubscribe PubSub method unimplemented
* 5017 - _publish PubSub method unimplemented
* 5018 - Required QueryEmitter listener not assigned
* 5019 - getMilestoneSnapshot MilestoneDB method unimplemented
* 5020 - saveMilestoneSnapshot MilestoneDB method unimplemented
* 5021 - getMilestoneSnapshotAtOrBeforeTime MilestoneDB method unimplemented
* 5022 - getMilestoneSnapshotAtOrAfterTime MilestoneDB method unimplemented
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
Loading