Compare commits

...

17 Commits

Author SHA1 Message Date
Kevin Jahns
0600197977 v13.0.0-2 -- distribution files 2017-07-05 18:48:37 +02:00
Kevin Jahns
f4327529b9 13.0.0-2 2017-07-05 18:41:26 +02:00
Kevin Jahns
67189f4d44 dont lint in postversion 2017-07-05 18:40:41 +02:00
Kevin Jahns
6225fb4dfd fix linting of examples 2017-07-05 18:33:16 +02:00
Kevin Jahns
a7550fe5d3 13.0.0-1 2017-07-05 18:12:35 +02:00
Kevin Jahns
9d9c84f40e fit y-memory in helper.js 2017-07-05 18:10:24 +02:00
Kevin Jahns
ae91902de3 Yjs throws "error" event in unexpected cases. fixes #72 2017-07-05 17:58:19 +02:00
Kevin Jahns
033d24eee7 use y-memory@v8 directory structure 2017-07-05 17:44:17 +02:00
Kevin Jahns
8abef69aa7 implemented named event handler 2017-07-05 17:01:21 +02:00
Kevin Jahns
7e4dedab38 always use generateUniqueUserId. fixes #74 2017-07-05 11:40:19 +02:00
Kevin Jahns
85e488bbe6 Throw proper error stack when observer function thrown an error - implements #75 2017-07-05 11:37:22 +02:00
Kevin Jahns
a6a321da10 fix textarea example to fit new directory structure 2017-07-05 11:34:28 +02:00
Kevin Jahns
008764ccdc remove dist submodule 2017-07-05 11:26:20 +02:00
Kevin Jahns
de5f4abe32 filter deleted ops only if gc is disabled 2017-07-04 04:59:07 -07:00
Kevin Jahns
382d06f6d4 reworked getOperations (decrease size of sent operations, fixe some gc issues). garbageCollectOperation now sets origin to the direct left operation, if possible 2017-07-03 23:19:11 -07:00
Kevin Jahns
66de422749 fix issues with new master-slave tests 2017-06-30 15:18:07 -07:00
Kevin Jahns
bbf5e39408 implemented client-server model (untested) 2017-06-30 14:07:14 -07:00
21 changed files with 1436 additions and 218 deletions

4
.gitmodules vendored
View File

@@ -1,4 +0,0 @@
[submodule "dist"]
path = dist
url = https://github.com/y-js/yjs.git
branch = dist

1
dist

Submodule dist deleted from 42aa7ec5c9

View File

@@ -1,5 +1,4 @@
/* @flow */ /* global Y, chat */
/* global Y */
// initialize a shared object. This function call returns a promise! // initialize a shared object. This function call returns a promise!
Y({ Y({
@@ -17,10 +16,10 @@ Y({
}).then(function (y) { }).then(function (y) {
window.yChat = y window.yChat = y
// This functions inserts a message at the specified position in the DOM // This functions inserts a message at the specified position in the DOM
function appendMessage(message, position) { function appendMessage (message, position) {
var p = document.createElement('p') var p = document.createElement('p')
var uname = document.createElement('span') var uname = document.createElement('span')
uname.appendChild(document.createTextNode(message.username + ": ")) uname.appendChild(document.createTextNode(message.username + ': '))
p.appendChild(uname) p.appendChild(uname)
p.appendChild(document.createTextNode(message.message)) p.appendChild(document.createTextNode(message.message))
document.querySelector('#chat').insertBefore(p, chat.children[position] || null) document.querySelector('#chat').insertBefore(p, chat.children[position] || null)
@@ -28,23 +27,22 @@ Y({
// This function makes sure that only 7 messages exist in the chat history. // This function makes sure that only 7 messages exist in the chat history.
// The rest is deleted // The rest is deleted
function cleanupChat () { function cleanupChat () {
var len if (y.share.chat.length > 7) {
while ((len = y.share.chat.length) > 7) { y.share.chat.delete(0, y.chat.length - 7)
y.share.chat.delete(0)
} }
} }
// Insert the initial content // Insert the initial content
y.share.chat.toArray().forEach(appendMessage) y.share.chat.toArray().forEach(appendMessage)
cleanupChat() cleanupChat()
// whenever content changes, make sure to reflect the changes in the DOM // whenever content changes, make sure to reflect the changes in the DOM
y.share.chat.observe(function (event) { y.share.chat.observe(function (event) {
if (event.type === 'insert') { if (event.type === 'insert') {
for (var i = 0; i < event.length; i++) { for (let i = 0; i < event.length; i++) {
appendMessage(event.values[i], event.index + i) appendMessage(event.values[i], event.index + i)
} }
} else if (event.type === 'delete') { } else if (event.type === 'delete') {
for (var i = 0; i < event.length; i++) { for (let i = 0; i < event.length; i++) {
chat.children[event.index].remove() chat.children[event.index].remove()
} }
} }
@@ -54,8 +52,8 @@ Y({
document.querySelector('#chatform').onsubmit = function (event) { document.querySelector('#chatform').onsubmit = function (event) {
// the form is submitted // the form is submitted
var message = { var message = {
username: this.querySelector("[name=username]").value, username: this.querySelector('[name=username]').value,
message: this.querySelector("[name=message]").value message: this.querySelector('[name=message]').value
} }
if (message.username.length > 0 && message.message.length > 0) { if (message.username.length > 0 && message.message.length > 0) {
if (y.share.chat.length > 6) { if (y.share.chat.length > 6) {
@@ -66,10 +64,10 @@ Y({
// This will call the observe function (see line 40) // This will call the observe function (see line 40)
// and reflect the change in the DOM // and reflect the change in the DOM
y.share.chat.push([message]) y.share.chat.push([message])
this.querySelector("[name=message]").value = "" this.querySelector('[name=message]').value = ''
} }
// Do not send this form! // Do not send this form!
event.preventDefault() event.preventDefault()
return false return false
} }
}) })

View File

@@ -19,11 +19,16 @@ Y({
} }
}).then(function (y) { }).then(function (y) {
window.yJigsaw = y window.yJigsaw = y
var origin // mouse start position - translation of piece var origin // mouse start position - translation of piece
var drag = d3.behavior.drag() var drag = d3.behavior.drag()
.on('dragstart', function (params) { .on('dragstart', function (params) {
// get the translation of the element // get the translation of the element
var translation = d3.select(this).attr('transform').slice(10,-1).split(',').map(Number) var translation = d3
.select(this)
.attr('transform')
.slice(10, -1)
.split(',')
.map(Number)
// mouse coordinates // mouse coordinates
var mouse = d3.mouse(this.parentNode) var mouse = d3.mouse(this.parentNode)
origin = { origin = {
@@ -31,11 +36,11 @@ Y({
y: mouse[1] - translation[1] y: mouse[1] - translation[1]
} }
}) })
.on("drag", function(){ .on('drag', function () {
var mouse = d3.mouse(this.parentNode) var mouse = d3.mouse(this.parentNode)
var x = mouse[0] - origin.x // =^= mouse - mouse at dragstart + translation at dragstart var x = mouse[0] - origin.x // =^= mouse - mouse at dragstart + translation at dragstart
var y = mouse[1] - origin.y var y = mouse[1] - origin.y
d3.select(this).attr("transform", "translate(" + x + "," + y + ")") d3.select(this).attr('transform', 'translate(' + x + ',' + y + ')')
}) })
.on('dragend', function (piece, i) { .on('dragend', function (piece, i) {
// save the current translation of the puzzle piece // save the current translation of the puzzle piece
@@ -46,24 +51,24 @@ Y({
}) })
var data = [y.share.piece1, y.share.piece2, y.share.piece3, y.share.piece4] var data = [y.share.piece1, y.share.piece2, y.share.piece3, y.share.piece4]
var pieces = d3.select(document.querySelector("#puzzle-example")).selectAll("path").data(data) var pieces = d3.select(document.querySelector('#puzzle-example')).selectAll('path').data(data)
pieces pieces
.classed('draggable', true) .classed('draggable', true)
.attr("transform", function (piece) { .attr('transform', function (piece) {
var translation = piece.get('translation') || {x: 0, y: 0} var translation = piece.get('translation') || {x: 0, y: 0}
return "translate(" + translation.x + "," + translation.y + ")" return 'translate(' + translation.x + ',' + translation.y + ')'
}).call(drag) }).call(drag)
data.forEach(function(piece){ data.forEach(function (piece) {
piece.observe(function () { piece.observe(function () {
// whenever a property of a piece changes, update the translation of the pieces // whenever a property of a piece changes, update the translation of the pieces
pieces pieces
.transition() .transition()
.attr("transform", function (piece) { .attr('transform', function (piece) {
var translation = piece.get('translation') || {x: 0, y: 0} var translation = piece.get('translation') || {x: 0, y: 0}
return "translate(" + translation.x + "," + translation.y + ")" return 'translate(' + translation.x + ',' + translation.y + ')'
}) })
}) })
}) })
}) })

View File

@@ -1,8 +1,8 @@
/* global Y */ /* global Y, monaco */
require.config({ paths: { 'vs': '../node_modules/monaco-editor/min/vs' }}) require.config({ paths: { 'vs': '../node_modules/monaco-editor/min/vs' } })
require(['vs/editor/editor.main'], function() {
require(['vs/editor/editor.main'], function () {
// Initialize a shared object. This function call returns a promise! // Initialize a shared object. This function call returns a promise!
Y({ Y({
db: { db: {
@@ -28,4 +28,3 @@ require(['vs/editor/editor.main'], function() {
y.share.monaco.bindMonaco(editor) y.share.monaco.bindMonaco(editor)
}) })
}) })

1173
examples/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,5 +6,11 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"monaco-editor": "^0.8.3" "monaco-editor": "^0.8.3"
},
"devDependencies": {
"standard": "^10.0.2"
},
"standard": {
"ignore": ["bower_components"]
} }
} }

View File

@@ -29,11 +29,11 @@ Y({
[{ script: 'sub' }, { script: 'super' }], [{ script: 'sub' }, { script: 'super' }],
['link', 'image'], ['link', 'image'],
['link', 'code-block'], ['link', 'code-block'],
[{list: 'ordered' }] [{ list: 'ordered' }]
] ]
}, },
theme: 'snow' theme: 'snow'
}); })
// bind quill to richtext type // bind quill to richtext type
y.share.richtext.bind(window.quill) y.share.richtext.bind(window.quill)
}) })

View File

@@ -1,13 +1,13 @@
/* global Y, Quill */ /* global Y, Quill */
// register yjs service worker // register yjs service worker
if('serviceWorker' in navigator){ if ('serviceWorker' in navigator) {
// Register service worker // Register service worker
// it is important to copy yjs-sw-template to the root directory! // it is important to copy yjs-sw-template to the root directory!
navigator.serviceWorker.register('./yjs-sw-template.js').then(function(reg){ navigator.serviceWorker.register('./yjs-sw-template.js').then(function (reg) {
console.log("Yjs service worker registration succeeded. Scope is " + reg.scope); console.log('Yjs service worker registration succeeded. Scope is ' + reg.scope)
}).catch(function(err){ }).catch(function (err) {
console.error("Yjs service worker registration failed with error " + err); console.error('Yjs service worker registration failed with error ' + err)
}) })
} }
@@ -39,7 +39,7 @@ Y({
[{ script: 'sub' }, { script: 'super' }], [{ script: 'sub' }, { script: 'super' }],
['link', 'image'], ['link', 'image'],
['link', 'code-block'], ['link', 'code-block'],
[{list: 'ordered' }] [{ list: 'ordered' }]
] ]
}, },
theme: 'snow' theme: 'snow'

View File

@@ -2,7 +2,11 @@
<html> <html>
<body> <body>
<textarea style="width:80%;" rows=40 id="textfield" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea> <textarea style="width:80%;" rows=40 id="textfield" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
<script src="../bower_components/yjs/y.js"></script> <script src="../../y.js"></script>
<script src="../../../y-array/y-array.js"></script>
<script src="../../../y-text/dist/y-text.js"></script>
<script src="../../../y-memory/y-memory.js"></script>
<script src="../../../y-websockets-client/dist/y-websockets-client.js"></script>
<script src="./index.js"></script> <script src="./index.js"></script>
</body> </body>
</html> </html>

166
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "yjs", "name": "yjs",
"version": "13.0.0-0", "version": "13.0.0-2",
"lockfileVersion": 1, "lockfileVersion": 1,
"dependencies": { "dependencies": {
"acorn": { "acorn": {
@@ -149,15 +149,7 @@
"version": "6.25.0", "version": "6.25.0",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.25.0.tgz", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.25.0.tgz",
"integrity": "sha1-fdQrBGPHQunVKW3rPsZ6kyLa1yk=", "integrity": "sha1-fdQrBGPHQunVKW3rPsZ6kyLa1yk=",
"dev": true, "dev": true
"dependencies": {
"debug": {
"version": "2.6.8",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
"integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
"dev": true
}
}
}, },
"babel-generator": { "babel-generator": {
"version": "6.25.0", "version": "6.25.0",
@@ -487,15 +479,7 @@
"version": "6.25.0", "version": "6.25.0",
"resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz",
"integrity": "sha1-IldJfi/NGbie3BPEyROB+VEklvE=", "integrity": "sha1-IldJfi/NGbie3BPEyROB+VEklvE=",
"dev": true, "dev": true
"dependencies": {
"debug": {
"version": "2.6.8",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
"integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
"dev": true
}
}
}, },
"babel-types": { "babel-types": {
"version": "6.25.0", "version": "6.25.0",
@@ -504,9 +488,9 @@
"dev": true "dev": true
}, },
"babylon": { "babylon": {
"version": "6.17.3", "version": "6.17.4",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.3.tgz", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz",
"integrity": "sha512-mq0x3HCAGGmQyZXviOVe5TRsw37Ijy3D43jCqt/9WVf+onx2dUgW3PosnqCbScAFhRO9DGs8nxoMzU0iiosMqQ==", "integrity": "sha512-kChlV+0SXkjE0vUn9OZ7pBMWRFd8uq3mZe8x1K6jhuNcAFAtEnjchFAqB+dYEXKyd+JpT6eppRR78QAr5gTsUw==",
"dev": true "dev": true
}, },
"balanced-match": { "balanced-match": {
@@ -593,9 +577,9 @@
"dev": true "dev": true
}, },
"chance": { "chance": {
"version": "1.0.9", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/chance/-/chance-1.0.9.tgz", "resolved": "https://registry.npmjs.org/chance/-/chance-1.0.10.tgz",
"integrity": "sha1-ha5SwUXEM9afbx7/JPBWASt3pg8=", "integrity": "sha1-A1ALBK2U53jdKJGwnsc6ath7GZY=",
"dev": true "dev": true
}, },
"chokidar": { "chokidar": {
@@ -642,9 +626,9 @@
"dev": true "dev": true
}, },
"commander": { "commander": {
"version": "2.9.0", "version": "2.10.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.10.0.tgz",
"integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", "integrity": "sha512-q/r9trjmuikWDRJNTBHAVnWhuU6w+z80KgBq7j9YDclik5E7X4xi0KnlZBNFA1zOQ+SH/vHMWd2mC9QTOz7GpA==",
"dev": true "dev": true
}, },
"concat-map": { "concat-map": {
@@ -660,9 +644,9 @@
"dev": true "dev": true
}, },
"concurrently": { "concurrently": {
"version": "3.4.0", "version": "3.5.0",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.4.0.tgz", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.5.0.tgz",
"integrity": "sha1-YGYrPe/eBzdbrhmqwKt4DsdIunk=", "integrity": "sha1-jPG3cHppFqeKT/W3e7BN7FSzebI=",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"ansi-regex": { "ansi-regex": {
@@ -878,12 +862,6 @@
"integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"debug": {
"version": "2.6.8",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
"integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
"dev": true
},
"user-home": { "user-home": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz",
@@ -908,35 +886,13 @@
"version": "0.2.3", "version": "0.2.3",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz",
"integrity": "sha1-Wt2BBujJKNssuiMrzZ76hG49oWw=", "integrity": "sha1-Wt2BBujJKNssuiMrzZ76hG49oWw=",
"dev": true, "dev": true
"dependencies": {
"debug": {
"version": "2.6.8",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
"integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
"dev": true
}
}
}, },
"eslint-module-utils": { "eslint-module-utils": {
"version": "2.0.0", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.0.0.tgz", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz",
"integrity": "sha1-pvjCHZATWHWc3DXbrBmCrh7li84=", "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==",
"dev": true, "dev": true
"dependencies": {
"debug": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
"integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
"dev": true
},
"ms": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
"integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=",
"dev": true
}
}
}, },
"eslint-plugin-import": { "eslint-plugin-import": {
"version": "2.2.0", "version": "2.2.0",
@@ -944,12 +900,6 @@
"integrity": "sha1-crowb60wXWfEgWNIpGmaQimsi04=", "integrity": "sha1-crowb60wXWfEgWNIpGmaQimsi04=",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"debug": {
"version": "2.6.8",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
"integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
"dev": true
},
"doctrine": { "doctrine": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
@@ -1017,18 +967,10 @@
"dev": true "dev": true
}, },
"esrecurse": { "esrecurse": {
"version": "4.1.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz",
"integrity": "sha1-RxO2U2rffyrE8yfVWed1a/9kgiA=", "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=",
"dev": true, "dev": true
"dependencies": {
"estraverse": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz",
"integrity": "sha1-9srKcokzqFDvkGYdDheYK6RxEaI=",
"dev": true
}
}
}, },
"estraverse": { "estraverse": {
"version": "4.2.0", "version": "4.2.0",
@@ -1090,6 +1032,12 @@
"integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
"dev": true "dev": true
}, },
"ez-async": {
"version": "1.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/ez-async/-/ez-async-1.0.0-alpha.1.tgz",
"integrity": "sha1-ysNCuPqJAm7+c6Jg/p9rgE9J5H8=",
"dev": true
},
"fast-levenshtein": { "fast-levenshtein": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
@@ -1121,9 +1069,9 @@
"dev": true "dev": true
}, },
"find-root": { "find-root": {
"version": "1.0.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.0.0.tgz", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha1-li/yEaqyXGUg/u641ih/j26VgHo=", "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
"dev": true "dev": true
}, },
"find-up": { "find-up": {
@@ -2169,9 +2117,9 @@
"dev": true "dev": true
}, },
"js-tokens": { "js-tokens": {
"version": "3.0.1", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
"integrity": "sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc=", "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
"dev": true "dev": true
}, },
"js-yaml": { "js-yaml": {
@@ -2587,9 +2535,9 @@
} }
}, },
"readable-stream": { "readable-stream": {
"version": "2.2.11", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.11.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
"integrity": "sha512-h+8+r3MKEhkiVrwdKL8aWs1oc1VvBu33ueshOvS26RsZQ3Amhx/oO3TKe4lApSV9ueY6as8EAh7mtuFjdlhg9Q==", "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
"dev": true "dev": true
}, },
"readdirp": { "readdirp": {
@@ -2834,9 +2782,9 @@
"dev": true "dev": true
}, },
"safe-buffer": { "safe-buffer": {
"version": "5.0.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"dev": true "dev": true
}, },
"semver": { "semver": {
@@ -2921,9 +2869,9 @@
} }
}, },
"string_decoder": { "string_decoder": {
"version": "1.0.2", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true "dev": true
}, },
"string-width": { "string-width": {
@@ -2962,6 +2910,12 @@
"integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
@@ -2969,13 +2923,25 @@
"dev": true "dev": true
}, },
"string-width": { "string-width": {
"version": "2.0.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.0.tgz",
"integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4=", "integrity": "sha1-AwZkVh/BRslCPsfZeP4kV0N/5tA=",
"dev": true
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true "dev": true
} }
} }
}, },
"tag-dist-files": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/tag-dist-files/-/tag-dist-files-0.1.6.tgz",
"integrity": "sha1-h64FrBQw1H2m76Hrx7Bw+QxnTVQ=",
"dev": true
},
"text-table": { "text-table": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",

View File

@@ -1,21 +1,23 @@
{ {
"name": "yjs", "name": "yjs",
"version": "13.0.0-0", "version": "13.0.0-2",
"description": "A framework for real-time p2p shared editing on any data", "description": "A framework for real-time p2p shared editing on any data",
"main": "./src/y.js", "main": "./src/y.js",
"scripts": { "scripts": {
"test": "npm run lint",
"lint": "standard", "lint": "standard",
"dist": "rollup -c rollup.dist.js", "dist": "rollup -c rollup.dist.js",
"serve": "concurrently 'serve examples' 'rollup -wc rollup.dist.js -o examples/bower_components/yjs/y.js'" "serve": "concurrently 'serve ..' 'rollup -wc rollup.dist.js -o examples/bower_components/yjs/y.js'",
"postversion": "npm run dist",
"postpublish": "tag-dist-files --overwrite-existing-tag"
}, },
"pre-commit": [ "files": [
"lint", "y.*"
"test"
], ],
"standard": { "standard": {
"ignore": [ "ignore": [
"./y.js", "/y.js",
"./y.js.map" "/y.js.map"
] ]
}, },
"repository": { "repository": {
@@ -40,10 +42,10 @@
"homepage": "http://y-js.org", "homepage": "http://y-js.org",
"devDependencies": { "devDependencies": {
"babel-cli": "^6.24.1", "babel-cli": "^6.24.1",
"babel-preset-latest": "^6.24.1",
"babel-plugin-external-helpers": "^6.22.0", "babel-plugin-external-helpers": "^6.22.0",
"babel-plugin-transform-regenerator": "^6.24.1", "babel-plugin-transform-regenerator": "^6.24.1",
"babel-plugin-transform-runtime": "^6.23.0", "babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-latest": "^6.24.1",
"chance": "^1.0.9", "chance": "^1.0.9",
"concurrently": "^3.4.0", "concurrently": "^3.4.0",
"rollup-plugin-babel": "^2.7.1", "rollup-plugin-babel": "^2.7.1",
@@ -54,7 +56,8 @@
"rollup-plugin-uglify": "^1.0.2", "rollup-plugin-uglify": "^1.0.2",
"rollup-regenerator-runtime": "^6.23.1", "rollup-regenerator-runtime": "^6.23.1",
"rollup-watch": "^3.2.2", "rollup-watch": "^3.2.2",
"standard": "^10.0.2" "standard": "^10.0.2",
"tag-dist-files": "^0.1.6"
}, },
"dependencies": { "dependencies": {
"debug": "^2.6.8" "debug": "^2.6.8"

View File

@@ -63,7 +63,7 @@ export default function extendConnector (Y/* :any */) {
this.protocolVersion = 11 this.protocolVersion = 11
this.authInfo = opts.auth || null this.authInfo = opts.auth || null
this.checkAuth = opts.checkAuth || function () { return Promise.resolve('write') } // default is everyone has write access this.checkAuth = opts.checkAuth || function () { return Promise.resolve('write') } // default is everyone has write access
if (opts.generateUserId === true) { if (opts.generateUserId !== false) {
this.setUserId(Y.utils.generateGuid()) this.setUserId(Y.utils.generateGuid())
} }
} }
@@ -185,7 +185,7 @@ export default function extendConnector (Y/* :any */) {
this.currentSyncTarget = syncUser this.currentSyncTarget = syncUser
this.y.db.requestTransaction(function * () { this.y.db.requestTransaction(function * () {
var stateSet = yield * this.getStateSet() var stateSet = yield * this.getStateSet()
var deleteSet = yield * this.getDeleteSet() // var deleteSet = yield * this.getDeleteSet()
var answer = { var answer = {
type: 'sync step 1', type: 'sync step 1',
stateSet: stateSet, stateSet: stateSet,
@@ -346,21 +346,21 @@ export default function extendConnector (Y/* :any */) {
let m = message let m = message
// apply operations first // apply operations first
db.requestTransaction(function * () { db.requestTransaction(function * () {
yield * this.applyDeleteSet(m.deleteSet) // yield * this.applyDeleteSet(m.deleteSet)
if (m.osUntransformed != null) { if (m.osUntransformed != null) {
yield * this.applyOperationsUntransformed(m.osUntransformed, m.stateSet) yield * this.applyOperationsUntransformed(m.osUntransformed, m.stateSet)
} else { } else {
this.store.apply(m.os) this.store.apply(m.os)
} }
defer.resolve() // defer.resolve()
}) })
/*/ then apply ds // then apply ds
db.whenTransactionsFinished().then(() => { db.whenTransactionsFinished().then(() => {
db.requestTransaction(function * () { db.requestTransaction(function * () {
yield * this.applyDeleteSet(m.deleteSet) yield * this.applyDeleteSet(m.deleteSet)
}) })
defer.resolve() defer.resolve()
})*/ })
return defer.promise return defer.promise
} else if (message.type === 'sync done') { } else if (message.type === 'sync done') {
var self = this var self = this

View File

@@ -81,10 +81,6 @@ export default function extendDatabase (Y /* :any */) {
function garbageCollect () { function garbageCollect () {
return os.whenTransactionsFinished().then(function () { return os.whenTransactionsFinished().then(function () {
if (os.gcTimeout > 0 && (os.gc1.length > 0 || os.gc2.length > 0)) { if (os.gcTimeout > 0 && (os.gc1.length > 0 || os.gc2.length > 0)) {
// debug
if (os.y.connector.isSynced === false) {
debugger
}
if (!os.y.connector.isSynced) { if (!os.y.connector.isSynced) {
console.warn('gc should be empty when not synced!') console.warn('gc should be empty when not synced!')
} }
@@ -124,7 +120,7 @@ export default function extendDatabase (Y /* :any */) {
startGarbageCollector () { startGarbageCollector () {
this.gc = this.dbOpts.gc this.gc = this.dbOpts.gc
if (this.gc) { if (this.gc) {
this.gcTimeout = !this.dbOpts.gcTimeout ? 50000 : this.dbOpts.gcTimeout this.gcTimeout = !this.dbOpts.gcTimeout ? 100000 : this.dbOpts.gcTimeout
} else { } else {
this.gcTimeout = -1 this.gcTimeout = -1
} }
@@ -405,11 +401,11 @@ export default function extendDatabase (Y /* :any */) {
whenOperationsExist: any; whenOperationsExist: any;
*/ */
* tryExecute (op) { * tryExecute (op) {
this.store.addToDebug('yield* this.store.tryExecute.call(this, ', JSON.stringify(op), ')') this.store.addToDebug('yield * this.store.tryExecute.call(this, ', JSON.stringify(op), ')')
if (op.struct === 'Delete') { if (op.struct === 'Delete') {
yield * Y.Struct.Delete.execute.call(this, op) yield * Y.Struct.Delete.execute.call(this, op)
// this is now called in Transaction.deleteOperation! // this is now called in Transaction.deleteOperation!
// yield* this.store.operationAdded(this, op) // yield * this.store.operationAdded(this, op)
} else { } else {
// check if this op was defined // check if this op was defined
var defined = yield * this.getInsertion(op.id) var defined = yield * this.getInsertion(op.id)

View File

@@ -162,14 +162,14 @@ export default function extendTransaction (Y) {
if (target.start != null) { if (target.start != null) {
// TODO: don't do it like this .. -.- // TODO: don't do it like this .. -.-
yield * this.deleteList(target.start) yield * this.deleteList(target.start)
// yield* this.deleteList(target.id) -- do not gc itself because this may still get referenced // yield * this.deleteList(target.id) -- do not gc itself because this may still get referenced
} }
if (target.map != null) { if (target.map != null) {
for (var name in target.map) { for (var name in target.map) {
yield * this.deleteList(target.map[name]) yield * this.deleteList(target.map[name])
} }
// TODO: here to.. (see above) // TODO: here to.. (see above)
// yield* this.deleteList(target.id) -- see above // yield * this.deleteList(target.id) -- see above
} }
if (target.opContent != null) { if (target.opContent != null) {
yield * this.deleteOperation(target.opContent) yield * this.deleteOperation(target.opContent)
@@ -223,7 +223,7 @@ export default function extendTransaction (Y) {
*/ */
* markGarbageCollected (id, len) { * markGarbageCollected (id, len) {
// this.mem.push(["gc", id]); // this.mem.push(["gc", id]);
this.store.addToDebug('yield* this.markGarbageCollected(', id, ', ', len, ')') this.store.addToDebug('yield * this.markGarbageCollected(', id, ', ', len, ')')
var n = yield * this.markDeleted(id, len) var n = yield * this.markDeleted(id, len)
if (n.id[1] < id[1] && !n.gc) { if (n.id[1] < id[1] && !n.gc) {
// un-extend left // un-extend left
@@ -294,8 +294,9 @@ export default function extendTransaction (Y) {
yield * this.ds.put(n) yield * this.ds.put(n)
} else { } else {
// already gc'd // already gc'd
throw new Error('Cannot happen! (it dit though.. :()') throw new Error(
// return n 'DS reached an inconsistent state. Please report this issue!'
)
} }
} }
} else { } else {
@@ -366,6 +367,7 @@ export default function extendTransaction (Y) {
operations that can be gc'd and add them to the garbage collector. operations that can be gc'd and add them to the garbage collector.
*/ */
* garbageCollectAfterSync () { * garbageCollectAfterSync () {
// debugger
if (this.store.gc1.length > 0 || this.store.gc2.length > 0) { if (this.store.gc1.length > 0 || this.store.gc2.length > 0) {
console.warn('gc should be empty after sync') console.warn('gc should be empty after sync')
} }
@@ -417,7 +419,7 @@ export default function extendTransaction (Y) {
* reset origins of all right ops * reset origins of all right ops
*/ */
* garbageCollectOperation (id) { * garbageCollectOperation (id) {
this.store.addToDebug('yield* this.garbageCollectOperation(', id, ')') this.store.addToDebug('yield * this.garbageCollectOperation(', id, ')')
var o = yield * this.getOperation(id) var o = yield * this.getOperation(id)
yield * this.markGarbageCollected(id, (o != null && o.content != null) ? o.content.length : 1) // always mark gc'd yield * this.markGarbageCollected(id, (o != null && o.content != null) ? o.content.length : 1) // always mark gc'd
// if op exists, then clean that mess up.. // if op exists, then clean that mess up..
@@ -459,16 +461,8 @@ export default function extendTransaction (Y) {
if (o.originOf != null && o.originOf.length > 0) { if (o.originOf != null && o.originOf.length > 0) {
// find new origin of right ops // find new origin of right ops
// origin is the first left deleted operation // origin is the first left operation
var neworigin = o.left var neworigin = o.left
var neworigin_ = null
while (neworigin != null) {
neworigin_ = yield * this.getInsertion(neworigin)
if (neworigin_.deleted) {
break
}
neworigin = neworigin_.left
}
// reset origin of all right ops (except first right - duh!), // reset origin of all right ops (except first right - duh!),
@@ -482,7 +476,7 @@ export default function extendTransaction (Y) {
right.origin = neworigin right.origin = neworigin
// search until you find origin pointer to the left of o // search until you find origin pointer to the left of o
if (right.right != null) { if (right.right != null) {
var i = yield* this.getOperation(right.right) var i = yield * this.getOperation(right.right)
var ids = [o.id, o.right] var ids = [o.id, o.right]
while (ids.some(function (id) { while (ids.some(function (id) {
return Y.utils.compareIds(id, i.origin) return Y.utils.compareIds(id, i.origin)
@@ -490,14 +484,14 @@ export default function extendTransaction (Y) {
if (Y.utils.compareIds(i.origin, o.id)) { if (Y.utils.compareIds(i.origin, o.id)) {
// reset origin of i // reset origin of i
i.origin = neworigin i.origin = neworigin
yield* this.setOperation(i) yield * this.setOperation(i)
} }
// get next i // get next i
if (i.right == null) { if (i.right == null) {
break break
} else { } else {
ids.push(i.id) ids.push(i.id)
i = yield* this.getOperation(i.right) i = yield * this.getOperation(i.right)
} }
} }
} }
@@ -513,6 +507,7 @@ export default function extendTransaction (Y) {
} }
} }
if (neworigin != null) { if (neworigin != null) {
var neworigin_ = yield * this.getInsertion(neworigin)
if (neworigin_.originOf == null) { if (neworigin_.originOf == null) {
neworigin_.originOf = o.originOf neworigin_.originOf = o.originOf
} else { } else {
@@ -838,10 +833,10 @@ export default function extendTransaction (Y) {
yield * this.setOperation(op) yield * this.setOperation(op)
return op return op
} else { } else {
// won't be called. but just in case.. throw new Error(
console.error('Unexpected case. How can this happen?') 'Unexpected case. Operation cannot be generated correctly!' +
debugger // eslint-disable-line 'Incompatible Yjs version?'
return null )
} }
} }
} }
@@ -937,29 +932,38 @@ export default function extendTransaction (Y) {
var send = [] var send = []
var endSV = yield * this.getStateVector() var endSV = yield * this.getStateVector()
for (var endState of endSV) { for (let endState of endSV) {
var user = endState.user let user = endState.user
if (user === '_') { if (user === '_') {
continue continue
} }
var startPos = startSS[user] || 0 let startPos = startSS[user] || 0
if (startPos > 0) { if (startPos > 0) {
// There is a change that [user, startPos] is in a composed Insertion (with a smaller counter) // There is a change that [user, startPos] is in a composed Insertion (with a smaller counter)
// find out if that is the case // find out if that is the case
var firstMissing = yield * this.getInsertion([user, startPos]) let firstMissing = yield * this.getInsertion([user, startPos])
if (firstMissing != null) { if (firstMissing != null) {
// update startPos // update startPos
startPos = firstMissing.id[1] startPos = firstMissing.id[1]
startSS[user] = startPos
} }
} }
startSS[user] = startPos
}
for (let endState of endSV) {
let user = endState.user
let startPos = startSS[user]
if (user === '_') {
continue
}
yield * this.os.iterate(this, [user, startPos], [user, Number.MAX_VALUE], function * (op) { yield * this.os.iterate(this, [user, startPos], [user, Number.MAX_VALUE], function * (op) {
op = Y.Struct[op.struct].encode(op) op = Y.Struct[op.struct].encode(op)
if (op.struct !== 'Insert') { if (op.struct !== 'Insert') {
send.push(op) send.push(op)
} else if (op.right == null || op.right[1] < (startSS[op.right[0]] || 0)) { } else if (op.right == null || op.right[1] < (startSS[op.right[0]] || 0)) {
// case 1. op.right is known // case 1. op.right is known
var o = op // this case is only reached if op.right is known.
// => this is not called for op.left, as op.right is unknown
let o = op
// Remember: ? // Remember: ?
// -> set op.right // -> set op.right
// 1. to the first operation that is known (according to startSS) // 1. to the first operation that is known (according to startSS)
@@ -972,11 +976,14 @@ export default function extendTransaction (Y) {
if (o.left == null) { if (o.left == null) {
op.left = null op.left = null
send.push(op) send.push(op)
if (!Y.utils.compareIds(o.id, op.id)) { /* not necessary, as o is already sent..
if (!Y.utils.compareIds(o.id, op.id) && o.id[1] >= (startSS[o.id[0]] || 0)) {
// o is not op && o is unknown
o = Y.Struct[op.struct].encode(o) o = Y.Struct[op.struct].encode(o)
o.right = missingOrigins[missingOrigins.length - 1].id o.right = missingOrigins[missingOrigins.length - 1].id
send.push(o) send.push(o)
} }
*/
break break
} }
o = yield * this.getInsertion(o.left) o = yield * this.getInsertion(o.left)
@@ -996,8 +1003,10 @@ export default function extendTransaction (Y) {
op = Y.Struct[op.struct].encode(o) op = Y.Struct[op.struct].encode(o)
op.right = newright op.right = newright
if (missingOrigins.length > 0) { if (missingOrigins.length > 0) {
debugger throw new Error(
console.log('This should not happen .. :( please report this') 'Reached inconsistent OS state.' +
'Operations are not correctly connected.'
)
} }
missingOrigins = [op] missingOrigins = [op]
} else { } else {
@@ -1075,7 +1084,7 @@ export default function extendTransaction (Y) {
// or the o that has no origin to the right of op // or the o that has no origin to the right of op
// (this is why we use the ids array) // (this is why we use the ids array)
while (o.right != null) { while (o.right != null) {
var right = yield* this.getOperation(o.right) var right = yield * this.getOperation(o.right)
if (o.right[1] < (startSS[o.right[0]] || 0) || !ids.some(function (id) { if (o.right[1] < (startSS[o.right[0]] || 0) || !ids.some(function (id) {
return Y.utils.compareIds(id, right.origin) return Y.utils.compareIds(id, right.origin)
})) { })) {

View File

@@ -42,6 +42,32 @@ export default function Utils (Y) {
} }
} }
class NamedEventHandler {
constructor () {
this._eventListener = {}
}
on (name, f) {
if (this._eventListener[name] == null) {
this._eventListener[name] = []
}
this._eventListener[name].push(f)
}
off (name, f) {
if (name == null || f == null) {
throw new Error('You must specify event name and function!')
}
let listener = this._eventListener[name] || []
this._eventListener[name] = listener.filter(e => e !== f)
}
emit (name, value) {
(this._eventListener[name] || []).forEach(l => l(value))
}
destroy () {
this._eventListener = null
}
}
Y.utils.NamedEventHandler = NamedEventHandler
class EventListenerHandler { class EventListenerHandler {
constructor () { constructor () {
this.eventListeners = [] this.eventListeners = []
@@ -72,7 +98,12 @@ export default function Utils (Y) {
} }
this.eventListeners[i](_event) this.eventListeners[i](_event)
} catch (e) { } catch (e) {
console.error('Your observer threw an error. This error was caught so that Yjs still can ensure data consistency! In order to debug this error you have to check "Pause On Caught Exceptions"', e) /*
Your observer threw an error. This error was caught so that Yjs
can ensure data consistency! In order to debug this error you
have to check "Pause On Caught Exceptions" in developer tools.
*/
console.error(e)
} }
} }
} }
@@ -610,6 +641,7 @@ export default function Utils (Y) {
} }
} }
} }
return false
} }
Y.utils.matchesId = matchesId Y.utils.matchesId = matchesId

View File

@@ -144,7 +144,7 @@ export default function Y (opts/* :YOptions */) /* :Promise<YConfig> */ {
}) })
} }
class YConfig { class YConfig extends Y.utils.NamedEventHandler {
/* :: /* ::
db: Y.AbstractDatabase; db: Y.AbstractDatabase;
connector: Y.AbstractConnector; connector: Y.AbstractConnector;
@@ -152,6 +152,7 @@ class YConfig {
options: Object; options: Object;
*/ */
constructor (opts, callback) { constructor (opts, callback) {
super()
this.options = opts this.options = opts
this.db = new Y[opts.db.name](this, opts.db) this.db = new Y[opts.db.name](this, opts.db)
this.connector = new Y[opts.connector.name](this, opts.connector) this.connector = new Y[opts.connector.name](this, opts.connector)
@@ -215,6 +216,9 @@ class YConfig {
} else { } else {
return Promise.resolve() return Promise.resolve()
} }
}).then(() => {
// remove existing event listener
super.destroy()
}) })
} }
close () { close () {

View File

@@ -1,7 +1,7 @@
import _Y from '../../yjs/src/y.js' import _Y from '../../yjs/src/y.js'
import yMemory from '../../y-memory/src/Memory.js' import yMemory from '../../y-memory/src/y-memory.js'
import yArray from '../../y-array/src/y-array.js' import yArray from '../../y-array/src/y-array.js'
import yMap from '../../y-map/src/Map.js' import yMap from '../../y-map/src/Map.js'
import yTest from './test-connector.js' import yTest from './test-connector.js'
@@ -30,12 +30,14 @@ export async function compareUsers (t, users) {
await wait(100) await wait(100)
} }
await flushAll(t, users) await flushAll(t, users)
await wait()
await users[0].db.garbageCollect() await flushAll(t, users)
await users[0].db.garbageCollect()
var userTypeContents = users.map(u => u.share.array._content.map(c => c.val || JSON.stringify(c.type))) var userTypeContents = users.map(u => u.share.array._content.map(c => c.val || JSON.stringify(c.type)))
await users[0].db.garbageCollect()
await users[0].db.garbageCollect()
// disconnect all except user 0 // disconnect all except user 0
await Promise.all(users.slice(1).map(async u => await Promise.all(users.slice(1).map(async u =>
u.disconnect() u.disconnect()
@@ -54,18 +56,29 @@ export async function compareUsers (t, users) {
u.connector.whenSynced(resolve) u.connector.whenSynced(resolve)
}) })
)) ))
let filterDeletedOps = users.every(u => u.db.gc === false)
var data = await Promise.all(users.map(async (u) => { var data = await Promise.all(users.map(async (u) => {
var data = {} var data = {}
u.db.requestTransaction(function * () { u.db.requestTransaction(function * () {
var os = yield * this.getOperationsUntransformed() var os = yield * this.getOperationsUntransformed()
data.os = {} data.os = {}
os.untransformed.forEach((op) => { for (let i = 0; i < os.untransformed.length; i++) {
let op = os.untransformed[i]
op = Y.Struct[op.struct].encode(op) op = Y.Struct[op.struct].encode(op)
delete op.origin delete op.origin
data.os[JSON.stringify(op.id)] = op /*
return op If gc = false, it is necessary to filter deleted ops
}) as they might have been split up differently..
*/
if (filterDeletedOps) {
let opIsDeleted = yield * this.isDeleted(op.id)
if (!opIsDeleted) {
data.os[JSON.stringify(op.id)] = op
}
} else {
data.os[JSON.stringify(op.id)] = op
}
}
data.ds = yield * this.getDeleteSet() data.ds = yield * this.getDeleteSet()
data.ss = yield * this.getStateSet() data.ss = yield * this.getStateSet()
}) })

View File

@@ -14,8 +14,11 @@ export class TestRoom {
connector.setUserId('' + (this.nextUserId++)) connector.setUserId('' + (this.nextUserId++))
} }
Object.keys(this.users).forEach(uid => { Object.keys(this.users).forEach(uid => {
this.users[uid].userJoined(connector.userId, connector.role) let user = this.users[uid]
connector.userJoined(uid, this.users[uid].role) if (user.role === 'master' || connector.role === 'master') {
this.users[uid].userJoined(connector.userId, connector.role)
connector.userJoined(uid, this.users[uid].role)
}
}) })
this.users[connector.userId] = connector this.users[connector.userId] = connector
} }
@@ -67,6 +70,7 @@ export default function extendTestConnector (Y) {
if (options.room == null) { if (options.room == null) {
throw new Error('You must define a room name!') throw new Error('You must define a room name!')
} }
options.forwardAppliedOperations = options.role === 'master'
super(y, options) super(y, options)
this.options = options this.options = options
this.room = options.room this.room = options.room
@@ -128,13 +132,14 @@ export default function extendTestConnector (Y) {
var finished = [] var finished = []
for (let i = 0; i < flushUsers.length; i++) { for (let i = 0; i < flushUsers.length; i++) {
let userId = flushUsers[i].connector.userId let userId = flushUsers[i].connector.userId
if (userId === this.userId) continue if (userId !== this.userId && this.connections[userId] != null) {
let buffer = this.connections[userId].buffer let buffer = this.connections[userId].buffer
if (buffer != null) { if (buffer != null) {
var messages = buffer.splice(0) var messages = buffer.splice(0)
for (let j = 0; j < messages.length; j++) { for (let j = 0; j < messages.length; j++) {
let p = super.receiveMessage(userId, messages[j]) let p = super.receiveMessage(userId, messages[j])
finished.push(p) finished.push(p)
}
} }
} }
} }

9
y.js Normal file

File diff suppressed because one or more lines are too long

1
y.js.map Normal file

File diff suppressed because one or more lines are too long