Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 45 additions & 0 deletions packages/automerge-client/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default class AutomergeClient {
this.socket = socket
this.save = save
this.docs = doLoad(savedData)
this.pausedDocs = []
this.onChange = onChange || (() => {})
this.subscribeList = []

Expand Down Expand Up @@ -114,6 +115,10 @@ export default class AutomergeClient {
return false
}
this.docs[id] = Automerge.change(this.docs[id], changer)
if(this.pausedDocs.includes(id)){
// trying to update the docset of a document on pause. Shouldn't propagate to server.
return true
}
if (this.docSet) {
this.docSet.setDoc(id, this.docs[id])
}
Expand All @@ -131,4 +136,44 @@ export default class AutomergeClient {
)
}
}

unsubscribe(ids) {
if (ids.length <= 0) return

this.subscribeList = this.subscribeList.filter((value,index) => {
return ids.indexOf(value) == -1
})

if (this.socket.readyState === 1) {
// OPEN
this.socket.send(
JSON.stringify({ action: 'unsubscribe', ids: ids.filter(unique) }),
)
}
}

pause(ids){
if(ids.length <= 0) return
this.pausedDocs = this.pausedDocs.concat(ids).filter(unique)
}

resume(ids){
if(ids.length <=0 ) return

//if the document is part of pausedDocs
this.pausedDocs = this.pausedDocs.filter((value,index) => {
return ids.indexOf(value) == -1
})

for(i=0; i<ids.length; i++){

if(!this.docSet.getDoc(ids[i])){
console.log("You cannot resume this document. It is not part of the docSet.")
return false
}
let currentDocument = ids[i]
this.docSet.setDoc(currentDocument, this.docs[currentDocument])
}

}
}
2 changes: 1 addition & 1 deletion packages/automerge-server-test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@
"ws": "^6.0.0"
},
"devDependencies": {
"nodemon": "^1.18.4"
"nodemon": "^2.0.2"
}
}
58 changes: 58 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Automerge Client-Server

This project builds on [Automerge](https://github.com/automerge/automerge) to provide server-centered synchronization features. This is especially useful when providing access control to users.

The project currently provides:

* loading and saving documents on server-side (via provided functions)
* It allows different clients to sync different documents
* Document-level access control (very under-tested)
* Offline-first client (will sync after reconnect using reconnecting-websocket)

## Client API

```
const client = new AutomergeClient({
// websocket
socket,
// string with data which were saved with save. like localStorage.getItem('automerge')
savedData,
// function which accepts string to save
save: data => ...,
// called when doc changes (locally or remotely)
onChange: (id, doc) => ...,
})
client.subscribe(['a', 'b']) // begins sync of a and b
client.change('a', doc => ...) // changes document a
```

The ```websocket``` can be any regular socket, however in the example projects a ```reconnecting-websocket``` is used.

Subscribing to a document will update the client ```docSet```, an automerge concept used to keep track of document synchronization with the server instance.

Once you subscribe to a document, the client will sync even after the instance has been closed. On re-opening, these documents will update to the latest version.

The library implements ```pause()``` and ```resume()``` functions in case you need to temporarily disable the automatic updates on your document.

## Server API

```
const server = new AutomergeServer({
// return promise of
// - false (failed to load)
// - or string (loaded saved doc)
// - or null (new document)
loadDocument: (id) => ...,
// text is what should be saved (and later returned from load).
// Doc contains automerge document (usefull for saving in other formats)
saveDocument: (id, text, doc) => ...,
// Returns promise of true (access granted) or false (access denied)
checkAccess: (id, req) => ...,
})
// in wss.on('connection')
server.handleSocket(ws, req)
```

The server is mainly an interface on saving automerge documents by serializing them, loading and providing access control.

The access control function needs to be implemented according to the project needs.