permission protocol + reduce circular dependencies
This commit is contained in:
parent
e09ef15349
commit
04066a5678
@ -9,10 +9,13 @@ Until [this](https://github.com/Microsoft/TypeScript/issues/7546) is fixed, the
|
||||
|
||||
```json
|
||||
{
|
||||
"checkJs": true,
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
..
|
||||
},
|
||||
"include": [
|
||||
"./node_modules/yjs/"
|
||||
]
|
||||
..
|
||||
}
|
||||
```
|
||||
|
@ -94,7 +94,13 @@ export const cursorPlugin = new Plugin({
|
||||
const decorations = []
|
||||
awareness.forEach((aw, userID) => {
|
||||
if (aw.cursor != null) {
|
||||
const username = `User: ${userID}`
|
||||
let user = aw.user || {}
|
||||
if (user.color == null) {
|
||||
user.color = '#ffa50070'
|
||||
}
|
||||
if (user.name == null) {
|
||||
user.name = `User: ${userID}`
|
||||
}
|
||||
let anchor = relativePositionToAbsolutePosition(ystate.type, aw.cursor.anchor || null, ystate.binding.mapping)
|
||||
let head = relativePositionToAbsolutePosition(ystate.type, aw.cursor.head || null, ystate.binding.mapping)
|
||||
if (anchor !== null && head !== null) {
|
||||
@ -104,14 +110,16 @@ export const cursorPlugin = new Plugin({
|
||||
decorations.push(Decoration.widget(head, () => {
|
||||
const cursor = document.createElement('span')
|
||||
cursor.classList.add('ProseMirror-yjs-cursor')
|
||||
const user = document.createElement('div')
|
||||
user.insertBefore(document.createTextNode(username), null)
|
||||
cursor.insertBefore(user, null)
|
||||
cursor.setAttribute('style', `border-color: ${user.color}`)
|
||||
const userDiv = document.createElement('div')
|
||||
userDiv.setAttribute('style', `background-color: ${user.color}`)
|
||||
userDiv.insertBefore(document.createTextNode(user.name), null)
|
||||
cursor.insertBefore(userDiv, null)
|
||||
return cursor
|
||||
}, { key: username }))
|
||||
}, { key: userID + '' }))
|
||||
const from = math.min(anchor, head)
|
||||
const to = math.max(anchor, head)
|
||||
decorations.push(Decoration.inline(from, to, { style: 'background-color: #ffa50070' }))
|
||||
decorations.push(Decoration.inline(from, to, { style: `background-color: ${user.color}` }))
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -429,7 +437,10 @@ export const createTypeFromNode = (node, mapping) => {
|
||||
} else {
|
||||
type = new YXmlElement(node.type.name)
|
||||
for (let key in node.attrs) {
|
||||
type.setAttribute(key, node.attrs[key])
|
||||
const val = node.attrs[key]
|
||||
if (val !== null) {
|
||||
type.setAttribute(key, val)
|
||||
}
|
||||
}
|
||||
const ins = []
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
@ -441,15 +452,25 @@ export const createTypeFromNode = (node, mapping) => {
|
||||
return type
|
||||
}
|
||||
|
||||
const equalAttrs = (pattrs, yattrs) => {
|
||||
const keys = Object.keys(pattrs).filter(key => pattrs[key] === null)
|
||||
let eq = keys.length === Object.keys(yattrs).filter(key => yattrs[key] === null).length
|
||||
for (let i = 0; i < keys.length && eq; i++) {
|
||||
const key = keys[i]
|
||||
eq = pattrs[key] === yattrs[key]
|
||||
}
|
||||
return eq
|
||||
}
|
||||
|
||||
const equalYTextPText = (ytext, ptext) => {
|
||||
const d = ytext.toDelta()[0]
|
||||
return d.insert === ptext.text && object.keys(d.attributes || {}).length === ptext.marks.length && ptext.marks.every(mark => object.equalFlat(d.attributes[mark.type.name], mark.attrs))
|
||||
return d.insert === ptext.text && object.keys(d.attributes || {}).length === ptext.marks.length && ptext.marks.every(mark => equalAttrs(d.attributes[mark.type.name], mark.attrs))
|
||||
}
|
||||
|
||||
const equalYTypePNode = (ytype, pnode) =>
|
||||
ytype.constructor === YText
|
||||
? equalYTextPText(ytype, pnode)
|
||||
: (matchNodeName(ytype, pnode) && ytype.length === pnode.childCount && object.equalFlat(ytype.getAttributes(), pnode.attrs) && ytype.toArray().every((ychild, i) => equalYTypePNode(ychild, pnode.child(i))))
|
||||
: (matchNodeName(ytype, pnode) && ytype.length === pnode.childCount && equalAttrs(ytype.getAttributes(), pnode.attrs) && ytype.toArray().every((ychild, i) => equalYTypePNode(ychild, pnode.child(i))))
|
||||
|
||||
const computeChildEqualityFactor = (ytype, pnode, mapping) => {
|
||||
const yChildren = ytype.toArray()
|
||||
@ -497,13 +518,19 @@ const updateYFragment = (yDomFragment, pContent, mapping) => {
|
||||
// update attributes
|
||||
if (yDomFragment instanceof YXmlElement) {
|
||||
const yDomAttrs = yDomFragment.getAttributes()
|
||||
for (let key in pContent.attrs) {
|
||||
if (yDomAttrs[key] !== pContent.attrs[key]) {
|
||||
yDomFragment.setAttribute(key, pContent.attrs[key])
|
||||
const pAttrs = pContent.attrs
|
||||
for (let key in pAttrs) {
|
||||
if (pAttrs[key] !== null) {
|
||||
if (yDomAttrs[key] !== pAttrs[key]) {
|
||||
yDomFragment.setAttribute(key, pAttrs[key])
|
||||
}
|
||||
} else {
|
||||
yDomFragment.removeAttribute(key)
|
||||
}
|
||||
}
|
||||
// remove all keys that are no longer in pAttrs
|
||||
for (let key in yDomAttrs) {
|
||||
if (yDomAttrs[key] === undefined) {
|
||||
if (pAttrs[key] === undefined) {
|
||||
yDomFragment.removeAttribute(key)
|
||||
}
|
||||
}
|
||||
|
13
index.js
13
index.js
@ -1,4 +1,5 @@
|
||||
|
||||
import './structs/Item.js'
|
||||
import { Delete } from './structs/Delete.js'
|
||||
import { ItemJSON } from './structs/ItemJSON.js'
|
||||
import { ItemString } from './structs/ItemString.js'
|
||||
@ -15,6 +16,14 @@ import { YXmlElement, YXmlFragment } from './types/YXmlElement.js'
|
||||
|
||||
import { registerStruct } from './utils/structReferences.js'
|
||||
|
||||
import * as decoding from './lib/decoding.js'
|
||||
import * as encoding from './lib/encoding.js'
|
||||
import * as awarenessProtocol from './protocols/awareness.js'
|
||||
import * as syncProtocol from './protocols/sync.js'
|
||||
import * as authProtocol from './protocols/auth.js'
|
||||
|
||||
export { decoding, encoding, awarenessProtocol, syncProtocol, authProtocol }
|
||||
|
||||
export { Y } from './utils/Y.js'
|
||||
export { UndoManager } from './utils/UndoManager.js'
|
||||
export { Transaction } from './utils/Transaction.js'
|
||||
@ -28,10 +37,6 @@ export { YXmlElement as XmlElement, YXmlFragment as XmlFragment } from './types/
|
||||
|
||||
export { getRelativePosition, fromRelativePosition } from './utils/relativePosition.js'
|
||||
export { registerStruct } from './utils/structReferences.js'
|
||||
export * from './protocols/syncProtocol.js'
|
||||
export * from './protocols/awarenessProtocol.js'
|
||||
export * from './lib/encoding.js'
|
||||
export * from './lib/decoding.js'
|
||||
export * from './lib/mutex.js'
|
||||
|
||||
registerStruct(0, GC)
|
||||
|
@ -148,6 +148,21 @@ export const readVarUint = decoder => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look ahead and read varUint without incrementing position
|
||||
*
|
||||
* @function
|
||||
* @param {Decoder} decoder
|
||||
* @return {number}
|
||||
*/
|
||||
export const peekVarUint = decoder => {
|
||||
let pos = decoder.pos
|
||||
let s = readVarUint(decoder)
|
||||
decoder.pos = pos
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read string of variable length
|
||||
* * varUint is used to store the length of the string
|
||||
@ -189,3 +204,4 @@ export const peekVarString = decoder => {
|
||||
decoder.pos = pos
|
||||
return s
|
||||
}
|
||||
|
||||
|
46
package-lock.json
generated
46
package-lock.json
generated
@ -67,12 +67,28 @@
|
||||
"integrity": "sha512-F/v7t1LwS4vnXuPooJQGBRKRGIoxWUTmA4VHfqjOccFsNDThD5bfUNpITive6s352O7o384wcpEaDV8rHCehDA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/events": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz",
|
||||
"integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "6.0.110",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.110.tgz",
|
||||
"integrity": "sha512-LiaH3mF+OAqR+9Wo1OTJDbZDtCewAVjTbMhF1ZgUJ3fc8xqOJq6VqbpBh9dJVCVzByGmYIg2fREbuXNX0TKiJA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/ws": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.1.tgz",
|
||||
"integrity": "sha512-EzH8k1gyZ4xih/MaZTXwT2xOkPiIMSrhQ9b8wrlX88L0T02eYsddatQlwVFlEPyEqV0ChpdpNnE51QPH6NVT4Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/events": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"abab": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
|
||||
@ -3413,14 +3429,12 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -3435,20 +3449,17 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -3565,8 +3576,7 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -3578,7 +3588,6 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -3593,7 +3602,6 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -3601,14 +3609,12 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.2.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.1",
|
||||
"yallist": "^3.0.0"
|
||||
@ -3627,7 +3633,6 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -3708,8 +3713,7 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -3721,7 +3725,6 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -3843,7 +3846,6 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -5958,7 +5960,7 @@
|
||||
},
|
||||
"quill": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "http://registry.npmjs.org/quill/-/quill-1.3.6.tgz",
|
||||
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.6.tgz",
|
||||
"integrity": "sha512-K0mvhimWZN6s+9OQ249CH2IEPZ9JmkFuCQeHAOQax3EZ2nDJ3wfGh59mnlQaZV2i7u8eFarx6wAtvQKgShojug==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -66,6 +66,7 @@
|
||||
},
|
||||
"homepage": "http://y-js.org",
|
||||
"devDependencies": {
|
||||
"@types/ws": "^6.0.1",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-plugin-external-helpers": "^6.22.0",
|
||||
"babel-plugin-transform-regenerator": "^6.26.0",
|
||||
|
33
protocols/auth.js
Normal file
33
protocols/auth.js
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
import * as encoding from '../lib/encoding.js'
|
||||
import * as decoding from '../lib/decoding.js'
|
||||
import { Y } from '../utils/Y.js';
|
||||
|
||||
export const messagePermissionDenied = 0
|
||||
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @param {string} reason
|
||||
*/
|
||||
export const writePermissionDenied = (encoder, reason) => {
|
||||
encoding.writeVarUint(encoder, messagePermissionDenied)
|
||||
encoding.writeVarString(encoder, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* @callback PermissionDeniedHandler
|
||||
* @param {any} y
|
||||
* @param {string} reason
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {decoding.Decoder} decoder
|
||||
* @param {Y} y
|
||||
* @param {PermissionDeniedHandler} permissionDeniedHandler
|
||||
*/
|
||||
export const readAuthMessage = (decoder, y, permissionDeniedHandler) => {
|
||||
switch (decoding.readVarUint(decoder)) {
|
||||
case messagePermissionDenied: permissionDeniedHandler(y, decoding.readVarString(decoder))
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ const messageUsersStateChanged = 0
|
||||
/**
|
||||
* @typedef {Object} UserStateUpdate
|
||||
* @property {number} UserStateUpdate.userID
|
||||
* @property {Object} state
|
||||
* @property {Object} UserStateUpdate.state
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -91,13 +91,22 @@ export const readAwarenessMessage = (decoder, y) => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} UserState
|
||||
* @property {number} UserState.userID
|
||||
* @property {any} UserState.state
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {decoding.Decoder} decoder
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @return {Array<UserState>} Array of state updates
|
||||
*/
|
||||
export const forwardAwarenessMessage = (decoder, encoder) => {
|
||||
let s = []
|
||||
switch (decoding.readVarUint(decoder)) {
|
||||
case messageUsersStateChanged:
|
||||
return forwardUsersStateChange(decoder, encoder)
|
||||
s = forwardUsersStateChange(decoder, encoder)
|
||||
}
|
||||
return s
|
||||
}
|
@ -10,6 +10,7 @@ import { deleteItemRange } from '../utils/structManipulation.js'
|
||||
import { integrateRemoteStruct } from '../utils/integrateRemoteStructs.js'
|
||||
import { Y } from '../utils/Y.js' // eslint-disable-line
|
||||
import { Item } from '../structs/Item.js'
|
||||
import * as stringify from '../utils/structStringify.js'
|
||||
|
||||
/**
|
||||
* @typedef {Map<number, number>} StateSet
|
||||
@ -40,9 +41,9 @@ import { Item } from '../structs/Item.js'
|
||||
* stringify[messageType] stringifies a message definition (messageType is already read from the bufffer)
|
||||
*/
|
||||
|
||||
const messageYjsSyncStep1 = 0
|
||||
const messageYjsSyncStep2 = 1
|
||||
const messageYjsUpdate = 2
|
||||
export const messageYjsSyncStep1 = 0
|
||||
export const messageYjsSyncStep2 = 1
|
||||
export const messageYjsUpdate = 2
|
||||
|
||||
/**
|
||||
* Stringifies a message-encoded Delete Set.
|
||||
@ -234,50 +235,6 @@ export const readStateSet = decoder => {
|
||||
return ss
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringify an item id.
|
||||
*
|
||||
* @param {ID.ID | ID.RootID} id
|
||||
* @return {string}
|
||||
*/
|
||||
export const stringifyID = id => id instanceof ID.ID ? `(${id.user},${id.clock})` : `(${id.name},${id.type})`
|
||||
|
||||
/**
|
||||
* Stringify an item as ID. HHere, an item could also be a Yjs instance (e.g. item._parent).
|
||||
*
|
||||
* @param {Item | Y | null} item
|
||||
* @return {string}
|
||||
*/
|
||||
export const stringifyItemID = item => {
|
||||
let result
|
||||
if (item === null) {
|
||||
result = '()'
|
||||
} else if (item instanceof Item) {
|
||||
result = stringifyID(item._id)
|
||||
} else {
|
||||
// must be a Yjs instance
|
||||
// Don't include Y in this module, so we prevent circular dependencies.
|
||||
result = 'y'
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper utility to convert an item to a readable format.
|
||||
*
|
||||
* @param {String} name The name of the item class (YText, ItemString, ..).
|
||||
* @param {Item} item The item instance.
|
||||
* @param {String} [append] Additional information to append to the returned
|
||||
* string.
|
||||
* @return {String} A readable string that represents the item object.
|
||||
*
|
||||
*/
|
||||
export const logItemHelper = (name, item, append) => {
|
||||
const left = item._left !== null ? stringifyID(item._left._lastId) : '()'
|
||||
const origin = item._origin !== null ? stringifyID(item._origin._lastId) : '()'
|
||||
return `${name}(id:${stringifyItemID(item)},left:${left},origin:${origin},right:${stringifyItemID(item._right)},parent:${stringifyItemID(item._parent)},parentSub:${item._parentSub}${append !== undefined ? ' - ' + append : ''})`
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {decoding.Decoder} decoder
|
||||
* @param {Y} y
|
||||
@ -293,7 +250,7 @@ export const stringifyStructs = (decoder, y) => {
|
||||
let missing = struct._fromBinary(y, decoder)
|
||||
let logMessage = ' ' + struct._logString()
|
||||
if (missing.length > 0) {
|
||||
logMessage += ' .. missing: ' + missing.map(stringifyItemID).join(', ')
|
||||
logMessage += ' .. missing: ' + missing.map(stringify.stringifyItemID).join(', ')
|
||||
}
|
||||
str += logMessage + '\n'
|
||||
}
|
||||
@ -410,10 +367,9 @@ export const stringifySyncStep2 = (decoder, y) => {
|
||||
* Read and apply Structs and then DeleteSet to a y instance.
|
||||
*
|
||||
* @param {decoding.Decoder} decoder
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @param {Y} y
|
||||
*/
|
||||
export const readSyncStep2 = (decoder, encoder, y) => {
|
||||
export const readSyncStep2 = (decoder, y) => {
|
||||
readStructs(decoder, y)
|
||||
readDeleteSet(decoder, y)
|
||||
}
|
||||
@ -480,7 +436,7 @@ export const readSyncMessage = (decoder, encoder, y) => {
|
||||
readSyncStep1(decoder, encoder, y)
|
||||
break
|
||||
case messageYjsSyncStep2:
|
||||
y.transact(() => readSyncStep2(decoder, encoder, y), true)
|
||||
y.transact(() => readSyncStep2(decoder, y), true)
|
||||
break
|
||||
case messageYjsUpdate:
|
||||
y.transact(() => readUpdate(decoder, y), true)
|
@ -9,28 +9,37 @@ import * as bc from '../../lib/broadcastchannel.js'
|
||||
|
||||
const messageSync = 0
|
||||
const messageAwareness = 1
|
||||
const messageAuth = 2
|
||||
|
||||
const reconnectTimeout = 3000
|
||||
|
||||
/**
|
||||
* @param {WebsocketsSharedDocument} doc
|
||||
* @param {string} reason
|
||||
*/
|
||||
const permissionDeniedHandler = (doc, reason) => console.warn(`Permission denied to access ${doc.url}.\n${reason}`)
|
||||
|
||||
/**
|
||||
* @param {WebsocketsSharedDocument} doc
|
||||
* @param {ArrayBuffer} buf
|
||||
* @return {Y.Encoder}
|
||||
* @return {Y.encoding.Encoder}
|
||||
*/
|
||||
const readMessage = (doc, buf) => {
|
||||
const decoder = Y.createDecoder(buf)
|
||||
const encoder = Y.createEncoder()
|
||||
const messageType = Y.readVarUint(decoder)
|
||||
const decoder = Y.decoding.createDecoder(buf)
|
||||
const encoder = Y.encoding.createEncoder()
|
||||
const messageType = Y.decoding.readVarUint(decoder)
|
||||
switch (messageType) {
|
||||
case messageSync:
|
||||
Y.writeVarUint(encoder, messageSync)
|
||||
Y.encoding.writeVarUint(encoder, messageSync)
|
||||
doc.mux(() =>
|
||||
Y.readSyncMessage(decoder, encoder, doc)
|
||||
Y.syncProtocol.readSyncMessage(decoder, encoder, doc)
|
||||
)
|
||||
break
|
||||
case messageAwareness:
|
||||
Y.readAwarenessMessage(decoder, doc)
|
||||
Y.awarenessProtocol.readAwarenessMessage(decoder, doc)
|
||||
break
|
||||
case messageAuth:
|
||||
Y.authProtocol.readAuthMessage(decoder, doc, permissionDeniedHandler)
|
||||
}
|
||||
return encoder
|
||||
}
|
||||
@ -41,8 +50,8 @@ const setupWS = (doc, url) => {
|
||||
doc.ws = websocket
|
||||
websocket.onmessage = event => {
|
||||
const encoder = readMessage(doc, event.data)
|
||||
if (Y.length(encoder) > 1) {
|
||||
websocket.send(Y.toBuffer(encoder))
|
||||
if (Y.encoding.length(encoder) > 1) {
|
||||
websocket.send(Y.encoding.toBuffer(encoder))
|
||||
}
|
||||
}
|
||||
websocket.onclose = () => {
|
||||
@ -59,10 +68,10 @@ const setupWS = (doc, url) => {
|
||||
status: 'disconnected'
|
||||
})
|
||||
// always send sync step 1 when connected
|
||||
const encoder = Y.createEncoder()
|
||||
Y.writeVarUint(encoder, messageSync)
|
||||
Y.writeSyncStep1(encoder, doc)
|
||||
websocket.send(Y.toBuffer(encoder))
|
||||
const encoder = Y.encoding.createEncoder()
|
||||
Y.encoding.writeVarUint(encoder, messageSync)
|
||||
Y.syncProtocol.writeSyncStep1(encoder, doc)
|
||||
websocket.send(Y.encoding.toBuffer(encoder))
|
||||
// force send stored awareness info
|
||||
doc.setAwarenessField(null, null)
|
||||
}
|
||||
@ -71,10 +80,10 @@ const setupWS = (doc, url) => {
|
||||
const broadcastUpdate = (y, transaction) => {
|
||||
if (transaction.encodedStructsLen > 0) {
|
||||
y.mux(() => {
|
||||
const encoder = Y.createEncoder()
|
||||
Y.writeVarUint(encoder, messageSync)
|
||||
Y.writeUpdate(encoder, transaction.encodedStructsLen, transaction.encodedStructs)
|
||||
const buf = Y.toBuffer(encoder)
|
||||
const encoder = Y.encoding.createEncoder()
|
||||
Y.encoding.writeVarUint(encoder, messageSync)
|
||||
Y.syncProtocol.writeUpdate(encoder, transaction.encodedStructsLen, transaction.encodedStructs)
|
||||
const buf = Y.encoding.toBuffer(encoder)
|
||||
if (y.wsconnected) {
|
||||
y.ws.send(buf)
|
||||
}
|
||||
@ -95,20 +104,20 @@ class WebsocketsSharedDocument extends Y.Y {
|
||||
setupWS(this, url)
|
||||
this.on('afterTransaction', broadcastUpdate)
|
||||
this._bcSubscriber = data => {
|
||||
const encoder = readMessage(this, data)
|
||||
if (Y.length(encoder) > 1) {
|
||||
this.mux(() => {
|
||||
bc.publish(url, Y.toBuffer(encoder))
|
||||
})
|
||||
}
|
||||
this.mux(() => {
|
||||
const encoder = readMessage(this, data)
|
||||
if (Y.encoding.length(encoder) > 1) {
|
||||
bc.publish(url, Y.encoding.toBuffer(encoder))
|
||||
}
|
||||
})
|
||||
}
|
||||
bc.subscribe(url, this._bcSubscriber)
|
||||
// send sync step1 to bc
|
||||
this.mux(() => {
|
||||
const encoder = Y.createEncoder()
|
||||
Y.writeVarUint(encoder, messageSync)
|
||||
Y.writeSyncStep1(encoder, this)
|
||||
bc.publish(url, Y.toBuffer(encoder))
|
||||
const encoder = Y.encoding.createEncoder()
|
||||
Y.encoding.writeVarUint(encoder, messageSync)
|
||||
Y.syncProtocol.writeSyncStep1(encoder, this)
|
||||
bc.publish(url, Y.encoding.toBuffer(encoder))
|
||||
})
|
||||
}
|
||||
getLocalAwarenessInfo () {
|
||||
@ -122,10 +131,10 @@ class WebsocketsSharedDocument extends Y.Y {
|
||||
this._localAwarenessState[field] = value
|
||||
}
|
||||
if (this.wsconnected) {
|
||||
const encoder = Y.createEncoder()
|
||||
Y.writeVarUint(encoder, messageAwareness)
|
||||
Y.writeUsersStateChange(encoder, [{ userID: this.userID, state: this._localAwarenessState }])
|
||||
const buf = Y.toBuffer(encoder)
|
||||
const encoder = Y.encoding.createEncoder()
|
||||
Y.encoding.writeVarUint(encoder, messageAwareness)
|
||||
Y.awarenessProtocol.writeUsersStateChange(encoder, [{ userID: this.userID, state: this._localAwarenessState }])
|
||||
const buf = Y.encoding.toBuffer(encoder)
|
||||
this.ws.send(buf)
|
||||
}
|
||||
}
|
||||
|
@ -19,13 +19,14 @@ const docs = new Map()
|
||||
|
||||
const messageSync = 0
|
||||
const messageAwareness = 1
|
||||
const messageAuth = 2
|
||||
|
||||
const afterTransaction = (doc, transaction) => {
|
||||
if (transaction.encodedStructsLen > 0) {
|
||||
const encoder = Y.createEncoder()
|
||||
Y.writeVarUint(encoder, messageSync)
|
||||
Y.writeUpdate(encoder, transaction.encodedStructsLen, transaction.encodedStructs)
|
||||
const message = Y.toBuffer(encoder)
|
||||
const encoder = Y.encoding.createEncoder()
|
||||
Y.encoding.writeVarUint(encoder, messageSync)
|
||||
Y.syncProtocol.writeUpdate(encoder, transaction.encodedStructsLen, transaction.encodedStructs)
|
||||
const message = Y.encoding.toBuffer(encoder)
|
||||
doc.conns.forEach((_, conn) => conn.send(message))
|
||||
}
|
||||
}
|
||||
@ -45,25 +46,25 @@ class WSSharedDoc extends Y.Y {
|
||||
}
|
||||
|
||||
const messageListener = (conn, doc, message) => {
|
||||
const encoder = Y.createEncoder()
|
||||
const decoder = Y.createDecoder(message)
|
||||
const messageType = Y.readVarUint(decoder)
|
||||
const encoder = Y.encoding.createEncoder()
|
||||
const decoder = Y.decoding.createDecoder(message)
|
||||
const messageType = Y.decoding.readVarUint(decoder)
|
||||
switch (messageType) {
|
||||
case messageSync:
|
||||
Y.writeVarUint(encoder, messageSync)
|
||||
Y.readSyncMessage(decoder, encoder, doc)
|
||||
if (Y.length(encoder) > 1) {
|
||||
conn.send(Y.toBuffer(encoder))
|
||||
Y.encoding.writeVarUint(encoder, messageSync)
|
||||
Y.syncProtocol.readSyncMessage(decoder, encoder, doc)
|
||||
if (Y.encoding.length(encoder) > 1) {
|
||||
conn.send(Y.encoding.toBuffer(encoder))
|
||||
}
|
||||
break
|
||||
case messageAwareness: {
|
||||
Y.writeVarUint(encoder, messageAwareness)
|
||||
const updates = Y.forwardAwarenessMessage(decoder, encoder)
|
||||
Y.encoding.writeVarUint(encoder, messageAwareness)
|
||||
const updates = Y.awarenessProtocol.forwardAwarenessMessage(decoder, encoder)
|
||||
updates.forEach(update => {
|
||||
doc.awareness.set(update.userID, update.state)
|
||||
doc.conns.get(conn).add(update.userID)
|
||||
})
|
||||
const buff = Y.toBuffer(encoder)
|
||||
const buff = Y.encoding.toBuffer(encoder)
|
||||
doc.conns.forEach((_, c) => {
|
||||
c.send(buff)
|
||||
})
|
||||
@ -86,29 +87,29 @@ const setupConnection = (conn, req) => {
|
||||
conn.on('close', () => {
|
||||
const controlledIds = doc.conns.get(conn)
|
||||
doc.conns.delete(conn)
|
||||
const encoder = Y.createEncoder()
|
||||
Y.writeVarUint(encoder, messageAwareness)
|
||||
Y.writeUsersStateChange(encoder, Array.from(controlledIds).map(userID => {
|
||||
const encoder = Y.encoding.createEncoder()
|
||||
Y.encoding.writeVarUint(encoder, messageAwareness)
|
||||
Y.awarenessProtocol.writeUsersStateChange(encoder, Array.from(controlledIds).map(userID => {
|
||||
doc.awareness.delete(userID)
|
||||
return { userID, state: null }
|
||||
}))
|
||||
const buf = Y.toBuffer(encoder)
|
||||
const buf = Y.encoding.toBuffer(encoder)
|
||||
doc.conns.forEach((_, conn) => conn.send(buf))
|
||||
})
|
||||
// send sync step 1
|
||||
const encoder = Y.createEncoder()
|
||||
Y.writeVarUint(encoder, messageSync)
|
||||
Y.writeSyncStep1(encoder, doc)
|
||||
conn.send(Y.toBuffer(encoder))
|
||||
const encoder = Y.encoding.createEncoder()
|
||||
Y.encoding.writeVarUint(encoder, messageSync)
|
||||
Y.syncProtocol.writeSyncStep1(encoder, doc)
|
||||
conn.send(Y.encoding.toBuffer(encoder))
|
||||
if (doc.awareness.size > 0) {
|
||||
const encoder = Y.createEncoder()
|
||||
const encoder = Y.encoding.createEncoder()
|
||||
const userStates = []
|
||||
doc.awareness.forEach((state, userID) => {
|
||||
userStates.push({ state, userID })
|
||||
})
|
||||
Y.writeVarUint(encoder, messageAwareness)
|
||||
Y.writeUsersStateChange(encoder, userStates)
|
||||
conn.send(Y.toBuffer(encoder))
|
||||
Y.encoding.writeVarUint(encoder, messageAwareness)
|
||||
Y.awarenessProtocol.writeUsersStateChange(encoder, userStates)
|
||||
conn.send(Y.encoding.toBuffer(encoder))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,13 @@
|
||||
|
||||
import { getStructReference } from '../utils/structReferences.js'
|
||||
import * as ID from '../utils/ID.js'
|
||||
import { stringifyID } from '../protocols/syncProtocol.js'
|
||||
import { writeStructToTransaction } from '../utils/Transaction.js'
|
||||
import { writeStructToTransaction } from '../utils/structEncoding.js'
|
||||
import * as decoding from '../lib/decoding.js'
|
||||
import * as encoding from '../lib/encoding.js'
|
||||
import { Item } from './Item.js' // eslint-disable-line
|
||||
import { Y } from '../utils/Y.js' // eslint-disable-line
|
||||
// import { Item } from './Item.js' // eslint-disable-line
|
||||
// import { Y } from '../utils/Y.js' // eslint-disable-line
|
||||
import { deleteItemRange } from '../utils/structManipulation.js'
|
||||
import * as stringify from '../utils/structStringify.js'
|
||||
|
||||
/**
|
||||
* @private
|
||||
@ -99,6 +99,6 @@ export class Delete {
|
||||
* @private
|
||||
*/
|
||||
_logString () {
|
||||
return `Delete - target: ${stringifyID(this._targetID)}, len: ${this._length}`
|
||||
return `Delete - target: ${stringify.stringifyID(this._targetID)}, len: ${this._length}`
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,10 @@
|
||||
|
||||
import { getStructReference } from '../utils/structReferences.js'
|
||||
import * as ID from '../utils/ID.js'
|
||||
import { writeStructToTransaction } from '../utils/Transaction.js'
|
||||
import { writeStructToTransaction } from '../utils/structEncoding.js'
|
||||
import * as decoding from '../lib/decoding.js'
|
||||
import * as encoding from '../lib/encoding.js'
|
||||
import { Y } from '../utils/Y.js' // eslint-disable-line
|
||||
// import { Y } from '../utils/Y.js' // eslint-disable-line
|
||||
|
||||
// TODO should have the same base class as Item
|
||||
export class GC {
|
||||
|
@ -5,12 +5,27 @@
|
||||
import { getStructReference } from '../utils/structReferences.js'
|
||||
import * as ID from '../utils/ID.js'
|
||||
import { Delete } from './Delete.js'
|
||||
import { transactionTypeChanged, writeStructToTransaction } from '../utils/Transaction.js'
|
||||
import { writeStructToTransaction } from '../utils/structEncoding.js'
|
||||
import { GC } from './GC.js'
|
||||
import * as encoding from '../lib/encoding.js'
|
||||
import * as decoding from '../lib/decoding.js'
|
||||
import { Y } from '../utils/Y.js'
|
||||
import { Type } from './Type.js' // eslint-disable-line
|
||||
// import { Type } from './Type.js' // eslint-disable-line
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export const transactionTypeChanged = (y, type, sub) => {
|
||||
if (type !== y && !type._deleted && !y._transaction.newTypes.has(type)) {
|
||||
const changedTypes = y._transaction.changedTypes
|
||||
let subs = changedTypes.get(type)
|
||||
if (subs === undefined) {
|
||||
// create if it doesn't exist yet
|
||||
subs = new Set()
|
||||
changedTypes.set(type, subs)
|
||||
}
|
||||
subs.add(sub)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
@ -159,7 +174,7 @@ export class Item {
|
||||
if (this._redone !== null) {
|
||||
return this._redone
|
||||
}
|
||||
if (this._parent instanceof Y) {
|
||||
if (!(this._parent instanceof Item)) {
|
||||
return
|
||||
}
|
||||
let struct = this._copy()
|
||||
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { Item } from './Item.js'
|
||||
import { logItemHelper } from '../protocols/syncProtocol.js'
|
||||
import * as stringify from '../utils/structStringify.js'
|
||||
import * as encoding from '../lib/encoding.js'
|
||||
import * as decoding from '../lib/decoding.js'
|
||||
import { Y } from '../utils/Y.js' // eslint-disable-line
|
||||
@ -44,6 +44,6 @@ export class ItemEmbed extends Item {
|
||||
* @private
|
||||
*/
|
||||
_logString () {
|
||||
return logItemHelper('ItemEmbed', this, `embed:${JSON.stringify(this.embed)}`)
|
||||
return stringify.logItemHelper('ItemEmbed', this, `embed:${JSON.stringify(this.embed)}`)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { Item } from './Item.js'
|
||||
import { logItemHelper } from '../protocols/syncProtocol.js'
|
||||
import * as stringify from '../utils/structStringify.js'
|
||||
import * as encoding from '../lib/encoding.js'
|
||||
import * as decoding from '../lib/decoding.js'
|
||||
import { Y } from '../utils/Y.js' // eslint-disable-line
|
||||
@ -51,6 +51,6 @@ export class ItemFormat extends Item {
|
||||
* @private
|
||||
*/
|
||||
_logString () {
|
||||
return logItemHelper('ItemFormat', this, `key:${JSON.stringify(this.key)},value:${JSON.stringify(this.value)}`)
|
||||
return stringify.logItemHelper('ItemFormat', this, `key:${JSON.stringify(this.key)},value:${JSON.stringify(this.value)}`)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { Item, splitHelper } from './Item.js'
|
||||
import { logItemHelper } from '../protocols/syncProtocol.js'
|
||||
import * as stringify from '../utils/structStringify.js'
|
||||
import * as encoding from '../lib/encoding.js'
|
||||
import * as decoding from '../lib/decoding.js'
|
||||
import { Y } from '../utils/Y.js' // eslint-disable-line
|
||||
@ -19,7 +19,8 @@ export class ItemJSON extends Item {
|
||||
return struct
|
||||
}
|
||||
get _length () {
|
||||
return this._content.length
|
||||
const c = this._content
|
||||
return c !== null ? c.length : 0
|
||||
}
|
||||
/**
|
||||
* @param {Y} y
|
||||
@ -46,11 +47,11 @@ export class ItemJSON extends Item {
|
||||
*/
|
||||
_toBinary (encoder) {
|
||||
super._toBinary(encoder)
|
||||
let len = this._content.length
|
||||
const len = this._length
|
||||
encoding.writeVarUint(encoder, len)
|
||||
for (let i = 0; i < len; i++) {
|
||||
let encoded
|
||||
let content = this._content[i]
|
||||
const content = this._content[i]
|
||||
if (content === undefined) {
|
||||
encoded = 'undefined'
|
||||
} else {
|
||||
@ -66,7 +67,7 @@ export class ItemJSON extends Item {
|
||||
* @private
|
||||
*/
|
||||
_logString () {
|
||||
return logItemHelper('ItemJSON', this, `content:${JSON.stringify(this._content)}`)
|
||||
return stringify.logItemHelper('ItemJSON', this, `content:${JSON.stringify(this._content)}`)
|
||||
}
|
||||
_splitAt (y, diff) {
|
||||
if (diff === 0) {
|
||||
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { Item, splitHelper } from './Item.js'
|
||||
import { logItemHelper } from '../protocols/syncProtocol.js'
|
||||
import * as stringify from '../utils/structStringify.js'
|
||||
import * as encoding from '../lib/encoding.js'
|
||||
import * as decoding from '../lib/decoding.js'
|
||||
import { Y } from '../utils/Y.js' // eslint-disable-line
|
||||
@ -44,7 +44,7 @@ export class ItemString extends Item {
|
||||
* @private
|
||||
*/
|
||||
_logString () {
|
||||
return logItemHelper('ItemString', this, `content:"${this._content}"`)
|
||||
return stringify.logItemHelper('ItemString', this, `content:"${this._content}"`)
|
||||
}
|
||||
_splitAt (y, diff) {
|
||||
if (diff === 0) {
|
||||
|
@ -6,7 +6,7 @@ import { defragmentItemContent } from '../utils/defragmentItemContent.js'
|
||||
import Quill from 'quill'
|
||||
import { GC } from '../structs/GC.js'
|
||||
import * as random from '../lib/prng/prng.js'
|
||||
import * as syncProtocol from '../protocols/syncProtocol.js'
|
||||
import * as syncProtocol from '../protocols/sync.js'
|
||||
import * as encoding from '../lib/encoding.js'
|
||||
import * as decoding from '../lib/decoding.js'
|
||||
import { createMutex } from '../lib/mutex.js'
|
||||
|
58
tsconfig.json
Normal file
58
tsconfig.json
Normal file
@ -0,0 +1,58 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
"target": "es2018",
|
||||
"lib": ["es2018", "dom"], /* Specify library files to be included in the compilation. */
|
||||
"allowJs": true, /* Allow javascript files to be compiled. */
|
||||
"checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
// "outDir": "./build", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
"noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
import { Type } from '../structs/Type.js'
|
||||
import { ItemJSON } from '../structs/ItemJSON.js'
|
||||
import { ItemString } from '../structs/ItemString.js'
|
||||
import { stringifyItemID, logItemHelper } from '../protocols/syncProtocol.js'
|
||||
import * as stringify from '../utils/structStringify.js'
|
||||
import { YEvent } from '../utils/YEvent.js'
|
||||
import { Transaction } from '../utils/Transaction.js' // eslint-disable-line
|
||||
import { Item } from '../structs/Item.js' // eslint-disable-line
|
||||
@ -368,6 +368,6 @@ export class YArray extends Type {
|
||||
* @private
|
||||
*/
|
||||
_logString () {
|
||||
return logItemHelper('YArray', this, `start:${stringifyItemID(this._start)}"`)
|
||||
return stringify.logItemHelper('YArray', this, `start:${stringify.stringifyItemID(this._start)}"`)
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
import { Item } from '../structs/Item.js'
|
||||
import { Type } from '../structs/Type.js'
|
||||
import { ItemJSON } from '../structs/ItemJSON.js'
|
||||
import { logItemHelper } from '../protocols/syncProtocol.js'
|
||||
import * as stringify from '../utils/structStringify.js'
|
||||
import { YEvent } from '../utils/YEvent.js'
|
||||
|
||||
/**
|
||||
@ -175,6 +175,6 @@ export class YMap extends Type {
|
||||
* @private
|
||||
*/
|
||||
_logString () {
|
||||
return logItemHelper('YMap', this, `mapSize:${this._map.size}`)
|
||||
return stringify.logItemHelper('YMap', this, `mapSize:${this._map.size}`)
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
import { ItemEmbed } from '../structs/ItemEmbed.js'
|
||||
import { ItemString } from '../structs/ItemString.js'
|
||||
import { ItemFormat } from '../structs/ItemFormat.js'
|
||||
import { logItemHelper } from '../protocols/syncProtocol.js'
|
||||
import * as stringify from '../utils/structStringify.js'
|
||||
import { YArrayEvent, YArray } from './YArray.js'
|
||||
|
||||
/**
|
||||
@ -702,6 +702,6 @@ export class YText extends YArray {
|
||||
* @private
|
||||
*/
|
||||
_logString () {
|
||||
return logItemHelper('YText', this)
|
||||
return stringify.logItemHelper('YText', this)
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import { DomBinding } from '../bindings/dom/DomBinding.js' // eslint-disable-lin
|
||||
import { YXmlTreeWalker } from './YXmlTreeWalker.js'
|
||||
import { YArray } from './YArray.js'
|
||||
import { YXmlEvent } from './YXmlEvent.js'
|
||||
import { logItemHelper } from '../protocols/syncProtocol.js'
|
||||
import * as stringify from '../utils/structStringify.js'
|
||||
|
||||
/**
|
||||
* Dom filter function.
|
||||
@ -164,7 +164,7 @@ export class YXmlFragment extends YArray {
|
||||
* @private
|
||||
*/
|
||||
_logString () {
|
||||
return logItemHelper('YXml', this)
|
||||
return stringify.logItemHelper('YXml', this)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,8 @@
|
||||
import { Tree } from '../lib/Tree.js'
|
||||
import * as ID from '../utils/ID.js'
|
||||
import { getStruct } from '../utils/structReferences.js'
|
||||
import { stringifyID, stringifyItemID } from '../protocols/syncProtocol.js'
|
||||
import { GC } from '../structs/GC.js'
|
||||
import * as stringify from '../utils/structStringify.js'
|
||||
|
||||
export class OperationStore extends Tree {
|
||||
constructor (y) {
|
||||
@ -18,18 +18,18 @@ export class OperationStore extends Tree {
|
||||
this.iterate(null, null, item => {
|
||||
if (item.constructor === GC) {
|
||||
items.push({
|
||||
id: stringifyItemID(item),
|
||||
id: stringify.stringifyItemID(item),
|
||||
content: item._length,
|
||||
deleted: 'GC'
|
||||
})
|
||||
} else {
|
||||
items.push({
|
||||
id: stringifyItemID(item),
|
||||
origin: item._origin === null ? '()' : stringifyID(item._origin._lastId),
|
||||
left: item._left === null ? '()' : stringifyID(item._left._lastId),
|
||||
right: stringifyItemID(item._right),
|
||||
right_origin: stringifyItemID(item._right_origin),
|
||||
parent: stringifyItemID(item._parent),
|
||||
id: stringify.stringifyItemID(item),
|
||||
origin: item._origin === null ? '()' : stringify.stringifyID(item._origin._lastId),
|
||||
left: item._left === null ? '()' : stringify.stringifyID(item._left._lastId),
|
||||
right: stringify.stringifyItemID(item._right),
|
||||
right_origin: stringify.stringifyItemID(item._right_origin),
|
||||
parent: stringify.stringifyItemID(item._parent),
|
||||
parentSub: item._parentSub,
|
||||
deleted: item._deleted,
|
||||
content: JSON.stringify(item._content)
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
import * as encoding from '../lib/encoding.js'
|
||||
import { Y } from '../utils/Y.js' // eslint-disable-line
|
||||
import { Type } from '../structs/Type.js' // eslint-disable-line
|
||||
import { Item } from '../structs/Item.js' // eslint-disable-line
|
||||
import { Type } from '../structs/Type.js' // eslint-disable-line
|
||||
import { YEvent } from './YEvent.js' // eslint-disable-line
|
||||
/**
|
||||
* A transaction is created for every change on the Yjs model. It is possible
|
||||
@ -70,24 +70,3 @@ export class Transaction {
|
||||
this.encodedStructs = encoding.createEncoder()
|
||||
}
|
||||
}
|
||||
|
||||
export const writeStructToTransaction = (transaction, struct) => {
|
||||
transaction.encodedStructsLen++
|
||||
struct._toBinary(transaction.encodedStructs)
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export const transactionTypeChanged = (y, type, sub) => {
|
||||
if (type !== y && !type._deleted && !y._transaction.newTypes.has(type)) {
|
||||
const changedTypes = y._transaction.changedTypes
|
||||
let subs = changedTypes.get(type)
|
||||
if (subs === undefined) {
|
||||
// create if it doesn't exist yet
|
||||
subs = new Set()
|
||||
changedTypes.set(type, subs)
|
||||
}
|
||||
subs.add(sub)
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import { createRootID } from './ID.js'
|
||||
import { NamedEventHandler } from '../lib/NamedEventHandler.js'
|
||||
import { Transaction } from './Transaction.js'
|
||||
import * as encoding from '../lib/encoding.js'
|
||||
import * as message from '../protocols/syncProtocol.js'
|
||||
import * as message from '../protocols/sync.js'
|
||||
import { integrateRemoteStructs } from './integrateRemoteStructs.js'
|
||||
import { Type } from '../structs/Type.js' // eslint-disable-line
|
||||
import { Decoder } from '../lib/decoding.js' // eslint-disable-line
|
||||
|
5
utils/structEncoding.js
Normal file
5
utils/structEncoding.js
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
export const writeStructToTransaction = (transaction, struct) => {
|
||||
transaction.encodedStructsLen++
|
||||
struct._toBinary(transaction.encodedStructs)
|
||||
}
|
47
utils/structStringify.js
Normal file
47
utils/structStringify.js
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
import * as ID from './ID.js'
|
||||
|
||||
/**
|
||||
* Stringify an item id.
|
||||
*
|
||||
* @param {ID.ID | ID.RootID} id
|
||||
* @return {string}
|
||||
*/
|
||||
export const stringifyID = id => id instanceof ID.ID ? `(${id.user},${id.clock})` : `(${id.name},${id.type})`
|
||||
|
||||
/**
|
||||
* Stringify an item as ID. HHere, an item could also be a Yjs instance (e.g. item._parent).
|
||||
*
|
||||
* @param {Item | Y | null} item
|
||||
* @return {string}
|
||||
*/
|
||||
export const stringifyItemID = item => {
|
||||
let result
|
||||
if (item === null) {
|
||||
result = '()'
|
||||
} else if (item._id != null) {
|
||||
result = stringifyID(item._id)
|
||||
} else {
|
||||
// must be a Yjs instance
|
||||
// Don't include Y in this module, so we prevent circular dependencies.
|
||||
result = 'y'
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper utility to convert an item to a readable format.
|
||||
*
|
||||
* @param {String} name The name of the item class (YText, ItemString, ..).
|
||||
* @param {Item} item The item instance.
|
||||
* @param {String} [append] Additional information to append to the returned
|
||||
* string.
|
||||
* @return {String} A readable string that represents the item object.
|
||||
*
|
||||
*/
|
||||
export const logItemHelper = (name, item, append) => {
|
||||
const left = item._left !== null ? stringifyID(item._left._lastId) : '()'
|
||||
const origin = item._origin !== null ? stringifyID(item._origin._lastId) : '()'
|
||||
return `${name}(id:${stringifyItemID(item)},left:${left},origin:${origin},right:${stringifyItemID(item._right)},parent:${stringifyItemID(item._parent)},parentSub:${item._parentSub}${append !== undefined ? ' - ' + append : ''})`
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user