fix all type issues

This commit is contained in:
Kevin Jahns
2026-01-06 20:35:18 +01:00
parent ec11ef6f08
commit 2053f7987a
14 changed files with 344 additions and 295 deletions

View File

@@ -231,11 +231,13 @@ document private.
</dd>
<dt><a href="https://github.com/liveblocks/liveblocks">@liveblocks/yjs </a> 🌟</dt>
<dd>
<a href="https://liveblocks.io/docs/api-reference/liveblocks-yjs">Liveblocks Yjs</a> provides a fully
hosted WebSocket infrastructure and persisted data store for Yjs
documents. No configuration or maintenance is required. It also features
Yjs webhook events, REST API to read and update Yjs documents, and a
browser DevTools extension.
<a href="https://liveblocks.io/docs/api-reference/liveblocks-yjs">
Liveblocks Yjs
</a>
provides a fully hosted WebSocket infrastructure and persisted data
store for Yjs documents. No configuration or maintenance is required. It
also features Yjs webhook events, REST API to read and update Yjs
documents, and a browser DevTools extension.
</dd>
<dt><a href="https://github.com/ueberdosis/hocuspocus">Hocuspocus</a> ⭐</dt>
<dd>

65
package-lock.json generated
View File

@@ -14,6 +14,7 @@
"devDependencies": {
"@types/node": "^22.14.1",
"@y/protocols": "^1.0.6-1",
"markdownlint": "^0.40.0",
"markdownlint-cli": "^0.45.0",
"rollup": "^4.52.5",
"standard": "^17.1.2",
@@ -30,7 +31,7 @@
}
},
"../lib0": {
"version": "0.2.116",
"version": "0.2.117",
"license": "MIT",
"bin": {
"0ecdsa-generate-keypair": "src/bin/0ecdsa-generate-keypair.js",
@@ -2352,6 +2353,19 @@
"node": ">= 0.4"
}
},
"node_modules/get-east-asian-width": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz",
"integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -3450,9 +3464,9 @@
}
},
"node_modules/markdownlint": {
"version": "0.38.0",
"resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.38.0.tgz",
"integrity": "sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ==",
"version": "0.40.0",
"resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.40.0.tgz",
"integrity": "sha512-UKybllYNheWac61Ia7T6fzuQNDZimFIpCg2w6hHjgV1Qu0w1TV0LlSgryUGzM0bkKQCBhy2FDhEELB73Kb0kAg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3463,7 +3477,8 @@
"micromark-extension-gfm-footnote": "2.1.0",
"micromark-extension-gfm-table": "2.1.1",
"micromark-extension-math": "3.1.0",
"micromark-util-types": "2.0.2"
"micromark-util-types": "2.0.2",
"string-width": "8.1.0"
},
"engines": {
"node": ">=20"
@@ -3498,6 +3513,46 @@
"node": ">=20"
}
},
"node_modules/markdownlint-cli/node_modules/markdownlint": {
"version": "0.38.0",
"resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.38.0.tgz",
"integrity": "sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"micromark": "4.0.2",
"micromark-core-commonmark": "2.0.3",
"micromark-extension-directive": "4.0.0",
"micromark-extension-gfm-autolink-literal": "2.1.0",
"micromark-extension-gfm-footnote": "2.1.0",
"micromark-extension-gfm-table": "2.1.1",
"micromark-extension-math": "3.1.0",
"micromark-util-types": "2.0.2"
},
"engines": {
"node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/DavidAnson"
}
},
"node_modules/markdownlint/node_modules/string-width": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz",
"integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==",
"dev": true,
"license": "MIT",
"dependencies": {
"get-east-asian-width": "^1.3.0",
"strip-ansi": "^7.1.0"
},
"engines": {
"node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",

View File

@@ -78,6 +78,7 @@
"devDependencies": {
"@types/node": "^22.14.1",
"@y/protocols": "^1.0.6-1",
"markdownlint": "^0.40.0",
"markdownlint-cli": "^0.45.0",
"rollup": "^4.52.5",
"standard": "^17.1.2",

View File

@@ -37,7 +37,7 @@ export class StackItem {
*/
const clearUndoManagerStackItem = (tr, um, stackItem) => {
iterateStructsByIdSet(tr, stackItem.deletions, item => {
if (item instanceof Item && um.scope.some(type => type === tr.doc || isParentOf(/** @type {import('../utils/types.js').YType} */ (type), item))) {
if (item instanceof Item && um.scope.some(type => type === tr.doc || isParentOf(/** @type {YType} */ (type), item))) {
keepItem(item, false)
}
})
@@ -79,7 +79,7 @@ const popStackItem = (undoManager, stack, eventType) => {
}
struct = item
}
if (!struct.deleted && scope.some(type => type === transaction.doc || isParentOf(/** @type {import('../utils/types.js').YType} */ (type), /** @type {Item} */ (struct)))) {
if (!struct.deleted && scope.some(type => type === transaction.doc || isParentOf(/** @type {YType} */ (type), /** @type {Item} */ (struct)))) {
itemsToDelete.push(struct)
}
}
@@ -87,7 +87,7 @@ const popStackItem = (undoManager, stack, eventType) => {
iterateStructsByIdSet(transaction, stackItem.deletions, struct => {
if (
struct instanceof Item &&
scope.some(type => type === transaction.doc || isParentOf(/** @type {import('../utils/types.js').YType} */ (type), struct)) &&
scope.some(type => type === transaction.doc || isParentOf(/** @type {YType} */ (type), struct)) &&
// Never redo structs in stackItem.insertions because they were created and deleted in the same capture interval.
!stackItem.insertions.hasId(struct.id)
) {
@@ -143,7 +143,7 @@ const popStackItem = (undoManager, stack, eventType) => {
* @property {StackItem} StackItemEvent.stackItem
* @property {any} StackItemEvent.origin
* @property {'undo'|'redo'} StackItemEvent.type
* @property {Map<import('../utils/types.js').YType,Array<YEvent<any>>>} StackItemEvent.changedParentTypes
* @property {Map<YType,Array<YEvent<any>>>} StackItemEvent.changedParentTypes
*/
/**
@@ -157,7 +157,7 @@ const popStackItem = (undoManager, stack, eventType) => {
*/
export class UndoManager extends ObservableV2 {
/**
* @param {Doc|import('../utils/types.js').YType|Array<import('../utils/types.js').YType>} typeScope Limits the scope of the UndoManager. If this is set to a ydoc instance, all changes on that ydoc will be undone. If set to a specific type, only changes on that type or its children will be undone. Also accepts an array of types.
* @param {Doc|YType|Array<YType>} typeScope Limits the scope of the UndoManager. If this is set to a ydoc instance, all changes on that ydoc will be undone. If set to a specific type, only changes on that type or its children will be undone. Also accepts an array of types.
* @param {UndoManagerOptions} options
*/
constructor (typeScope, {
@@ -170,7 +170,7 @@ export class UndoManager extends ObservableV2 {
} = {}) {
super()
/**
* @type {Array<import('../utils/types.js').YType | Doc>}
* @type {Array<YType | Doc>}
*/
this.scope = []
this.doc = doc
@@ -210,7 +210,7 @@ export class UndoManager extends ObservableV2 {
// Only track certain transactions
if (
!this.captureTransaction(transaction) ||
!this.scope.some(type => transaction.changedParentTypes.has(/** @type {import('../utils/types.js').YType} */ (type)) || type === this.doc) ||
!this.scope.some(type => transaction.changedParentTypes.has(/** @type {YType} */ (type)) || type === this.doc) ||
(!this.trackedOrigins.has(transaction.origin) && (!transaction.origin || !this.trackedOrigins.has(transaction.origin.constructor)))
) {
return
@@ -242,7 +242,7 @@ export class UndoManager extends ObservableV2 {
}
// make sure that deleted structs are not gc'd
iterateStructsByIdSet(transaction, transaction.deleteSet, /** @param {Item|GC} item */ item => {
if (item instanceof Item && this.scope.some(type => type === transaction.doc || isParentOf(/** @type {import('../utils/types.js').YType} */ (type), item))) {
if (item instanceof Item && this.scope.some(type => type === transaction.doc || isParentOf(/** @type {YType} */ (type), item))) {
keepItem(item, true)
}
})
@@ -265,7 +265,7 @@ export class UndoManager extends ObservableV2 {
/**
* Extend the scope.
*
* @param {Array<import('../utils/types.js').YType | Doc> | import('../utils/types.js').YType | Doc} ytypes
* @param {Array<YType | Doc> | YType | Doc} ytypes
*/
addToScope (ytypes) {
const tmpSet = new Set(this.scope)

View File

@@ -4,8 +4,7 @@ import {
noAttributionsManager,
YType, Doc, AbstractAttributionManager, Item, Transaction, AbstractStruct, // eslint-disable-line
createAbsolutePositionFromRelativePosition,
createRelativePosition,
AbsolutePosition
createRelativePosition
} from '../internals.js'
import * as map from 'lib0/map'
@@ -202,7 +201,7 @@ export const getPathTo = (parent, child, am = noAttributionsManager) => {
} else {
const parent = /** @type {import('../ytype.js').YType} */ (child._item.parent)
// parent is array-ish
const apos = /** @type {AbsolutePosition} */ (createAbsolutePositionFromRelativePosition(createRelativePosition(parent, child._item.id), doc, false, am))
const apos = /** @type {import('../utils/RelativePosition.js').AbsolutePosition} */ (createAbsolutePositionFromRelativePosition(createRelativePosition(parent, child._item.id), doc, false, am))
path.unshift(apos.index)
}
child = /** @type {YType} */ (child._item.parent)

View File

@@ -213,7 +213,7 @@ export const testUserAttributionEncodingBenchmark = tc => {
idmap.insertIntoIdMap(attributions, idmap.createIdMapFromIdSet(tr.deleteSet, [createAttributionItem('delete', 'userX'), createAttributionItem('deleteAt', currentTime)]))
currentTime += 1
})
const ytext = ydoc.getText()
const ytext = ydoc.get()
const N = 10000
t.measureTime(`time to attribute ${N / 1000}k changes`, () => {
for (let i = 0; i < N; i++) {

View File

@@ -17,7 +17,7 @@ export const testArrayCompatibilityV1 = _tc => {
const oldVal = JSON.parse('[[1,2,3,4],472,472,{"someprop":44},472,[1,2,3,4],{"someprop":44},[1,2,3,4],[1,2,3,4],[1,2,3,4],{"someprop":44},449,448,[1,2,3,4],[1,2,3,4],{"someprop":44},452,{"someprop":44},[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4],452,[1,2,3,4],497,{"someprop":44},497,497,497,{"someprop":44},[1,2,3,4],522,522,452,470,{"someprop":44},[1,2,3,4],453,{"someprop":44},480,480,480,508,508,508,[1,2,3,4],[1,2,3,4],502,492,492,453,{"someprop":44},496,496,496,[1,2,3,4],496,493,495,495,495,495,493,[1,2,3,4],493,493,453,{"someprop":44},{"someprop":44},505,505,517,517,505,[1,2,3,4],{"someprop":44},509,{"someprop":44},521,521,521,509,477,{"someprop":44},{"someprop":44},485,485,{"someprop":44},515,{"someprop":44},451,{"someprop":44},[1,2,3,4],516,516,516,516,{"someprop":44},499,499,469,469,[1,2,3,4],[1,2,3,4],512,512,512,{"someprop":44},454,487,487,487,[1,2,3,4],[1,2,3,4],454,[1,2,3,4],[1,2,3,4],{"someprop":44},[1,2,3,4],459,[1,2,3,4],513,459,{"someprop":44},[1,2,3,4],482,{"someprop":44},[1,2,3,4],[1,2,3,4],459,[1,2,3,4],{"someprop":44},[1,2,3,4],484,454,510,510,510,510,468,{"someprop":44},468,[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4],467,[1,2,3,4],467,486,486,486,[1,2,3,4],489,451,[1,2,3,4],{"someprop":44},[1,2,3,4],[1,2,3,4],{"someprop":44},{"someprop":44},483,[1,2,3,4],{"someprop":44},{"someprop":44},{"someprop":44},{"someprop":44},519,519,519,519,506,506,[1,2,3,4],{"someprop":44},464,{"someprop":44},481,481,[1,2,3,4],{"someprop":44},[1,2,3,4],464,475,475,475,463,{"someprop":44},[1,2,3,4],518,[1,2,3,4],[1,2,3,4],463,455,498,498,498,466,471,471,471,501,[1,2,3,4],501,501,476,{"someprop":44},466,[1,2,3,4],{"someprop":44},503,503,503,466,455,490,474,{"someprop":44},457,494,494,{"someprop":44},457,479,{"someprop":44},[1,2,3,4],500,500,500,{"someprop":44},[1,2,3,4],[1,2,3,4],{"someprop":44},{"someprop":44},{"someprop":44},[1,2,3,4],[1,2,3,4],{"someprop":44},[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3],491,491,[1,2,3,4],504,504,504,504,465,[1,2,3,4],{"someprop":44},460,{"someprop":44},488,488,488,[1,2,3,4],[1,2,3,4],{"someprop":44},{"someprop":44},514,514,514,514,{"someprop":44},{"someprop":44},{"someprop":44},458,[1,2,3,4],[1,2,3,4],462,[1,2,3,4],[1,2,3,4],{"someprop":44},462,{"someprop":44},[1,2,3,4],{"someprop":44},[1,2,3,4],507,{"someprop":44},{"someprop":44},507,507,{"someprop":44},{"someprop":44},[1,2,3,4],{"someprop":44},461,{"someprop":44},473,461,[1,2,3,4],461,511,511,461,{"someprop":44},{"someprop":44},520,520,520,[1,2,3,4],458]')
const doc = new Y.Doc()
Y.applyUpdate(doc, buffer.fromBase64(oldDoc))
t.compare(doc.getArray('array').toJSON(), oldVal)
t.compare(doc.get('array').toJSON().children, oldVal)
}
/**
@@ -29,7 +29,7 @@ export const testMapDecodingCompatibilityV1 = _tc => {
const oldVal = /** @type {any} */ ({"one":[1,2,3,4],"two":{"deepkey":"deepvalue"}})
const doc = new Y.Doc()
Y.applyUpdate(doc, buffer.fromBase64(oldDoc))
t.compare(doc.getMap('map').toJSON(), oldVal)
t.compare(doc.get('map').toJSON().children, oldVal)
}
/**
@@ -41,5 +41,5 @@ export const testTextDecodingCompatibilityV1 = _tc => {
const oldVal = [{"insert":"1306rup"},{"insert":"uj","format":{"italic":true,"color":"#888"}},{"insert":"ikkcjnrcpsckw1319bccgkp\n"},{"insert":"\n1131","format":{"bold":true}},{"insert":"1326rpcznqahopcrtd","format":{"italic":true}},{"insert":"3axhkthhu","format":{"bold":true}},{"insert":"28"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"9"},{"insert":"04ku","format":{"italic":true}},{"insert":"1323nucvxsqlznwlfavmpc\nu"},{"insert":"tc","format":{"italic":true}},{"insert":"je1318jwskjabdndrdlmjae\n1293tj\nj1292qrmf"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"k\nuf"},{"insert":"14hs","format":{"italic":true}},{"insert":"13dccxdyxg"},{"insert":"zc","format":{"italic":true,"color":"#888"}},{"insert":"apo"},{"insert":"tn","format":{"bold":true}},{"insert":"r"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"gn\n"},{"insert":"z","format":{"italic":true}},{"insert":"\n121"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"291311kk9zjznywohpx"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"cnbrcaq\n"},{"insert":"1","format":{"italic":true,"color":"#888"}},{"insert":"1310g"},{"insert":"ws","format":{"italic":true,"color":"#888"}},{"insert":"hxwych"},{"insert":"kq","format":{"italic":true}},{"insert":"sdru1320cohbvcrkrpjngdoc\njqic\n"},{"insert":"2","format":{"italic":true,"color":"#888"}},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"90n1297zm"},{"insert":"v1309zlgvjx","format":{"bold":true}},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"g","format":{"bold":true}},{"insert":"1314pycavu","format":{"italic":true,"color":"#888"}},{"insert":"pkzqcj"},{"insert":"sa","format":{"italic":true,"color":"#888"}},{"insert":"sjy\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"xr\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"},{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}, {"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"1"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"1295qfrvlyfap201312qrwt"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"b1322rnbaokorixenvp\nrxq"},{"insert":"j","format":{"italic":true}},{"insert":"x","format":{"italic":true,"color":"#888"}},{"insert":"15mziwabzkrrmscvdovao\n0","format":{"italic":true}},{"insert":"hx","format":{"italic":true,"bold":true}},{"insert":"ojeetrjhxkr13031317pfcyhksrkpkt\nuhv1","format":{"italic":true}},{"insert":"32","format":{"italic":true,"color":"#888"}},{"insert":"4rorywthq1325iodbzizxhmlibvpyrxmq\n\nganln\nqne\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"dvf"},{"insert":"ac","format":{"bold":true}},{"insert":"1302xciwa"},{"insert":"1305rl","format":{"bold":true}},{"insert":"08\n"},{"insert":"eyk","format":{"bold":true}},{"insert":"y1321apgivydqsjfsehhezukiqtt1307tvjiejlh"},{"insert":"1316zlpkmctoqomgfthbpg","format":{"bold":true}},{"insert":"gv"},{"insert":"lb","format":{"bold":true}},{"insert":"f\nhntk\njv1uu\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]}].map(x => ({ type: 'insert', ...x }))
const doc = new Y.Doc()
Y.applyUpdate(doc, buffer.fromBase64(oldDoc))
t.compare(doc.getText('text').getContent().toJSON().children, /** @type {any} */ (oldVal))
t.compare(doc.get('text').getContent().toJSON().children, /** @type {any} */ (oldVal))
}

View File

@@ -29,10 +29,9 @@ export const testFindTypeInOtherDoc = _tc => {
const ydocClone = new Y.Doc()
Y.applyUpdate(ydocClone, Y.encodeStateAsUpdate(ydoc))
/**
* @template {Y.Type} Type
* @param {Type} ytype
* @param {Y.Type} ytype
* @param {Y.Doc} otherYdoc
* @return {Type}
* @return {Y.Type}
*/
const findTypeInOtherYdoc = (ytype, otherYdoc) => {
const ydoc = /** @type {Y.Doc} */ (ytype.doc)
@@ -47,7 +46,7 @@ export const testFindTypeInOtherDoc = _tc => {
if (rootKey == null) {
throw new Error('type does not exist in other ydoc')
}
return /** @type {Type} */ (otherYdoc.get(rootKey, /** @type {import('../src/utils/ts.js').YTypeConstructors} */ (ytype.constructor)))
return otherYdoc.get(rootKey)
} else {
/**
* If it is a sub type, we use the item id to find the history type.
@@ -60,7 +59,7 @@ export const testFindTypeInOtherDoc = _tc => {
)
const otherItem = /** @type {Y.Item} */ (otherStructs[itemIndex])
const otherContent = /** @type {Y.ContentType} */ (otherItem.content)
return /** @type {Type} */ (otherContent.type)
return /** @type {Y.Type} */ (otherContent.type)
}
}
t.assert(findTypeInOtherYdoc(ymap, ydocClone) != null)
@@ -78,7 +77,7 @@ export const testClientIdDuplicateChange = _tc => {
const doc2 = new Y.Doc()
doc2.clientID = 0
t.assert(doc2.clientID === doc1.clientID)
doc1.getArray('a').insert(0, [1, 2])
doc1.get('a').insert(0, [1, 2])
Y.applyUpdate(doc2, Y.encodeStateAsUpdate(doc1))
t.assert(doc2.clientID !== doc1.clientID)
}
@@ -88,12 +87,12 @@ export const testClientIdDuplicateChange = _tc => {
*/
export const testGetTypeEmptyId = _tc => {
const doc1 = new Y.Doc()
doc1.getText('').insert(0, 'h')
doc1.getText().insert(1, 'i')
doc1.get('').insert(0, 'h')
doc1.get().insert(1, 'i')
const doc2 = new Y.Doc()
Y.applyUpdate(doc2, Y.encodeStateAsUpdate(doc1))
t.assert(doc2.getText().toString() === 'hi')
t.assert(doc2.getText('').toString() === 'hi')
t.assert(doc2.get().toString() === 'hi')
t.assert(doc2.get('').toString() === 'hi')
}
/**
@@ -103,14 +102,14 @@ export const testToJSON = _tc => {
const doc = new Y.Doc()
t.compare(doc.toJSON(), {}, 'doc.toJSON yields empty object')
const arr = doc.getArray('array')
const arr = doc.get('array')
arr.push(['test1'])
const map = doc.getMap('map')
map.set('k1', 'v1')
const map2 = new Y.Map()
map.set('k2', map2)
map2.set('m2k1', 'm2v1')
const map = doc.get('map')
map.setAttr('k1', 'v1')
const map2 = new Y.Type()
map.setAttr('k2', map2)
map2.setAttr('m2k1', 'm2v1')
t.compare(doc.toJSON(), {
array: ['test1'],
@@ -137,30 +136,30 @@ export const testSubdoc = _tc => {
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)]
})
const subdocs = doc.getMap('mysubdocs')
const subdocs = doc.get('mysubdocs')
const docA = new Y.Doc({ guid: 'a' })
docA.load()
subdocs.set('a', docA)
subdocs.setAttr('a', docA)
t.compare(event, [['a'], [], ['a']])
event = null
subdocs.get('a').load()
subdocs.getAttr('a').load()
t.assert(event === null)
event = null
subdocs.get('a').destroy()
subdocs.getAttr('a').destroy()
t.compare(event, [['a'], ['a'], []])
subdocs.get('a').load()
subdocs.getAttr('a').load()
t.compare(event, [[], [], ['a']])
subdocs.set('b', new Y.Doc({ guid: 'a', shouldLoad: false }))
subdocs.setAttr('b', new Y.Doc({ guid: 'a', shouldLoad: false }))
t.compare(event, [['a'], [], []])
subdocs.get('b').load()
subdocs.getAttr('b').load()
t.compare(event, [[], [], ['a']])
const docC = new Y.Doc({ guid: 'c' })
docC.load()
subdocs.set('c', docC)
subdocs.setAttr('c', docC)
t.compare(event, [['c'], [], ['c']])
t.compare(Array.from(doc.getSubdocGuids()), ['a', 'c'])
@@ -179,12 +178,12 @@ export const testSubdoc = _tc => {
Y.applyUpdate(doc2, Y.encodeStateAsUpdate(doc))
t.compare(event, [['a', 'a', 'c'], [], []])
doc2.getMap('mysubdocs').get('a').load()
doc2.get('mysubdocs').getAttr('a').load()
t.compare(event, [[], [], ['a']])
t.compare(Array.from(doc2.getSubdocGuids()), ['a', 'c'])
doc2.getMap('mysubdocs').delete('a')
doc2.get('mysubdocs').deleteAttr('a')
t.compare(event, [[], ['a'], []])
t.compare(Array.from(doc2.getSubdocGuids()), ['a', 'c'])
}
@@ -195,7 +194,7 @@ export const testSubdoc = _tc => {
*/
export const testSubdocLoadEdgeCases = _tc => {
const ydoc = new Y.Doc()
const yarray = ydoc.getArray()
const yarray = ydoc.get()
const subdoc1 = new Y.Doc()
/**
* @type {any}
@@ -225,7 +224,7 @@ export const testSubdocLoadEdgeCases = _tc => {
lastEvent = event
})
Y.applyUpdate(ydoc2, Y.encodeStateAsUpdate(ydoc))
const subdoc3 = ydoc2.getArray().get(0)
const subdoc3 = ydoc2.get().get(0)
t.assert(subdoc3.shouldLoad === false)
t.assert(subdoc3.autoLoad === false)
t.assert(lastEvent !== null && lastEvent.added.has(subdoc3))
@@ -242,7 +241,7 @@ export const testSubdocLoadEdgeCases = _tc => {
*/
export const testSubdocLoadEdgeCasesAutoload = _tc => {
const ydoc = new Y.Doc()
const yarray = ydoc.getArray()
const yarray = ydoc.get()
const subdoc1 = new Y.Doc({ autoLoad: true })
/**
* @type {any}
@@ -272,7 +271,7 @@ export const testSubdocLoadEdgeCasesAutoload = _tc => {
lastEvent = event
})
Y.applyUpdate(ydoc2, Y.encodeStateAsUpdate(ydoc))
const subdoc3 = ydoc2.getArray().get(0)
const subdoc3 = ydoc2.get().get(0)
t.assert(subdoc1.shouldLoad)
t.assert(subdoc1.autoLoad)
t.assert(lastEvent !== null && lastEvent.added.has(subdoc3))
@@ -284,7 +283,7 @@ export const testSubdocLoadEdgeCasesAutoload = _tc => {
*/
export const testSubdocsUndo = _tc => {
const ydoc = new Y.Doc()
const elems = ydoc.getXmlFragment()
const elems = ydoc.get()
const undoManager = new Y.UndoManager(elems)
const subdoc = new Y.Doc()
// @ts-ignore

View File

@@ -42,12 +42,12 @@ export const testDiffStateVectorOfUpdateIsEmpty = _tc => {
* @type {any}
*/
let sv = null
ydoc.getText().insert(0, 'a')
ydoc.get().insert(0, 'a')
ydoc.on('update', update => {
sv = Y.encodeStateVectorFromUpdate(update)
})
// should produce an update with an empty state vector (because previous ops are missing)
ydoc.getText().insert(0, 'a')
ydoc.get().insert(0, 'a')
t.assert(sv !== null && sv.byteLength === 1 && sv[0] === 0)
}
@@ -64,9 +64,9 @@ export const testDiffStateVectorOfUpdateIgnoresSkips = _tc => {
ydoc.on('update', update => {
updates.push(update)
})
ydoc.getText().insert(0, 'a')
ydoc.getText().insert(0, 'b')
ydoc.getText().insert(0, 'c')
ydoc.get().insert(0, 'a')
ydoc.get().insert(0, 'b')
ydoc.get().insert(0, 'c')
const update13 = Y.mergeUpdates([updates[0], updates[2]])
const sv = Y.encodeStateVectorFromUpdate(update13)
const state = Y.decodeStateVector(sv)

View File

@@ -2,7 +2,7 @@ import * as Y from '../src/index.js'
import * as t from 'lib0/testing'
/**
* @param {Y.Text} ytext
* @param {Y.Type<{text:true}>} ytext
*/
const checkRelativePositions = ytext => {
// test if all positions are encoded and restored correctly
@@ -20,11 +20,11 @@ const checkRelativePositions = ytext => {
}
/**
* @param {t.TestCase} tc
* @param {t.TestCase} _tc
*/
export const testRelativePositionCase1 = tc => {
export const testRelativePositionCase1 = _tc => {
const ydoc = new Y.Doc()
const ytext = ydoc.getText()
const ytext = ydoc.get()
ytext.insert(0, '1')
ytext.insert(0, 'abc')
ytext.insert(0, 'z')
@@ -34,21 +34,21 @@ export const testRelativePositionCase1 = tc => {
}
/**
* @param {t.TestCase} tc
* @param {t.TestCase} _tc
*/
export const testRelativePositionCase2 = tc => {
export const testRelativePositionCase2 = _tc => {
const ydoc = new Y.Doc()
const ytext = ydoc.getText()
const ytext = ydoc.get()
ytext.insert(0, 'abc')
checkRelativePositions(ytext)
}
/**
* @param {t.TestCase} tc
* @param {t.TestCase} _tc
*/
export const testRelativePositionCase3 = tc => {
export const testRelativePositionCase3 = _tc => {
const ydoc = new Y.Doc()
const ytext = ydoc.getText()
const ytext = ydoc.get()
ytext.insert(0, 'abc')
ytext.insert(0, '1')
ytext.insert(0, 'xyz')
@@ -56,43 +56,43 @@ export const testRelativePositionCase3 = tc => {
}
/**
* @param {t.TestCase} tc
* @param {t.TestCase} _tc
*/
export const testRelativePositionCase4 = tc => {
export const testRelativePositionCase4 = _tc => {
const ydoc = new Y.Doc()
const ytext = ydoc.getText()
const ytext = ydoc.get()
ytext.insert(0, '1')
checkRelativePositions(ytext)
}
/**
* @param {t.TestCase} tc
* @param {t.TestCase} _tc
*/
export const testRelativePositionCase5 = tc => {
export const testRelativePositionCase5 = _tc => {
const ydoc = new Y.Doc()
const ytext = ydoc.getText()
const ytext = ydoc.get()
ytext.insert(0, '2')
ytext.insert(0, '1')
checkRelativePositions(ytext)
}
/**
* @param {t.TestCase} tc
* @param {t.TestCase} _tc
*/
export const testRelativePositionCase6 = tc => {
export const testRelativePositionCase6 = _tc => {
const ydoc = new Y.Doc()
const ytext = ydoc.getText()
const ytext = ydoc.get()
checkRelativePositions(ytext)
}
/**
* Testing https://github.com/yjs/yjs/issues/657
*
* @param {t.TestCase} tc
* @param {t.TestCase} _tc
*/
export const testRelativePositionCase7 = tc => {
export const testRelativePositionCase7 = _tc => {
const docA = new Y.Doc()
const textA = docA.getText('text')
const textA = docA.get('text')
textA.insert(0, 'abcde')
// Create a relative position at index 2 in 'textA'
const relativePosition = Y.createRelativePositionFromTypeIndex(textA, 2)
@@ -106,11 +106,11 @@ export const testRelativePositionCase7 = tc => {
}
/**
* @param {t.TestCase} tc
* @param {t.TestCase} _tc
*/
export const testRelativePositionAssociationDifference = tc => {
export const testRelativePositionAssociationDifference = _tc => {
const ydoc = new Y.Doc()
const ytext = ydoc.getText()
const ytext = ydoc.get()
ytext.insert(0, '2')
ytext.insert(0, '1')
const rposRight = Y.createRelativePositionFromTypeIndex(ytext, 1, 0)
@@ -123,11 +123,11 @@ export const testRelativePositionAssociationDifference = tc => {
}
/**
* @param {t.TestCase} tc
* @param {t.TestCase} _tc
*/
export const testRelativePositionWithUndo = tc => {
export const testRelativePositionWithUndo = _tc => {
const ydoc = new Y.Doc()
const ytext = ydoc.getText()
const ytext = ydoc.get()
ytext.insert(0, 'hello world')
const rpos = Y.createRelativePositionFromTypeIndex(ytext, 1)
const um = new Y.UndoManager(ytext)

View File

@@ -1,17 +1,18 @@
import * as Y from '../src/index.js'
import * as t from 'lib0/testing'
import { init } from './testHelper.js'
import * as delta from 'lib0/delta'
/**
* @param {t.TestCase} _tc
*/
export const testBasic = _tc => {
const ydoc = new Y.Doc({ gc: false })
ydoc.getText().insert(0, 'world!')
ydoc.get().insert(0, 'world!')
const snapshot = Y.snapshot(ydoc)
ydoc.getText().insert(0, 'hello ')
ydoc.get().insert(0, 'hello ')
const restored = Y.createDocFromSnapshot(ydoc, snapshot)
t.assert(restored.getText().toString() === 'world!')
t.assert(restored.get().getContent().equals(delta.create().insert('world!')))
}
/**
@@ -19,7 +20,7 @@ export const testBasic = _tc => {
*/
export const testBasicXmlAttributes = _tc => {
const ydoc = new Y.Doc({ gc: false })
const yxml = ydoc.getMap().set('el', new Y.XmlElement('div'))
const yxml = ydoc.get().setAttr('el', new Y.Type('div'))
const snapshot1 = Y.snapshot(ydoc)
yxml.setAttribute('a', '1')
const snapshot2 = Y.snapshot(ydoc)
@@ -34,14 +35,14 @@ export const testBasicXmlAttributes = _tc => {
*/
export const testBasicRestoreSnapshot = _tc => {
const doc = new Y.Doc({ gc: false })
doc.getArray('array').insert(0, ['hello'])
doc.get('array').insert(0, ['hello'])
const snap = Y.snapshot(doc)
doc.getArray('array').insert(1, ['world'])
doc.get('array').insert(1, ['world'])
const docRestored = Y.createDocFromSnapshot(doc, snap)
t.compare(docRestored.getArray('array').toArray(), ['hello'])
t.compare(doc.getArray('array').toArray(), ['hello', 'world'])
t.compare(docRestored.get('array').toArray(), ['hello'])
t.compare(doc.get('array').toJSON().children, ['hello', 'world'])
}
/**
@@ -51,17 +52,17 @@ export const testEmptyRestoreSnapshot = _tc => {
const doc = new Y.Doc({ gc: false })
const snap = Y.snapshot(doc)
snap.sv.set(9999, 0)
doc.getArray().insert(0, ['world'])
doc.get().insert(0, ['world'])
const docRestored = Y.createDocFromSnapshot(doc, snap)
t.compare(docRestored.getArray().toArray(), [])
t.compare(doc.getArray().toArray(), ['world'])
t.compare(docRestored.get().toArray(), [])
t.compare(doc.get().toArray(), ['world'])
// now this snapshot reflects the latest state. It should still work.
const snap2 = Y.snapshot(doc)
const docRestored2 = Y.createDocFromSnapshot(doc, snap2)
t.compare(docRestored2.getArray().toArray(), ['world'])
t.compare(docRestored2.get().toArray(), ['world'])
}
/**
@@ -69,8 +70,8 @@ export const testEmptyRestoreSnapshot = _tc => {
*/
export const testRestoreSnapshotWithSubType = _tc => {
const doc = new Y.Doc({ gc: false })
doc.getArray('array').insert(0, [new Y.Map()])
const subMap = doc.getArray('array').get(0)
doc.get('array').insert(0, [new Y.Type()])
const subMap = doc.get('array').get(0)
subMap.set('key1', 'value1')
const snap = Y.snapshot(doc)
@@ -78,10 +79,10 @@ export const testRestoreSnapshotWithSubType = _tc => {
const docRestored = Y.createDocFromSnapshot(doc, snap)
t.compare(docRestored.getArray('array').toJSON(), [{
t.compare(docRestored.get('array').toJSON().children, [{
key1: 'value1'
}])
t.compare(doc.getArray('array').toJSON(), [{
t.compare(doc.get('array').toJSON().children, [{
key1: 'value1',
key2: 'value2'
}])
@@ -92,15 +93,15 @@ export const testRestoreSnapshotWithSubType = _tc => {
*/
export const testRestoreDeletedItem1 = _tc => {
const doc = new Y.Doc({ gc: false })
doc.getArray('array').insert(0, ['item1', 'item2'])
doc.get('array').insert(0, ['item1', 'item2'])
const snap = Y.snapshot(doc)
doc.getArray('array').delete(0)
doc.get('array').delete(0)
const docRestored = Y.createDocFromSnapshot(doc, snap)
t.compare(docRestored.getArray('array').toArray(), ['item1', 'item2'])
t.compare(doc.getArray('array').toArray(), ['item2'])
t.compare(docRestored.get('array').toArray(), ['item1', 'item2'])
t.compare(doc.get('array').toArray(), ['item2'])
}
/**
@@ -108,17 +109,17 @@ export const testRestoreDeletedItem1 = _tc => {
*/
export const testRestoreLeftItem = _tc => {
const doc = new Y.Doc({ gc: false })
doc.getArray('array').insert(0, ['item1'])
doc.getMap('map').set('test', 1)
doc.getArray('array').insert(0, ['item0'])
doc.get('array').insert(0, ['item1'])
doc.get('map').setAttr('test', 1)
doc.get('array').insert(0, ['item0'])
const snap = Y.snapshot(doc)
doc.getArray('array').delete(1)
doc.get('array').delete(1)
const docRestored = Y.createDocFromSnapshot(doc, snap)
t.compare(docRestored.getArray('array').toArray(), ['item0', 'item1'])
t.compare(doc.getArray('array').toArray(), ['item0'])
t.compare(docRestored.get('array').toArray(), ['item0', 'item1'])
t.compare(doc.get('array').toArray(), ['item0'])
}
/**
@@ -126,15 +127,15 @@ export const testRestoreLeftItem = _tc => {
*/
export const testDeletedItemsBase = _tc => {
const doc = new Y.Doc({ gc: false })
doc.getArray('array').insert(0, ['item1'])
doc.getArray('array').delete(0)
doc.get('array').insert(0, ['item1'])
doc.get('array').delete(0)
const snap = Y.snapshot(doc)
doc.getArray('array').insert(0, ['item0'])
doc.get('array').insert(0, ['item0'])
const docRestored = Y.createDocFromSnapshot(doc, snap)
t.compare(docRestored.getArray('array').toArray(), [])
t.compare(doc.getArray('array').toArray(), ['item0'])
t.compare(docRestored.get('array').toArray(), [])
t.compare(doc.get('array').toArray(), ['item0'])
}
/**
@@ -142,15 +143,15 @@ export const testDeletedItemsBase = _tc => {
*/
export const testDeletedItems2 = _tc => {
const doc = new Y.Doc({ gc: false })
doc.getArray('array').insert(0, ['item1', 'item2', 'item3'])
doc.getArray('array').delete(1)
doc.get('array').insert(0, ['item1', 'item2', 'item3'])
doc.get('array').delete(1)
const snap = Y.snapshot(doc)
doc.getArray('array').insert(0, ['item0'])
doc.get('array').insert(0, ['item0'])
const docRestored = Y.createDocFromSnapshot(doc, snap)
t.compare(docRestored.getArray('array').toArray(), ['item1', 'item3'])
t.compare(doc.getArray('array').toArray(), ['item0', 'item1', 'item3'])
t.compare(docRestored.get('array').toArray(), ['item1', 'item3'])
t.compare(doc.get('array').toArray(), ['item0', 'item1', 'item3'])
}
/**
@@ -191,10 +192,10 @@ export const testDependentChanges = tc => {
testConnector.syncAll()
const docRestored0 = Y.createDocFromSnapshot(array0.doc, snap)
t.compare(docRestored0.getArray('array').toArray(), ['user1item1', 'user2item1'])
t.compare(docRestored0.get('array').toArray(), ['user1item1', 'user2item1'])
const docRestored1 = Y.createDocFromSnapshot(array1.doc, snap)
t.compare(docRestored1.getArray('array').toArray(), ['user1item1', 'user2item1'])
t.compare(docRestored1.get('array').toArray(), ['user1item1', 'user2item1'])
}
/**
@@ -209,7 +210,7 @@ export const testContainsUpdate = _tc => {
ydoc.on('update', update => {
updates.push(update)
})
const yarr = ydoc.getArray()
const yarr = ydoc.get()
const snapshot1 = Y.snapshot(ydoc)
yarr.insert(0, [1])
const snapshot2 = Y.snapshot(ydoc)

View File

@@ -468,8 +468,6 @@ export const compare = users => {
t.assert(u.store.pendingDs === null)
t.assert(u.store.pendingStructs === null)
}
// Test Array iterator
t.compare(users[0].get('array').toArray(), Array.from(users[0].get('array')))
// Test Map iterator
const ymapkeys = Array.from(users[0].get('map').attrKeys())
t.assert(ymapkeys.length === Object.keys(userMapValues[0]).length)
@@ -478,8 +476,10 @@ export const compare = users => {
* @type {Object<string,any>}
*/
const mapRes = {}
for (const [k, v] of users[0].get('map')) {
mapRes[k] = v instanceof Y.AbstractType ? v.toJSON() : v
const attrs0 = users[0].get('map').getAttrs()
for (const k in attrs0) {
const v = attrs0[k]
mapRes[k] = v instanceof Y.Type ? v.toJSON() : v
}
t.compare(userMapValues[0], mapRes)
// Compare all users

View File

@@ -8,14 +8,14 @@ export const testInconsistentFormat = () => {
* @param {Y.Doc} ydoc
*/
const testYjsMerge = ydoc => {
const content = /** @type {Y.XmlText} */ (ydoc.get('text', Y.XmlText))
const content = ydoc.get('text')
content.format(0, 6, { bold: null })
content.format(6, 4, { type: 'text' })
t.compare(content.getContent(), delta.create().insert('Merge Test', { type: 'text' }).insert(' After', { type: 'text', italic: true }).done())
}
const initializeYDoc = () => {
const yDoc = new Y.Doc({ gc: false })
const content = /** @type {Y.XmlText} */ (yDoc.get('text', Y.XmlText))
const content = yDoc.get('text')
content.insert(0, ' After', { type: 'text', italic: true })
content.insert(0, 'Test', { type: 'text' })
content.insert(0, 'Merge ', { type: 'text', bold: true })
@@ -99,7 +99,7 @@ export const testUndoText = tc => {
export const testEmptyTypeScope = _tc => {
const ydoc = new Y.Doc()
const um = new Y.UndoManager([], { doc: ydoc })
const yarray = ydoc.getArray()
const yarray = ydoc.get()
um.addToScope(yarray)
yarray.insert(0, [1])
um.undo()
@@ -111,15 +111,15 @@ export const testEmptyTypeScope = _tc => {
*/
export const testRejectUpdateExample = _tc => {
const tmpydoc1 = new Y.Doc()
tmpydoc1.getArray('restricted').insert(0, [1])
tmpydoc1.getArray('public').insert(0, [1])
tmpydoc1.get('restricted').insert(0, [1])
tmpydoc1.get('public').insert(0, [1])
const update1 = Y.encodeStateAsUpdate(tmpydoc1)
const tmpydoc2 = new Y.Doc()
tmpydoc2.getArray('public').insert(0, [2])
tmpydoc2.get('public').insert(0, [2])
const update2 = Y.encodeStateAsUpdate(tmpydoc2)
const ydoc = new Y.Doc()
const restrictedType = ydoc.getArray('restricted')
const restrictedType = ydoc.get('restricted')
/**
* Assume this function handles incoming updates via a communication channel like websockets.
@@ -156,7 +156,7 @@ export const testRejectUpdateExample = _tc => {
updateHandler(update1)
updateHandler(update2)
t.assert(restrictedType.length === 0)
t.assert(ydoc.getArray('public').length === 2)
t.assert(ydoc.get('public').length === 2)
}
/**
@@ -166,7 +166,7 @@ export const testRejectUpdateExample = _tc => {
export const testGlobalScope = _tc => {
const ydoc = new Y.Doc()
const um = new Y.UndoManager(ydoc)
const yarray = ydoc.getArray()
const yarray = ydoc.get()
yarray.insert(0, [1])
um.undo()
t.assert(yarray.length === 0)
@@ -178,7 +178,7 @@ export const testGlobalScope = _tc => {
*/
export const testDoubleUndo = _tc => {
const doc = new Y.Doc()
const text = doc.getText()
const text = doc.get()
text.insert(0, '1221')
const manager = new Y.UndoManager(text)
@@ -199,39 +199,39 @@ export const testDoubleUndo = _tc => {
*/
export const testUndoMap = tc => {
const { testConnector, map0, map1 } = init(tc, { users: 2 })
map0.set('a', 0)
map0.setAttr('a', 0)
const undoManager = new Y.UndoManager(map0)
map0.set('a', 1)
map0.setAttr('a', 1)
undoManager.undo()
t.assert(map0.get('a') === 0)
t.assert(map0.getAttr('a') === 0)
undoManager.redo()
t.assert(map0.get('a') === 1)
t.assert(map0.getAttr('a') === 1)
// testing sub-types and if it can restore a whole type
const subType = new Y.Map()
map0.set('a', subType)
subType.set('x', 42)
const subType = new Y.Type()
map0.setAttr('a', subType)
subType.setAttr('x', 42)
t.compare(map0.toJSON(), /** @type {any} */ ({ a: { x: 42 } }))
undoManager.undo()
t.assert(map0.get('a') === 1)
t.assert(map0.getAttr('a') === 1)
undoManager.redo()
t.compare(map0.toJSON(), /** @type {any} */ ({ a: { x: 42 } }))
testConnector.syncAll()
// if content is overwritten by another user, undo operations should be skipped
map1.set('a', 44)
map1.setAttr('a', 44)
testConnector.syncAll()
undoManager.undo()
t.assert(map0.get('a') === 44)
t.assert(map0.getAttr('a') === 44)
undoManager.redo()
t.assert(map0.get('a') === 44)
t.assert(map0.getAttr('a') === 44)
// test setting value multiple times
map0.set('b', 'initial')
map0.setAttr('b', 'initial')
undoManager.stopCapturing()
map0.set('b', 'val1')
map0.set('b', 'val2')
map0.setAttr('b', 'val1')
map0.setAttr('b', 'val2')
undoManager.stopCapturing()
undoManager.undo()
t.assert(map0.get('b') === 'initial')
t.assert(map0.getAttr('b') === 'initial')
}
/**
@@ -257,32 +257,32 @@ export const testUndoArray = tc => {
t.compare(array0.toArray(), [2, 3, 4, 5, 6])
array0.delete(0, 5)
// test nested structure
const ymap = new Y.Map()
const ymap = new Y.Type()
array0.insert(0, [ymap])
t.compare(array0.toJSON(), [{}])
t.compare(array0.toJSON().children, [{}])
undoManager.stopCapturing()
ymap.set('a', 1)
t.compare(array0.toJSON(), [{ a: 1 }])
ymap.setAttr('a', 1)
t.compare(array0.toJSON().children, [{ a: 1 }])
undoManager.undo()
t.compare(array0.toJSON(), [{}])
t.compare(array0.toJSON().children, [{}])
undoManager.undo()
t.compare(array0.toJSON(), [2, 3, 4, 5, 6])
t.compare(array0.toJSON().children, [2, 3, 4, 5, 6])
undoManager.redo()
t.compare(array0.toJSON(), [{}])
t.compare(array0.toJSON().children, [{}])
undoManager.redo()
t.compare(array0.toJSON(), [{ a: 1 }])
t.compare(array0.toJSON().children, [{ a: 1 }])
testConnector.syncAll()
array1.get(0).set('b', 2)
testConnector.syncAll()
t.compare(array0.toJSON(), [{ a: 1, b: 2 }])
t.compare(array0.toJSON().children, [{ a: 1, b: 2 }])
undoManager.undo()
t.compare(array0.toJSON(), [{ b: 2 }])
t.compare(array0.toJSON().children, [{ b: 2 }])
undoManager.undo()
t.compare(array0.toJSON(), [2, 3, 4, 5, 6])
t.compare(array0.toJSON().children, [2, 3, 4, 5, 6])
undoManager.redo()
t.compare(array0.toJSON(), [{ b: 2 }])
t.compare(array0.toJSON().children, [{ b: 2 }])
undoManager.redo()
t.compare(array0.toJSON(), [{ a: 1, b: 2 }])
t.compare(array0.toJSON().children, [{ a: 1, b: 2 }])
}
/**
@@ -291,11 +291,10 @@ export const testUndoArray = tc => {
export const testUndoXml = tc => {
const { xml0 } = init(tc, { users: 3 })
const undoManager = new Y.UndoManager(xml0)
const child = new Y.XmlElement('p')
const child = new Y.Type('p')
xml0.insert(0, [child])
const textchild = new Y.XmlText('content')
const textchild = new Y.Type('content')
child.insert(0, [textchild])
t.assert(xml0.toString() === '<undefined><p>content</p></undefined>')
// format textchild and revert that change
undoManager.stopCapturing()
textchild.format(3, 4, { bold: true })
@@ -358,8 +357,8 @@ export const testTrackClass = tc => {
export const testTypeScope = tc => {
const { array0 } = init(tc, { users: 3 })
// only track origins that are numbers
const text0 = new Y.Text()
const text1 = new Y.Text()
const text0 = new Y.Type()
const text1 = new Y.Type()
array0.insert(0, [text0, text1])
const undoManager = new Y.UndoManager(text0)
const undoManagerBoth = new Y.UndoManager([text0, text1])
@@ -379,9 +378,9 @@ export const testTypeScope = tc => {
export const testUndoInEmbed = tc => {
const { text0 } = init(tc, { users: 3 })
const undoManager = new Y.UndoManager(text0)
const nestedText = new Y.Text('initial text')
const nestedText = new Y.Type('initial text')
undoManager.stopCapturing()
text0.insertEmbed(0, nestedText, { bold: true })
text0.insert(0, [nestedText], { bold: true })
t.assert(nestedText.toString() === 'initial text')
undoManager.stopCapturing()
nestedText.delete(0, nestedText.length)
@@ -397,14 +396,11 @@ export const testUndoInEmbed = tc => {
* @param {t.TestCase} tc
*/
export const testUndoDeleteFilter = tc => {
/**
* @type {Y.Array<any>}
*/
const array0 = /** @type {any} */ (init(tc, { users: 3 }).array0)
const array0 = init(tc, { users: 3 }).array0
const undoManager = new Y.UndoManager(array0, { deleteFilter: item => !(item instanceof Y.Item) || (item.content instanceof Y.ContentType && item.content.type._map.size === 0) })
const map0 = new Y.Map()
map0.set('hi', 1)
const map1 = new Y.Map()
const map0 = new Y.Type()
map0.setAttr('hi', 1)
const map1 = new Y.Type()
array0.insert(0, [map0, map1])
undoManager.undo()
t.assert(array0.length === 1)
@@ -422,25 +418,25 @@ export const testUndoUntilChangePerformed = _tc => {
doc.on('update', update => Y.applyUpdate(doc2, update))
doc2.on('update', update => Y.applyUpdate(doc, update))
const yArray = doc.getArray('array')
const yArray2 = doc2.getArray('array')
const yMap = new Y.Map()
yMap.set('hello', 'world')
const yArray = doc.get('array')
const yArray2 = doc2.get('array')
const yMap = new Y.Type()
yMap.setAttr('hello', 'world')
yArray.push([yMap])
const yMap2 = new Y.Map()
yMap2.set('key', 'value')
const yMap2 = new Y.Type()
yMap2.setAttr('key', 'value')
yArray.push([yMap2])
const undoManager = new Y.UndoManager([yArray], { trackedOrigins: new Set([doc.clientID]) })
const undoManager2 = new Y.UndoManager([doc2.get('array')], { trackedOrigins: new Set([doc2.clientID]) })
Y.transact(doc, () => yMap2.set('key', 'value modified'), doc.clientID)
Y.transact(doc, () => yMap2.setAttr('key', 'value modified'), doc.clientID)
undoManager.stopCapturing()
Y.transact(doc, () => yMap.set('hello', 'world modified'), doc.clientID)
Y.transact(doc, () => yMap.setAttr('hello', 'world modified'), doc.clientID)
Y.transact(doc2, () => yArray2.delete(0), doc2.clientID)
undoManager2.undo()
undoManager.undo()
t.compareStrings(yMap2.get('key'), 'value')
t.compareStrings(yMap2.getAttr('key'), 'value')
}
/**
@@ -449,53 +445,50 @@ export const testUndoUntilChangePerformed = _tc => {
*/
export const testUndoNestedUndoIssue = _tc => {
const doc = new Y.Doc({ gc: false })
const design = doc.getMap()
const design = doc.get()
const undoManager = new Y.UndoManager(design, { captureTimeout: 0 })
/**
* @type {Y.Map<any>}
*/
const text = new Y.Map()
const text = new Y.Type()
const blocks1 = new Y.Array()
const blocks1block = new Y.Map()
const blocks1 = new Y.Type()
const blocks1block = new Y.Type()
doc.transact(() => {
blocks1block.set('text', 'Type Something')
blocks1block.setAttr('text', 'Type Something')
blocks1.push([blocks1block])
text.set('blocks', blocks1block)
design.set('text', text)
text.setAttr('blocks', blocks1block)
design.setAttr('text', text)
})
const blocks2 = new Y.Array()
const blocks2block = new Y.Map()
const blocks2 = new Y.Type()
const blocks2block = new Y.Type()
doc.transact(() => {
blocks2block.set('text', 'Something')
blocks2block.setAttr('text', 'Something')
blocks2.push([blocks2block])
text.set('blocks', blocks2block)
text.setAttr('blocks', blocks2block)
})
const blocks3 = new Y.Array()
const blocks3block = new Y.Map()
const blocks3 = new Y.Type()
const blocks3block = new Y.Type()
doc.transact(() => {
blocks3block.set('text', 'Something Else')
blocks3block.setAttr('text', 'Something Else')
blocks3.push([blocks3block])
text.set('blocks', blocks3block)
text.setAttr('blocks', blocks3block)
})
t.compare(design.toJSON(), { text: { blocks: { text: 'Something Else' } } })
t.compare(design.toJSON().attrs, { text: { blocks: { text: 'Something Else' } } })
undoManager.undo()
t.compare(design.toJSON(), { text: { blocks: { text: 'Something' } } })
t.compare(design.toJSON().attrs, { text: { blocks: { text: 'Something' } } })
undoManager.undo()
t.compare(design.toJSON(), { text: { blocks: { text: 'Type Something' } } })
t.compare(design.toJSON().attrs, { text: { blocks: { text: 'Type Something' } } })
undoManager.undo()
t.compare(design.toJSON(), { })
t.compare(design.toJSON().attrs, { })
undoManager.redo()
t.compare(design.toJSON(), { text: { blocks: { text: 'Type Something' } } })
t.compare(design.toJSON().attrs, { text: { blocks: { text: 'Type Something' } } })
undoManager.redo()
t.compare(design.toJSON(), { text: { blocks: { text: 'Something' } } })
t.compare(design.toJSON().attrs, { text: { blocks: { text: 'Something' } } })
undoManager.redo()
t.compare(design.toJSON(), { text: { blocks: { text: 'Something Else' } } })
t.compare(design.toJSON().attrs, { text: { blocks: { text: 'Something Else' } } })
}
/**
@@ -505,48 +498,48 @@ export const testUndoNestedUndoIssue = _tc => {
*/
export const testConsecutiveRedoBug = _tc => {
const doc = new Y.Doc()
const yRoot = doc.getMap()
const yRoot = doc.get()
const undoMgr = new Y.UndoManager(yRoot)
let yPoint = new Y.Map()
yPoint.set('x', 0)
yPoint.set('y', 0)
yRoot.set('a', yPoint)
let yPoint = new Y.Type()
yPoint.setAttr('x', 0)
yPoint.setAttr('y', 0)
yRoot.setAttr('a', yPoint)
undoMgr.stopCapturing()
yPoint.set('x', 100)
yPoint.set('y', 100)
yPoint.setAttr('x', 100)
yPoint.setAttr('y', 100)
undoMgr.stopCapturing()
yPoint.set('x', 200)
yPoint.set('y', 200)
yPoint.setAttr('x', 200)
yPoint.setAttr('y', 200)
undoMgr.stopCapturing()
yPoint.set('x', 300)
yPoint.set('y', 300)
yPoint.setAttr('x', 300)
yPoint.setAttr('y', 300)
undoMgr.stopCapturing()
t.compare(yPoint.toJSON(), { x: 300, y: 300 })
t.compare(yPoint.toJSON().attrs, { x: 300, y: 300 })
undoMgr.undo() // x=200, y=200
t.compare(yPoint.toJSON(), { x: 200, y: 200 })
t.compare(yPoint.toJSON().attrs, { x: 200, y: 200 })
undoMgr.undo() // x=100, y=100
t.compare(yPoint.toJSON(), { x: 100, y: 100 })
t.compare(yPoint.toJSON().attrs, { x: 100, y: 100 })
undoMgr.undo() // x=0, y=0
t.compare(yPoint.toJSON(), { x: 0, y: 0 })
t.compare(yPoint.toJSON().attrs, { x: 0, y: 0 })
undoMgr.undo() // nil
t.compare(yRoot.get('a'), undefined)
t.compare(yRoot.getAttr('a'), undefined)
undoMgr.redo() // x=0, y=0
yPoint = yRoot.get('a')
yPoint = yRoot.getAttr('a')
t.compare(yPoint.toJSON(), { x: 0, y: 0 })
t.compare(yPoint.toJSON().attrs, { x: 0, y: 0 })
undoMgr.redo() // x=100, y=100
t.compare(yPoint.toJSON(), { x: 100, y: 100 })
t.compare(yPoint.toJSON().attrs, { x: 100, y: 100 })
undoMgr.redo() // x=200, y=200
t.compare(yPoint.toJSON(), { x: 200, y: 200 })
t.compare(yPoint.toJSON().attrs, { x: 200, y: 200 })
undoMgr.redo() // expected x=300, y=300, actually nil
t.compare(yPoint.toJSON(), { x: 300, y: 300 })
t.compare(yPoint.toJSON().attrs, { x: 300, y: 300 })
}
/**
@@ -557,7 +550,7 @@ export const testConsecutiveRedoBug = _tc => {
export const testUndoXmlBug = _tc => {
const origin = 'origin'
const doc = new Y.Doc()
const fragment = doc.getXmlFragment('t')
const fragment = doc.get('t')
const undoManager = new Y.UndoManager(fragment, {
captureTimeout: 0,
trackedOrigins: new Set([origin])
@@ -565,9 +558,9 @@ export const testUndoXmlBug = _tc => {
// create element
doc.transact(() => {
const e = new Y.XmlElement('test-node')
e.setAttribute('a', '100')
e.setAttribute('b', '0')
const e = new Y.Type('test-node')
e.setAttr('a', '100')
e.setAttr('b', '0')
fragment.insert(fragment.length, [e])
}, origin)
@@ -601,44 +594,44 @@ export const testUndoXmlBug = _tc => {
*/
export const testUndoBlockBug = _tc => {
const doc = new Y.Doc({ gc: false })
const design = doc.getMap()
const design = doc.get()
const undoManager = new Y.UndoManager(design, { captureTimeout: 0 })
const text = new Y.Map()
const text = new Y.Type()
const blocks1 = new Y.Array()
const blocks1block = new Y.Map()
const blocks1 = new Y.Type()
const blocks1block = new Y.Type()
doc.transact(() => {
blocks1block.set('text', '1')
blocks1block.setAttr('text', '1')
blocks1.push([blocks1block])
text.set('blocks', blocks1block)
design.set('text', text)
text.setAttr('blocks', blocks1block)
design.setAttr('text', text)
})
const blocks2 = new Y.Array()
const blocks2block = new Y.Map()
const blocks2 = new Y.Type()
const blocks2block = new Y.Type()
doc.transact(() => {
blocks2block.set('text', '2')
blocks2block.setAttr('text', '2')
blocks2.push([blocks2block])
text.set('blocks', blocks2block)
text.setAttr('blocks', blocks2block)
})
const blocks3 = new Y.Array()
const blocks3block = new Y.Map()
const blocks3 = new Y.Type()
const blocks3block = new Y.Type()
doc.transact(() => {
blocks3block.set('text', '3')
blocks3block.setAttr('text', '3')
blocks3.push([blocks3block])
text.set('blocks', blocks3block)
text.setAttr('blocks', blocks3block)
})
const blocks4 = new Y.Array()
const blocks4block = new Y.Map()
const blocks4 = new Y.Type()
const blocks4block = new Y.Type()
doc.transact(() => {
blocks4block.set('text', '4')
blocks4block.setAttr('text', '4')
blocks4.push([blocks4block])
text.set('blocks', blocks4block)
text.setAttr('blocks', blocks4block)
})
// {"text":{"blocks":{"text":"4"}}}
@@ -650,7 +643,7 @@ export const testUndoBlockBug = _tc => {
undoManager.redo() // {"text":{"blocks":{"text":"2"}}}
undoManager.redo() // {"text":{"blocks":{"text":"3"}}}
undoManager.redo() // {"text":{}}
t.compare(design.toJSON(), { text: { blocks: { text: '4' } } })
t.compare(design.toJSON().attrs, { text: { blocks: { text: '4' } } })
}
/**
@@ -661,10 +654,10 @@ export const testUndoBlockBug = _tc => {
*/
export const testUndoDeleteTextFormat = _tc => {
const doc = new Y.Doc()
const text = doc.getText()
const text = doc.get()
text.insert(0, 'Attack ships on fire off the shoulder of Orion.')
const doc2 = new Y.Doc()
const text2 = doc2.getText()
const text2 = doc2.get()
Y.applyUpdate(doc2, Y.encodeStateAsUpdate(doc))
const undoManager = new Y.UndoManager(text)
@@ -698,16 +691,16 @@ export const testBehaviorOfIgnoreremotemapchangesProperty = _tc => {
const doc2 = new Y.Doc()
doc.on('update', update => Y.applyUpdate(doc2, update, doc))
doc2.on('update', update => Y.applyUpdate(doc, update, doc2))
const map1 = doc.getMap()
const map2 = doc2.getMap()
const map1 = doc.get()
const map2 = doc2.get()
const um1 = new Y.UndoManager(map1, { ignoreRemoteMapChanges: true })
map1.set('x', 1)
map2.set('x', 2)
map1.set('x', 3)
map2.set('x', 4)
map1.setAttr('x', 1)
map2.setAttr('x', 2)
map1.setAttr('x', 3)
map2.setAttr('x', 4)
um1.undo()
t.assert(map1.get('x') === 2)
t.assert(map2.get('x') === 2)
t.assert(map1.getAttr('x') === 2)
t.assert(map2.getAttr('x') === 2)
}
/**
@@ -719,12 +712,12 @@ export const testBehaviorOfIgnoreremotemapchangesProperty = _tc => {
export const testSpecialDeletionCase = _tc => {
const origin = 'undoable'
const doc = new Y.Doc()
const fragment = doc.getXmlFragment()
const fragment = doc.get()
const undoManager = new Y.UndoManager(fragment, { trackedOrigins: new Set([origin]) })
doc.transact(() => {
const e = new Y.XmlElement('test')
e.setAttribute('a', '1')
e.setAttribute('b', '2')
const e = new Y.Type('test')
e.setAttr('a', '1')
e.setAttr('b', '2')
fragment.insert(0, [e])
})
t.compareStrings(fragment.toString(), '<test a="1" b="2"></test>')
@@ -748,26 +741,26 @@ export const testSpecialDeletionCase = _tc => {
export const testUndoDeleteInMap = (tc) => {
const { map0 } = init(tc, { users: 3 })
const undoManager = new Y.UndoManager(map0, { captureTimeout: 0 })
map0.set('a', 'a')
map0.delete('a')
map0.set('a', 'b')
map0.delete('a')
map0.set('a', 'c')
map0.delete('a')
map0.set('a', 'd')
t.compare(map0.toJSON(), { a: 'd' })
map0.setAttr('a', 'a')
map0.deleteAttr('a')
map0.setAttr('a', 'b')
map0.deleteAttr('a')
map0.setAttr('a', 'c')
map0.deleteAttr('a')
map0.setAttr('a', 'd')
t.compare(map0.toJSON().attrs, { a: 'd' })
undoManager.undo()
t.compare(map0.toJSON(), {})
t.compare(map0.toJSON().attrs, {})
undoManager.undo()
t.compare(map0.toJSON(), { a: 'c' })
t.compare(map0.toJSON().attrs, { a: 'c' })
undoManager.undo()
t.compare(map0.toJSON(), {})
t.compare(map0.toJSON().attrs, {})
undoManager.undo()
t.compare(map0.toJSON(), { a: 'b' })
t.compare(map0.toJSON().attrs, { a: 'b' })
undoManager.undo()
t.compare(map0.toJSON(), {})
t.compare(map0.toJSON().attrs, {})
undoManager.undo()
t.compare(map0.toJSON(), { a: 'a' })
t.compare(map0.toJSON().attrs, { a: 'a' })
}
/**
@@ -777,7 +770,7 @@ export const testUndoDeleteInMap = (tc) => {
*/
export const testUndoDoingStackItem = async (_tc) => {
const doc = new Y.Doc()
const text = doc.getText('text')
const text = doc.get('text')
const undoManager = new Y.UndoManager([text])
undoManager.on('stack-item-added', /** @param {any} event */ event => {
event.stackItem.meta.set('str', '42')

View File

@@ -3,7 +3,6 @@ import * as t from 'lib0/testing'
import * as prng from 'lib0/prng'
import * as math from 'lib0/math'
import * as delta from 'lib0/delta'
import * as list from 'lib0/list'
import { createIdMapFromIdSet, noAttributionsManager, TwosetAttributionManager, createAttributionManagerFromSnapshots } from 'yjs/internals'
const { init, compare } = Y
@@ -1215,7 +1214,7 @@ export const testDeltaBug2 = _tc => {
})
ytext.applyDelta(changeEvent)
const d = ytext.getContent()
t.compare(list.toArray(d.children)[40].toJSON(), {
t.compare(d.toJSON().children?.[40], {
type: 'insert',
insert: '\n',
format: {