Merge f22dbb7036a4cff3140a55cc7580e91d6538abd8 into ad0d915794713a4cfb6c0fc32a3b998278301b36
This commit is contained in:
commit
f9bf59fba4
@ -21,12 +21,37 @@ import * as iterator from 'lib0/iterator'
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
|
* @typedef {Extract<keyof T, string>} StringKey<T>
|
||||||
|
*
|
||||||
|
* Like keyof, but guarantees the returned type is a subset of string.
|
||||||
|
* `keyof` will include number and Symbol even if the input type requires string keys.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @typedef {readonly {
|
||||||
|
* [K in StringKey<T>]: [K, T[K]];
|
||||||
|
* }[StringKey<T>][]} EntriesOf<T>
|
||||||
|
*
|
||||||
|
* Converts an object schema into a readonly array containing valid key-value pairs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This works around some weird JSDoc+TS circular reference issues: https://github.com/microsoft/TypeScript/issues/46369
|
||||||
|
* @typedef {boolean|null|string|number|Uint8Array|JsonArray|JsonObject} Json
|
||||||
|
* @typedef {Json[]} JsonArray
|
||||||
|
* @typedef {{ [key: string]: Json }} JsonObject
|
||||||
|
* @typedef {Json|AbstractType<any>|Doc} MapValue
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {Record<string, MapValue>} T
|
||||||
* @extends YEvent<YMap<T>>
|
* @extends YEvent<YMap<T>>
|
||||||
* Event that describes the changes on a YMap.
|
* Event that describes the changes on a YMap.
|
||||||
*/
|
*/
|
||||||
export class YMapEvent extends YEvent {
|
export class YMapEvent extends YEvent {
|
||||||
/**
|
/**
|
||||||
* @param {YMap<T>} ymap The YArray that changed.
|
* @param {YMap<T>} ymap The YMap that changed.
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {Set<any>} subs The keys that changed.
|
* @param {Set<any>} subs The keys that changed.
|
||||||
*/
|
*/
|
||||||
@ -37,21 +62,21 @@ export class YMapEvent extends YEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template MapType
|
* @template {Record<string, MapValue>} MapType
|
||||||
* A shared Map implementation.
|
* A shared Map implementation.
|
||||||
*
|
*
|
||||||
* @extends AbstractType<YMapEvent<MapType>>
|
* @extends AbstractType<YMapEvent<MapType>>
|
||||||
* @implements {Iterable<[string, MapType]>}
|
* @implements {Iterable<[StringKey<MapType>, MapType[StringKey<MapType>]]>}
|
||||||
*/
|
*/
|
||||||
export class YMap extends AbstractType {
|
export class YMap extends AbstractType {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Iterable<readonly [string, any]>=} entries - an optional iterable to initialize the YMap
|
* @param {EntriesOf<MapType>=} entries - an optional iterable to initialize the YMap
|
||||||
*/
|
*/
|
||||||
constructor (entries) {
|
constructor (entries) {
|
||||||
super()
|
super()
|
||||||
/**
|
/**
|
||||||
* @type {Map<string,any>?}
|
* @type {Map<StringKey<MapType>, MapType[StringKey<MapType>]>?}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this._prelimContent = null
|
this._prelimContent = null
|
||||||
@ -75,7 +100,7 @@ export class YMap extends AbstractType {
|
|||||||
*/
|
*/
|
||||||
_integrate (y, item) {
|
_integrate (y, item) {
|
||||||
super._integrate(y, item)
|
super._integrate(y, item)
|
||||||
;/** @type {Map<string, any>} */ (this._prelimContent).forEach((value, key) => {
|
this._prelimContent?.forEach((value, key) => {
|
||||||
this.set(key, value)
|
this.set(key, value)
|
||||||
})
|
})
|
||||||
this._prelimContent = null
|
this._prelimContent = null
|
||||||
@ -119,12 +144,12 @@ export class YMap extends AbstractType {
|
|||||||
/**
|
/**
|
||||||
* Transforms this Shared Type to a JSON object.
|
* Transforms this Shared Type to a JSON object.
|
||||||
*
|
*
|
||||||
* @return {Object<string,any>}
|
* @return {Partial<MapType>}
|
||||||
*/
|
*/
|
||||||
toJSON () {
|
toJSON () {
|
||||||
this.doc ?? warnPrematureAccess()
|
this.doc ?? warnPrematureAccess()
|
||||||
/**
|
/**
|
||||||
* @type {Object<string,MapType>}
|
* @type {any}
|
||||||
*/
|
*/
|
||||||
const map = {}
|
const map = {}
|
||||||
this._map.forEach((item, key) => {
|
this._map.forEach((item, key) => {
|
||||||
@ -157,7 +182,7 @@ export class YMap extends AbstractType {
|
|||||||
/**
|
/**
|
||||||
* Returns the values for each element in the YMap Type.
|
* Returns the values for each element in the YMap Type.
|
||||||
*
|
*
|
||||||
* @return {IterableIterator<MapType>}
|
* @return {IterableIterator<MapType[keyof MapType]>}
|
||||||
*/
|
*/
|
||||||
values () {
|
values () {
|
||||||
return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => v[1].content.getContent()[v[1].length - 1])
|
return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => v[1].content.getContent()[v[1].length - 1])
|
||||||
@ -166,7 +191,7 @@ export class YMap extends AbstractType {
|
|||||||
/**
|
/**
|
||||||
* Returns an Iterator of [key, value] pairs
|
* Returns an Iterator of [key, value] pairs
|
||||||
*
|
*
|
||||||
* @return {IterableIterator<[string, MapType]>}
|
* @return {IterableIterator<[StringKey<MapType>, MapType[StringKey<MapType>]]>}
|
||||||
*/
|
*/
|
||||||
entries () {
|
entries () {
|
||||||
return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => /** @type {any} */ ([v[0], v[1].content.getContent()[v[1].length - 1]]))
|
return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => /** @type {any} */ ([v[0], v[1].content.getContent()[v[1].length - 1]]))
|
||||||
@ -175,13 +200,13 @@ export class YMap extends AbstractType {
|
|||||||
/**
|
/**
|
||||||
* Executes a provided function on once on every key-value pair.
|
* Executes a provided function on once on every key-value pair.
|
||||||
*
|
*
|
||||||
* @param {function(MapType,string,YMap<MapType>):void} f A function to execute on every element of this YArray.
|
* @param {function(MapType[StringKey<MapType>],StringKey<MapType>,YMap<MapType>):void} f A function to execute on every element of this YArray.
|
||||||
*/
|
*/
|
||||||
forEach (f) {
|
forEach (f) {
|
||||||
this.doc ?? warnPrematureAccess()
|
this.doc ?? warnPrematureAccess()
|
||||||
this._map.forEach((item, key) => {
|
this._map.forEach((item, key) => {
|
||||||
if (!item.deleted) {
|
if (!item.deleted) {
|
||||||
f(item.content.getContent()[item.length - 1], key, this)
|
f(item.content.getContent()[item.length - 1], /** @type {StringKey<MapType>} */ (key), this)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -189,7 +214,7 @@ export class YMap extends AbstractType {
|
|||||||
/**
|
/**
|
||||||
* Returns an Iterator of [key, value] pairs
|
* Returns an Iterator of [key, value] pairs
|
||||||
*
|
*
|
||||||
* @return {IterableIterator<[string, MapType]>}
|
* @return {IterableIterator<[StringKey<MapType>, MapType[StringKey<MapType>]]>}
|
||||||
*/
|
*/
|
||||||
[Symbol.iterator] () {
|
[Symbol.iterator] () {
|
||||||
return this.entries()
|
return this.entries()
|
||||||
@ -211,17 +236,18 @@ export class YMap extends AbstractType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @template {StringKey<MapType>} Key
|
||||||
|
* @template {MapType[Key]} Value
|
||||||
* Adds or updates an element with a specified key and value.
|
* Adds or updates an element with a specified key and value.
|
||||||
* @template {MapType} VAL
|
|
||||||
*
|
*
|
||||||
* @param {string} key The key of the element to add to this YMap
|
* @param {Key} key The key of the element to add to this YMap
|
||||||
* @param {VAL} value The value of the element to add
|
* @param {Value} value The value of the element to add
|
||||||
* @return {VAL}
|
* @return {Value}
|
||||||
*/
|
*/
|
||||||
set (key, value) {
|
set (key, value) {
|
||||||
if (this.doc !== null) {
|
if (this.doc !== null) {
|
||||||
transact(this.doc, transaction => {
|
transact(this.doc, transaction => {
|
||||||
typeMapSet(transaction, this, key, /** @type {any} */ (value))
|
typeMapSet(transaction, this, key, value)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
/** @type {Map<string, any>} */ (this._prelimContent).set(key, value)
|
/** @type {Map<string, any>} */ (this._prelimContent).set(key, value)
|
||||||
@ -230,13 +256,14 @@ export class YMap extends AbstractType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @template {StringKey<MapType>} Key
|
||||||
* Returns a specified element from this YMap.
|
* Returns a specified element from this YMap.
|
||||||
*
|
*
|
||||||
* @param {string} key
|
* @param {Key} key
|
||||||
* @return {MapType|undefined}
|
* @return {MapType[Key]|undefined}
|
||||||
*/
|
*/
|
||||||
get (key) {
|
get (key) {
|
||||||
return /** @type {any} */ (typeMapGet(this, key))
|
return /** @type {MapType[Key]|undefined} */ (typeMapGet(this, key))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -268,14 +268,14 @@ export class Doc extends ObservableV2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template {Record<string, import("../internals.js").MapValue>} T
|
||||||
* @param {string} [name]
|
* @param {string} [name]
|
||||||
* @return {YMap<T>}
|
* @return {YMap<T>}
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getMap (name = '') {
|
getMap (name = '') {
|
||||||
return /** @type {YMap<T>} */ (this.get(name, YMap))
|
return /** @type {YMap<T>} */ /** @type {any} */ (this.get(name, YMap))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -114,6 +114,7 @@ export const testSubdoc = _tc => {
|
|||||||
doc.on('subdocs', subdocs => {
|
doc.on('subdocs', subdocs => {
|
||||||
event = [Array.from(subdocs.added).map(x => x.guid), Array.from(subdocs.removed).map(x => x.guid), Array.from(subdocs.loaded).map(x => x.guid)]
|
event = [Array.from(subdocs.added).map(x => x.guid), Array.from(subdocs.removed).map(x => x.guid), Array.from(subdocs.loaded).map(x => x.guid)]
|
||||||
})
|
})
|
||||||
|
/** @type {Y.Map<{ a: Y.Doc; b: Y.Doc, c: Y.Doc; }>} */
|
||||||
const subdocs = doc.getMap('mysubdocs')
|
const subdocs = doc.getMap('mysubdocs')
|
||||||
const docA = new Y.Doc({ guid: 'a' })
|
const docA = new Y.Doc({ guid: 'a' })
|
||||||
docA.load()
|
docA.load()
|
||||||
@ -121,18 +122,18 @@ export const testSubdoc = _tc => {
|
|||||||
t.compare(event, [['a'], [], ['a']])
|
t.compare(event, [['a'], [], ['a']])
|
||||||
|
|
||||||
event = null
|
event = null
|
||||||
subdocs.get('a').load()
|
subdocs.get('a')?.load()
|
||||||
t.assert(event === null)
|
t.assert(event === null)
|
||||||
|
|
||||||
event = null
|
event = null
|
||||||
subdocs.get('a').destroy()
|
subdocs.get('a')?.destroy()
|
||||||
t.compare(event, [['a'], ['a'], []])
|
t.compare(event, [['a'], ['a'], []])
|
||||||
subdocs.get('a').load()
|
subdocs.get('a')?.load()
|
||||||
t.compare(event, [[], [], ['a']])
|
t.compare(event, [[], [], ['a']])
|
||||||
|
|
||||||
subdocs.set('b', new Y.Doc({ guid: 'a', shouldLoad: false }))
|
subdocs.set('b', new Y.Doc({ guid: 'a', shouldLoad: false }))
|
||||||
t.compare(event, [['a'], [], []])
|
t.compare(event, [['a'], [], []])
|
||||||
subdocs.get('b').load()
|
subdocs.get('b')?.load()
|
||||||
t.compare(event, [[], [], ['a']])
|
t.compare(event, [[], [], ['a']])
|
||||||
|
|
||||||
const docC = new Y.Doc({ guid: 'c' })
|
const docC = new Y.Doc({ guid: 'c' })
|
||||||
@ -156,7 +157,9 @@ export const testSubdoc = _tc => {
|
|||||||
Y.applyUpdate(doc2, Y.encodeStateAsUpdate(doc))
|
Y.applyUpdate(doc2, Y.encodeStateAsUpdate(doc))
|
||||||
t.compare(event, [['a', 'a', 'c'], [], []])
|
t.compare(event, [['a', 'a', 'c'], [], []])
|
||||||
|
|
||||||
doc2.getMap('mysubdocs').get('a').load()
|
/** @type {Y.Map<Record<string, Y.Doc>>} */
|
||||||
|
const mysubdocs = doc2.getMap('mysubdocs')
|
||||||
|
mysubdocs.get('a')?.load()
|
||||||
t.compare(event, [[], [], ['a']])
|
t.compare(event, [[], [], ['a']])
|
||||||
|
|
||||||
t.compare(Array.from(doc2.getSubdocGuids()), ['a', 'c'])
|
t.compare(Array.from(doc2.getSubdocGuids()), ['a', 'c'])
|
||||||
|
92
tests/test-types.ts
Normal file
92
tests/test-types.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* This file serves to validate the public TypeScript API for YJS.
|
||||||
|
*
|
||||||
|
* It is not included in `npm run lint` or any other automated type checking, but can be used
|
||||||
|
* by those working on YJS types to ensure that the public-facing type interface remains valid.
|
||||||
|
*
|
||||||
|
* Any lines which are supposed to demonstrate statements that _would_ generate type errors
|
||||||
|
* should be clearly marked with the type error that is expected to result, to provide a
|
||||||
|
* negative test case.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as Y from "../dist/src/index";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Typed maps
|
||||||
|
*
|
||||||
|
* - Key names are autocompleted in first parameter of `get` and `set`.
|
||||||
|
* - `MapType` value types are constrained to valid Y.Map contents.
|
||||||
|
*/
|
||||||
|
type MyType = {
|
||||||
|
foo: string;
|
||||||
|
bar: number | null;
|
||||||
|
baz?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructor argument keys & values are typechecked, and keys are autocompleted.
|
||||||
|
// Multiple items for each key and partial initialization are allowed.
|
||||||
|
const map = new Y.Map<MyType>([
|
||||||
|
["foo", ""],
|
||||||
|
["foo", "even better"],
|
||||||
|
// ERROR: Type '["baz", number]' is not assignable to type '["foo", string] | ["bar", number | null] | ["baz", boolean | undefined]'.
|
||||||
|
["baz", 3],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Entries are still allowed to be omitted, so get() still returns <type> | undefined.
|
||||||
|
const defaultMap = new Y.Map<MyType>();
|
||||||
|
|
||||||
|
// `json` has a type of `Partial<MyType>`
|
||||||
|
const json = defaultMap.toJSON();
|
||||||
|
|
||||||
|
// string | undefined
|
||||||
|
const fooValue = map.get("foo");
|
||||||
|
// literal "hi" (string)
|
||||||
|
const fooSet = map.set("foo", "hi");
|
||||||
|
// number | null | undefined
|
||||||
|
const barValue = map.get("bar");
|
||||||
|
// ERROR: Argument of type '"hi"' is not assignable to parameter of type 'number | null'.
|
||||||
|
const barSet = map.set("bar", "hi");
|
||||||
|
// ERROR: Argument of type '"bomb"' is not assignable to parameter of type 'keyof MyType'.
|
||||||
|
const missingGet = map.get("bomb");
|
||||||
|
// Escape hatch: get<any>()
|
||||||
|
const migrateGet = map.get<any>("extraneousKey");
|
||||||
|
|
||||||
|
// ERROR: Type '<type>' does not satisfy the constraint 'Record<string, MapValue>'.
|
||||||
|
const invalidMap = new Y.Map<{ invalid: () => void }>();
|
||||||
|
const invalidMap2 = new Y.Map<{ invalid: Blob }>();
|
||||||
|
// Arbitrarily complex valid types are still allowed
|
||||||
|
type ComplexType = {
|
||||||
|
n: null;
|
||||||
|
b: boolean;
|
||||||
|
s: string;
|
||||||
|
i: number;
|
||||||
|
u: Uint8Array;
|
||||||
|
a: null | boolean | string | number | Uint8Array[];
|
||||||
|
};
|
||||||
|
const complexValidType = new Y.Map<
|
||||||
|
ComplexType & { nested: ComplexType & { deeper: ComplexType[] } }
|
||||||
|
>();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default behavior
|
||||||
|
*
|
||||||
|
* Provides basic typechecking over the range of possible map values.
|
||||||
|
*/
|
||||||
|
const untyped = new Y.Map();
|
||||||
|
|
||||||
|
// MapValue | undefined
|
||||||
|
const boop = untyped.get("default");
|
||||||
|
// Still validates value types: ERROR: Argument of type '() => string' is not assignable to parameter of type 'MapValue'.
|
||||||
|
const moop = untyped.set("anything", () => "whoops");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* `any` maps (bypass typechecking)
|
||||||
|
*/
|
||||||
|
const anyMap = new Y.Map<any>();
|
||||||
|
|
||||||
|
// any
|
||||||
|
const fooValueAny = anyMap.get("foo");
|
||||||
|
// literal "hi" (string)
|
||||||
|
const fooSetAny = anyMap.set("foo", "hi");
|
||||||
|
// Allowed because `any` unlocks cowboy mode
|
||||||
|
const barSetAny = anyMap.set("bar", () => "hi");
|
@ -435,6 +435,7 @@ export const testUndoUntilChangePerformed = _tc => {
|
|||||||
const yMap = new Y.Map()
|
const yMap = new Y.Map()
|
||||||
yMap.set('hello', 'world')
|
yMap.set('hello', 'world')
|
||||||
yArray.push([yMap])
|
yArray.push([yMap])
|
||||||
|
/** @type {Y.Map<{ key: string }>} */
|
||||||
const yMap2 = new Y.Map()
|
const yMap2 = new Y.Map()
|
||||||
yMap2.set('key', 'value')
|
yMap2.set('key', 'value')
|
||||||
yArray.push([yMap2])
|
yArray.push([yMap2])
|
||||||
@ -448,7 +449,7 @@ export const testUndoUntilChangePerformed = _tc => {
|
|||||||
Y.transact(doc2, () => yArray2.delete(0), doc2.clientID)
|
Y.transact(doc2, () => yArray2.delete(0), doc2.clientID)
|
||||||
undoManager2.undo()
|
undoManager2.undo()
|
||||||
undoManager.undo()
|
undoManager.undo()
|
||||||
t.compareStrings(yMap2.get('key'), 'value')
|
t.compareStrings(yMap2.get('key') || '', 'value')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -513,6 +514,7 @@ export const testUndoNestedUndoIssue = _tc => {
|
|||||||
*/
|
*/
|
||||||
export const testConsecutiveRedoBug = _tc => {
|
export const testConsecutiveRedoBug = _tc => {
|
||||||
const doc = new Y.Doc()
|
const doc = new Y.Doc()
|
||||||
|
/** @type {Y.Map<Record<string, Y.Map<any>>>} */
|
||||||
const yRoot = doc.getMap()
|
const yRoot = doc.getMap()
|
||||||
const undoMgr = new Y.UndoManager(yRoot)
|
const undoMgr = new Y.UndoManager(yRoot)
|
||||||
|
|
||||||
@ -546,7 +548,7 @@ export const testConsecutiveRedoBug = _tc => {
|
|||||||
t.compare(yRoot.get('a'), undefined)
|
t.compare(yRoot.get('a'), undefined)
|
||||||
|
|
||||||
undoMgr.redo() // x=0, y=0
|
undoMgr.redo() // x=0, y=0
|
||||||
yPoint = yRoot.get('a')
|
yPoint = yRoot.get('a') || new Y.Map()
|
||||||
|
|
||||||
t.compare(yPoint.toJSON(), { x: 0, y: 0 })
|
t.compare(yPoint.toJSON(), { x: 0, y: 0 })
|
||||||
undoMgr.redo() // x=100, y=100
|
undoMgr.redo() // x=100, y=100
|
||||||
|
@ -14,7 +14,7 @@ import * as prng from 'lib0/prng'
|
|||||||
export const testIterators = _tc => {
|
export const testIterators = _tc => {
|
||||||
const ydoc = new Y.Doc()
|
const ydoc = new Y.Doc()
|
||||||
/**
|
/**
|
||||||
* @type {Y.Map<number>}
|
* @type {Y.Map<Record<string, number>>}
|
||||||
*/
|
*/
|
||||||
const ymap = ydoc.getMap()
|
const ymap = ydoc.getMap()
|
||||||
// we are only checking if the type assumptions are correct
|
// we are only checking if the type assumptions are correct
|
||||||
@ -67,14 +67,16 @@ export const testMapHavingIterableAsConstructorParamTests = tc => {
|
|||||||
t.assert(m1.get('number') === 1)
|
t.assert(m1.get('number') === 1)
|
||||||
t.assert(m1.get('string') === 'hello')
|
t.assert(m1.get('string') === 'hello')
|
||||||
|
|
||||||
|
/** @type {Y.Map<{ object: { x: number; }; boolean: boolean; }>} */
|
||||||
const m2 = new Y.Map([
|
const m2 = new Y.Map([
|
||||||
['object', { x: 1 }],
|
['object', { x: 1 }],
|
||||||
['boolean', true]
|
['boolean', true]
|
||||||
])
|
])
|
||||||
map0.set('m2', m2)
|
map0.set('m2', m2)
|
||||||
t.assert(m2.get('object').x === 1)
|
t.assert(m2.get('object')?.x === 1)
|
||||||
t.assert(m2.get('boolean') === true)
|
t.assert(m2.get('boolean') === true)
|
||||||
|
|
||||||
|
/** @type {Y.Map<any>} */
|
||||||
const m3 = new Y.Map([...m1, ...m2])
|
const m3 = new Y.Map([...m1, ...m2])
|
||||||
map0.set('m3', m3)
|
map0.set('m3', m3)
|
||||||
t.assert(m3.get('number') === 1)
|
t.assert(m3.get('number') === 1)
|
||||||
@ -329,7 +331,7 @@ export const testGetAndSetAndDeleteOfMapPropertyWithThreeConflicts = tc => {
|
|||||||
*/
|
*/
|
||||||
export const testObserveDeepProperties = tc => {
|
export const testObserveDeepProperties = tc => {
|
||||||
const { testConnector, users, map1, map2, map3 } = init(tc, { users: 4 })
|
const { testConnector, users, map1, map2, map3 } = init(tc, { users: 4 })
|
||||||
const _map1 = map1.set('map', new Y.Map())
|
const _map1 = map1.set('map', /** @type {Y.Map<{ deepmap: Y.Map<any> }>} */ (new Y.Map()))
|
||||||
let calls = 0
|
let calls = 0
|
||||||
let dmapid
|
let dmapid
|
||||||
map1.observeDeep(events => {
|
map1.observeDeep(events => {
|
||||||
@ -354,10 +356,10 @@ export const testObserveDeepProperties = tc => {
|
|||||||
const dmap2 = _map2.get('deepmap')
|
const dmap2 = _map2.get('deepmap')
|
||||||
const dmap3 = _map3.get('deepmap')
|
const dmap3 = _map3.get('deepmap')
|
||||||
t.assert(calls > 0)
|
t.assert(calls > 0)
|
||||||
t.assert(compareIDs(dmap1._item.id, dmap2._item.id))
|
t.assert(compareIDs(dmap1?._item?.id || null, dmap2._item.id))
|
||||||
t.assert(compareIDs(dmap1._item.id, dmap3._item.id))
|
t.assert(compareIDs(dmap1?._item?.id || null, dmap3._item.id))
|
||||||
// @ts-ignore we want the possibility of dmapid being undefined
|
// @ts-ignore we want the possibility of dmapid being undefined
|
||||||
t.assert(compareIDs(dmap1._item.id, dmapid))
|
t.assert(compareIDs(dmap1?._item?.id || null, dmapid))
|
||||||
compare(users)
|
compare(users)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user