draft for WeakLink API
This commit is contained in:
parent
5db1eed181
commit
c7539b028a
@ -10,6 +10,7 @@ export {
|
||||
YXmlHook as XmlHook,
|
||||
YXmlElement as XmlElement,
|
||||
YXmlFragment as XmlFragment,
|
||||
WeakLink,
|
||||
YXmlEvent,
|
||||
YMapEvent,
|
||||
YArrayEvent,
|
||||
|
@ -27,6 +27,7 @@ export * from './types/YXmlElement.js'
|
||||
export * from './types/YXmlEvent.js'
|
||||
export * from './types/YXmlHook.js'
|
||||
export * from './types/YXmlText.js'
|
||||
export * from './types/WeakLink.js'
|
||||
|
||||
export * from './structs/AbstractStruct.js'
|
||||
export * from './structs/GC.js'
|
||||
@ -39,5 +40,6 @@ export * from './structs/ContentJSON.js'
|
||||
export * from './structs/ContentAny.js'
|
||||
export * from './structs/ContentString.js'
|
||||
export * from './structs/ContentType.js'
|
||||
export * from './structs/ContentLink.js'
|
||||
export * from './structs/Item.js'
|
||||
export * from './structs/Skip.js'
|
||||
|
99
src/structs/ContentLink.js
Normal file
99
src/structs/ContentLink.js
Normal file
@ -0,0 +1,99 @@
|
||||
import {
|
||||
UpdateEncoderV1, UpdateEncoderV2, UpdateDecoderV1, UpdateDecoderV2, Transaction, Item, StructStore // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
export class ContentLink {
|
||||
/**
|
||||
* @param {Item} item
|
||||
*/
|
||||
constructor (item) {
|
||||
/**
|
||||
* @type {Item}
|
||||
*/
|
||||
this.item = item
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
getLength () {
|
||||
return this.item.length
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Array<any>}
|
||||
*/
|
||||
getContent () {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
isCountable () {
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {ContentLink}
|
||||
*/
|
||||
copy () {
|
||||
return new ContentLink(this.item)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} offset
|
||||
* @return {ContentLink}
|
||||
*/
|
||||
splice (offset) {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ContentLink} right
|
||||
* @return {boolean}
|
||||
*/
|
||||
mergeWith (right) {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
* @param {Item} item
|
||||
*/
|
||||
integrate (transaction, item) {}
|
||||
|
||||
/**
|
||||
* @param {Transaction} transaction
|
||||
*/
|
||||
delete (transaction) {}
|
||||
|
||||
/**
|
||||
* @param {StructStore} store
|
||||
*/
|
||||
gc (store) {}
|
||||
|
||||
/**
|
||||
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
||||
* @param {number} offset
|
||||
*/
|
||||
write (encoder, offset) {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
getRef () {
|
||||
return 10
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
|
||||
* @return {ContentLink}
|
||||
*/
|
||||
export const readContentWeakLink = decoder => {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
readContentString,
|
||||
readContentEmbed,
|
||||
readContentDoc,
|
||||
readContentWeakLink,
|
||||
createID,
|
||||
readContentFormat,
|
||||
readContentType,
|
||||
@ -719,6 +720,7 @@ export const contentRefs = [
|
||||
readContentType, // 7
|
||||
readContentAny, // 8
|
||||
readContentDoc, // 9
|
||||
readContentWeakLink, // 10
|
||||
() => { error.unexpectedCase() } // 10 - Skip is not ItemContent
|
||||
]
|
||||
|
||||
|
17
src/types/WeakLink.js
Normal file
17
src/types/WeakLink.js
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*
|
||||
* Weak link to another value stored somewhere in the document.
|
||||
*/
|
||||
export class WeakLink {
|
||||
|
||||
/**
|
||||
* Returns a reference to an underlying value existing somewhere on in the document.
|
||||
*
|
||||
* @return {T|undefined}
|
||||
*/
|
||||
deref() {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
}
|
@ -16,7 +16,8 @@ import {
|
||||
YArrayRefID,
|
||||
callTypeObservers,
|
||||
transact,
|
||||
ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line
|
||||
ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line
|
||||
WeakLink
|
||||
} from '../internals.js'
|
||||
import { typeListSlice } from './AbstractType.js'
|
||||
|
||||
@ -201,6 +202,16 @@ export class YArray extends AbstractType {
|
||||
return typeListGet(this, index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the weak link to i-th element from a YArray.
|
||||
*
|
||||
* @param {number} index The index of the element to return from the YArray
|
||||
* @return {WeakLink<T>}
|
||||
*/
|
||||
link(index) {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms this YArray to a JavaScript Array.
|
||||
*
|
||||
|
@ -14,7 +14,8 @@ import {
|
||||
YMapRefID,
|
||||
callTypeObservers,
|
||||
transact,
|
||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line
|
||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line
|
||||
WeakLink
|
||||
} from '../internals.js'
|
||||
|
||||
import * as iterator from 'lib0/iterator'
|
||||
@ -233,6 +234,16 @@ export class YMap extends AbstractType {
|
||||
return /** @type {any} */ (typeMapGet(this, key))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a weak reference link to another element stored in the same document.
|
||||
*
|
||||
* @param {string} key
|
||||
* @return {WeakLink<MapType>|undefined}
|
||||
*/
|
||||
link(key) {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating whether the specified key exists or not.
|
||||
*
|
||||
|
@ -11,6 +11,7 @@ import * as doc from './doc.tests.js'
|
||||
import * as snapshot from './snapshot.tests.js'
|
||||
import * as updates from './updates.tests.js'
|
||||
import * as relativePositions from './relativePositions.tests.js'
|
||||
import * as weakLinks from './weakLinks.tests.js'
|
||||
|
||||
import { runTests } from 'lib0/testing'
|
||||
import { isBrowser, isNode } from 'lib0/environment'
|
||||
@ -20,7 +21,7 @@ if (isBrowser) {
|
||||
log.createVConsole(document.body)
|
||||
}
|
||||
runTests({
|
||||
doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions
|
||||
doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, weakLinks
|
||||
}).then(success => {
|
||||
/* istanbul ignore next */
|
||||
if (isNode) {
|
||||
|
176
tests/weakLinks.tests.js
Normal file
176
tests/weakLinks.tests.js
Normal file
@ -0,0 +1,176 @@
|
||||
import * as Y from '../src/index.js'
|
||||
import * as t from 'lib0/testing'
|
||||
import { init, compare } from './testHelper.js'
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
export const testBasic = tc => {
|
||||
const doc = new Y.Doc()
|
||||
const map = doc.getMap('map')
|
||||
|
||||
const nested = new Y.Map()
|
||||
nested.set('a1', 'hello')
|
||||
map.set('a', nested)
|
||||
const link = nested.link('a')
|
||||
map.set('b', link)
|
||||
|
||||
const nested2 = map.get('b')
|
||||
t.compare(nested2.toJSON(), nested.toJSON())
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
export const testUpdate = tc => {
|
||||
const { testConnector, users, map0, map1 } = init(tc, { users: 2 })
|
||||
map0.set('a', new Y.Map([['a1', 'hello']]))
|
||||
const link0 = /** @type {Y.WeakLink<Y.Map<any>>} */ (map0.link('a'))
|
||||
map0.set('b', link0)
|
||||
|
||||
testConnector.flushAllMessages()
|
||||
const link1 = /** @type {Y.WeakLink<Y.Map<any>>} */ (map1.get('b'))
|
||||
let l1 = /** @type {Y.Map<any>} */ (link1.deref())
|
||||
let l0 = /** @type {Y.Map<any>} */ (link0.deref())
|
||||
t.compare(l1.get('a1'), l0.get('a1'))
|
||||
|
||||
map1.get('a').set('a2', 'world')
|
||||
|
||||
testConnector.flushAllMessages()
|
||||
|
||||
l1 = /** @type {Y.Map<any>} */ (link1.deref())
|
||||
l0 = /** @type {Y.Map<any>} */ (link0.deref())
|
||||
t.compare(l1.get('a2'), l0.get('a2'))
|
||||
|
||||
compare(users)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
export const testDeleteWeakLink = tc => {
|
||||
const { testConnector, users, map0, map1 } = init(tc, { users: 2 })
|
||||
map0.set('a', new Y.Map([['a1', 'hello']]))
|
||||
const link0 = /** @type {Y.WeakLink<Y.Map<any>>} */ (map0.link('a'))
|
||||
map0.set('b', link0)
|
||||
|
||||
testConnector.flushAllMessages()
|
||||
const link1 = /** @type {Y.WeakLink<Y.Map>} */ map1.get('b')
|
||||
const l0 = /** @type {Y.Map<any>} */ (link0.deref())
|
||||
t.compare(link1.ref.get('a1'), l0.get('a1'))
|
||||
|
||||
map1.delete('b') // delete links
|
||||
|
||||
testConnector.flushAllMessages()
|
||||
|
||||
// since links have been deleted, they no longer refer to any content
|
||||
t.compare(link0.deref(), undefined)
|
||||
t.compare(link1.deref(), undefined)
|
||||
|
||||
compare(users)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
export const testDeleteSource = tc => {
|
||||
const { testConnector, users, map0, map1 } = init(tc, { users: 2 })
|
||||
map0.set('a', new Y.Map([['a1', 'hello']]))
|
||||
const link0 = /** @type {Y.WeakLink<Y.Map<any>>} */ (map0.link('a'))
|
||||
map0.set('b', link0)
|
||||
|
||||
testConnector.flushAllMessages()
|
||||
const link1 = /** @type {Y.WeakLink<Y.Map<any>>} */ (map1.get('b'))
|
||||
let l1 = /** @type {Y.Map<any>} */ (link1.deref())
|
||||
let l0 = /** @type {Y.Map<any>} */ (link0.deref())
|
||||
t.compare(l1.get('a1'), l0.get('a1'))
|
||||
|
||||
map1.delete('a') // delete source of the link
|
||||
|
||||
testConnector.flushAllMessages()
|
||||
|
||||
// since source have been deleted, links no longer refer to any content
|
||||
t.compare(link0.deref(), undefined)
|
||||
t.compare(link1.deref(), undefined)
|
||||
|
||||
compare(users)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
export const testObserve = tc => {
|
||||
const doc = new Y.Doc()
|
||||
const map = doc.getMap('map')
|
||||
const array = doc.getArray('array')
|
||||
/**
|
||||
* @type {Array<any>}
|
||||
*/
|
||||
let delta
|
||||
array.observe((e) => delta = e.changes.delta)
|
||||
|
||||
map.set('key', 'value1')
|
||||
const link = map.link('key')
|
||||
array.insert(0, [link])
|
||||
|
||||
delta = []
|
||||
|
||||
map.set('key', 'value2')
|
||||
t.compare(delta, [{ delete: 1 }, { insert: 'value2' }])
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
export const testDeepObserve = tc => {
|
||||
const doc = new Y.Doc()
|
||||
const map = doc.getMap('map')
|
||||
const array = doc.getArray('array')
|
||||
/**
|
||||
* @type {Array<any>}
|
||||
*/
|
||||
let events
|
||||
array.observeDeep((e) => events = e)
|
||||
|
||||
const nested = new Y.Map([['key', 'value']])
|
||||
map.set('key', nested)
|
||||
const link = map.link('key')
|
||||
array.insert(0, [link])
|
||||
|
||||
events = []
|
||||
|
||||
nested.set('key', 'value2')
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
let e = events[i]
|
||||
throw new Error('todo')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
export const testObserveRecursive = tc => {
|
||||
const doc = new Y.Doc()
|
||||
const map = doc.getMap('map')
|
||||
const array = doc.getArray('array')
|
||||
/**
|
||||
* @type {any}
|
||||
*/
|
||||
let arrayChanges
|
||||
array.observe((e) => arrayChanges = e.changes)
|
||||
/**
|
||||
* @type {any}
|
||||
*/
|
||||
let mapChanges
|
||||
map.observe((e) => mapChanges = e.changes)
|
||||
|
||||
Y.transact(doc, () => {
|
||||
map.set('key', 'map-value')
|
||||
array.insert(0, [map.link('key')])
|
||||
map.set('key2', array.link(0))
|
||||
})
|
||||
t.compare(arrayChanges.delta, [{ insert: 'map-value' }])
|
||||
t.compare(mapChanges.keys.get('key2'), [{ action: 'insert', oldValue: 'map-value' }])
|
||||
|
||||
t.compare(map.get('key2').deref().deref(), 'map-value')
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user