Compare commits

..

14 Commits

Author SHA1 Message Date
Kevin Jahns
05e9ba4145 basic delta implementation 2025-03-25 11:15:17 +01:00
Kevin Jahns
b2b1863624 Run tests without compilation. optimize testHelper output. 2025-03-25 11:14:55 +01:00
Kevin Jahns
ad0d915794 Merge pull request #701 from hacklschorsch/patch-2
README: Remove duplicate btw mention
2025-03-18 20:18:35 +01:00
Florian Sesser
2ef9ccd170 README: Remove duplicate btw mention
'btw' was mentioned twice; remove one mention.
2025-03-18 19:23:51 +01:00
Kevin Jahns
3ecfb4e898 add rowsncolumns 2025-03-09 20:57:52 +01:00
Kevin Jahns
35c030d834 improve reject update example 2025-03-06 10:36:18 +01:00
Kevin Jahns
e3739bce8e test example for rejecting updates 2025-03-05 14:15:26 +01:00
Kevin Jahns
afa4c35866 update titanic funding information - closes #696 2025-03-05 14:15:12 +01:00
Kevin Jahns
09fbb62ba9 improve documentation on global UndoManager 2025-03-04 14:52:19 +01:00
Kevin Jahns
78e0527b46 13.6.24 2025-03-04 14:44:19 +01:00
Kevin Jahns
69d4a5c821 [UndoManager] support global undo 2025-03-04 14:42:19 +01:00
Kevin Jahns
cc9a857441 slightly optimize TreeWalker and integration process 2025-02-24 20:30:48 +01:00
Kevin Jahns
4b865764b8 Merge pull request #691 from reknih/add-typst
Add Typst to Yjs users in README
2025-01-17 12:00:53 +01:00
Martin Haug
40725e373b Add Typst to Yjs users in README 2025-01-17 11:58:36 +01:00
18 changed files with 992 additions and 604 deletions

View File

@@ -99,7 +99,6 @@ Showcase](https://yjs-diagram.synergy.codes/).
* [AWS SageMaker](https://aws.amazon.com/sagemaker/) Tools for building Machine
Learning Models
* [linear](https://linear.app) Streamline issues, projects, and product roadmaps.
* [btw](https://www.btw.so) - Personal website builder
* [AWS SageMaker](https://aws.amazon.com/sagemaker/) - Machine Learning Service
* [Arkiter](https://www.arkiter.com/) - Live interview software
* [Appflowy](https://www.appflowy.io/) - They use Yrs
@@ -123,6 +122,7 @@ Showcase](https://yjs-diagram.synergy.codes/).
* [ScienHub](https://scienhub.com) - Collaborative LaTeX editor in the browser.
* [Open Collaboration Tools](https://www.open-collab.tools/) - Collaborative
editing for your IDE or custom editor
* [Typst](https://typst.app/) - Compose, edit, and automate technical documents
## Table of Contents
@@ -164,6 +164,7 @@ are implemented in separate modules.
| React / Vue / Svelte / MobX | | [SyncedStore](https://syncedstore.org) | [demo](https://syncedstore.org/docs/react) |
| [mobx-keystone](https://mobx-keystone.js.org/) | | [mobx-keystone-yjs](https://github.com/xaviergonz/mobx-keystone/tree/master/packages/mobx-keystone-yjs) | [demo](https://mobx-keystone.js.org/examples/yjs-binding) |
| [PSPDFKit](https://www.nutrient.io/) | | [yjs-pspdfkit](https://github.com/hoangqwe159/yjs-pspdfkit) | [demo](https://github.com/hoangqwe159/yjs-pspdfkit) |
| [Rows n'Columns](https://www.rowsncolumns.app/) | ✔ | [@rowsncolumns/y-spreadsheet](https://docs.rowsncolumns.app/collaboration/yjs-collaboration) | |
### Providers

View File

@@ -34,20 +34,19 @@
]
},
{
"guid": "ystream",
"name": "Y/Stream",
"guid": "Titanic",
"name": "Y/Titanic",
"description": "A provider for syncing millions of docs efficiently with other peers. This will become the foundation for building real local-first apps with Yjs.",
"webpageUrl": {
"url": "https://github.com/yjs/ystream",
"wellKnown": "https://github.com/yjs/ystream/blob/main/.well-known/funding-manifest-urls"
"url": "https://github.com/yjs/titanic",
"wellKnown": "https://github.com/yjs/titanic/blob/main/.well-known/funding-manifest-urls"
},
"repositoryUrl": {
"url": "https://github.com/yjs/ystream",
"wellKnown": "https://github.com/yjs/ystream/blob/main/.well-known/funding-manifest-urls"
"url": "https://github.com/yjs/titanic",
"wellKnown": "https://github.com/yjs/titanic/blob/main/.well-known/funding-manifest-urls"
},
"licenses": [
"spdx:MIT",
"spdx:GPL-3.0"
"spdx:MIT"
],
"tags": [
"privacy",
@@ -90,9 +89,9 @@
]
},
{
"guid": "ystream-funding",
"guid": "titanic-funding",
"status": "active",
"name": "YStream Funding",
"name": "Titanic Funding",
"description": "Fund the next generation of local-first providers.",
"amount": 30000,
"currency": "USD",

770
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "yjs",
"version": "13.6.23",
"version": "13.6.24",
"description": "Shared Editing Library",
"main": "./dist/yjs.cjs",
"module": "./dist/yjs.mjs",
@@ -13,28 +13,38 @@
},
"scripts": {
"clean": "rm -rf dist docs",
"test": "npm run dist && NODE_ENV=development node ./dist/tests.cjs --repetition-time 50",
"test-extensive": "npm run lint && npm run dist && node ./dist/tests.cjs --production --repetition-time 10000",
"test": "NODE_ENV=development node ./tests/index.js --repetition-time 50",
"test-extensive": "node ./tests/index.js --production --repetition-time 10000",
"dist": "npm run clean && rollup -c && tsc",
"watch": "rollup -wc",
"lint": "markdownlint README.md && standard && tsc",
"docs": "rm -rf docs; jsdoc --configure ./.jsdoc.json --verbose --readme ./README.md --package ./package.json || true",
"serve-docs": "npm run docs && http-server ./docs/",
"preversion": "npm run lint && PRODUCTION=1 npm run dist && npm run docs && node ./dist/tests.cjs --repetition-time 1000 && test -e dist/src/index.d.ts && test -e dist/yjs.cjs && test -e dist/yjs.cjs",
"debug": "concurrently 'http-server -o test.html' 'npm run watch'",
"trace-deopt": "clear && rollup -c && node --trace-deopt dist/test.cjs",
"trace-opt": "clear && rollup -c && node --trace-opt dist/test.cjs"
"serve-docs": "npm run docs && 0serve ./docs/",
"preversion": "npm run lint && PRODUCTION=1 npm run dist && npm run docs && node ./tests/index.js --repetition-time 1000 && test -e dist/src/index.d.ts && test -e dist/yjs.cjs && test -e dist/yjs.cjs",
"debug": "npm run gentesthtml && 0serve -o test.html",
"trace-deopt": "clear && node --trace-deopt ./tests/index.js",
"trace-opt": "clear && node --trace-opt ./tests/index.js",
"gentesthtml": "0gentesthtml --script ./tests/index.js > test.html"
},
"exports": {
".": {
"types": "./dist/src/index.d.ts",
"module": "./dist/yjs.mjs",
"import": "./dist/yjs.mjs",
"require": "./dist/yjs.cjs"
"require": "./dist/yjs.cjs",
"import": "./src/index.js"
},
"./internals": {
"types": "./dist/src/internals.d.ts",
"module": "./dist/internals.mjs",
"require": "./dist/internals.cjs",
"import": "./src/internals.js"
},
"./testHelper": {
"types": "./dist/testHelper.d.ts",
"module": "./dist/testHelper.mjs",
"require": "./dist/testHelper.cjs",
"import": "./tests/testHelper.js"
},
"./src/index.js": "./src/index.js",
"./tests/testHelper.js": "./tests/testHelper.js",
"./testHelper": "./dist/testHelper.mjs",
"./package.json": "./package.json"
},
"files": [
@@ -76,21 +86,19 @@
},
"homepage": "https://docs.yjs.dev",
"dependencies": {
"lib0": "^0.2.99"
"lib0": "^0.2.101",
"y-protocols": "^1.0.5"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-node-resolve": "^15.0.1",
"@types/node": "^18.15.5",
"concurrently": "^3.6.1",
"http-server": "^0.12.3",
"jsdoc": "^3.6.7",
"markdownlint-cli": "^0.41.0",
"rollup": "^3.20.0",
"rollup": "^4.37.0",
"standard": "^16.0.4",
"tui-jsdoc-template": "^1.2.2",
"typescript": "^4.9.5",
"y-protocols": "^1.0.5"
"yjs": "."
},
"engines": {
"npm": ">=8.0.0",

View File

@@ -1,106 +1,44 @@
import nodeResolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
const localImports = process.env.LOCALIMPORTS
const customModules = new Set([
'y-websocket',
'y-codemirror',
'y-ace',
'y-textarea',
'y-quill',
'y-dom',
'y-prosemirror'
])
/**
* @type {Set<any>}
*/
const customLibModules = new Set([
'lib0',
'y-protocols'
])
const debugResolve = {
const resolver = {
resolveId (importee) {
return
if (importee === 'yjs') {
return `${process.cwd()}/src/index.js`
}
if (localImports) {
if (customModules.has(importee.split('/')[0])) {
return `${process.cwd()}/../${importee}/src/${importee}.js`
}
if (customLibModules.has(importee.split('/')[0])) {
return `${process.cwd()}/../${importee}`
}
}
return null
}
}
export default [{
input: './src/index.js',
// cjs output
input: {
yjs: './src/index.js',
testHelper: './tests/testHelper.js',
internals: './src/internals.js'
},
output: {
name: 'Y',
file: 'dist/yjs.cjs',
dir: 'dist',
format: 'cjs',
sourcemap: true
},
external: id => /^lib0\//.test(id)
}, {
input: './src/index.js',
output: {
name: 'Y',
file: 'dist/yjs.mjs',
format: 'esm',
sourcemap: true
},
external: id => /^lib0\//.test(id)
}, {
input: './tests/testHelper.js',
output: {
name: 'Y',
file: 'dist/testHelper.mjs',
format: 'esm',
sourcemap: true
},
external: id => /^lib0\//.test(id) || id === 'yjs',
plugins: [{
resolveId (importee) {
if (importee === '../src/index.js') {
return 'yjs'
}
return null
}
}]
}, {
input: './tests/index.js',
output: {
name: 'test',
file: 'dist/tests.js',
format: 'iife',
entryFileNames : '[name].cjs',
sourcemap: true
},
plugins: [
debugResolve,
nodeResolve({
mainFields: ['browser', 'module', 'main']
}),
commonjs()
]
}, {
input: './tests/index.js',
output: {
name: 'test',
file: 'dist/tests.cjs',
format: 'cjs',
sourcemap: true
},
plugins: [
debugResolve,
nodeResolve({
mainFields: ['node', 'module', 'main'],
exportConditions: ['node', 'module', 'import', 'default']
}),
commonjs()
resolver
],
external: id => /^lib0\//.test(id)
external: id => /^(lib0|y-protocols)\//.test(id)
}, {
// esm output
input: {
yjs: './src/index.js',
testHelper: './tests/testHelper.js',
internals: './src/internals.js'
},
output: {
dir: 'dist',
format: 'esm',
entryFileNames : '[name].mjs',
sourcemap: true
},
plugins: [
resolver
],
external: id => /^(lib0|y-protocols)\//.test(id)
}]

View File

@@ -393,8 +393,7 @@ export class Item extends AbstractStruct {
if (this.left && this.left.constructor === Item) {
this.parent = this.left.parent
this.parentSub = this.left.parentSub
}
if (this.right && this.right.constructor === Item) {
} else if (this.right && this.right.constructor === Item) {
this.parent = this.right.parent
this.parentSub = this.right.parentSub
}

View File

@@ -96,8 +96,12 @@ export class YXmlTreeWalker {
} else {
// walk right or up in the tree
while (n !== null) {
if (n.right !== null) {
n = n.right
/**
* @type {Item | null}
*/
const nxt = n.next
if (nxt !== null) {
n = nxt
break
} else if (n.parent === this._root) {
n = null

177
src/utils/Delta.js Normal file
View File

@@ -0,0 +1,177 @@
import * as object from 'lib0/object'
import * as array from 'lib0/array'
/**
* @typedef {InsertOp|RetainOp|DeleteOp} DeltaOp
*/
/**
* @typedef {{ [key: string]: any }} FormattingAttributes
*/
/**
* @typedef {Object} Attribution
* @property {boolean} [Attribution.isDeleted]
* @property {boolean} [Attribution.isAdded]
* @property {string} [Attribution.creator]
* @property {number} [Attribution.timestamp]
*/
export class InsertOp {
/**
* @param {string} insert
* @param {FormattingAttributes|null} attributes
* @param {Attribution|null} attribution
*/
constructor (insert, attributes, attribution) {
this.insert = insert
this.attributes = attributes
this.attribution = attribution
}
toJSON () {
return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : {}, this.attribution ? { attribution: this.attribution } : {})
}
}
class DeleteOp {
/**
* @param {number} len
*/
constructor (len) {
this.delete = len
}
toJSON () {
return { delete: this.delete }
}
}
class RetainOp {
/**
* @param {number} retain
* @param {FormattingAttributes|null} attributes
* @param {Attribution|null} attribution
*/
constructor (retain, attributes, attribution) {
this.retain = retain
this.attributes = attributes
this.attribution = attribution
}
toJSON () {
return object.assign({ retain: this.retain }, this.attributes ? { attributes: this.attributes } : {}, this.attribution ? { attribution: this.attribution } : {})
}
}
export class Delta {
constructor () {
/**
* @type {Array<DeltaOp>}
*/
this.ops = []
}
toJSON () {
return { ops: this.ops.map(o => o.toJSON()) }
}
}
/**
* Helper function to merge attribution and attributes. The latter input "wins".
*
* @template {{ [key: string]: any }} T
* @param {T | null} a
* @param {T | null} b
*/
const mergeAttrs = (a, b) => a == null ? b : (b == null ? a : object.assign({}, a, b))
export class DeltaBuilder extends Delta {
constructor () {
super()
/**
* @private
* @type {FormattingAttributes?}
*/
this._useAttributes = null
/**
* @private
* @type {Attribution?}
*/
this._useAttribution = null
/**
* @private
* @type {DeltaOp?}
*/
this._lastOp = null
}
/**
* @param {FormattingAttributes} attributes
* @return {this}
*/
useAttributes (attributes) {
if (this._useAttributes === attributes) return this
this._useAttributes = object.assign({}, attributes)
if (this._lastOp?.constructor !== DeleteOp) this._lastOp = null
return this
}
/**
* @param {Attribution} attribution
*/
useAttribution (attribution) {
if (this._useAttribution === attribution) return this
this._useAttribution = object.assign({}, attribution)
if (this._lastOp?.constructor !== DeleteOp) this._lastOp = null
return this
}
/**
* @param {string} insert
* @param {FormattingAttributes?} attributes
* @param {Attribution?} attribution
* @return {this}
*/
insert (insert, attributes = null, attribution = null) {
if (attributes === null && attribution === null && this._lastOp instanceof InsertOp) {
this._lastOp.insert += insert
} else {
this.ops.push(this._lastOp = new InsertOp(insert, mergeAttrs(this.useAttributes, attributes), mergeAttrs(this._useAttribution, attribution)))
}
return this
}
/**
* @param {number} retain
* @param {FormattingAttributes?} attributes
* @param {Attribution?} attribution
* @return {this}
*/
retain (retain, attributes = null, attribution = null) {
if (attributes === null && attribution === null && this._lastOp instanceof RetainOp) {
this._lastOp.retain += retain
} else {
this.ops.push(this._lastOp = new RetainOp(retain, mergeAttrs(this.useAttributes, attributes), mergeAttrs(this._useAttribution, attribution)))
}
return this
}
/**
* @param {number} len
* @return {this}
*/
delete (len) {
if (this._lastOp instanceof DeleteOp) {
this._lastOp.delete += len
} else {
this.ops.push(this._lastOp = new DeleteOp(len))
}
return this
}
/**
* @return {Delta}
*/
done () {
return this
}
}
export const create = () => new DeltaBuilder()

View File

@@ -39,7 +39,7 @@ export class StackItem {
*/
const clearUndoManagerStackItem = (tr, um, stackItem) => {
iterateDeletedStructs(tr, stackItem.deletions, item => {
if (item instanceof Item && um.scope.some(type => isParentOf(type, item))) {
if (item instanceof Item && um.scope.some(type => type === tr.doc || isParentOf(/** @type {AbstractType<any>} */ (type), item))) {
keepItem(item, false)
}
})
@@ -81,7 +81,7 @@ const popStackItem = (undoManager, stack, eventType) => {
}
struct = item
}
if (!struct.deleted && scope.some(type => isParentOf(type, /** @type {Item} */ (struct)))) {
if (!struct.deleted && scope.some(type => type === transaction.doc || isParentOf(/** @type {AbstractType<any>} */ (type), /** @type {Item} */ (struct)))) {
itemsToDelete.push(struct)
}
}
@@ -89,7 +89,7 @@ const popStackItem = (undoManager, stack, eventType) => {
iterateDeletedStructs(transaction, stackItem.deletions, struct => {
if (
struct instanceof Item &&
scope.some(type => isParentOf(type, struct)) &&
scope.some(type => type === transaction.doc || isParentOf(/** @type {AbstractType<any>} */ (type), struct)) &&
// Never redo structs in stackItem.insertions because they were created and deleted in the same capture interval.
!isDeleted(stackItem.insertions, struct.id)
) {
@@ -159,7 +159,7 @@ const popStackItem = (undoManager, stack, eventType) => {
*/
export class UndoManager extends ObservableV2 {
/**
* @param {AbstractType<any>|Array<AbstractType<any>>} typeScope Accepts either a single type, or an array of types
* @param {Doc|AbstractType<any>|Array<AbstractType<any>>} 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, {
@@ -168,11 +168,11 @@ export class UndoManager extends ObservableV2 {
deleteFilter = () => true,
trackedOrigins = new Set([null]),
ignoreRemoteMapChanges = false,
doc = /** @type {Doc} */ (array.isArray(typeScope) ? typeScope[0].doc : typeScope.doc)
doc = /** @type {Doc} */ (array.isArray(typeScope) ? typeScope[0].doc : typeScope instanceof Doc ? typeScope : typeScope.doc)
} = {}) {
super()
/**
* @type {Array<AbstractType<any>>}
* @type {Array<AbstractType<any> | Doc>}
*/
this.scope = []
this.doc = doc
@@ -212,7 +212,7 @@ export class UndoManager extends ObservableV2 {
// Only track certain transactions
if (
!this.captureTransaction(transaction) ||
!this.scope.some(type => transaction.changedParentTypes.has(type)) ||
!this.scope.some(type => transaction.changedParentTypes.has(/** @type {AbstractType<any>} */ (type)) || type === this.doc) ||
(!this.trackedOrigins.has(transaction.origin) && (!transaction.origin || !this.trackedOrigins.has(transaction.origin.constructor)))
) {
return
@@ -251,7 +251,7 @@ export class UndoManager extends ObservableV2 {
}
// make sure that deleted structs are not gc'd
iterateDeletedStructs(transaction, transaction.deleteSet, /** @param {Item|GC} item */ item => {
if (item instanceof Item && this.scope.some(type => isParentOf(type, item))) {
if (item instanceof Item && this.scope.some(type => type === transaction.doc || isParentOf(/** @type {AbstractType<any>} */ (type), item))) {
keepItem(item, true)
}
})
@@ -272,13 +272,17 @@ export class UndoManager extends ObservableV2 {
}
/**
* @param {Array<AbstractType<any>> | AbstractType<any>} ytypes
* Extend the scope.
*
* @param {Array<AbstractType<any> | Doc> | AbstractType<any> | Doc} ytypes
*/
addToScope (ytypes) {
const tmpSet = new Set(this.scope)
ytypes = array.isArray(ytypes) ? ytypes : [ytypes]
ytypes.forEach(ytype => {
if (this.scope.every(yt => yt !== ytype)) {
if (ytype.doc !== this.doc) logging.warn('[yjs#509] Not same Y.Doc') // use MultiDocUndoManager instead. also see https://github.com/yjs/yjs/issues/509
if (!tmpSet.has(ytype)) {
tmpSet.add(ytype)
if (ytype instanceof AbstractType ? ytype.doc !== this.doc : ytype !== this.doc) logging.warn('[yjs#509] Not same Y.Doc') // use MultiDocUndoManager instead. also see https://github.com/yjs/yjs/issues/509
this.scope.push(ytype)
}
})

322
test.html
View File

@@ -1,9 +1,327 @@
<!DOCTYPE html>
<html>
<head>
<title>Testing Yjs</title>
<title>Testing yjs</title>
<script type="importmap">
{
"imports": {
"yjs": "./src/index.js",
"yjs/internals": "./src/internals.js",
"yjs/testHelper": "./tests/testHelper.js",
"yjs/package.json": "./package.json",
"lib0/package.json": "./node_modules/lib0/package.json",
"lib0": "./node_modules/lib0/index.js",
"lib0/array.js": "./node_modules/lib0/array.js",
"lib0/dist/array.cjs": "./node_modules/lib0/dist/array.cjs",
"lib0/array": "./node_modules/lib0/array.js",
"lib0/binary.js": "./node_modules/lib0/binary.js",
"lib0/dist/binary.cjs": "./node_modules/lib0/dist/binary.cjs",
"lib0/binary": "./node_modules/lib0/binary.js",
"lib0/broadcastchannel.js": "./node_modules/lib0/broadcastchannel.js",
"lib0/dist/broadcastchannel.cjs": "./node_modules/lib0/dist/broadcastchannel.cjs",
"lib0/broadcastchannel": "./node_modules/lib0/broadcastchannel.js",
"lib0/buffer.js": "./node_modules/lib0/buffer.js",
"lib0/dist/buffer.cjs": "./node_modules/lib0/dist/buffer.cjs",
"lib0/buffer": "./node_modules/lib0/buffer.js",
"lib0/cache.js": "./node_modules/lib0/cache.js",
"lib0/dist/cache.cjs": "./node_modules/lib0/dist/cache.cjs",
"lib0/cache": "./node_modules/lib0/cache.js",
"lib0/component.js": "./node_modules/lib0/component.js",
"lib0/dist/component.cjs": "./node_modules/lib0/dist/component.cjs",
"lib0/component": "./node_modules/lib0/component.js",
"lib0/conditions.js": "./node_modules/lib0/conditions.js",
"lib0/dist/conditions.cjs": "./node_modules/lib0/dist/conditions.cjs",
"lib0/conditions": "./node_modules/lib0/conditions.js",
"lib0/crypto/jwt": "./node_modules/lib0/crypto/jwt.js",
"lib0/crypto/aes-gcm": "./node_modules/lib0/crypto/aes-gcm.js",
"lib0/crypto/ecdsa": "./node_modules/lib0/crypto/ecdsa.js",
"lib0/crypto/rsa-oaep": "./node_modules/lib0/crypto/rsa-oaep.js",
"lib0/hash/rabin": "./node_modules/lib0/hash/rabin.js",
"lib0/hash/sha256": "./node_modules/lib0/hash/sha256.js",
"lib0/decoding.js": "./node_modules/lib0/decoding.js",
"lib0/dist/decoding.cjs": "./node_modules/lib0/dist/decoding.cjs",
"lib0/decoding": "./node_modules/lib0/decoding.js",
"lib0/diff.js": "./node_modules/lib0/diff.js",
"lib0/dist/diff.cjs": "./node_modules/lib0/dist/diff.cjs",
"lib0/diff": "./node_modules/lib0/diff.js",
"lib0/dom.js": "./node_modules/lib0/dom.js",
"lib0/dist/dom.cjs": "./node_modules/lib0/dist/dom.cjs",
"lib0/dom": "./node_modules/lib0/dom.js",
"lib0/encoding.js": "./node_modules/lib0/encoding.js",
"lib0/dist/encoding.cjs": "./node_modules/lib0/dist/encoding.cjs",
"lib0/encoding": "./node_modules/lib0/encoding.js",
"lib0/environment.js": "./node_modules/lib0/environment.js",
"lib0/dist/environment.cjs": "./node_modules/lib0/dist/environment.cjs",
"lib0/environment": "./node_modules/lib0/environment.js",
"lib0/error.js": "./node_modules/lib0/error.js",
"lib0/dist/error.cjs": "./node_modules/lib0/dist/error.cjs",
"lib0/error": "./node_modules/lib0/error.js",
"lib0/eventloop.js": "./node_modules/lib0/eventloop.js",
"lib0/dist/eventloop.cjs": "./node_modules/lib0/dist/eventloop.cjs",
"lib0/eventloop": "./node_modules/lib0/eventloop.js",
"lib0/function.js": "./node_modules/lib0/function.js",
"lib0/dist/function.cjs": "./node_modules/lib0/dist/function.cjs",
"lib0/function": "./node_modules/lib0/function.js",
"lib0/indexeddb.js": "./node_modules/lib0/indexeddb.js",
"lib0/dist/indexeddb.cjs": "./node_modules/lib0/dist/indexeddb.cjs",
"lib0/indexeddb": "./node_modules/lib0/indexeddb.js",
"lib0/isomorphic.js": "./node_modules/lib0/isomorphic.js",
"lib0/dist/isomorphic.cjs": "./node_modules/lib0/dist/isomorphic.cjs",
"lib0/isomorphic": "./node_modules/lib0/isomorphic.js",
"lib0/iterator.js": "./node_modules/lib0/iterator.js",
"lib0/dist/iterator.cjs": "./node_modules/lib0/dist/iterator.cjs",
"lib0/iterator": "./node_modules/lib0/iterator.js",
"lib0/json.js": "./node_modules/lib0/json.js",
"lib0/dist/json.cjs": "./node_modules/lib0/dist/json.cjs",
"lib0/json": "./node_modules/lib0/json.js",
"lib0/list.js": "./node_modules/lib0/list.js",
"lib0/dist/list.cjs": "./node_modules/lib0/dist/list.cjs",
"lib0/list": "./node_modules/lib0/list.js",
"lib0/logging.js": "./node_modules/lib0/logging.js",
"lib0/dist/logging.cjs": "./node_modules/lib0/dist/logging.node.cjs",
"lib0/logging": "./node_modules/lib0/logging.js",
"lib0/map.js": "./node_modules/lib0/map.js",
"lib0/dist/map.cjs": "./node_modules/lib0/dist/map.cjs",
"lib0/map": "./node_modules/lib0/map.js",
"lib0/math.js": "./node_modules/lib0/math.js",
"lib0/dist/math.cjs": "./node_modules/lib0/dist/math.cjs",
"lib0/math": "./node_modules/lib0/math.js",
"lib0/metric.js": "./node_modules/lib0/metric.js",
"lib0/dist/metric.cjs": "./node_modules/lib0/dist/metric.cjs",
"lib0/metric": "./node_modules/lib0/metric.js",
"lib0/mutex.js": "./node_modules/lib0/mutex.js",
"lib0/dist/mutex.cjs": "./node_modules/lib0/dist/mutex.cjs",
"lib0/mutex": "./node_modules/lib0/mutex.js",
"lib0/number.js": "./node_modules/lib0/number.js",
"lib0/dist/number.cjs": "./node_modules/lib0/dist/number.cjs",
"lib0/number": "./node_modules/lib0/number.js",
"lib0/object.js": "./node_modules/lib0/object.js",
"lib0/dist/object.cjs": "./node_modules/lib0/dist/object.cjs",
"lib0/object": "./node_modules/lib0/object.js",
"lib0/observable.js": "./node_modules/lib0/observable.js",
"lib0/dist/observable.cjs": "./node_modules/lib0/dist/observable.cjs",
"lib0/observable": "./node_modules/lib0/observable.js",
"lib0/pair.js": "./node_modules/lib0/pair.js",
"lib0/dist/pair.cjs": "./node_modules/lib0/dist/pair.cjs",
"lib0/pair": "./node_modules/lib0/pair.js",
"lib0/prng.js": "./node_modules/lib0/prng.js",
"lib0/dist/prng.cjs": "./node_modules/lib0/dist/prng.cjs",
"lib0/prng": "./node_modules/lib0/prng.js",
"lib0/promise.js": "./node_modules/lib0/promise.js",
"lib0/dist/promise.cjs": "./node_modules/lib0/dist/promise.cjs",
"lib0/promise": "./node_modules/lib0/promise.js",
"lib0/queue.js": "./node_modules/lib0/queue.js",
"lib0/dist/queue.cjs": "./node_modules/lib0/dist/queue.cjs",
"lib0/queue": "./node_modules/lib0/queue.js",
"lib0/random.js": "./node_modules/lib0/random.js",
"lib0/dist/random.cjs": "./node_modules/lib0/dist/random.cjs",
"lib0/random": "./node_modules/lib0/random.js",
"lib0/set.js": "./node_modules/lib0/set.js",
"lib0/dist/set.cjs": "./node_modules/lib0/dist/set.cjs",
"lib0/set": "./node_modules/lib0/set.js",
"lib0/sort.js": "./node_modules/lib0/sort.js",
"lib0/dist/sort.cjs": "./node_modules/lib0/dist/sort.cjs",
"lib0/sort": "./node_modules/lib0/sort.js",
"lib0/statistics.js": "./node_modules/lib0/statistics.js",
"lib0/dist/statistics.cjs": "./node_modules/lib0/dist/statistics.cjs",
"lib0/statistics": "./node_modules/lib0/statistics.js",
"lib0/storage.js": "./node_modules/lib0/storage.js",
"lib0/dist/storage.cjs": "./node_modules/lib0/dist/storage.cjs",
"lib0/storage": "./node_modules/lib0/storage.js",
"lib0/string.js": "./node_modules/lib0/string.js",
"lib0/dist/string.cjs": "./node_modules/lib0/dist/string.cjs",
"lib0/string": "./node_modules/lib0/string.js",
"lib0/symbol.js": "./node_modules/lib0/symbol.js",
"lib0/dist/symbol.cjs": "./node_modules/lib0/dist/symbol.cjs",
"lib0/symbol": "./node_modules/lib0/symbol.js",
"lib0/testing.js": "./node_modules/lib0/testing.js",
"lib0/dist/testing.cjs": "./node_modules/lib0/dist/testing.cjs",
"lib0/testing": "./node_modules/lib0/testing.js",
"lib0/time.js": "./node_modules/lib0/time.js",
"lib0/dist/time.cjs": "./node_modules/lib0/dist/time.cjs",
"lib0/time": "./node_modules/lib0/time.js",
"lib0/tree.js": "./node_modules/lib0/tree.js",
"lib0/dist/tree.cjs": "./node_modules/lib0/dist/tree.cjs",
"lib0/tree": "./node_modules/lib0/tree.js",
"lib0/url.js": "./node_modules/lib0/url.js",
"lib0/dist/url.cjs": "./node_modules/lib0/dist/url.cjs",
"lib0/url": "./node_modules/lib0/url.js",
"lib0/websocket.js": "./node_modules/lib0/websocket.js",
"lib0/dist/websocket.cjs": "./node_modules/lib0/dist/websocket.cjs",
"lib0/websocket": "./node_modules/lib0/websocket.js",
"lib0/webcrypto": "./node_modules/lib0/webcrypto.js",
"lib0/performance.js": "./node_modules/lib0/performance.js",
"lib0/dist/performance.cjs": "./node_modules/lib0/dist/performance.node.cjs",
"lib0/performance": "./node_modules/lib0/performance.js",
"y-protocols/package.json": "./node_modules/y-protocols/package.json",
"y-protocols/sync.js": "./node_modules/y-protocols/sync.js",
"y-protocols/dist/sync.cjs": "./node_modules/y-protocols/dist/sync.cjs",
"y-protocols/sync": "./node_modules/y-protocols/sync.js",
"y-protocols/awareness.js": "./node_modules/y-protocols/awareness.js",
"y-protocols/dist/awareness.cjs": "./node_modules/y-protocols/dist/awareness.cjs",
"y-protocols/awareness": "./node_modules/y-protocols/awareness.js",
"y-protocols/auth.js": "./node_modules/y-protocols/auth.js",
"y-protocols/dist/auth.cjs": "./node_modules/y-protocols/dist/auth.cjs",
"y-protocols/auth": "./node_modules/y-protocols/auth.js"
},
"scopes": {
"./node_modules/lib0/": {
"isomorphic.js": "./node_modules/isomorphic.js/browser.mjs",
"isomorphic.js/package.json": "./node_modules/isomorphic.js/package.json"
},
"./node_modules/y-protocols/": {
"lib0/package.json": "./node_modules/lib0/package.json",
"lib0": "./node_modules/lib0/index.js",
"lib0/array.js": "./node_modules/lib0/array.js",
"lib0/dist/array.cjs": "./node_modules/lib0/dist/array.cjs",
"lib0/array": "./node_modules/lib0/array.js",
"lib0/binary.js": "./node_modules/lib0/binary.js",
"lib0/dist/binary.cjs": "./node_modules/lib0/dist/binary.cjs",
"lib0/binary": "./node_modules/lib0/binary.js",
"lib0/broadcastchannel.js": "./node_modules/lib0/broadcastchannel.js",
"lib0/dist/broadcastchannel.cjs": "./node_modules/lib0/dist/broadcastchannel.cjs",
"lib0/broadcastchannel": "./node_modules/lib0/broadcastchannel.js",
"lib0/buffer.js": "./node_modules/lib0/buffer.js",
"lib0/dist/buffer.cjs": "./node_modules/lib0/dist/buffer.cjs",
"lib0/buffer": "./node_modules/lib0/buffer.js",
"lib0/cache.js": "./node_modules/lib0/cache.js",
"lib0/dist/cache.cjs": "./node_modules/lib0/dist/cache.cjs",
"lib0/cache": "./node_modules/lib0/cache.js",
"lib0/component.js": "./node_modules/lib0/component.js",
"lib0/dist/component.cjs": "./node_modules/lib0/dist/component.cjs",
"lib0/component": "./node_modules/lib0/component.js",
"lib0/conditions.js": "./node_modules/lib0/conditions.js",
"lib0/dist/conditions.cjs": "./node_modules/lib0/dist/conditions.cjs",
"lib0/conditions": "./node_modules/lib0/conditions.js",
"lib0/crypto/jwt": "./node_modules/lib0/crypto/jwt.js",
"lib0/crypto/aes-gcm": "./node_modules/lib0/crypto/aes-gcm.js",
"lib0/crypto/ecdsa": "./node_modules/lib0/crypto/ecdsa.js",
"lib0/crypto/rsa-oaep": "./node_modules/lib0/crypto/rsa-oaep.js",
"lib0/hash/rabin": "./node_modules/lib0/hash/rabin.js",
"lib0/hash/sha256": "./node_modules/lib0/hash/sha256.js",
"lib0/decoding.js": "./node_modules/lib0/decoding.js",
"lib0/dist/decoding.cjs": "./node_modules/lib0/dist/decoding.cjs",
"lib0/decoding": "./node_modules/lib0/decoding.js",
"lib0/diff.js": "./node_modules/lib0/diff.js",
"lib0/dist/diff.cjs": "./node_modules/lib0/dist/diff.cjs",
"lib0/diff": "./node_modules/lib0/diff.js",
"lib0/dom.js": "./node_modules/lib0/dom.js",
"lib0/dist/dom.cjs": "./node_modules/lib0/dist/dom.cjs",
"lib0/dom": "./node_modules/lib0/dom.js",
"lib0/encoding.js": "./node_modules/lib0/encoding.js",
"lib0/dist/encoding.cjs": "./node_modules/lib0/dist/encoding.cjs",
"lib0/encoding": "./node_modules/lib0/encoding.js",
"lib0/environment.js": "./node_modules/lib0/environment.js",
"lib0/dist/environment.cjs": "./node_modules/lib0/dist/environment.cjs",
"lib0/environment": "./node_modules/lib0/environment.js",
"lib0/error.js": "./node_modules/lib0/error.js",
"lib0/dist/error.cjs": "./node_modules/lib0/dist/error.cjs",
"lib0/error": "./node_modules/lib0/error.js",
"lib0/eventloop.js": "./node_modules/lib0/eventloop.js",
"lib0/dist/eventloop.cjs": "./node_modules/lib0/dist/eventloop.cjs",
"lib0/eventloop": "./node_modules/lib0/eventloop.js",
"lib0/function.js": "./node_modules/lib0/function.js",
"lib0/dist/function.cjs": "./node_modules/lib0/dist/function.cjs",
"lib0/function": "./node_modules/lib0/function.js",
"lib0/indexeddb.js": "./node_modules/lib0/indexeddb.js",
"lib0/dist/indexeddb.cjs": "./node_modules/lib0/dist/indexeddb.cjs",
"lib0/indexeddb": "./node_modules/lib0/indexeddb.js",
"lib0/isomorphic.js": "./node_modules/lib0/isomorphic.js",
"lib0/dist/isomorphic.cjs": "./node_modules/lib0/dist/isomorphic.cjs",
"lib0/isomorphic": "./node_modules/lib0/isomorphic.js",
"lib0/iterator.js": "./node_modules/lib0/iterator.js",
"lib0/dist/iterator.cjs": "./node_modules/lib0/dist/iterator.cjs",
"lib0/iterator": "./node_modules/lib0/iterator.js",
"lib0/json.js": "./node_modules/lib0/json.js",
"lib0/dist/json.cjs": "./node_modules/lib0/dist/json.cjs",
"lib0/json": "./node_modules/lib0/json.js",
"lib0/list.js": "./node_modules/lib0/list.js",
"lib0/dist/list.cjs": "./node_modules/lib0/dist/list.cjs",
"lib0/list": "./node_modules/lib0/list.js",
"lib0/logging.js": "./node_modules/lib0/logging.js",
"lib0/dist/logging.cjs": "./node_modules/lib0/dist/logging.node.cjs",
"lib0/logging": "./node_modules/lib0/logging.js",
"lib0/map.js": "./node_modules/lib0/map.js",
"lib0/dist/map.cjs": "./node_modules/lib0/dist/map.cjs",
"lib0/map": "./node_modules/lib0/map.js",
"lib0/math.js": "./node_modules/lib0/math.js",
"lib0/dist/math.cjs": "./node_modules/lib0/dist/math.cjs",
"lib0/math": "./node_modules/lib0/math.js",
"lib0/metric.js": "./node_modules/lib0/metric.js",
"lib0/dist/metric.cjs": "./node_modules/lib0/dist/metric.cjs",
"lib0/metric": "./node_modules/lib0/metric.js",
"lib0/mutex.js": "./node_modules/lib0/mutex.js",
"lib0/dist/mutex.cjs": "./node_modules/lib0/dist/mutex.cjs",
"lib0/mutex": "./node_modules/lib0/mutex.js",
"lib0/number.js": "./node_modules/lib0/number.js",
"lib0/dist/number.cjs": "./node_modules/lib0/dist/number.cjs",
"lib0/number": "./node_modules/lib0/number.js",
"lib0/object.js": "./node_modules/lib0/object.js",
"lib0/dist/object.cjs": "./node_modules/lib0/dist/object.cjs",
"lib0/object": "./node_modules/lib0/object.js",
"lib0/observable.js": "./node_modules/lib0/observable.js",
"lib0/dist/observable.cjs": "./node_modules/lib0/dist/observable.cjs",
"lib0/observable": "./node_modules/lib0/observable.js",
"lib0/pair.js": "./node_modules/lib0/pair.js",
"lib0/dist/pair.cjs": "./node_modules/lib0/dist/pair.cjs",
"lib0/pair": "./node_modules/lib0/pair.js",
"lib0/prng.js": "./node_modules/lib0/prng.js",
"lib0/dist/prng.cjs": "./node_modules/lib0/dist/prng.cjs",
"lib0/prng": "./node_modules/lib0/prng.js",
"lib0/promise.js": "./node_modules/lib0/promise.js",
"lib0/dist/promise.cjs": "./node_modules/lib0/dist/promise.cjs",
"lib0/promise": "./node_modules/lib0/promise.js",
"lib0/queue.js": "./node_modules/lib0/queue.js",
"lib0/dist/queue.cjs": "./node_modules/lib0/dist/queue.cjs",
"lib0/queue": "./node_modules/lib0/queue.js",
"lib0/random.js": "./node_modules/lib0/random.js",
"lib0/dist/random.cjs": "./node_modules/lib0/dist/random.cjs",
"lib0/random": "./node_modules/lib0/random.js",
"lib0/set.js": "./node_modules/lib0/set.js",
"lib0/dist/set.cjs": "./node_modules/lib0/dist/set.cjs",
"lib0/set": "./node_modules/lib0/set.js",
"lib0/sort.js": "./node_modules/lib0/sort.js",
"lib0/dist/sort.cjs": "./node_modules/lib0/dist/sort.cjs",
"lib0/sort": "./node_modules/lib0/sort.js",
"lib0/statistics.js": "./node_modules/lib0/statistics.js",
"lib0/dist/statistics.cjs": "./node_modules/lib0/dist/statistics.cjs",
"lib0/statistics": "./node_modules/lib0/statistics.js",
"lib0/storage.js": "./node_modules/lib0/storage.js",
"lib0/dist/storage.cjs": "./node_modules/lib0/dist/storage.cjs",
"lib0/storage": "./node_modules/lib0/storage.js",
"lib0/string.js": "./node_modules/lib0/string.js",
"lib0/dist/string.cjs": "./node_modules/lib0/dist/string.cjs",
"lib0/string": "./node_modules/lib0/string.js",
"lib0/symbol.js": "./node_modules/lib0/symbol.js",
"lib0/dist/symbol.cjs": "./node_modules/lib0/dist/symbol.cjs",
"lib0/symbol": "./node_modules/lib0/symbol.js",
"lib0/testing.js": "./node_modules/lib0/testing.js",
"lib0/dist/testing.cjs": "./node_modules/lib0/dist/testing.cjs",
"lib0/testing": "./node_modules/lib0/testing.js",
"lib0/time.js": "./node_modules/lib0/time.js",
"lib0/dist/time.cjs": "./node_modules/lib0/dist/time.cjs",
"lib0/time": "./node_modules/lib0/time.js",
"lib0/tree.js": "./node_modules/lib0/tree.js",
"lib0/dist/tree.cjs": "./node_modules/lib0/dist/tree.cjs",
"lib0/tree": "./node_modules/lib0/tree.js",
"lib0/url.js": "./node_modules/lib0/url.js",
"lib0/dist/url.cjs": "./node_modules/lib0/dist/url.cjs",
"lib0/url": "./node_modules/lib0/url.js",
"lib0/websocket.js": "./node_modules/lib0/websocket.js",
"lib0/dist/websocket.cjs": "./node_modules/lib0/dist/websocket.cjs",
"lib0/websocket": "./node_modules/lib0/websocket.js",
"lib0/webcrypto": "./node_modules/lib0/webcrypto.js",
"lib0/performance.js": "./node_modules/lib0/performance.js",
"lib0/dist/performance.cjs": "./node_modules/lib0/dist/performance.node.cjs",
"lib0/performance": "./node_modules/lib0/performance.js"
}
}
}
</script>
</head>
<body>
<script type="module" src="./dist/tests.js"></script>
<script type="module" src="./tests/index.js"></script>
</body>
</html>

11
tests/delta.tests.js Normal file
View File

@@ -0,0 +1,11 @@
import * as t from 'lib0/testing'
import * as delta from '../src/utils/Delta.js'
/**
* @param {t.TestCase} _tc
*/
export const testDelta = _tc => {
const d = delta.create().insert('hello').insert(' ').useAttributes({ bold: true }).insert('world').useAttribution({ creator: 'tester' }).insert('!').done()
t.compare(d.toJSON().ops, [{ insert: 'hello' }, { insert: ' world', attributes: { bold: true } }, { insert: '!', attributes: { bold: true }, attribution: { creator: 'tester' } }])
}

View File

@@ -11,11 +11,11 @@ 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 delta from './delta.tests.js'
import { runTests } from 'lib0/testing'
import { isBrowser, isNode } from 'lib0/environment'
import * as log from 'lib0/logging'
import { environment } from 'lib0'
if (isBrowser) {
log.createVConsole(document.body)
@@ -25,14 +25,10 @@ if (isBrowser) {
* @type {any}
*/
const tests = {
doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions
doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, delta
}
const run = async () => {
if (environment.isNode) {
// tests.nodejs = await import('./node.tests.js')
}
const success = await runTests(tests)
/* istanbul ignore next */
if (isNode) {

View File

@@ -1,6 +1,5 @@
import { init } from './testHelper.js' // eslint-disable-line
import * as Y from '../src/index.js'
import { init } from './testHelper.js' // eslint-disable-line
import * as t from 'lib0/testing'
export const testInconsistentFormat = () => {
@@ -116,6 +115,72 @@ export const testEmptyTypeScope = _tc => {
t.assert(yarray.length === 0)
}
/**
* @param {t.TestCase} _tc
*/
export const testRejectUpdateExample = _tc => {
const tmpydoc1 = new Y.Doc()
tmpydoc1.getArray('restricted').insert(0, [1])
tmpydoc1.getArray('public').insert(0, [1])
const update1 = Y.encodeStateAsUpdate(tmpydoc1)
const tmpydoc2 = new Y.Doc()
tmpydoc2.getArray('public').insert(0, [2])
const update2 = Y.encodeStateAsUpdate(tmpydoc2)
const ydoc = new Y.Doc()
const restrictedType = ydoc.getArray('restricted')
/**
* Assume this function handles incoming updates via a communication channel like websockets.
* Changes to the `ydoc.getMap('restricted')` type should be rejected.
*
* - set up undo manager on the restricted types
* - cache pending* updates from the Ydoc to avoid certain attacks
* - apply received update and check whether the restricted type (or any of its children) has been changed.
* - catch errors that might try to circumvent the restrictions
* - undo changes on restricted types
* - reapply pending* updates
*
* @param {Uint8Array} update
*/
const updateHandler = (update) => {
// don't handle changes of the local undo manager, which is used to undo invalid changes
const um = new Y.UndoManager(restrictedType, { trackedOrigins: new Set(['remote change']) })
const beforePendingDs = ydoc.store.pendingDs
const beforePendingStructs = ydoc.store.pendingStructs?.update
try {
Y.applyUpdate(ydoc, update, 'remote change')
} finally {
while (um.undoStack.length) {
um.undo()
}
um.destroy()
ydoc.store.pendingDs = beforePendingDs
ydoc.store.pendingStructs = null
if (beforePendingStructs) {
Y.applyUpdateV2(ydoc, beforePendingStructs)
}
}
}
updateHandler(update1)
updateHandler(update2)
t.assert(restrictedType.length === 0)
t.assert(ydoc.getArray('public').length === 2)
}
/**
* Test case to fix #241
* @param {t.TestCase} _tc
*/
export const testGlobalScope = _tc => {
const ydoc = new Y.Doc()
const um = new Y.UndoManager(ydoc)
const yarray = ydoc.getArray()
yarray.insert(0, [1])
um.undo()
t.assert(yarray.length === 0)
}
/**
* Test case to fix #241
* @param {t.TestCase} _tc

View File

@@ -1,6 +1,6 @@
import * as t from 'lib0/testing'
import { init, compare } from './testHelper.js' // eslint-disable-line
import * as Y from '../src/index.js'
import { init, compare } from './testHelper.js' // eslint-disable-line
import { readClientsStructRefs, readDeleteSet, UpdateDecoderV2, UpdateEncoderV2, writeDeleteSet } from '../src/internals.js'
import * as encoding from 'lib0/encoding'
import * as decoding from 'lib0/decoding'

View File

@@ -1,5 +1,4 @@
import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line
import * as Y from '../src/index.js'
import * as t from 'lib0/testing'
import * as prng from 'lib0/prng'

View File

@@ -1,10 +1,8 @@
import * as Y from '../src/index.js'
import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line
import {
compareIDs
} from '../src/internals.js'
import * as Y from '../src/index.js'
import * as t from 'lib0/testing'
import * as prng from 'lib0/prng'

View File

@@ -1,6 +1,5 @@
import { init, compare } from './testHelper.js'
import * as Y from '../src/index.js'
import { init, compare } from './testHelper.js'
import * as t from 'lib0/testing'
export const testCustomTypings = () => {

View File

@@ -14,7 +14,9 @@
"noImplicitAny": true,
"moduleResolution": "nodenext",
"paths": {
"yjs": ["./src/index.js"]
"yjs": ["./src/index.js"],
"yjs/internals": ["./src/internals.js"],
"yjs/testHelper": ["./tests/testHelper.js"]
}
},
"include": ["./src/**/*.js", "./tests/**/*.js"]