implemented experimental websockets-connector

This commit is contained in:
Kevin Jahns 2018-05-23 14:01:00 +02:00
parent 684d38d6c8
commit cccc0e1015
86 changed files with 1646 additions and 795 deletions

View File

@ -1,9 +1,7 @@
<!DOCTYPE html>
<html>
</head>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="./index.js"></script>
<script src="./index.mjs" type="module"></script>
</head>
<body contenteditable="true">
</body>

View File

@ -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()
}
}

View 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()
}
}

View File

@ -1,7 +1,7 @@
import Y from '../src/Y.js'
import yWebsocketsClient from '../../y-websockets-client/src/y-websockets-client.js'
import extendYIndexedDBPersistence from '../../y-indexeddb/src/y-indexeddb.js'
import Y from '../src/Y..mjs'
import yWebsocketsClient from '../../y-websockets-client/src/y-websockets-client.mjs'
import extendYIndexedDBPersistence from '../../y-indexeddb/src/y-indexeddb.mjs'
Y.extend(yWebsocketsClient)
extendYIndexedDBPersistence(Y)

1349
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
"scripts": {
"test": "npm run lint",
"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",
"serve-docs": "npm run docs && serve ./docs/",
"dist": "rollup -c rollup.browser.js; rollup -c rollup.node.js",
@ -55,6 +55,7 @@
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-latest": "^6.24.1",
"chance": "^1.0.9",
"codemirror": "^5.37.0",
"concurrently": "^3.4.0",
"cutest": "^0.1.9",
"esdoc": "^1.0.4",
@ -70,7 +71,8 @@
"rollup-plugin-uglify": "^1.0.2",
"rollup-regenerator-runtime": "^6.23.1",
"rollup-watch": "^3.2.2",
"standard": "^10.0.2",
"tag-dist-files": "^0.1.6"
"standard": "^11.0.1",
"tag-dist-files": "^0.1.6",
"uws": "^10.148.0"
}
}

View File

@ -5,7 +5,7 @@ import commonjs from 'rollup-plugin-commonjs'
var pkg = require('./package.json')
export default {
input: 'src/Y.dist.js',
input: 'src/Y.dist.mjs',
name: 'Y',
sourcemap: true,
output: {

View File

@ -3,7 +3,7 @@ import commonjs from 'rollup-plugin-commonjs'
var pkg = require('./package.json')
export default {
input: 'src/Y.dist.js',
input: 'src/Y.dist.mjs',
nameame: 'Y',
sourcemap: true,
output: {

View File

@ -3,7 +3,7 @@ import commonjs from 'rollup-plugin-commonjs'
import multiEntry from 'rollup-plugin-multi-entry'
export default {
input: 'test/index.js',
input: 'test/index.mjs',
name: 'y-tests',
sourcemap: true,
output: {

View File

@ -1,5 +1,5 @@
import { createMutualExclude } from '../Util/mutualExclude.js'
import { createMutualExclude } from '../Util/mutualExclude.mjs'
/**
* Abstract class for bindings.

View File

@ -1,7 +1,7 @@
import Binding from '../Binding.js'
import simpleDiff from '../../Util/simpleDiff.js'
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.js'
import Binding from '../Binding.mjs'
import simpleDiff from '../../Util/simpleDiff.mjs'
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.mjs'
function typeObserver () {
this._mutualExclude(() => {

View File

@ -1,11 +1,11 @@
/* global MutationObserver */
import Binding from '../Binding.js'
import { createAssociation, removeAssociation } from './util.js'
import { beforeTransactionSelectionFixer, afterTransactionSelectionFixer } from './selection.js'
import { defaultFilter, applyFilterOnType } from './filter.js'
import typeObserver from './typeObserver.js'
import domObserver from './domObserver.js'
import Binding from '../Binding.mjs'
import { createAssociation, removeAssociation } from './util.mjs'
import { beforeTransactionSelectionFixer, afterTransactionSelectionFixer } from './selection.mjs'
import { defaultFilter, applyFilterOnType } from './filter.mjs'
import typeObserver from './typeObserver.mjs'
import domObserver from './domObserver.mjs'
/**
* A binding that binds the children of a YXmlFragment to a DOM element.

View File

@ -1,11 +1,11 @@
import YXmlHook from '../../Types/YXml/YXmlHook.js'
import YXmlHook from '../../Types/YXml/YXmlHook.mjs'
import {
iterateUntilUndeleted,
removeAssociation,
insertNodeHelper } from './util.js'
import diff from '../../Util/simpleDiff.js'
import YXmlFragment from '../../Types/YXml/YXmlFragment.js'
insertNodeHelper } from './util.mjs'
import diff from '../../Util/simpleDiff.mjs'
import YXmlFragment from '../../Types/YXml/YXmlFragment.mjs'
/**
* 1. Check if any of the nodes was deleted

View File

@ -1,9 +1,9 @@
import YXmlText from '../../Types/YXml/YXmlText.js'
import YXmlHook from '../../Types/YXml/YXmlHook.js'
import YXmlElement from '../../Types/YXml/YXmlElement.js'
import { createAssociation, domsToTypes } from './util.js'
import { filterDomAttributes, defaultFilter } from './filter.js'
import YXmlText from '../../Types/YXml/YXmlText.mjs'
import YXmlHook from '../../Types/YXml/YXmlHook.mjs'
import YXmlElement from '../../Types/YXml/YXmlElement.mjs'
import { createAssociation, domsToTypes } from './util.mjs'
import { filterDomAttributes, defaultFilter } from './filter.mjs'
/**
* Creates a Yjs type (YXml) based on the contents of a DOM Element.

View File

@ -1,4 +1,4 @@
import isParentOf from '../../Util/isParentOf.js'
import isParentOf from '../../Util/isParentOf.mjs'
/**
* Default filter method (does nothing).

View File

@ -1,6 +1,6 @@
/* globals getSelection */
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.js'
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.mjs'
let browserSelection = null
let relativeSelection = null

View File

@ -1,8 +1,8 @@
/* global getSelection */
import YXmlText from '../../Types/YXml/YXmlText.js'
import YXmlHook from '../../Types/YXml/YXmlHook.js'
import { removeDomChildrenUntilElementFound } from './util.js'
import YXmlText from '../../Types/YXml/YXmlText.mjs'
import YXmlHook from '../../Types/YXml/YXmlHook.mjs'
import { removeDomChildrenUntilElementFound } from './util.mjs'
function findScrollReference (scrollingElement) {
if (scrollingElement !== null) {

View File

@ -1,5 +1,5 @@
import domToType from './domToType.js'
import domToType from './domToType.mjs'
/**
* Iterates items until an undeleted item is found.

View File

@ -1,4 +1,4 @@
import Binding from '../Binding.js'
import Binding from '../Binding.mjs'
function typeObserver (event) {
const quill = this.target

View 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()
}
}

View File

@ -1,9 +1,9 @@
import BinaryEncoder from './Util/Binary/Encoder.js'
import BinaryDecoder from './Util/Binary/Decoder.js'
import BinaryEncoder from './Util/Binary/Encoder.mjs'
import BinaryDecoder from './Util/Binary/Decoder.mjs'
import { sendSyncStep1, readSyncStep1 } from './MessageHandler/syncStep1.js'
import { readSyncStep2 } from './MessageHandler/syncStep2.js'
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.js'
import { sendSyncStep1, readSyncStep1 } from './MessageHandler/syncStep1.mjs'
import { readSyncStep2 } from './MessageHandler/syncStep2.mjs'
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.mjs'
import debug from 'debug'

View 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))
}
}
}

View 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
}

View 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)
}
})
})

View File

@ -1,8 +1,8 @@
import { writeStructs } from './syncStep1.js'
import { integrateRemoteStructs } from './integrateRemoteStructs.js'
import { readDeleteSet, writeDeleteSet } from './deleteSet.js'
import BinaryEncoder from '../Util/Binary/Encoder.js'
import { writeStructs } from './syncStep1.mjs'
import { integrateRemoteStructs } from './integrateRemoteStructs.mjs'
import { readDeleteSet, writeDeleteSet } from './deleteSet.mjs'
import BinaryEncoder from '../Util/Binary/Encoder.mjs'
/**
* Read the Decoder and fill the Yjs instance with data in the decoder.

View File

@ -1,5 +1,5 @@
import { deleteItemRange } from '../Struct/Delete.js'
import ID from '../Util/ID/ID.js'
import { deleteItemRange } from '../Struct/Delete.mjs'
import ID from '../Util/ID/ID.mjs'
export function stringifyDeleteSet (y, decoder, strBuilder) {
let dsLength = decoder.readUint32()

View File

@ -1,7 +1,7 @@
import { getStruct } from '../Util/structReferences.js'
import BinaryDecoder from '../Util/Binary/Decoder.js'
import { logID } from './messageToString.js'
import GC from '../Struct/GC.js'
import { getStruct } from '../Util/structReferences.mjs'
import BinaryDecoder from '../Util/Binary/Decoder.mjs'
import { logID } from './messageToString.mjs'
import GC from '../Struct/GC.mjs'
class MissingEntry {
constructor (decoder, missing, struct) {

View File

@ -1,10 +1,10 @@
import BinaryDecoder from '../Util/Binary/Decoder.js'
import { stringifyStructs } from './integrateRemoteStructs.js'
import { stringifySyncStep1 } from './syncStep1.js'
import { stringifySyncStep2 } from './syncStep2.js'
import ID from '../Util/ID/ID.js'
import RootID from '../Util/ID/RootID.js'
import Y from '../Y.js'
import BinaryDecoder from '../Util/Binary/Decoder.mjs'
import { stringifyStructs } from './integrateRemoteStructs.mjs'
import { stringifySyncStep1 } from './syncStep1.mjs'
import { stringifySyncStep2 } from './syncStep2.mjs'
import ID from '../Util/ID/ID.mjs'
import RootID from '../Util/ID/RootID.mjs'
import Y from '../Y.mjs'
export function messageToString ([y, buffer]) {
let decoder = new BinaryDecoder(buffer)

View File

@ -1,8 +1,8 @@
import BinaryEncoder from '../Util/Binary/Encoder.js'
import { readStateSet, writeStateSet } from './stateSet.js'
import { writeDeleteSet } from './deleteSet.js'
import ID from '../Util/ID/ID.js'
import { RootFakeUserID } from '../Util/ID/RootID.js'
import BinaryEncoder from '../Util/Binary/Encoder.mjs'
import { readStateSet, writeStateSet } from './stateSet.mjs'
import { writeDeleteSet } from './deleteSet.mjs'
import ID from '../Util/ID/ID.mjs'
import { RootFakeUserID } from '../Util/ID/RootID.mjs'
export function stringifySyncStep1 (y, decoder, strBuilder) {
let auth = decoder.readVarString()

View File

@ -1,5 +1,5 @@
import { stringifyStructs, integrateRemoteStructs } from './integrateRemoteStructs.js'
import { readDeleteSet } from './deleteSet.js'
import { stringifyStructs, integrateRemoteStructs } from './integrateRemoteStructs.mjs'
import { readDeleteSet } from './deleteSet.mjs'
export function stringifySyncStep2 (y, decoder, strBuilder) {
strBuilder.push(' - auth: ' + decoder.readVarString())

View File

@ -1,8 +1,8 @@
import BinaryEncoder from './Util/Binary/Encoder.js'
import BinaryDecoder from './Util/Binary/Decoder.js'
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.js'
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.js'
import { createMutualExclude } from './Util/mutualExclude.js'
import BinaryEncoder from './Util/Binary/Encoder.mjs'
import BinaryDecoder from './Util/Binary/Decoder.mjs'
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.mjs'
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.mjs'
import { createMutualExclude } from './Util/mutualExclude.mjs'
function getFreshCnf () {
let buffer = new BinaryEncoder()

View File

@ -0,0 +1,2 @@
export default class AbstractPersistence {}

View File

@ -0,0 +1 @@

View 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
}

View File

@ -1,6 +1,6 @@
import Tree from '../Util/Tree.js'
import ID from '../Util/ID/ID.js'
import Tree from '../Util/Tree.mjs'
import ID from '../Util/ID/ID.mjs'
class DSNode {
constructor (id, len, gc) {

View File

@ -1,8 +1,8 @@
import Tree from '../Util/Tree.js'
import RootID from '../Util/ID/RootID.js'
import { getStruct } from '../Util/structReferences.js'
import { logID } from '../MessageHandler/messageToString.js'
import GC from '../Struct/GC.js'
import Tree from '../Util/Tree.mjs'
import RootID from '../Util/ID/RootID.mjs'
import { getStruct } from '../Util/structReferences.mjs'
import { logID } from '../MessageHandler/messageToString.mjs'
import GC from '../Struct/GC.mjs'
export default class OperationStore extends Tree {
constructor (y) {

View File

@ -1,4 +1,4 @@
import ID from '../Util/ID/ID.js'
import ID from '../Util/ID/ID.mjs'
export default class StateStore {
constructor (y) {

View File

@ -1,6 +1,7 @@
import { getStructReference } from '../Util/structReferences.js'
import ID from '../Util/ID/ID.js'
import { logID } from '../MessageHandler/messageToString.js'
import { getStructReference } from '../Util/structReferences.mjs'
import ID from '../Util/ID/ID.mjs'
import { logID } from '../MessageHandler/messageToString.mjs'
import { writeStructToTransaction } from '../Transaction.mjs'
/**
* @private
@ -108,6 +109,7 @@ export default class Delete {
if (y.persistence !== null) {
y.persistence.saveStruct(y, this)
}
writeStructToTransaction(y._transaction, this)
}
/**

View File

@ -1,6 +1,7 @@
import { getStructReference } from '../Util/structReferences.js'
import { RootFakeUserID } from '../Util/ID/RootID.js'
import ID from '../Util/ID/ID.js'
import { getStructReference } from '../Util/structReferences.mjs'
import { RootFakeUserID } from '../Util/ID/RootID.mjs'
import ID from '../Util/ID/ID.mjs'
import { writeStructToTransaction } from '../Transaction.mjs'
// TODO should have the same base class as Item
export default class GC {
@ -43,6 +44,7 @@ export default class GC {
if (y.persistence !== null) {
y.persistence.saveStruct(y, this)
}
writeStructToTransaction(y._transaction, this)
}
}

View File

@ -1,9 +1,9 @@
import { getStructReference } from '../Util/structReferences.js'
import ID from '../Util/ID/ID.js'
import { default as RootID, RootFakeUserID } from '../Util/ID/RootID.js'
import Delete from './Delete.js'
import { transactionTypeChanged } from '../Transaction.js'
import GC from './GC.js'
import { getStructReference } from '../Util/structReferences.mjs'
import ID from '../Util/ID/ID.mjs'
import { default as RootID, RootFakeUserID } from '../Util/ID/RootID.mjs'
import Delete from './Delete.mjs'
import { transactionTypeChanged, writeStructToTransaction } from '../Transaction.mjs'
import GC from './GC.mjs'
/**
* @private
@ -385,6 +385,7 @@ export default class Item {
if (y.persistence !== null) {
y.persistence.saveStruct(y, this)
}
writeStructToTransaction(y._transaction, this)
}
}

View File

@ -1,5 +1,5 @@
import { default as Item } from './Item.js'
import { logItemHelper } from '../MessageHandler/messageToString.js'
import Item from './Item.mjs'
import { logItemHelper } from '../MessageHandler/messageToString.mjs'
export default class ItemEmbed extends Item {
constructor () {

View File

@ -1,5 +1,5 @@
import { default as Item } from './Item.js'
import { logItemHelper } from '../MessageHandler/messageToString.js'
import Item from './Item.mjs'
import { logItemHelper } from '../MessageHandler/messageToString.mjs'
export default class ItemFormat extends Item {
constructor () {

View File

@ -1,5 +1,5 @@
import { splitHelper, default as Item } from './Item.js'
import { logItemHelper } from '../MessageHandler/messageToString.js'
import Item, { splitHelper } from './Item.mjs'
import { logItemHelper } from '../MessageHandler/messageToString.mjs'
export default class ItemJSON extends Item {
constructor () {

View File

@ -1,5 +1,5 @@
import { splitHelper, default as Item } from './Item.js'
import { logItemHelper } from '../MessageHandler/messageToString.js'
import Item, { splitHelper } from './Item.mjs'
import { logItemHelper } from '../MessageHandler/messageToString.mjs'
export default class ItemString extends Item {
constructor () {

View File

@ -1,6 +1,6 @@
import Item from './Item.js'
import EventHandler from '../Util/EventHandler.js'
import ID from '../Util/ID/ID.js'
import Item from './Item.mjs'
import EventHandler from '../Util/EventHandler.mjs'
import ID from '../Util/ID/ID.mjs'
// restructure children as if they were inserted one after another
function integrateChildren (y, start) {

View File

@ -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
@ -58,7 +59,19 @@ export default class Transaction {
* @type {Map<YType,Array<YEvent>>}
*/
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)
}
/**

View File

@ -1,8 +1,8 @@
import Type from '../../Struct/Type.js'
import ItemJSON from '../../Struct/ItemJSON.js'
import ItemString from '../../Struct/ItemString.js'
import { logID, logItemHelper } from '../../MessageHandler/messageToString.js'
import YEvent from '../../Util/YEvent.js'
import Type from '../../Struct/Type.mjs'
import ItemJSON from '../../Struct/ItemJSON.mjs'
import ItemString from '../../Struct/ItemString.mjs'
import { logID, logItemHelper } from '../../MessageHandler/messageToString.mjs'
import YEvent from '../../Util/YEvent.mjs'
/**
* Event that describes the changes on a YArray

View File

@ -1,8 +1,8 @@
import Type from '../../Struct/Type.js'
import Item from '../../Struct/Item.js'
import ItemJSON from '../../Struct/ItemJSON.js'
import { logItemHelper } from '../../MessageHandler/messageToString.js'
import YEvent from '../../Util/YEvent.js'
import Item from '../../Struct/Item.mjs'
import Type from '../../Struct/Type.mjs'
import ItemJSON from '../../Struct/ItemJSON.mjs'
import { logItemHelper } from '../../MessageHandler/messageToString.mjs'
import YEvent from '../../Util/YEvent.mjs'
/**
* Event that describes the changes on a YMap.

View File

@ -1,8 +1,8 @@
import ItemString from '../../Struct/ItemString.js'
import ItemEmbed from '../../Struct/ItemEmbed.js'
import ItemFormat from '../../Struct/ItemFormat.js'
import { logItemHelper } from '../../MessageHandler/messageToString.js'
import { YArrayEvent, default as YArray } from '../YArray/YArray.js'
import ItemEmbed from '../../Struct/ItemEmbed.mjs'
import ItemString from '../../Struct/ItemString.mjs'
import ItemFormat from '../../Struct/ItemFormat.mjs'
import { logItemHelper } from '../../MessageHandler/messageToString.mjs'
import { YArrayEvent, default as YArray } from '../YArray/YArray.mjs'
/**
* @private

View File

@ -1,6 +1,6 @@
import YMap from '../YMap/YMap.js'
import YXmlFragment from './YXmlFragment.js'
import { createAssociation } from '../../Bindings/DomBinding/util.js'
import YMap from '../YMap/YMap.mjs'
import YXmlFragment from './YXmlFragment.mjs'
import { createAssociation } from '../../Bindings/DomBinding/util.mjs'
/**
* An YXmlElement imitates the behavior of a

View File

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

View File

@ -1,9 +1,9 @@
import { createAssociation } from '../../Bindings/DomBinding/util.js'
import YXmlTreeWalker from './YXmlTreeWalker.js'
import { createAssociation } from '../../Bindings/DomBinding/util.mjs'
import YXmlTreeWalker from './YXmlTreeWalker.mjs'
import YArray from '../YArray/YArray.js'
import YXmlEvent from './YXmlEvent.js'
import { logItemHelper } from '../../MessageHandler/messageToString.js'
import YArray from '../YArray/YArray.mjs'
import YXmlEvent from './YXmlEvent.mjs'
import { logItemHelper } from '../../MessageHandler/messageToString.mjs'
/**
* Dom filter function.

View File

@ -1,5 +1,5 @@
import YMap from '../YMap/YMap.js'
import { createAssociation } from '../../Bindings/DomBinding/util.js'
import YMap from '../YMap/YMap.mjs'
import { createAssociation } from '../../Bindings/DomBinding/util.mjs'
/**
* You can manage binding to a custom type with YXmlHook.

View File

@ -1,5 +1,5 @@
import YText from '../YText/YText.js'
import { createAssociation } from '../../Bindings/DomBinding/util.js'
import YText from '../YText/YText.mjs'
import { createAssociation } from '../../Bindings/DomBinding/util.mjs'
/**
* Represents text in a Dom Element. In the future this type will also handle

View File

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

View File

@ -1,5 +1,5 @@
import ID from '../ID/ID.js'
import { default as RootID, RootFakeUserID } from '../ID/RootID.js'
import ID from '../ID/ID.mjs'
import { default as RootID, RootFakeUserID } from '../ID/RootID.mjs'
/**
* A BinaryDecoder handles the decoding of an ArrayBuffer.
@ -25,6 +25,10 @@ export default class BinaryDecoder {
this.pos = 0
}
hasContent () {
return this.pos !== this.uint8arr.length
}
/**
* Clone this decoder instance.
* Optionally set a new position parameter.

View File

@ -1,4 +1,4 @@
import { RootFakeUserID } from '../ID/RootID.js'
import { RootFakeUserID } from '../ID/RootID.mjs'
const bits7 = 0b1111111
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.
*

View File

@ -1,4 +1,4 @@
import { getStructReference } from '../structReferences.js'
import { getStructReference } from '../structReferences.mjs'
export const RootFakeUserID = 0xFFFFFF

View File

@ -1,5 +1,5 @@
import ID from './ID/ID.js'
import isParentOf from './isParentOf.js'
import ID from './ID/ID.mjs'
import isParentOf from './isParentOf.mjs'
class ReverseOperation {
constructor (y, transaction) {

View File

@ -1,7 +1,7 @@
import ID from '../Util/ID/ID.js'
import ItemJSON from '../Struct/ItemJSON.js'
import ItemString from '../Struct/ItemString.js'
import ID from '../Util/ID/ID.mjs'
import ItemJSON from '../Struct/ItemJSON.mjs'
import ItemString from '../Struct/ItemString.mjs'
/**
* Try to merge all items in os with their successors.

View File

@ -1,6 +1,6 @@
import ID from './ID/ID.js'
import RootID from './ID/RootID.js'
import GC from '../Struct/GC.js'
import ID from './ID/ID.mjs'
import RootID from './ID/RootID.mjs'
import GC from '../Struct/GC.mjs'
// TODO: Implement function to describe ranges

View File

@ -1,17 +1,17 @@
import YArray from '../Types/YArray/YArray.js'
import YMap from '../Types/YMap/YMap.js'
import YText from '../Types/YText/YText.js'
import YXmlText from '../Types/YXml/YXmlText.js'
import YXmlHook from '../Types/YXml/YXmlHook.js'
import YXmlFragment from '../Types/YXml/YXmlFragment.js'
import YXmlElement from '../Types/YXml/YXmlElement.js'
import Delete from '../Struct/Delete.mjs'
import ItemJSON from '../Struct/ItemJSON.mjs'
import ItemString from '../Struct/ItemString.mjs'
import ItemFormat from '../Struct/ItemFormat.mjs'
import ItemEmbed from '../Struct/ItemEmbed.mjs'
import GC from '../Struct/GC.mjs'
import Delete from '../Struct/Delete.js'
import ItemJSON from '../Struct/ItemJSON.js'
import ItemString from '../Struct/ItemString.js'
import ItemFormat from '../Struct/ItemFormat.js'
import ItemEmbed from '../Struct/ItemEmbed.js'
import GC from '../Struct/GC.js'
import YArray from '../Types/YArray/YArray.mjs'
import YMap from '../Types/YMap/YMap.mjs'
import YText from '../Types/YText/YText.mjs'
import YXmlText from '../Types/YXml/YXmlText.mjs'
import YXmlHook from '../Types/YXml/YXmlHook.mjs'
import YXmlFragment from '../Types/YXml/YXmlFragment.mjs'
import YXmlElement from '../Types/YXml/YXmlElement.mjs'
const structs = new Map()
const references = new Map()

View File

@ -1,30 +1,30 @@
import Y from './Y.js'
import UndoManager from './Util/UndoManager.js'
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.js'
import Y from './Y.mjs'
import UndoManager from './Util/UndoManager.mjs'
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 Persistence from './Persistence.js'
import YArray from './Types/YArray/YArray.js'
import YMap from './Types/YMap/YMap.js'
import YText from './Types/YText/YText.js'
import YXmlText from './Types/YXml/YXmlText.js'
import YXmlHook from './Types/YXml/YXmlHook.js'
import YXmlFragment from './Types/YXml/YXmlFragment.js'
import YXmlElement from './Types/YXml/YXmlElement.js'
import BinaryDecoder from './Util/Binary/Decoder.js'
import { getRelativePosition, fromRelativePosition } from './Util/relativePosition.js'
import { registerStruct } from './Util/structReferences.js'
import TextareaBinding from './Bindings/TextareaBinding/TextareaBinding.js'
import QuillBinding from './Bindings/QuillBinding/QuillBinding.js'
import DomBinding from './Bindings/DomBinding/DomBinding.js'
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.js'
import Connector from './Connector.mjs'
import Persistence from './Persistence.mjs'
import YArray from './Types/YArray/YArray.mjs'
import YMap from './Types/YMap/YMap.mjs'
import YText from './Types/YText/YText.mjs'
import YXmlText from './Types/YXml/YXmlText.mjs'
import YXmlHook from './Types/YXml/YXmlHook.mjs'
import YXmlFragment from './Types/YXml/YXmlFragment.mjs'
import YXmlElement from './Types/YXml/YXmlElement.mjs'
import BinaryDecoder from './Util/Binary/Decoder.mjs'
import { getRelativePosition, fromRelativePosition } from './Util/relativePosition.mjs'
import { registerStruct } from './Util/structReferences.mjs'
import TextareaBinding from './Bindings/TextareaBinding/TextareaBinding.mjs'
import QuillBinding from './Bindings/QuillBinding/QuillBinding.mjs'
import DomBinding from './Bindings/DomBinding/DomBinding.mjs'
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.mjs'
import debug from 'debug'
import domToType from './Bindings/DomBinding/domToType.js'
import { domsToTypes, switchAssociation } from './Bindings/DomBinding/util.js'
import domToType from './Bindings/DomBinding/domToType.mjs'
import { domsToTypes, switchAssociation } from './Bindings/DomBinding/util.mjs'
// TODO: The following assignments should be moved to yjs-dist
Y.AbstractConnector = Connector

View File

@ -1,12 +1,12 @@
import DeleteStore from './Store/DeleteStore.js'
import OperationStore from './Store/OperationStore.js'
import StateStore from './Store/StateStore.js'
import { generateRandomUint32 } from './Util/generateRandomUint32.js'
import RootID from './Util/ID/RootID.js'
import NamedEventHandler from './Util/NamedEventHandler.js'
import Transaction from './Transaction.js'
import DeleteStore from './Store/DeleteStore.mjs'
import OperationStore from './Store/OperationStore.mjs'
import StateStore from './Store/StateStore.mjs'
import { generateRandomUint32 } from './Util/generateRandomUint32.mjs'
import RootID from './Util/ID/RootID.mjs'
import NamedEventHandler from './Util/NamedEventHandler.mjs'
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
@ -28,7 +28,7 @@ export { default as DomBinding } from './Bindings/DomBinding/DomBinding.js'
* @param {AbstractPersistence} persistence Persistence adapter instance
*/
export default class Y extends NamedEventHandler {
constructor (room, opts, persistence, conf = {}) {
constructor (room, connector, persistence, conf = {}) {
super()
this.gcEnabled = conf.gc || false
/**
@ -36,16 +36,8 @@ export default class Y extends NamedEventHandler {
* @type {String}
*/
this.room = room
if (opts != null) {
opts.connector.room = room
}
this._contentReady = false
this._opts = opts
if (typeof opts.userID !== 'number') {
this.userID = generateRandomUint32()
} else {
this.userID = opts.userID
}
this.userID = generateRandomUint32()
// TODO: This should be a Map so we can use encodables as keys
this.share = {}
this.ds = new DeleteStore(this)
@ -61,10 +53,13 @@ export default class Y extends NamedEventHandler {
this.connector = null
this.connected = false
let initConnection = () => {
if (opts != null) {
this.connector = new Y[opts.connector.name](this, opts.connector)
this.connected = true
this.emit('connectorReady')
if (connector != null) {
if (connector.constructor === Object) {
connector.connector.room = room
this.connector = new Y[connector.connector.name](this, connector.connector)
this.connected = true
this.emit('connectorReady')
}
}
}
/**

View File

@ -1,7 +1,7 @@
import { test } from '../node_modules/cutest/cutest.mjs'
import Chance from 'chance'
import DeleteStore from '../src/Store/DeleteStore.js'
import ID from '../src/Util/ID/ID.js'
import DeleteStore from '../src/Store/DeleteStore.mjs'
import ID from '../src/Util/ID/ID.mjs'
/**
* Converts a DS to an array of length 10.

View File

@ -1,5 +1,5 @@
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'
function runDiffTest (t, a, b, expected) {

View File

@ -1,7 +1,7 @@
import { test } from '../node_modules/cutest/cutest.mjs'
import BinaryEncoder from '../src/Util/Binary/Encoder.js'
import BinaryDecoder from '../src/Util/Binary/Decoder.js'
import { generateRandomUint32 } from '../src/Util/generateRandomUint32.js'
import BinaryEncoder from '../src/Util/Binary/Encoder.mjs'
import BinaryDecoder from '../src/Util/Binary/Decoder.mjs'
import { generateRandomUint32 } from '../src/Util/generateRandomUint32.mjs'
import Chance from 'chance'
function testEncoding (t, write, read, val) {

View File

@ -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
View 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'

View File

@ -1,5 +1,5 @@
import RedBlackTree from '../src/Util/Tree.js'
import ID from '../src/Util/ID/ID.js'
import RedBlackTree from '../src/Util/Tree.mjs'
import ID from '../src/Util/ID/ID.mjs'
import Chance from 'chance'
import { test, proxyConsole } from 'cutest'

View File

@ -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'
proxyConsole()

View File

@ -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'
proxyConsole()

View File

@ -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'
proxyConsole()

View File

@ -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'
test('set property', async function xml0 (t) {

View File

@ -1,14 +1,14 @@
import _Y from '../src/Y.dist.js'
import { DomBinding } from '../src/Y.js'
import TestConnector from './test-connector.js'
import _Y from '../src/Y.dist.mjs'
import { DomBinding } from '../src/Y.mjs'
import TestConnector from './test-connector.mjs'
import Chance from 'chance'
import ItemJSON from '../src/Struct/ItemJSON.js'
import ItemString from '../src/Struct/ItemString.js'
import { defragmentItemContent } from '../src/Util/defragmentItemContent.js'
import ItemJSON from '../src/Struct/ItemJSON.mjs'
import ItemString from '../src/Struct/ItemString.mjs'
import { defragmentItemContent } from '../src/Util/defragmentItemContent.mjs'
import Quill from 'quill'
import GC from '../src/Struct/GC.js'
import GC from '../src/Struct/GC.mjs'
export const Y = _Y

View File

@ -1,6 +1,6 @@
import { wait } from './helper'
import { messageToString } from '../src/MessageHandler/messageToString'
import AbstractConnector from '../src/Connector.js'
import AbstractConnector from '../src/Connector.mjs'
var rooms = {}