implemented experimental websockets-connector
This commit is contained in:
parent
684d38d6c8
commit
cccc0e1015
@ -1,9 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
</head>
|
</head>
|
||||||
<script src="../../y.js"></script>
|
<script src="./index.mjs" type="module"></script>
|
||||||
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
|
|
||||||
<script src="./index.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body contenteditable="true">
|
<body contenteditable="true">
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
/* global Y */
|
|
||||||
|
|
||||||
window.onload = function () {
|
|
||||||
window.domBinding = new Y.DomBinding(window.yXmlType, document.body, { scrollingElement: document.scrollingElement })
|
|
||||||
}
|
|
||||||
|
|
||||||
let y = new Y('htmleditor', {
|
|
||||||
connector: {
|
|
||||||
name: 'websockets-client',
|
|
||||||
url: 'http://127.0.0.1:1234'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
window.y = y
|
|
||||||
window.yXmlType = y.define('xml', Y.XmlFragment)
|
|
||||||
window.undoManager = new Y.utils.UndoManager(window.yXmlType, {
|
|
||||||
captureTimeout: 500
|
|
||||||
})
|
|
||||||
|
|
||||||
document.onkeydown = function interceptUndoRedo (e) {
|
|
||||||
if (e.keyCode === 90 && (e.metaKey || e.ctrlKey)) {
|
|
||||||
if (!e.shiftKey) {
|
|
||||||
window.undoManager.undo()
|
|
||||||
} else {
|
|
||||||
window.undoManager.redo()
|
|
||||||
}
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
31
examples/html-editor/index.mjs
Normal file
31
examples/html-editor/index.mjs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
import YWebsocketsConnector from '../../src/Connectors/WebsocketsConnector/WebsocketsConnector.mjs'
|
||||||
|
import Y from '../../src/Y.mjs'
|
||||||
|
import DomBinding from '../../src/Bindings/DomBinding/DomBinding.mjs'
|
||||||
|
import UndoManager from '../../src/Util/UndoManager.mjs'
|
||||||
|
import XmlFragment from '../../src/Types/YXml/YXmlFragment.mjs'
|
||||||
|
|
||||||
|
const connector = new YWebsocketsConnector()
|
||||||
|
const y = new Y('html-editor')
|
||||||
|
connector.connectY('html-editor', y)
|
||||||
|
|
||||||
|
window.onload = function () {
|
||||||
|
window.domBinding = new DomBinding(window.yXmlType, document.body, { scrollingElement: document.scrollingElement })
|
||||||
|
}
|
||||||
|
|
||||||
|
window.y = y
|
||||||
|
window.yXmlType = y.define('xml', XmlFragment)
|
||||||
|
window.undoManager = new UndoManager(window.yXmlType, {
|
||||||
|
captureTimeout: 500
|
||||||
|
})
|
||||||
|
|
||||||
|
document.onkeydown = function interceptUndoRedo (e) {
|
||||||
|
if (e.keyCode === 90 && (e.metaKey || e.ctrlKey)) {
|
||||||
|
if (!e.shiftKey) {
|
||||||
|
window.undoManager.undo()
|
||||||
|
} else {
|
||||||
|
window.undoManager.redo()
|
||||||
|
}
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import Y from '../src/Y.js'
|
import Y from '../src/Y..mjs'
|
||||||
import yWebsocketsClient from '../../y-websockets-client/src/y-websockets-client.js'
|
import yWebsocketsClient from '../../y-websockets-client/src/y-websockets-client.mjs'
|
||||||
import extendYIndexedDBPersistence from '../../y-indexeddb/src/y-indexeddb.js'
|
import extendYIndexedDBPersistence from '../../y-indexeddb/src/y-indexeddb.mjs'
|
||||||
|
|
||||||
Y.extend(yWebsocketsClient)
|
Y.extend(yWebsocketsClient)
|
||||||
extendYIndexedDBPersistence(Y)
|
extendYIndexedDBPersistence(Y)
|
||||||
|
1349
package-lock.json
generated
1349
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "npm run lint",
|
"test": "npm run lint",
|
||||||
"debug": "concurrently 'rollup -wc rollup.test.js' 'cutest-serve y.test.js -o'",
|
"debug": "concurrently 'rollup -wc rollup.test.js' 'cutest-serve y.test.js -o'",
|
||||||
"lint": "standard",
|
"lint": "standard src/**/*.mjs test/**/*.mjs tests-lib/**/*.mjs",
|
||||||
"docs": "esdoc",
|
"docs": "esdoc",
|
||||||
"serve-docs": "npm run docs && serve ./docs/",
|
"serve-docs": "npm run docs && serve ./docs/",
|
||||||
"dist": "rollup -c rollup.browser.js; rollup -c rollup.node.js",
|
"dist": "rollup -c rollup.browser.js; rollup -c rollup.node.js",
|
||||||
@ -55,6 +55,7 @@
|
|||||||
"babel-plugin-transform-runtime": "^6.23.0",
|
"babel-plugin-transform-runtime": "^6.23.0",
|
||||||
"babel-preset-latest": "^6.24.1",
|
"babel-preset-latest": "^6.24.1",
|
||||||
"chance": "^1.0.9",
|
"chance": "^1.0.9",
|
||||||
|
"codemirror": "^5.37.0",
|
||||||
"concurrently": "^3.4.0",
|
"concurrently": "^3.4.0",
|
||||||
"cutest": "^0.1.9",
|
"cutest": "^0.1.9",
|
||||||
"esdoc": "^1.0.4",
|
"esdoc": "^1.0.4",
|
||||||
@ -70,7 +71,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": "^11.0.1",
|
||||||
"tag-dist-files": "^0.1.6"
|
"tag-dist-files": "^0.1.6",
|
||||||
|
"uws": "^10.148.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import commonjs from 'rollup-plugin-commonjs'
|
|||||||
var pkg = require('./package.json')
|
var pkg = require('./package.json')
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
input: 'src/Y.dist.js',
|
input: 'src/Y.dist.mjs',
|
||||||
name: 'Y',
|
name: 'Y',
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
output: {
|
output: {
|
||||||
|
@ -3,7 +3,7 @@ import commonjs from 'rollup-plugin-commonjs'
|
|||||||
var pkg = require('./package.json')
|
var pkg = require('./package.json')
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
input: 'src/Y.dist.js',
|
input: 'src/Y.dist.mjs',
|
||||||
nameame: 'Y',
|
nameame: 'Y',
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
output: {
|
output: {
|
||||||
|
@ -3,7 +3,7 @@ import commonjs from 'rollup-plugin-commonjs'
|
|||||||
import multiEntry from 'rollup-plugin-multi-entry'
|
import multiEntry from 'rollup-plugin-multi-entry'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
input: 'test/index.js',
|
input: 'test/index.mjs',
|
||||||
name: 'y-tests',
|
name: 'y-tests',
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
output: {
|
output: {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import { createMutualExclude } from '../Util/mutualExclude.js'
|
import { createMutualExclude } from '../Util/mutualExclude.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class for bindings.
|
* Abstract class for bindings.
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import Binding from '../Binding.js'
|
import Binding from '../Binding.mjs'
|
||||||
import simpleDiff from '../../Util/simpleDiff.js'
|
import simpleDiff from '../../Util/simpleDiff.mjs'
|
||||||
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.js'
|
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.mjs'
|
||||||
|
|
||||||
function typeObserver () {
|
function typeObserver () {
|
||||||
this._mutualExclude(() => {
|
this._mutualExclude(() => {
|
@ -1,11 +1,11 @@
|
|||||||
/* global MutationObserver */
|
/* global MutationObserver */
|
||||||
|
|
||||||
import Binding from '../Binding.js'
|
import Binding from '../Binding.mjs'
|
||||||
import { createAssociation, removeAssociation } from './util.js'
|
import { createAssociation, removeAssociation } from './util.mjs'
|
||||||
import { beforeTransactionSelectionFixer, afterTransactionSelectionFixer } from './selection.js'
|
import { beforeTransactionSelectionFixer, afterTransactionSelectionFixer } from './selection.mjs'
|
||||||
import { defaultFilter, applyFilterOnType } from './filter.js'
|
import { defaultFilter, applyFilterOnType } from './filter.mjs'
|
||||||
import typeObserver from './typeObserver.js'
|
import typeObserver from './typeObserver.mjs'
|
||||||
import domObserver from './domObserver.js'
|
import domObserver from './domObserver.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A binding that binds the children of a YXmlFragment to a DOM element.
|
* A binding that binds the children of a YXmlFragment to a DOM element.
|
@ -1,11 +1,11 @@
|
|||||||
|
|
||||||
import YXmlHook from '../../Types/YXml/YXmlHook.js'
|
import YXmlHook from '../../Types/YXml/YXmlHook.mjs'
|
||||||
import {
|
import {
|
||||||
iterateUntilUndeleted,
|
iterateUntilUndeleted,
|
||||||
removeAssociation,
|
removeAssociation,
|
||||||
insertNodeHelper } from './util.js'
|
insertNodeHelper } from './util.mjs'
|
||||||
import diff from '../../Util/simpleDiff.js'
|
import diff from '../../Util/simpleDiff.mjs'
|
||||||
import YXmlFragment from '../../Types/YXml/YXmlFragment.js'
|
import YXmlFragment from '../../Types/YXml/YXmlFragment.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1. Check if any of the nodes was deleted
|
* 1. Check if any of the nodes was deleted
|
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
import YXmlText from '../../Types/YXml/YXmlText.js'
|
import YXmlText from '../../Types/YXml/YXmlText.mjs'
|
||||||
import YXmlHook from '../../Types/YXml/YXmlHook.js'
|
import YXmlHook from '../../Types/YXml/YXmlHook.mjs'
|
||||||
import YXmlElement from '../../Types/YXml/YXmlElement.js'
|
import YXmlElement from '../../Types/YXml/YXmlElement.mjs'
|
||||||
import { createAssociation, domsToTypes } from './util.js'
|
import { createAssociation, domsToTypes } from './util.mjs'
|
||||||
import { filterDomAttributes, defaultFilter } from './filter.js'
|
import { filterDomAttributes, defaultFilter } from './filter.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Yjs type (YXml) based on the contents of a DOM Element.
|
* Creates a Yjs type (YXml) based on the contents of a DOM Element.
|
@ -1,4 +1,4 @@
|
|||||||
import isParentOf from '../../Util/isParentOf.js'
|
import isParentOf from '../../Util/isParentOf.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default filter method (does nothing).
|
* Default filter method (does nothing).
|
@ -1,6 +1,6 @@
|
|||||||
/* globals getSelection */
|
/* globals getSelection */
|
||||||
|
|
||||||
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.js'
|
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.mjs'
|
||||||
|
|
||||||
let browserSelection = null
|
let browserSelection = null
|
||||||
let relativeSelection = null
|
let relativeSelection = null
|
@ -1,8 +1,8 @@
|
|||||||
/* global getSelection */
|
/* global getSelection */
|
||||||
|
|
||||||
import YXmlText from '../../Types/YXml/YXmlText.js'
|
import YXmlText from '../../Types/YXml/YXmlText.mjs'
|
||||||
import YXmlHook from '../../Types/YXml/YXmlHook.js'
|
import YXmlHook from '../../Types/YXml/YXmlHook.mjs'
|
||||||
import { removeDomChildrenUntilElementFound } from './util.js'
|
import { removeDomChildrenUntilElementFound } from './util.mjs'
|
||||||
|
|
||||||
function findScrollReference (scrollingElement) {
|
function findScrollReference (scrollingElement) {
|
||||||
if (scrollingElement !== null) {
|
if (scrollingElement !== null) {
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import domToType from './domToType.js'
|
import domToType from './domToType.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates items until an undeleted item is found.
|
* Iterates items until an undeleted item is found.
|
@ -1,4 +1,4 @@
|
|||||||
import Binding from '../Binding.js'
|
import Binding from '../Binding.mjs'
|
||||||
|
|
||||||
function typeObserver (event) {
|
function typeObserver (event) {
|
||||||
const quill = this.target
|
const quill = this.target
|
56
src/Bindings/TextareaBinding/TextareaBinding.mjs
Normal file
56
src/Bindings/TextareaBinding/TextareaBinding.mjs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
import Binding from '../Binding.mjs'
|
||||||
|
import simpleDiff from '../../Util/simpleDiff.mjs'
|
||||||
|
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.mjs'
|
||||||
|
|
||||||
|
function typeObserver () {
|
||||||
|
this._mutualExclude(() => {
|
||||||
|
const textarea = this.target
|
||||||
|
const textType = this.type
|
||||||
|
const relativeStart = getRelativePosition(textType, textarea.selectionStart)
|
||||||
|
const relativeEnd = getRelativePosition(textType, textarea.selectionEnd)
|
||||||
|
textarea.value = textType.toString()
|
||||||
|
const start = fromRelativePosition(textType._y, relativeStart)
|
||||||
|
const end = fromRelativePosition(textType._y, relativeEnd)
|
||||||
|
textarea.setSelectionRange(start, end)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function domObserver () {
|
||||||
|
this._mutualExclude(() => {
|
||||||
|
let diff = simpleDiff(this.type.toString(), this.target.value)
|
||||||
|
this.type.delete(diff.pos, diff.remove)
|
||||||
|
this.type.insert(diff.pos, diff.insert)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A binding that binds a YText to a dom textarea.
|
||||||
|
*
|
||||||
|
* This binding is automatically destroyed when its parent is deleted.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const textare = document.createElement('textarea')
|
||||||
|
* const type = y.define('textarea', Y.Text)
|
||||||
|
* const binding = new Y.QuillBinding(type, textarea)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export default class TextareaBinding extends Binding {
|
||||||
|
constructor (textType, domTextarea) {
|
||||||
|
// Binding handles textType as this.type and domTextarea as this.target
|
||||||
|
super(textType, domTextarea)
|
||||||
|
// set initial value
|
||||||
|
domTextarea.value = textType.toString()
|
||||||
|
// Observers are handled by this class
|
||||||
|
this._typeObserver = typeObserver.bind(this)
|
||||||
|
this._domObserver = domObserver.bind(this)
|
||||||
|
textType.observe(this._typeObserver)
|
||||||
|
domTextarea.addEventListener('input', this._domObserver)
|
||||||
|
}
|
||||||
|
destroy () {
|
||||||
|
// Remove everything that is handled by this class
|
||||||
|
this.type.unobserve(this._typeObserver)
|
||||||
|
this.target.unobserve(this._domObserver)
|
||||||
|
super.destroy()
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import BinaryEncoder from './Util/Binary/Encoder.js'
|
import BinaryEncoder from './Util/Binary/Encoder.mjs'
|
||||||
import BinaryDecoder from './Util/Binary/Decoder.js'
|
import BinaryDecoder from './Util/Binary/Decoder.mjs'
|
||||||
|
|
||||||
import { sendSyncStep1, readSyncStep1 } from './MessageHandler/syncStep1.js'
|
import { sendSyncStep1, readSyncStep1 } from './MessageHandler/syncStep1.mjs'
|
||||||
import { readSyncStep2 } from './MessageHandler/syncStep2.js'
|
import { readSyncStep2 } from './MessageHandler/syncStep2.mjs'
|
||||||
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.js'
|
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.mjs'
|
||||||
|
|
||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
|
|
114
src/Connectors/WebsocketsConnector/WebsocketsConnector.mjs
Normal file
114
src/Connectors/WebsocketsConnector/WebsocketsConnector.mjs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import BinaryEncoder from '../../Util/Binary/Encoder.mjs'
|
||||||
|
/* global WebSocket */
|
||||||
|
import NamedEventHandler from '../../Util/NamedEventHandler.mjs'
|
||||||
|
import decodeMessage, { messageSS, messageSubscribe, messageStructs, messageGetSS } from './decodeMessage.mjs'
|
||||||
|
import { createMutualExclude } from '../../Util/mutualExclude.mjs'
|
||||||
|
|
||||||
|
export const STATE_CONNECTING = 0
|
||||||
|
export const STATE_SYNCING = 1
|
||||||
|
export const STATE_SYNCED = 2
|
||||||
|
export const STATE_DISCONNECTED = 3
|
||||||
|
|
||||||
|
export default class WebsocketsConnector extends NamedEventHandler {
|
||||||
|
constructor (url = 'ws://localhost:1234') {
|
||||||
|
super()
|
||||||
|
this.url = url
|
||||||
|
this._state = STATE_DISCONNECTED
|
||||||
|
this._socket = null
|
||||||
|
this._rooms = new Map()
|
||||||
|
this._connectToServer = true
|
||||||
|
this._reconnectTimeout = 300
|
||||||
|
this._mutualExclude = createMutualExclude()
|
||||||
|
this.connect()
|
||||||
|
}
|
||||||
|
|
||||||
|
getRoom (roomName) {
|
||||||
|
return this._rooms.get(roomName)
|
||||||
|
}
|
||||||
|
|
||||||
|
connectY (roomName, y) {
|
||||||
|
let room = this._rooms.get(roomName)
|
||||||
|
if (room !== undefined) {
|
||||||
|
throw new Error('Room is already taken! There can be only one Yjs instance per roomName!')
|
||||||
|
}
|
||||||
|
this._rooms.set(roomName, {
|
||||||
|
roomName,
|
||||||
|
y
|
||||||
|
})
|
||||||
|
y.on('afterTransaction', (y, transaction) => {
|
||||||
|
this._mutualExclude(() => {
|
||||||
|
if (transaction.encodedStructsLen > 0) {
|
||||||
|
const encoder = new BinaryEncoder()
|
||||||
|
messageStructs(roomName, y, encoder, transaction.encodedStructs)
|
||||||
|
this.send(encoder)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_setState (state) {
|
||||||
|
this.emit('stateChanged', {
|
||||||
|
state
|
||||||
|
})
|
||||||
|
this._state = state
|
||||||
|
}
|
||||||
|
|
||||||
|
get state () {
|
||||||
|
return this._state
|
||||||
|
}
|
||||||
|
|
||||||
|
_onOpen () {
|
||||||
|
const encoder = new BinaryEncoder()
|
||||||
|
for (const [roomName, room] of this._rooms) {
|
||||||
|
const y = room.y
|
||||||
|
messageGetSS(roomName, y, encoder)
|
||||||
|
messageSS(roomName, y, encoder)
|
||||||
|
messageSubscribe(roomName, y, encoder)
|
||||||
|
}
|
||||||
|
this.send(encoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
send (encoder) {
|
||||||
|
if (encoder.length > 0) {
|
||||||
|
this._socket.send(encoder.createBuffer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onClose () {
|
||||||
|
this._socket = null
|
||||||
|
if (this._connectToServer) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this._connectToServer) {
|
||||||
|
this.connect()
|
||||||
|
}
|
||||||
|
}, this._reconnectTimeout)
|
||||||
|
this.connect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onMessage (message) {
|
||||||
|
if (message.data.byteLength > 0) {
|
||||||
|
const reply = decodeMessage(this, message.data, null)
|
||||||
|
this.send(reply)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect (code = 1000, reason = 'Client manually disconnected') {
|
||||||
|
const socket = this._socket
|
||||||
|
this._connectToServer = false
|
||||||
|
socket.close(code, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
connect () {
|
||||||
|
if (this._socket === null) {
|
||||||
|
const socket = new WebSocket(this.url)
|
||||||
|
socket.binaryType = 'arraybuffer'
|
||||||
|
this._socket = socket
|
||||||
|
this._connectToServer = true
|
||||||
|
// Connection opened
|
||||||
|
socket.addEventListener('open', this._onOpen.bind(this))
|
||||||
|
socket.addEventListener('close', this._onClose.bind(this))
|
||||||
|
socket.addEventListener('message', this._onMessage.bind(this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
104
src/Connectors/WebsocketsConnector/decodeMessage.mjs
Normal file
104
src/Connectors/WebsocketsConnector/decodeMessage.mjs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import BinaryDecoder from '../../Util/Binary/Decoder.mjs'
|
||||||
|
import BinaryEncoder from '../../Util/Binary/Encoder.mjs'
|
||||||
|
import { readStateSet, writeStateSet } from '../../MessageHandler/stateSet.mjs'
|
||||||
|
import { writeStructs } from '../../MessageHandler/syncStep1.mjs'
|
||||||
|
import { writeDeleteSet, readDeleteSet } from '../../MessageHandler/deleteSet.mjs'
|
||||||
|
import { integrateRemoteStructs } from '../../MessageHandler/integrateRemoteStructs.mjs'
|
||||||
|
|
||||||
|
const CONTENT_GET_SS = 4
|
||||||
|
export function messageGetSS (roomName, y, encoder) {
|
||||||
|
encoder.writeVarString(roomName)
|
||||||
|
encoder.writeVarUint(CONTENT_GET_SS)
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONTENT_SUBSCRIBE = 3
|
||||||
|
export function messageSubscribe (roomName, y, encoder) {
|
||||||
|
encoder.writeVarString(roomName)
|
||||||
|
encoder.writeVarUint(CONTENT_SUBSCRIBE)
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONTENT_SS = 0
|
||||||
|
/**
|
||||||
|
* Message the current state set. The other side must respond with CONTENT_STRUCTS_DSS
|
||||||
|
*/
|
||||||
|
export function messageSS (roomName, y, encoder) {
|
||||||
|
encoder.writeVarString(roomName)
|
||||||
|
encoder.writeVarUint(CONTENT_SS)
|
||||||
|
writeStateSet(y, encoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONTENT_STRUCTS_DSS = 2
|
||||||
|
export function messageStructsDSS (roomName, y, encoder, ss) {
|
||||||
|
encoder.writeVarString(roomName)
|
||||||
|
encoder.writeVarUint(CONTENT_STRUCTS_DSS)
|
||||||
|
writeStructs(y, encoder, ss)
|
||||||
|
writeDeleteSet(y, encoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONTENT_STRUCTS = 5
|
||||||
|
export function messageStructs (roomName, y, encoder, structsBinaryEncoder) {
|
||||||
|
encoder.writeVarString(roomName)
|
||||||
|
encoder.writeVarUint(CONTENT_STRUCTS)
|
||||||
|
encoder.writeBinaryEncoder(structsBinaryEncoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a client-message.
|
||||||
|
*
|
||||||
|
* A client-message consists of multiple message-elements that are concatenated without delimiter.
|
||||||
|
* Each has the following structure:
|
||||||
|
* - roomName
|
||||||
|
* - content_type
|
||||||
|
* - content (additional info that is encoded based on the value of content_type)
|
||||||
|
*
|
||||||
|
* The message is encoded until no more message-elements are available.
|
||||||
|
*
|
||||||
|
* @param {*} connector The connector that handles the connections
|
||||||
|
* @param {*} message The binary encoded message
|
||||||
|
* @param {*} ws The connection object
|
||||||
|
*/
|
||||||
|
export default function decodeMessage (connector, message, ws) {
|
||||||
|
const decoder = new BinaryDecoder(message)
|
||||||
|
const encoder = new BinaryEncoder()
|
||||||
|
while (decoder.hasContent()) {
|
||||||
|
const roomName = decoder.readVarString()
|
||||||
|
const contentType = decoder.readVarUint()
|
||||||
|
const room = connector.getRoom(roomName)
|
||||||
|
const y = room.y
|
||||||
|
switch (contentType) {
|
||||||
|
case CONTENT_STRUCTS:
|
||||||
|
connector._mutualExclude(() => {
|
||||||
|
y.transact(() => {
|
||||||
|
integrateRemoteStructs(y, decoder)
|
||||||
|
}, true)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case CONTENT_GET_SS:
|
||||||
|
messageSS(roomName, y, encoder)
|
||||||
|
break
|
||||||
|
case CONTENT_SUBSCRIBE:
|
||||||
|
room.connections.add(ws)
|
||||||
|
break
|
||||||
|
case CONTENT_SS:
|
||||||
|
// received state set
|
||||||
|
// reply with missing content
|
||||||
|
const ss = readStateSet(decoder)
|
||||||
|
messageStructsDSS(roomName, y, encoder, ss)
|
||||||
|
break
|
||||||
|
case CONTENT_STRUCTS_DSS:
|
||||||
|
connector._mutualExclude(() => {
|
||||||
|
y.transact(() => {
|
||||||
|
integrateRemoteStructs(y, decoder)
|
||||||
|
readDeleteSet(y, decoder)
|
||||||
|
}, true)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
console.error('Unexpected content type!')
|
||||||
|
if (ws !== null) {
|
||||||
|
ws.close() // TODO: specify reason
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return encoder
|
||||||
|
}
|
99
src/Connectors/WebsocketsConnector/server.mjs
Normal file
99
src/Connectors/WebsocketsConnector/server.mjs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import Y from '../../Y.mjs'
|
||||||
|
import uws from 'uws'
|
||||||
|
import BinaryEncoder from '../../Util/Binary/Encoder.mjs'
|
||||||
|
import decodeMessage, { messageStructs } from './decodeMessage.mjs'
|
||||||
|
|
||||||
|
const WebsocketsServer = uws.Server
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps from room-name to ..
|
||||||
|
* {
|
||||||
|
* connections, // Set of ws-clients that listen to the room
|
||||||
|
* y // Yjs instance that handles the room
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
const rooms = new Map()
|
||||||
|
/**
|
||||||
|
* Maps from ws-connection to Set<roomName> - the set of connected roomNames
|
||||||
|
*/
|
||||||
|
const connections = new Map()
|
||||||
|
const wss = new WebsocketsServer({ port: 1234 })
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of room names that are scheduled to be sweeped (destroyed because they don't have a connection anymore)
|
||||||
|
*/
|
||||||
|
const scheduledSweeps = new Set()
|
||||||
|
setInterval(function sweepRoomes () {
|
||||||
|
scheduledSweeps.forEach(roomName => {
|
||||||
|
const room = rooms.get(roomName)
|
||||||
|
if (room !== undefined) {
|
||||||
|
if (room.connections.size === 0) {
|
||||||
|
room.y.destroy()
|
||||||
|
}
|
||||||
|
rooms.delete(roomName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 5000)
|
||||||
|
|
||||||
|
const wsConnector = {
|
||||||
|
_mutualExclude: f => { f() },
|
||||||
|
subscribe: function subscribe (roomName, ws) {
|
||||||
|
let roomNames = connections.get(ws)
|
||||||
|
if (roomNames === undefined) {
|
||||||
|
roomNames = new Set()
|
||||||
|
connections.set(ws, roomNames)
|
||||||
|
}
|
||||||
|
roomNames.add(roomName)
|
||||||
|
},
|
||||||
|
getRoom: function getRoom (roomName) {
|
||||||
|
let room = rooms.get(roomName)
|
||||||
|
if (room === undefined) {
|
||||||
|
const y = new Y(roomName)
|
||||||
|
y.on('afterTransaction', (y, transaction) => {
|
||||||
|
if (transaction.encodedStructsLen > 0) {
|
||||||
|
const encoder = new BinaryEncoder()
|
||||||
|
messageStructs(roomName, y, encoder, transaction.encodedStructs)
|
||||||
|
const message = encoder.createBuffer()
|
||||||
|
// when changed, broakcast update to all connections
|
||||||
|
room.connections.forEach(conn => {
|
||||||
|
conn.send(message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
room = {
|
||||||
|
name: roomName,
|
||||||
|
connections: new Set(),
|
||||||
|
y
|
||||||
|
}
|
||||||
|
rooms.set(roomName, room)
|
||||||
|
}
|
||||||
|
return room
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wss.on('connection', (ws) => {
|
||||||
|
ws.on('message', function onWSMessage (message) {
|
||||||
|
if (message.byteLength > 0) {
|
||||||
|
const reply = decodeMessage(wsConnector, message, ws)
|
||||||
|
if (reply.length > 0) {
|
||||||
|
ws.send(reply.createBuffer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
ws.on('close', function onWSClose () {
|
||||||
|
const roomNames = connections.get(ws)
|
||||||
|
if (roomNames !== undefined) {
|
||||||
|
roomNames.forEach(roomName => {
|
||||||
|
const room = rooms.get(roomName)
|
||||||
|
if (room !== undefined) {
|
||||||
|
const connections = room.connections
|
||||||
|
connections.delete(ws)
|
||||||
|
if (connections.size === 0) {
|
||||||
|
scheduledSweeps.add(roomName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
connections.delete(ws)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
import { writeStructs } from './syncStep1.js'
|
import { writeStructs } from './syncStep1.mjs'
|
||||||
import { integrateRemoteStructs } from './integrateRemoteStructs.js'
|
import { integrateRemoteStructs } from './integrateRemoteStructs.mjs'
|
||||||
import { readDeleteSet, writeDeleteSet } from './deleteSet.js'
|
import { readDeleteSet, writeDeleteSet } from './deleteSet.mjs'
|
||||||
import BinaryEncoder from '../Util/Binary/Encoder.js'
|
import BinaryEncoder from '../Util/Binary/Encoder.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the Decoder and fill the Yjs instance with data in the decoder.
|
* Read the Decoder and fill the Yjs instance with data in the decoder.
|
@ -1,5 +1,5 @@
|
|||||||
import { deleteItemRange } from '../Struct/Delete.js'
|
import { deleteItemRange } from '../Struct/Delete.mjs'
|
||||||
import ID from '../Util/ID/ID.js'
|
import ID from '../Util/ID/ID.mjs'
|
||||||
|
|
||||||
export function stringifyDeleteSet (y, decoder, strBuilder) {
|
export function stringifyDeleteSet (y, decoder, strBuilder) {
|
||||||
let dsLength = decoder.readUint32()
|
let dsLength = decoder.readUint32()
|
@ -1,7 +1,7 @@
|
|||||||
import { getStruct } from '../Util/structReferences.js'
|
import { getStruct } from '../Util/structReferences.mjs'
|
||||||
import BinaryDecoder from '../Util/Binary/Decoder.js'
|
import BinaryDecoder from '../Util/Binary/Decoder.mjs'
|
||||||
import { logID } from './messageToString.js'
|
import { logID } from './messageToString.mjs'
|
||||||
import GC from '../Struct/GC.js'
|
import GC from '../Struct/GC.mjs'
|
||||||
|
|
||||||
class MissingEntry {
|
class MissingEntry {
|
||||||
constructor (decoder, missing, struct) {
|
constructor (decoder, missing, struct) {
|
@ -1,10 +1,10 @@
|
|||||||
import BinaryDecoder from '../Util/Binary/Decoder.js'
|
import BinaryDecoder from '../Util/Binary/Decoder.mjs'
|
||||||
import { stringifyStructs } from './integrateRemoteStructs.js'
|
import { stringifyStructs } from './integrateRemoteStructs.mjs'
|
||||||
import { stringifySyncStep1 } from './syncStep1.js'
|
import { stringifySyncStep1 } from './syncStep1.mjs'
|
||||||
import { stringifySyncStep2 } from './syncStep2.js'
|
import { stringifySyncStep2 } from './syncStep2.mjs'
|
||||||
import ID from '../Util/ID/ID.js'
|
import ID from '../Util/ID/ID.mjs'
|
||||||
import RootID from '../Util/ID/RootID.js'
|
import RootID from '../Util/ID/RootID.mjs'
|
||||||
import Y from '../Y.js'
|
import Y from '../Y.mjs'
|
||||||
|
|
||||||
export function messageToString ([y, buffer]) {
|
export function messageToString ([y, buffer]) {
|
||||||
let decoder = new BinaryDecoder(buffer)
|
let decoder = new BinaryDecoder(buffer)
|
@ -1,8 +1,8 @@
|
|||||||
import BinaryEncoder from '../Util/Binary/Encoder.js'
|
import BinaryEncoder from '../Util/Binary/Encoder.mjs'
|
||||||
import { readStateSet, writeStateSet } from './stateSet.js'
|
import { readStateSet, writeStateSet } from './stateSet.mjs'
|
||||||
import { writeDeleteSet } from './deleteSet.js'
|
import { writeDeleteSet } from './deleteSet.mjs'
|
||||||
import ID from '../Util/ID/ID.js'
|
import ID from '../Util/ID/ID.mjs'
|
||||||
import { RootFakeUserID } from '../Util/ID/RootID.js'
|
import { RootFakeUserID } from '../Util/ID/RootID.mjs'
|
||||||
|
|
||||||
export function stringifySyncStep1 (y, decoder, strBuilder) {
|
export function stringifySyncStep1 (y, decoder, strBuilder) {
|
||||||
let auth = decoder.readVarString()
|
let auth = decoder.readVarString()
|
@ -1,5 +1,5 @@
|
|||||||
import { stringifyStructs, integrateRemoteStructs } from './integrateRemoteStructs.js'
|
import { stringifyStructs, integrateRemoteStructs } from './integrateRemoteStructs.mjs'
|
||||||
import { readDeleteSet } from './deleteSet.js'
|
import { readDeleteSet } from './deleteSet.mjs'
|
||||||
|
|
||||||
export function stringifySyncStep2 (y, decoder, strBuilder) {
|
export function stringifySyncStep2 (y, decoder, strBuilder) {
|
||||||
strBuilder.push(' - auth: ' + decoder.readVarString())
|
strBuilder.push(' - auth: ' + decoder.readVarString())
|
@ -1,8 +1,8 @@
|
|||||||
import BinaryEncoder from './Util/Binary/Encoder.js'
|
import BinaryEncoder from './Util/Binary/Encoder.mjs'
|
||||||
import BinaryDecoder from './Util/Binary/Decoder.js'
|
import BinaryDecoder from './Util/Binary/Decoder.mjs'
|
||||||
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.js'
|
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.mjs'
|
||||||
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.js'
|
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.mjs'
|
||||||
import { createMutualExclude } from './Util/mutualExclude.js'
|
import { createMutualExclude } from './Util/mutualExclude.mjs'
|
||||||
|
|
||||||
function getFreshCnf () {
|
function getFreshCnf () {
|
||||||
let buffer = new BinaryEncoder()
|
let buffer = new BinaryEncoder()
|
2
src/Persistences/AbstractPersistence.mjs
Normal file
2
src/Persistences/AbstractPersistence.mjs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
export default class AbstractPersistence {}
|
1
src/Persistences/FilePersistence.mjs
Normal file
1
src/Persistences/FilePersistence.mjs
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
174
src/Persistences/IndexeddbPersistence.mjs
Normal file
174
src/Persistences/IndexeddbPersistence.mjs
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
/* global indexedDB, location, BroadcastChannel */
|
||||||
|
|
||||||
|
import Y from '../Y.mjs'
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Request to Promise transformer
|
||||||
|
*/
|
||||||
|
function rtop (request) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
request.onerror = function (event) {
|
||||||
|
reject(new Error(event.target.error))
|
||||||
|
}
|
||||||
|
request.onblocked = function () {
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
request.onsuccess = function (event) {
|
||||||
|
resolve(event.target.result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDB (room) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
let request = indexedDB.open(room)
|
||||||
|
window.r1 = request
|
||||||
|
request.onupgradeneeded = function (event) {
|
||||||
|
const db = event.target.result
|
||||||
|
if (db.objectStoreNames.contains('model')) {
|
||||||
|
db.deleteObjectStore('updates')
|
||||||
|
db.deleteObjectStore('model')
|
||||||
|
db.deleteObjectStore('custom')
|
||||||
|
}
|
||||||
|
db.createObjectStore('updates', {autoIncrement: true})
|
||||||
|
db.createObjectStore('model')
|
||||||
|
db.createObjectStore('custom')
|
||||||
|
}
|
||||||
|
request.onerror = function (event) {
|
||||||
|
reject(new Error(event.target.error))
|
||||||
|
}
|
||||||
|
request.onblocked = function () {
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
request.onsuccess = function (event) {
|
||||||
|
const db = event.target.result
|
||||||
|
db.onversionchange = function () { db.close() }
|
||||||
|
resolve(db)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const PREFERRED_TRIM_SIZE = 500
|
||||||
|
|
||||||
|
export default class IndexedDBPersistence extends Y.AbstractPersistence {
|
||||||
|
constructor (opts) {
|
||||||
|
super(opts)
|
||||||
|
window.addEventListener('unload', () => {
|
||||||
|
this.ys.forEach(function (cnf, y) {
|
||||||
|
if (cnf.db !== null) {
|
||||||
|
cnf.db.close()
|
||||||
|
} else {
|
||||||
|
cnf._db.then(db => db.close())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
init (y) {
|
||||||
|
let cnf = this.ys.get(y)
|
||||||
|
let room = y.room
|
||||||
|
cnf.db = null
|
||||||
|
const dbOpened = openDB(room)
|
||||||
|
dbOpened.then(db => {
|
||||||
|
cnf.db = db
|
||||||
|
})
|
||||||
|
if (typeof BroadcastChannel !== 'undefined') {
|
||||||
|
cnf.channel = new BroadcastChannel('__yjs__' + room)
|
||||||
|
cnf.channel.addEventListener('message', e => {
|
||||||
|
cnf.mutualExclude(function () {
|
||||||
|
y.transact(function () {
|
||||||
|
Y.utils.integrateRemoteStructs(y, new Y.utils.BinaryDecoder(e.data))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
cnf.channel = null
|
||||||
|
}
|
||||||
|
return dbOpened
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit (y) {
|
||||||
|
let cnf = this.ys.get(y)
|
||||||
|
cnf.db.close()
|
||||||
|
super.deinit(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
set (y, key, value) {
|
||||||
|
const cnf = this.ys.get(y)
|
||||||
|
const t = cnf.db.transaction(['custom'], 'readwrite')
|
||||||
|
const customStore = t.objectStore('custom')
|
||||||
|
return rtop(customStore.put(value, key))
|
||||||
|
}
|
||||||
|
|
||||||
|
get (y, key) {
|
||||||
|
const cnf = this.ys.get(y)
|
||||||
|
const t = cnf.db.transaction(['custom'], 'readwrite')
|
||||||
|
const customStore = t.objectStore('custom')
|
||||||
|
return rtop(customStore.get(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all persisted data that belongs to a room.
|
||||||
|
* Automatically destroys all Yjs all Yjs instances that persist to
|
||||||
|
* the room. If `destroyYjsInstances = false` the persistence functionality
|
||||||
|
* will be removed from the Yjs instances.
|
||||||
|
*/
|
||||||
|
removePersistedData (room, destroyYjsInstances = true) {
|
||||||
|
super.removePersistedData(room, destroyYjsInstances)
|
||||||
|
return rtop(indexedDB.deleteDatabase(room))
|
||||||
|
}
|
||||||
|
|
||||||
|
saveUpdate (y, update) {
|
||||||
|
let cnf = this.ys.get(y)
|
||||||
|
if (cnf.channel !== null) {
|
||||||
|
cnf.channel.postMessage(update)
|
||||||
|
}
|
||||||
|
let t = cnf.db.transaction(['updates'], 'readwrite')
|
||||||
|
let updatesStore = t.objectStore('updates')
|
||||||
|
updatesStore.put(update)
|
||||||
|
let cntP = rtop(updatesStore.count())
|
||||||
|
cntP.then(cnt => {
|
||||||
|
if (cnt >= PREFERRED_TRIM_SIZE) {
|
||||||
|
this.persist(y)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
saveStruct (y, struct) {
|
||||||
|
super.saveStruct(y, struct)
|
||||||
|
}
|
||||||
|
|
||||||
|
retrieve (y) {
|
||||||
|
let cnf = this.ys.get(y)
|
||||||
|
let t = cnf.db.transaction(['updates', 'model'], 'readonly')
|
||||||
|
let modelStore = t.objectStore('model')
|
||||||
|
let updatesStore = t.objectStore('updates')
|
||||||
|
return Promise.all([rtop(modelStore.get(0)), rtop(updatesStore.getAll())])
|
||||||
|
.then(([model, updates]) => {
|
||||||
|
super.retrieve(y, model, updates)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
persist (y) {
|
||||||
|
let cnf = this.ys.get(y)
|
||||||
|
let db = cnf.db
|
||||||
|
let t = db.transaction(['updates', 'model'], 'readwrite')
|
||||||
|
let updatesStore = t.objectStore('updates')
|
||||||
|
return rtop(updatesStore.getAll())
|
||||||
|
.then(updates => {
|
||||||
|
// apply pending updates before deleting them
|
||||||
|
Y.AbstractPersistence.prototype.retrieve.call(this, y, null, updates)
|
||||||
|
// get binary model
|
||||||
|
let binaryModel = Y.AbstractPersistence.prototype.persist.call(this, y)
|
||||||
|
// delete all pending updates
|
||||||
|
if (updates.length > 0) {
|
||||||
|
let modelStore = t.objectStore('model')
|
||||||
|
modelStore.put(binaryModel, 0)
|
||||||
|
updatesStore.clear()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof Y !== 'undefined') {
|
||||||
|
extendYIndexedDBPersistence(Y) // eslint-disable-line
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
import Tree from '../Util/Tree.js'
|
import Tree from '../Util/Tree.mjs'
|
||||||
import ID from '../Util/ID/ID.js'
|
import ID from '../Util/ID/ID.mjs'
|
||||||
|
|
||||||
class DSNode {
|
class DSNode {
|
||||||
constructor (id, len, gc) {
|
constructor (id, len, gc) {
|
@ -1,8 +1,8 @@
|
|||||||
import Tree from '../Util/Tree.js'
|
import Tree from '../Util/Tree.mjs'
|
||||||
import RootID from '../Util/ID/RootID.js'
|
import RootID from '../Util/ID/RootID.mjs'
|
||||||
import { getStruct } from '../Util/structReferences.js'
|
import { getStruct } from '../Util/structReferences.mjs'
|
||||||
import { logID } from '../MessageHandler/messageToString.js'
|
import { logID } from '../MessageHandler/messageToString.mjs'
|
||||||
import GC from '../Struct/GC.js'
|
import GC from '../Struct/GC.mjs'
|
||||||
|
|
||||||
export default class OperationStore extends Tree {
|
export default class OperationStore extends Tree {
|
||||||
constructor (y) {
|
constructor (y) {
|
@ -1,4 +1,4 @@
|
|||||||
import ID from '../Util/ID/ID.js'
|
import ID from '../Util/ID/ID.mjs'
|
||||||
|
|
||||||
export default class StateStore {
|
export default class StateStore {
|
||||||
constructor (y) {
|
constructor (y) {
|
@ -1,6 +1,7 @@
|
|||||||
import { getStructReference } from '../Util/structReferences.js'
|
import { getStructReference } from '../Util/structReferences.mjs'
|
||||||
import ID from '../Util/ID/ID.js'
|
import ID from '../Util/ID/ID.mjs'
|
||||||
import { logID } from '../MessageHandler/messageToString.js'
|
import { logID } from '../MessageHandler/messageToString.mjs'
|
||||||
|
import { writeStructToTransaction } from '../Transaction.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@ -108,6 +109,7 @@ export default class Delete {
|
|||||||
if (y.persistence !== null) {
|
if (y.persistence !== null) {
|
||||||
y.persistence.saveStruct(y, this)
|
y.persistence.saveStruct(y, this)
|
||||||
}
|
}
|
||||||
|
writeStructToTransaction(y._transaction, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
@ -1,6 +1,7 @@
|
|||||||
import { getStructReference } from '../Util/structReferences.js'
|
import { getStructReference } from '../Util/structReferences.mjs'
|
||||||
import { RootFakeUserID } from '../Util/ID/RootID.js'
|
import { RootFakeUserID } from '../Util/ID/RootID.mjs'
|
||||||
import ID from '../Util/ID/ID.js'
|
import ID from '../Util/ID/ID.mjs'
|
||||||
|
import { writeStructToTransaction } from '../Transaction.mjs'
|
||||||
|
|
||||||
// TODO should have the same base class as Item
|
// TODO should have the same base class as Item
|
||||||
export default class GC {
|
export default class GC {
|
||||||
@ -43,6 +44,7 @@ export default class GC {
|
|||||||
if (y.persistence !== null) {
|
if (y.persistence !== null) {
|
||||||
y.persistence.saveStruct(y, this)
|
y.persistence.saveStruct(y, this)
|
||||||
}
|
}
|
||||||
|
writeStructToTransaction(y._transaction, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
|||||||
import { getStructReference } from '../Util/structReferences.js'
|
import { getStructReference } from '../Util/structReferences.mjs'
|
||||||
import ID from '../Util/ID/ID.js'
|
import ID from '../Util/ID/ID.mjs'
|
||||||
import { default as RootID, RootFakeUserID } from '../Util/ID/RootID.js'
|
import { default as RootID, RootFakeUserID } from '../Util/ID/RootID.mjs'
|
||||||
import Delete from './Delete.js'
|
import Delete from './Delete.mjs'
|
||||||
import { transactionTypeChanged } from '../Transaction.js'
|
import { transactionTypeChanged, writeStructToTransaction } from '../Transaction.mjs'
|
||||||
import GC from './GC.js'
|
import GC from './GC.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@ -385,6 +385,7 @@ export default class Item {
|
|||||||
if (y.persistence !== null) {
|
if (y.persistence !== null) {
|
||||||
y.persistence.saveStruct(y, this)
|
y.persistence.saveStruct(y, this)
|
||||||
}
|
}
|
||||||
|
writeStructToTransaction(y._transaction, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
import { default as Item } from './Item.js'
|
import Item from './Item.mjs'
|
||||||
import { logItemHelper } from '../MessageHandler/messageToString.js'
|
import { logItemHelper } from '../MessageHandler/messageToString.mjs'
|
||||||
|
|
||||||
export default class ItemEmbed extends Item {
|
export default class ItemEmbed extends Item {
|
||||||
constructor () {
|
constructor () {
|
@ -1,5 +1,5 @@
|
|||||||
import { default as Item } from './Item.js'
|
import Item from './Item.mjs'
|
||||||
import { logItemHelper } from '../MessageHandler/messageToString.js'
|
import { logItemHelper } from '../MessageHandler/messageToString.mjs'
|
||||||
|
|
||||||
export default class ItemFormat extends Item {
|
export default class ItemFormat extends Item {
|
||||||
constructor () {
|
constructor () {
|
@ -1,5 +1,5 @@
|
|||||||
import { splitHelper, default as Item } from './Item.js'
|
import Item, { splitHelper } from './Item.mjs'
|
||||||
import { logItemHelper } from '../MessageHandler/messageToString.js'
|
import { logItemHelper } from '../MessageHandler/messageToString.mjs'
|
||||||
|
|
||||||
export default class ItemJSON extends Item {
|
export default class ItemJSON extends Item {
|
||||||
constructor () {
|
constructor () {
|
@ -1,5 +1,5 @@
|
|||||||
import { splitHelper, default as Item } from './Item.js'
|
import Item, { splitHelper } from './Item.mjs'
|
||||||
import { logItemHelper } from '../MessageHandler/messageToString.js'
|
import { logItemHelper } from '../MessageHandler/messageToString.mjs'
|
||||||
|
|
||||||
export default class ItemString extends Item {
|
export default class ItemString extends Item {
|
||||||
constructor () {
|
constructor () {
|
@ -1,6 +1,6 @@
|
|||||||
import Item from './Item.js'
|
import Item from './Item.mjs'
|
||||||
import EventHandler from '../Util/EventHandler.js'
|
import EventHandler from '../Util/EventHandler.mjs'
|
||||||
import ID from '../Util/ID/ID.js'
|
import ID from '../Util/ID/ID.mjs'
|
||||||
|
|
||||||
// restructure children as if they were inserted one after another
|
// restructure children as if they were inserted one after another
|
||||||
function integrateChildren (y, start) {
|
function integrateChildren (y, start) {
|
@ -1,3 +1,4 @@
|
|||||||
|
import BinaryEncoder from './Util/Binary/Encoder.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A transaction is created for every change on the Yjs model. It is possible
|
* A transaction is created for every change on the Yjs model. It is possible
|
||||||
@ -58,7 +59,19 @@ export default class Transaction {
|
|||||||
* @type {Map<YType,Array<YEvent>>}
|
* @type {Map<YType,Array<YEvent>>}
|
||||||
*/
|
*/
|
||||||
this.changedParentTypes = new Map()
|
this.changedParentTypes = new Map()
|
||||||
|
this.encodedStructsLen = 0
|
||||||
|
this._encodedStructs = new BinaryEncoder()
|
||||||
|
this._encodedStructs.writeUint32(0)
|
||||||
}
|
}
|
||||||
|
get encodedStructs () {
|
||||||
|
this._encodedStructs.setUint32(0, this.encodedStructsLen)
|
||||||
|
return this._encodedStructs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeStructToTransaction (transaction, struct) {
|
||||||
|
transaction.encodedStructsLen++
|
||||||
|
struct._toBinary(transaction._encodedStructs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
@ -1,8 +1,8 @@
|
|||||||
import Type from '../../Struct/Type.js'
|
import Type from '../../Struct/Type.mjs'
|
||||||
import ItemJSON from '../../Struct/ItemJSON.js'
|
import ItemJSON from '../../Struct/ItemJSON.mjs'
|
||||||
import ItemString from '../../Struct/ItemString.js'
|
import ItemString from '../../Struct/ItemString.mjs'
|
||||||
import { logID, logItemHelper } from '../../MessageHandler/messageToString.js'
|
import { logID, logItemHelper } from '../../MessageHandler/messageToString.mjs'
|
||||||
import YEvent from '../../Util/YEvent.js'
|
import YEvent from '../../Util/YEvent.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event that describes the changes on a YArray
|
* Event that describes the changes on a YArray
|
@ -1,8 +1,8 @@
|
|||||||
import Type from '../../Struct/Type.js'
|
import Item from '../../Struct/Item.mjs'
|
||||||
import Item from '../../Struct/Item.js'
|
import Type from '../../Struct/Type.mjs'
|
||||||
import ItemJSON from '../../Struct/ItemJSON.js'
|
import ItemJSON from '../../Struct/ItemJSON.mjs'
|
||||||
import { logItemHelper } from '../../MessageHandler/messageToString.js'
|
import { logItemHelper } from '../../MessageHandler/messageToString.mjs'
|
||||||
import YEvent from '../../Util/YEvent.js'
|
import YEvent from '../../Util/YEvent.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event that describes the changes on a YMap.
|
* Event that describes the changes on a YMap.
|
@ -1,8 +1,8 @@
|
|||||||
import ItemString from '../../Struct/ItemString.js'
|
import ItemEmbed from '../../Struct/ItemEmbed.mjs'
|
||||||
import ItemEmbed from '../../Struct/ItemEmbed.js'
|
import ItemString from '../../Struct/ItemString.mjs'
|
||||||
import ItemFormat from '../../Struct/ItemFormat.js'
|
import ItemFormat from '../../Struct/ItemFormat.mjs'
|
||||||
import { logItemHelper } from '../../MessageHandler/messageToString.js'
|
import { logItemHelper } from '../../MessageHandler/messageToString.mjs'
|
||||||
import { YArrayEvent, default as YArray } from '../YArray/YArray.js'
|
import { YArrayEvent, default as YArray } from '../YArray/YArray.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
@ -1,6 +1,6 @@
|
|||||||
import YMap from '../YMap/YMap.js'
|
import YMap from '../YMap/YMap.mjs'
|
||||||
import YXmlFragment from './YXmlFragment.js'
|
import YXmlFragment from './YXmlFragment.mjs'
|
||||||
import { createAssociation } from '../../Bindings/DomBinding/util.js'
|
import { createAssociation } from '../../Bindings/DomBinding/util.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An YXmlElement imitates the behavior of a
|
* An YXmlElement imitates the behavior of a
|
@ -1,4 +1,4 @@
|
|||||||
import YEvent from '../../Util/YEvent.js'
|
import YEvent from '../../Util/YEvent.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An Event that describes changes on a YXml Element or Yxml Fragment
|
* An Event that describes changes on a YXml Element or Yxml Fragment
|
@ -1,9 +1,9 @@
|
|||||||
import { createAssociation } from '../../Bindings/DomBinding/util.js'
|
import { createAssociation } from '../../Bindings/DomBinding/util.mjs'
|
||||||
import YXmlTreeWalker from './YXmlTreeWalker.js'
|
import YXmlTreeWalker from './YXmlTreeWalker.mjs'
|
||||||
|
|
||||||
import YArray from '../YArray/YArray.js'
|
import YArray from '../YArray/YArray.mjs'
|
||||||
import YXmlEvent from './YXmlEvent.js'
|
import YXmlEvent from './YXmlEvent.mjs'
|
||||||
import { logItemHelper } from '../../MessageHandler/messageToString.js'
|
import { logItemHelper } from '../../MessageHandler/messageToString.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dom filter function.
|
* Dom filter function.
|
@ -1,5 +1,5 @@
|
|||||||
import YMap from '../YMap/YMap.js'
|
import YMap from '../YMap/YMap.mjs'
|
||||||
import { createAssociation } from '../../Bindings/DomBinding/util.js'
|
import { createAssociation } from '../../Bindings/DomBinding/util.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* You can manage binding to a custom type with YXmlHook.
|
* You can manage binding to a custom type with YXmlHook.
|
@ -1,5 +1,5 @@
|
|||||||
import YText from '../YText/YText.js'
|
import YText from '../YText/YText.mjs'
|
||||||
import { createAssociation } from '../../Bindings/DomBinding/util.js'
|
import { createAssociation } from '../../Bindings/DomBinding/util.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents text in a Dom Element. In the future this type will also handle
|
* Represents text in a Dom Element. In the future this type will also handle
|
@ -1,4 +1,4 @@
|
|||||||
import YXmlFragment from './YXmlFragment.js'
|
import YXmlFragment from './YXmlFragment.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the elements to which a set of CSS queries apply.
|
* Define the elements to which a set of CSS queries apply.
|
@ -1,5 +1,5 @@
|
|||||||
import ID from '../ID/ID.js'
|
import ID from '../ID/ID.mjs'
|
||||||
import { default as RootID, RootFakeUserID } from '../ID/RootID.js'
|
import { default as RootID, RootFakeUserID } from '../ID/RootID.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A BinaryDecoder handles the decoding of an ArrayBuffer.
|
* A BinaryDecoder handles the decoding of an ArrayBuffer.
|
||||||
@ -25,6 +25,10 @@ export default class BinaryDecoder {
|
|||||||
this.pos = 0
|
this.pos = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasContent () {
|
||||||
|
return this.pos !== this.uint8arr.length
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clone this decoder instance.
|
* Clone this decoder instance.
|
||||||
* Optionally set a new position parameter.
|
* Optionally set a new position parameter.
|
@ -1,4 +1,4 @@
|
|||||||
import { RootFakeUserID } from '../ID/RootID.js'
|
import { RootFakeUserID } from '../ID/RootID.mjs'
|
||||||
|
|
||||||
const bits7 = 0b1111111
|
const bits7 = 0b1111111
|
||||||
const bits8 = 0b11111111
|
const bits8 = 0b11111111
|
||||||
@ -127,6 +127,15 @@ export default class BinaryEncoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the content of another binary encoder.
|
||||||
|
*
|
||||||
|
* @param encoder The BinaryEncoder to be written.
|
||||||
|
*/
|
||||||
|
writeBinaryEncoder (encoder) {
|
||||||
|
this.data = this.data.concat(encoder.data)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write an ID at the current position.
|
* Write an ID at the current position.
|
||||||
*
|
*
|
@ -1,4 +1,4 @@
|
|||||||
import { getStructReference } from '../structReferences.js'
|
import { getStructReference } from '../structReferences.mjs'
|
||||||
|
|
||||||
export const RootFakeUserID = 0xFFFFFF
|
export const RootFakeUserID = 0xFFFFFF
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
import ID from './ID/ID.js'
|
import ID from './ID/ID.mjs'
|
||||||
import isParentOf from './isParentOf.js'
|
import isParentOf from './isParentOf.mjs'
|
||||||
|
|
||||||
class ReverseOperation {
|
class ReverseOperation {
|
||||||
constructor (y, transaction) {
|
constructor (y, transaction) {
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import ID from '../Util/ID/ID.js'
|
import ID from '../Util/ID/ID.mjs'
|
||||||
import ItemJSON from '../Struct/ItemJSON.js'
|
import ItemJSON from '../Struct/ItemJSON.mjs'
|
||||||
import ItemString from '../Struct/ItemString.js'
|
import ItemString from '../Struct/ItemString.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to merge all items in os with their successors.
|
* Try to merge all items in os with their successors.
|
@ -1,6 +1,6 @@
|
|||||||
import ID from './ID/ID.js'
|
import ID from './ID/ID.mjs'
|
||||||
import RootID from './ID/RootID.js'
|
import RootID from './ID/RootID.mjs'
|
||||||
import GC from '../Struct/GC.js'
|
import GC from '../Struct/GC.mjs'
|
||||||
|
|
||||||
// TODO: Implement function to describe ranges
|
// TODO: Implement function to describe ranges
|
||||||
|
|
@ -1,17 +1,17 @@
|
|||||||
import YArray from '../Types/YArray/YArray.js'
|
import Delete from '../Struct/Delete.mjs'
|
||||||
import YMap from '../Types/YMap/YMap.js'
|
import ItemJSON from '../Struct/ItemJSON.mjs'
|
||||||
import YText from '../Types/YText/YText.js'
|
import ItemString from '../Struct/ItemString.mjs'
|
||||||
import YXmlText from '../Types/YXml/YXmlText.js'
|
import ItemFormat from '../Struct/ItemFormat.mjs'
|
||||||
import YXmlHook from '../Types/YXml/YXmlHook.js'
|
import ItemEmbed from '../Struct/ItemEmbed.mjs'
|
||||||
import YXmlFragment from '../Types/YXml/YXmlFragment.js'
|
import GC from '../Struct/GC.mjs'
|
||||||
import YXmlElement from '../Types/YXml/YXmlElement.js'
|
|
||||||
|
|
||||||
import Delete from '../Struct/Delete.js'
|
import YArray from '../Types/YArray/YArray.mjs'
|
||||||
import ItemJSON from '../Struct/ItemJSON.js'
|
import YMap from '../Types/YMap/YMap.mjs'
|
||||||
import ItemString from '../Struct/ItemString.js'
|
import YText from '../Types/YText/YText.mjs'
|
||||||
import ItemFormat from '../Struct/ItemFormat.js'
|
import YXmlText from '../Types/YXml/YXmlText.mjs'
|
||||||
import ItemEmbed from '../Struct/ItemEmbed.js'
|
import YXmlHook from '../Types/YXml/YXmlHook.mjs'
|
||||||
import GC from '../Struct/GC.js'
|
import YXmlFragment from '../Types/YXml/YXmlFragment.mjs'
|
||||||
|
import YXmlElement from '../Types/YXml/YXmlElement.mjs'
|
||||||
|
|
||||||
const structs = new Map()
|
const structs = new Map()
|
||||||
const references = new Map()
|
const references = new Map()
|
@ -1,30 +1,30 @@
|
|||||||
|
|
||||||
import Y from './Y.js'
|
import Y from './Y.mjs'
|
||||||
import UndoManager from './Util/UndoManager.js'
|
import UndoManager from './Util/UndoManager.mjs'
|
||||||
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.js'
|
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.mjs'
|
||||||
|
|
||||||
import { messageToString, messageToRoomname } from './MessageHandler/messageToString.js'
|
import { messageToString, messageToRoomname } from './MessageHandler/messageToString.mjs'
|
||||||
|
|
||||||
import Connector from './Connector.js'
|
import Connector from './Connector.mjs'
|
||||||
import Persistence from './Persistence.js'
|
import Persistence from './Persistence.mjs'
|
||||||
import YArray from './Types/YArray/YArray.js'
|
import YArray from './Types/YArray/YArray.mjs'
|
||||||
import YMap from './Types/YMap/YMap.js'
|
import YMap from './Types/YMap/YMap.mjs'
|
||||||
import YText from './Types/YText/YText.js'
|
import YText from './Types/YText/YText.mjs'
|
||||||
import YXmlText from './Types/YXml/YXmlText.js'
|
import YXmlText from './Types/YXml/YXmlText.mjs'
|
||||||
import YXmlHook from './Types/YXml/YXmlHook.js'
|
import YXmlHook from './Types/YXml/YXmlHook.mjs'
|
||||||
import YXmlFragment from './Types/YXml/YXmlFragment.js'
|
import YXmlFragment from './Types/YXml/YXmlFragment.mjs'
|
||||||
import YXmlElement from './Types/YXml/YXmlElement.js'
|
import YXmlElement from './Types/YXml/YXmlElement.mjs'
|
||||||
import BinaryDecoder from './Util/Binary/Decoder.js'
|
import BinaryDecoder from './Util/Binary/Decoder.mjs'
|
||||||
import { getRelativePosition, fromRelativePosition } from './Util/relativePosition.js'
|
import { getRelativePosition, fromRelativePosition } from './Util/relativePosition.mjs'
|
||||||
import { registerStruct } from './Util/structReferences.js'
|
import { registerStruct } from './Util/structReferences.mjs'
|
||||||
import TextareaBinding from './Bindings/TextareaBinding/TextareaBinding.js'
|
import TextareaBinding from './Bindings/TextareaBinding/TextareaBinding.mjs'
|
||||||
import QuillBinding from './Bindings/QuillBinding/QuillBinding.js'
|
import QuillBinding from './Bindings/QuillBinding/QuillBinding.mjs'
|
||||||
import DomBinding from './Bindings/DomBinding/DomBinding.js'
|
import DomBinding from './Bindings/DomBinding/DomBinding.mjs'
|
||||||
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.js'
|
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.mjs'
|
||||||
|
|
||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import domToType from './Bindings/DomBinding/domToType.js'
|
import domToType from './Bindings/DomBinding/domToType.mjs'
|
||||||
import { domsToTypes, switchAssociation } from './Bindings/DomBinding/util.js'
|
import { domsToTypes, switchAssociation } from './Bindings/DomBinding/util.mjs'
|
||||||
|
|
||||||
// TODO: The following assignments should be moved to yjs-dist
|
// TODO: The following assignments should be moved to yjs-dist
|
||||||
Y.AbstractConnector = Connector
|
Y.AbstractConnector = Connector
|
@ -1,12 +1,12 @@
|
|||||||
import DeleteStore from './Store/DeleteStore.js'
|
import DeleteStore from './Store/DeleteStore.mjs'
|
||||||
import OperationStore from './Store/OperationStore.js'
|
import OperationStore from './Store/OperationStore.mjs'
|
||||||
import StateStore from './Store/StateStore.js'
|
import StateStore from './Store/StateStore.mjs'
|
||||||
import { generateRandomUint32 } from './Util/generateRandomUint32.js'
|
import { generateRandomUint32 } from './Util/generateRandomUint32.mjs'
|
||||||
import RootID from './Util/ID/RootID.js'
|
import RootID from './Util/ID/RootID.mjs'
|
||||||
import NamedEventHandler from './Util/NamedEventHandler.js'
|
import NamedEventHandler from './Util/NamedEventHandler.mjs'
|
||||||
import Transaction from './Transaction.js'
|
import Transaction from './Transaction.mjs'
|
||||||
|
|
||||||
export { default as DomBinding } from './Bindings/DomBinding/DomBinding.js'
|
export { default as DomBinding } from './Bindings/DomBinding/DomBinding.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Anything that can be encoded with `JSON.stringify` and can be decoded with
|
* Anything that can be encoded with `JSON.stringify` and can be decoded with
|
||||||
@ -28,7 +28,7 @@ export { default as DomBinding } from './Bindings/DomBinding/DomBinding.js'
|
|||||||
* @param {AbstractPersistence} persistence Persistence adapter instance
|
* @param {AbstractPersistence} persistence Persistence adapter instance
|
||||||
*/
|
*/
|
||||||
export default class Y extends NamedEventHandler {
|
export default class Y extends NamedEventHandler {
|
||||||
constructor (room, opts, persistence, conf = {}) {
|
constructor (room, connector, persistence, conf = {}) {
|
||||||
super()
|
super()
|
||||||
this.gcEnabled = conf.gc || false
|
this.gcEnabled = conf.gc || false
|
||||||
/**
|
/**
|
||||||
@ -36,16 +36,8 @@ export default class Y extends NamedEventHandler {
|
|||||||
* @type {String}
|
* @type {String}
|
||||||
*/
|
*/
|
||||||
this.room = room
|
this.room = room
|
||||||
if (opts != null) {
|
|
||||||
opts.connector.room = room
|
|
||||||
}
|
|
||||||
this._contentReady = false
|
this._contentReady = false
|
||||||
this._opts = opts
|
this.userID = generateRandomUint32()
|
||||||
if (typeof opts.userID !== 'number') {
|
|
||||||
this.userID = generateRandomUint32()
|
|
||||||
} else {
|
|
||||||
this.userID = opts.userID
|
|
||||||
}
|
|
||||||
// TODO: This should be a Map so we can use encodables as keys
|
// TODO: This should be a Map so we can use encodables as keys
|
||||||
this.share = {}
|
this.share = {}
|
||||||
this.ds = new DeleteStore(this)
|
this.ds = new DeleteStore(this)
|
||||||
@ -61,10 +53,13 @@ export default class Y extends NamedEventHandler {
|
|||||||
this.connector = null
|
this.connector = null
|
||||||
this.connected = false
|
this.connected = false
|
||||||
let initConnection = () => {
|
let initConnection = () => {
|
||||||
if (opts != null) {
|
if (connector != null) {
|
||||||
this.connector = new Y[opts.connector.name](this, opts.connector)
|
if (connector.constructor === Object) {
|
||||||
this.connected = true
|
connector.connector.room = room
|
||||||
this.emit('connectorReady')
|
this.connector = new Y[connector.connector.name](this, connector.connector)
|
||||||
|
this.connected = true
|
||||||
|
this.emit('connectorReady')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
@ -1,7 +1,7 @@
|
|||||||
import { test } from '../node_modules/cutest/cutest.mjs'
|
import { test } from '../node_modules/cutest/cutest.mjs'
|
||||||
import Chance from 'chance'
|
import Chance from 'chance'
|
||||||
import DeleteStore from '../src/Store/DeleteStore.js'
|
import DeleteStore from '../src/Store/DeleteStore.mjs'
|
||||||
import ID from '../src/Util/ID/ID.js'
|
import ID from '../src/Util/ID/ID.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a DS to an array of length 10.
|
* Converts a DS to an array of length 10.
|
@ -1,5 +1,5 @@
|
|||||||
import { test } from '../node_modules/cutest/cutest.mjs'
|
import { test } from '../node_modules/cutest/cutest.mjs'
|
||||||
import simpleDiff from '../src/Util/simpleDiff.js'
|
import simpleDiff from '../src/Util/simpleDiff.mjs'
|
||||||
import Chance from 'chance'
|
import Chance from 'chance'
|
||||||
|
|
||||||
function runDiffTest (t, a, b, expected) {
|
function runDiffTest (t, a, b, expected) {
|
@ -1,7 +1,7 @@
|
|||||||
import { test } from '../node_modules/cutest/cutest.mjs'
|
import { test } from '../node_modules/cutest/cutest.mjs'
|
||||||
import BinaryEncoder from '../src/Util/Binary/Encoder.js'
|
import BinaryEncoder from '../src/Util/Binary/Encoder.mjs'
|
||||||
import BinaryDecoder from '../src/Util/Binary/Decoder.js'
|
import BinaryDecoder from '../src/Util/Binary/Decoder.mjs'
|
||||||
import { generateRandomUint32 } from '../src/Util/generateRandomUint32.js'
|
import { generateRandomUint32 } from '../src/Util/generateRandomUint32.mjs'
|
||||||
import Chance from 'chance'
|
import Chance from 'chance'
|
||||||
|
|
||||||
function testEncoding (t, write, read, val) {
|
function testEncoding (t, write, read, val) {
|
@ -1,7 +0,0 @@
|
|||||||
import './red-black-tree.js'
|
|
||||||
import './y-array.tests.js'
|
|
||||||
import './y-text.tests.js'
|
|
||||||
import './y-map.tests.js'
|
|
||||||
import './y-xml.tests.js'
|
|
||||||
import './encode-decode.tests.js'
|
|
||||||
import './diff.tests.js'
|
|
7
test/index.mjs
Normal file
7
test/index.mjs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import './red-black-tree.mjs'
|
||||||
|
import './y-array.tests.mjs'
|
||||||
|
import './y-text.tests.mjs'
|
||||||
|
import './y-map.tests.mjs'
|
||||||
|
import './y-xml.tests.mjs'
|
||||||
|
import './encode-decode.tests.mjs'
|
||||||
|
import './diff.tests.mjs'
|
@ -1,5 +1,5 @@
|
|||||||
import RedBlackTree from '../src/Util/Tree.js'
|
import RedBlackTree from '../src/Util/Tree.mjs'
|
||||||
import ID from '../src/Util/ID/ID.js'
|
import ID from '../src/Util/ID/ID.mjs'
|
||||||
import Chance from 'chance'
|
import Chance from 'chance'
|
||||||
import { test, proxyConsole } from 'cutest'
|
import { test, proxyConsole } from 'cutest'
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
import { wait, initArrays, compareUsers, Y, flushAll, applyRandomTests } from '../tests-lib/helper.js'
|
import { wait, initArrays, compareUsers, Y, flushAll, applyRandomTests } from '../tests-lib/helper.mjs'
|
||||||
import { test, proxyConsole } from 'cutest'
|
import { test, proxyConsole } from 'cutest'
|
||||||
|
|
||||||
proxyConsole()
|
proxyConsole()
|
@ -1,4 +1,4 @@
|
|||||||
import { initArrays, compareUsers, Y, flushAll, applyRandomTests } from '../tests-lib/helper.js'
|
import { initArrays, compareUsers, Y, flushAll, applyRandomTests } from '../tests-lib/helper.mjs'
|
||||||
import { test, proxyConsole } from 'cutest'
|
import { test, proxyConsole } from 'cutest'
|
||||||
|
|
||||||
proxyConsole()
|
proxyConsole()
|
@ -1,4 +1,4 @@
|
|||||||
import { initArrays, compareUsers, flushAll } from '../tests-lib/helper.js'
|
import { initArrays, compareUsers, flushAll } from '../tests-lib/helper.mjs'
|
||||||
import { test, proxyConsole } from 'cutest'
|
import { test, proxyConsole } from 'cutest'
|
||||||
|
|
||||||
proxyConsole()
|
proxyConsole()
|
@ -1,4 +1,4 @@
|
|||||||
import { wait, initArrays, compareUsers, Y, flushAll, applyRandomTests } from '../../yjs/tests-lib/helper.js'
|
import { wait, initArrays, compareUsers, Y, flushAll, applyRandomTests } from '../../yjs/tests-lib/helper.mjs'
|
||||||
import { test } from 'cutest'
|
import { test } from 'cutest'
|
||||||
|
|
||||||
test('set property', async function xml0 (t) {
|
test('set property', async function xml0 (t) {
|
@ -1,14 +1,14 @@
|
|||||||
|
|
||||||
import _Y from '../src/Y.dist.js'
|
import _Y from '../src/Y.dist.mjs'
|
||||||
import { DomBinding } from '../src/Y.js'
|
import { DomBinding } from '../src/Y.mjs'
|
||||||
import TestConnector from './test-connector.js'
|
import TestConnector from './test-connector.mjs'
|
||||||
|
|
||||||
import Chance from 'chance'
|
import Chance from 'chance'
|
||||||
import ItemJSON from '../src/Struct/ItemJSON.js'
|
import ItemJSON from '../src/Struct/ItemJSON.mjs'
|
||||||
import ItemString from '../src/Struct/ItemString.js'
|
import ItemString from '../src/Struct/ItemString.mjs'
|
||||||
import { defragmentItemContent } from '../src/Util/defragmentItemContent.js'
|
import { defragmentItemContent } from '../src/Util/defragmentItemContent.mjs'
|
||||||
import Quill from 'quill'
|
import Quill from 'quill'
|
||||||
import GC from '../src/Struct/GC.js'
|
import GC from '../src/Struct/GC.mjs'
|
||||||
|
|
||||||
export const Y = _Y
|
export const Y = _Y
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
import { wait } from './helper'
|
import { wait } from './helper'
|
||||||
import { messageToString } from '../src/MessageHandler/messageToString'
|
import { messageToString } from '../src/MessageHandler/messageToString'
|
||||||
import AbstractConnector from '../src/Connector.js'
|
import AbstractConnector from '../src/Connector.mjs'
|
||||||
|
|
||||||
var rooms = {}
|
var rooms = {}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user