Compare commits

...

45 Commits

Author SHA1 Message Date
Kevin Jahns
d1e8d50c43 13.0.4 2020-02-12 10:53:56 +01:00
Kevin Jahns
18bb2d0719 fix imports in esm bundle 2020-02-12 10:52:51 +01:00
Kevin Jahns
45df311dd7 13.0.3 2020-02-12 10:38:28 +01:00
Kevin Jahns
62888b4004 bundle yjs as a module to prevent declaration issues from circular dependencies 2020-02-12 10:37:22 +01:00
Kevin Jahns
76c389dba0 13.0.2 2020-02-03 12:23:39 +01:00
Kevin Jahns
78fa98c000 add type definition for YText.length 2020-02-03 12:22:35 +01:00
Kevin Jahns
e9f9e08450 13.0.1 2020-01-27 03:43:45 +01:00
Kevin Jahns
e3c59b0aa7 more options to gc data (undomanager.clear and tryGc) 2020-01-27 03:42:32 +01:00
Kevin Jahns
705dce7838 add y-indexeddb section 2020-01-23 22:49:04 +01:00
Kevin Jahns
0fb55981ba 13.0.0 2020-01-23 21:53:02 +01:00
Kevin Jahns
89378e29ae publish stable Yjs release 🎆 2020-01-23 21:51:26 +01:00
Kevin Jahns
cce35270ec typescript typingis!!! fixes #180 2020-01-23 21:45:56 +01:00
Kevin Jahns
d78180bf97 make opts optional in PermanentUserData 2020-01-23 18:05:12 +01:00
Kevin Jahns
0ab415de3e 13.0.0-108 2020-01-23 05:01:05 +01:00
Kevin Jahns
ff3969caeb dedupe npm 2020-01-23 05:00:11 +01:00
Kevin Jahns
c82cc9f8d6 lint 2020-01-23 04:59:17 +01:00
Kevin Jahns
ef5c71bd8b PermanentUserData fixes 2020-01-23 04:58:02 +01:00
Kevin Jahns
bd6be3d23b 13.0.0-107 2020-01-22 16:45:48 +01:00
Kevin Jahns
0e6deab9c9 type toJSON returns 2020-01-22 16:44:30 +01:00
Kevin Jahns
6cd9e2be32 lint 2020-01-22 16:42:16 +01:00
Kevin Jahns
ac8dab1e88 Merge pull request #179 from garth/text-tojson
basic Y.Text toJSON returns {unformatted:string}
2020-01-22 16:19:01 +01:00
Garth Williams
38ed725c2c basic Y.Text toJSON returns unformatted string
This avoids text nodes in nested structures returning undefined when toJSON is called by a parent.
2020-01-22 13:34:13 +01:00
Kevin Jahns
a210bad25e update keywords 2020-01-19 00:43:23 +01:00
Kevin Jahns
6929a4f0f8 13.0.0-106 2020-01-14 05:16:43 +01:00
Kevin Jahns
52dacfa5f2 update package-lock 2020-01-14 05:15:36 +01:00
Kevin Jahns
27efe86f9c isParentOf 2020-01-14 05:13:51 +01:00
Kevin Jahns
882b9055c7 fix localimports path ending 2020-01-14 02:36:29 +01:00
Kevin Jahns
e089089413 fix debug resolve 2020-01-13 17:03:56 +01:00
Kevin Jahns
197932752e 13.0.0-105 2020-01-13 14:55:05 +01:00
Kevin Jahns
f0b2bdaf34 revert to classic cjs module 2020-01-13 14:54:07 +01:00
Kevin Jahns
b96362c0f1 use correct module script 2020-01-13 07:55:58 +01:00
Kevin Jahns
67f241cd7a 13.0.0-104 2020-01-13 07:48:47 +01:00
Kevin Jahns
c8af0bebf7 fix preversion script 2020-01-13 07:47:43 +01:00
Kevin Jahns
4f35e799a6 update to lib0@.2 2020-01-13 07:41:31 +01:00
Kevin Jahns
eb2a52dd26 update README with podcast links, consulting info, and y-webrtc 2019-12-11 13:26:46 +01:00
Kevin Jahns
189b1068ae 13.0.0-103 2019-12-10 20:52:20 +01:00
Kevin Jahns
7a3b60a5d7 add markdownlint-cli as dep 2019-12-10 20:51:07 +01:00
Kevin Jahns
99f06fc093 bump lib0 for improved encoding performance 2019-12-10 20:46:58 +01:00
Kevin Jahns
22917bca19 fix gc & proper options typings for Y.Doc, fixes #176 2019-12-10 17:51:49 +01:00
Kevin Jahns
7f0e25dcba permanent user store writes updates in separate transaction 2019-12-10 17:18:57 +01:00
Kevin Jahns
d90c9b1cb2 bump lib0 for faster text encoding 2019-12-10 00:26:28 +01:00
Kevin Jahns
c426055f17 spelling 2019-12-10 00:19:02 +01:00
Kevin Jahns
18c9010b63 Merge branch 'master' of github.com:y-js/yjs 2019-11-26 13:02:49 +01:00
Kevin Jahns
c3edac62ef doc typo 2019-11-26 13:02:43 +01:00
Kevin Jahns
755de18fd5 Create Funding.yml 2019-11-07 14:41:50 +01:00
40 changed files with 1487 additions and 3122 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: dmonad
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -17,9 +17,11 @@ suited for even large documents.
* Discuss: [https://discuss.yjs.dev](https://discuss.yjs.dev)
* Benchmarks:
[https://github.com/dmonad/crdt-benchmarks](https://github.com/dmonad/crdt-benchmarks)
* Podcast [**"Yjs Deep Dive into real time collaborative editing solutions":**](https://www.tag1consulting.com/blog/deep-dive-real-time-collaborative-editing-solutions-tagteamtalk-001-0)
* Podcast [**"Google Docs-style editing in Gutenberg with the YJS framework":**](https://publishpress.com/blog/yjs/)
:warning: This is the documentation for v13 (still in alpha). For the stable v12
release checkout the [v12 docs](./README.v12.md) :warning:
:construction_worker_woman: If you are looking for professional support to build
collaborative or distributed applications ping us at <yjs@tag1consulting.com>.
## Table of Contents
@@ -70,18 +72,25 @@ manage all that for you and are the perfect starting point for your
collaborative app.
<dl>
<dt><a href="http://github.com/yjs/y-webrtc">y-webrtc</a></dt>
<dd>
Propagates document updates peer-to-peer using WebRTC. The peers exchange
signaling data over signaling servers. Publically available signaling servers
are available. Communication over the signaling servers can be encrypted by
providing a shared secret, keeping the connection information and the shared
document private.
</dd>
<dt><a href="http://github.com/yjs/y-websocket">y-websocket</a></dt>
<dd>
A module that contains a simple websocket backend and a websocket client that
connects to that backend. The backend can be extended to persist updates in a
leveldb database.
</dd>
<dt><a href="http://github.com/yjs/y-mesh">y-mesh</a></dt>
<dt><a href="http://github.com/yjs/y-indexeddb">y-indexeddb</a></dt>
<dd>
[WIP] Creates a connected graph of webrtc connections with a high
<a href="https://en.wikipedia.org/wiki/Strength_of_a_graph">strength</a>. It
requires a signalling server that connects a client to the first peer. But after
that the network manages itself. It is well suited for large and small networks.
Efficiently persists document updates to the browsers indexeddb database.
The document is immediately available and only diffs need to be synced through the
network provider.
</dd>
<dt><a href="http://github.com/yjs/y-dat">y-dat</a></dt>
<dd>
@@ -97,7 +106,7 @@ hypercores and y-dat listens to changes and applies them to the Yjs document.
Install Yjs and a provider with your favorite package manager:
```sh
npm i yjs@13.0.0-97 y-websocket@1.0.0-6
npm i yjs y-websocket
```
Start the y-websocket server:
@@ -671,7 +680,7 @@ undo- or the redo-stack.
<code>
on('stack-item-popped', { stackItem: { meta: Map&lt;any,any&gt; }, type: 'undo'
| 'redo' })
</code>
</code>
</b>
<dd>
Register an event that is called when a <code>StackItem</code> is popped from

View File

@@ -1,305 +0,0 @@
# ![Yjs](https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png)
Yjs is a framework for offline-first p2p shared editing on structured data like
text, richtext, json, or XML. It is fairly easy to get started, as Yjs hides
most of the complexity of concurrent editing. For additional information, demos,
and tutorials visit [y-js.org](http://y-js.org/).
:warning: Checkout the [v13 docs](./README.md) for the upcoming release :warning:
### Extensions
Yjs only knows how to resolve conflicts on shared data. You have to choose a ..
* *Connector* - a communication protocol that propagates changes to the clients
* *Database* - a database to store your changes
* one or more *Types* - that represent the shared data
Connectors, Databases, and Types are available as modules that extend Yjs. Here
is a list of the modules we know of:
##### Connectors
|Name | Description |
|----------------|-----------------------------------|
|[webrtc](https://github.com/y-js/y-webrtc) | Propagate updates Browser2Browser via WebRTC|
|[websockets](https://github.com/y-js/y-websockets-client) | Set up [a central server](https://github.com/y-js/y-websockets-client), and connect to it via websockets |
|[xmpp](https://github.com/y-js/y-xmpp) | Propagate updates in a XMPP multi-user-chat room ([XEP-0045](http://xmpp.org/extensions/xep-0045.html))|
|[ipfs](https://github.com/ipfs-labs/y-ipfs-connector) | Connector for the [Interplanetary File System](https://ipfs.io/)!|
|[test](https://github.com/y-js/y-test) | A Connector for testing purposes. It is designed to simulate delays that happen in worst case scenarios|
##### Database adapters
|Name | Description |
|----------------|-----------------------------------|
|[memory](https://github.com/y-js/y-memory) | In-memory storage. |
|[indexeddb](https://github.com/y-js/y-indexeddb) | Offline storage for the browser |
|[leveldb](https://github.com/y-js/y-leveldb) | Persistent storage for node apps |
##### Types
| Name | Description |
|----------|-------------------|
|[map](https://github.com/y-js/y-map) | A shared Map implementation. Maps from text to any stringify-able object |
|[array](https://github.com/y-js/y-array) | A shared Array implementation |
|[xml](https://github.com/y-js/y-xml) | An implementation of the DOM. You can create a two way binding to Browser DOM objects |
|[text](https://github.com/y-js/y-text) | Collaborate on text. Supports two way binding to the [Ace Editor](https://ace.c9.io), [CodeMirror](https://codemirror.net/), [Monaco](https://github.com/Microsoft/monaco-editor), textareas, input elements, and HTML elements (e.g. <*h1*>, or <*p*>) |
|[richtext](https://github.com/y-js/y-richtext) | Collaborate on rich text. Supports two way binding to the [Quill Rich Text Editor](http://quilljs.com/)|
##### Other
| Name | Description |
|-----------|-------------------|
|[y-element](http://y-js.org/y-element/) | Yjs Polymer Element |
## Use it!
Install Yjs, and its modules with [bower](http://bower.io/), or
[npm](https://www.npmjs.org/package/yjs).
### Bower
```
bower install --save yjs y-array % add all y-* modules you want to use
```
You only need to include the `y.js` file. Yjs is able to automatically require
missing modules.
```
<script src="./bower_components/yjs/y.js"></script>
```
### CDN
```
<script src="https://cdn.jsdelivr.net/npm/yjs@12/dist/y.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-array@10/dist/y-array.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-websockets-client@8/dist/y-websockets-client.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-memory@8/dist/y-memory.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-map@10/dist/y-map.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-text@9/dist/y-text.js"></script>
// ..
// do the same for all modules you want to use
```
### Npm
```
npm install --save yjs % add all y-* modules you want to use
```
If you don't include via script tag, you have to explicitly include all modules!
(Same goes for other module systems)
```
var Y = require('yjs')
require('y-array')(Y) // add the y-array type to Yjs
require('y-websockets-client')(Y)
require('y-memory')(Y)
require('y-map')(Y)
require('y-text')(Y)
// ..
// do the same for all modules you want to use
```
### ES6 Syntax
```
import Y from 'yjs'
import yArray from 'y-array'
import yWebsocketsClient from 'y-webrtc'
import yMemory from 'y-memory'
import yMap from 'y-map'
import yText from 'y-text'
// ..
Y.extend(yArray, yWebsocketsClient, yMemory, yArray, yMap, yText /*, .. */)
```
# Text editing example
Install dependencies
```
bower i yjs y-memory y-webrtc y-array y-text
```
Here is a simple example of a shared textarea
```HTML
<!DOCTYPE html>
<html>
<body>
<script src="./bower_components/yjs/y.js"></script>
<!-- Yjs automatically includes all missing dependencies (browser only) -->
<script>
Y({
db: {
name: 'memory' // use memory database adapter.
// name: 'indexeddb' // use indexeddb database adapter instead for offline apps
},
connector: {
name: 'webrtc', // use webrtc connector
// name: 'websockets-client'
// name: 'xmpp'
room: 'my-room' // clients connecting to the same room share data
},
sourceDir: './bower_components', // location of the y-* modules (browser only)
share: {
textarea: 'Text' // y.share.textarea is of type y-text
}
}).then(function (y) {
// The Yjs instance `y` is available
// y.share.* contains the shared types
// Bind `y.share.textarea` to `<textarea/>`
y.share.textarea.bind(document.querySelector('textarea'))
})
</script>
<textarea></textarea>
</body>
</html>
```
## Get Help & Give Help
There are some friendly people on [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/y-js/yjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) who are eager to help, and answer questions. Please join!
Report _any_ issues to the
[Github issue page](https://github.com/y-js/yjs/issues)! I try to fix them very
soon, if possible.
# API
### Y(options)
* Y.extend(module1, module2, ..)
* Add extensions to Y
* `Y.extend(require('y-webrtc'))` has the same semantics as
`require('y-webrtc')(Y)`
* options.db
* Will be forwarded to the database adapter. Specify the database adaper on
`options.db.name`.
* Have a look at the used database adapter repository to see all available
options.
* options.connector
* Will be forwarded to the connector adapter. Specify the connector adaper on
`options.connector.name`.
* All our connectors implement a `room` property. Clients that specify the
same room share the same data.
* All of our connectors specify an `url` property that defines the connection
endpoint of the used connector.
* All of our connectors also have a default connection endpoint that you can
use for development.
* Set `options.connector.generateUserId = true` in order to genenerate a
userid, instead of receiving one from the server. This way the `Y(..)` is
immediately going to be resolved, without waiting for any confirmation from
the server. Use with caution.
* Have a look at the used connector repository to see all available options.
* *Only if you know what you are doing:* Set
`options.connector.preferUntransformed = true` in order receive the shared
data untransformed. This is very efficient as the database content is simply
copied to this client. This does only work if this client receives content
from only one client.
* options.sourceDir (browser only)
* Path where all y-* modules are stored
* Defaults to `/bower_components`
* Not required when running on `nodejs` / `iojs`
* When using nodejs you need to manually extend Yjs:
```
var Y = require('yjs')
// you have to require a db, connector, and *all* types you use!
require('y-memory')(Y)
require('y-webrtc')(Y)
require('y-map')(Y)
// ..
```
* options.share
* Specify on `options.share[arbitraryName]` types that are shared among all
users.
* E.g. Specify `options.share[arbitraryName] = 'Array'` to require y-array and
create an y-array type on `y.share[arbitraryName]`.
* If userA doesn't specify `options.share[arbitraryName]`, it won't be
available for userA.
* If userB specifies `options.share[arbitraryName]`, it still won't be
available for userA. But all the updates are send from userB to userA.
* In contrast to y-map, types on `y.share.*` cannot be overwritten or deleted.
Instead, they are merged among all users. This feature is only available on
`y.share.*`
* Weird behavior: It is supported that two users specify different types with
the same property name.
E.g. userA specifies `options.share.x = 'Array'`, and userB specifies
`options.share.x = 'Text'`. But they only share data if they specified the
same type with the same property name
* options.type (browser only)
* Array of modules that Yjs needs to require, before instantiating a shared
type.
* By default Yjs requires the specified database adapter, the specified
connector, and all modules that are used in `options.share.*`
* Put all types here that you intend to use, but are not used in y.share.*
### Instantiated Y object (y)
`Y(options)` returns a promise that is fulfilled when..
* All modules are loaded
* The specified database adapter is loaded
* The specified connector is loaded
* All types are included
* The connector is initialized, and a unique user id is set (received from the
server)
* Note: When using y-indexeddb, a retrieved user id is stored on `localStorage`
The promise returns an instance of Y. We denote it with a lower case `y`.
* y.share.*
* Instances of the types you specified on options.share.*
* y.share.* can only be defined once when you instantiate Y!
* y.connector is an instance of Y.AbstractConnector
* y.connector.onUserEvent(function (event) {..})
* Observe user events (event.action is either 'userLeft' or 'userJoined')
* y.connector.whenSynced(listener)
* `listener` is executed when y synced with at least one user.
* `listener` is not called when no other user is in the same room.
* y-websockets-client aways waits to sync with the server
* y.connector.disconnect()
* Force to disconnect this instance from the other instances
* y.connector.connect()
* Try to reconnect to the other instances (needs to be supported by the
connector)
* Not supported by y-xmpp
* y.close()
* Destroy this object.
* Destroys all types (they will throw weird errors if you still use them)
* Disconnects from the other instances (via connector)
* Returns a promise
* y.destroy()
* calls y.close()
* Removes all data from the database
* Returns a promise
* y.db.stopGarbageCollector()
* Stop the garbage collector. Call y.db.garbageCollect() to continue garbage
collection
* y.db.gc :: Boolean
* Whether gc is turned on
* y.db.gcTimeout :: Number (defaults to 50000 ms)
* Time interval between two garbage collect cycles
* It is required that all instances exchanged all messages after two garbage
collect cycles (after 100000 ms per default)
* y.db.userId :: String
* The used user id for this client. **Never overwrite this**
### Logging
Yjs uses [debug](https://github.com/visionmedia/debug) for logging. The flag
`y*` enables logging for all y-* components. You can selectively remove
components you are not interested in: E.g. The flag `y*,-y:connector-message`
will not log the long `y:connector-message` messages.
##### Enable logging in Node.js
```sh
DEBUG=y* node app.js
```
Remove the colors in order to log to a file:
```sh
DEBUG_COLORS=0 DEBUG=y* node app.js > log
```
##### Enable logging in the browser
```js
localStorage.debug = 'y*'
```
## License
Yjs is licensed under the [MIT License](./LICENSE).

3647
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,23 @@
{
"name": "yjs",
"version": "13.0.0-102",
"version": "13.0.4",
"description": "Shared Editing Library",
"main": "./dist/yjs.js",
"main": "./dist/yjs.cjs",
"module": "./dist/yjs.mjs",
"types": "./dist/src/index.d.ts",
"sideEffects": false,
"scripts": {
"test": "npm run dist && PRODUCTION=1 node ./dist/tests.js --repitition-time 50 --production",
"test-exhaustive": "npm run lint && npm run dist && node ./dist/tests.js --repitition-time 10000",
"dist": "rm -rf dist && rollup -c",
"test": "npm run dist && node ./dist/tests.cjs --repitition-time 50",
"test-extensive": "npm run lint && npm run dist && node ./dist/tests.cjs --production --repitition-time 10000",
"dist": "rm -rf dist && 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 && serve ./docs/",
"preversion": "npm run lint && PRODUCTION=1 npm run dist && npm run docs && node ./dist/tests.js --repitition-time 1000",
"postversion": "git push && git push --tags",
"debug": "concurrently 'live-server --port=3443 --entry-file=test.html' 'npm run watch'",
"trace-deopt": "clear && rollup -c && node --trace-deopt dist/test.js",
"trace-opt": "clear && rollup -c && node --trace-opt dist/test.js"
"serve-docs": "npm run docs && http-server ./docs/",
"preversion": "npm run lint && PRODUCTION=1 npm run dist && npm run docs && node ./dist/tests.cjs --repitition-time 1000",
"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"
},
"files": [
"dist/*",
@@ -41,7 +41,12 @@
"url": "https://github.com/yjs/yjs.git"
},
"keywords": [
"crdt"
"Yjs",
"CRDT",
"offline",
"shared editing",
"concurrency",
"collaboration"
],
"author": "Kevin Jahns",
"email": "kevin.jahns@protonmail.com",
@@ -51,18 +56,20 @@
},
"homepage": "https://yjs.dev",
"dependencies": {
"lib0": "^0.1.1"
"lib0": "^0.2.12"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^11.0.1",
"@rollup/plugin-node-resolve": "^7.0.0",
"concurrently": "^3.6.1",
"http-server": "^0.12.1",
"jsdoc": "^3.6.3",
"live-server": "^1.2.1",
"rollup": "^1.20.3",
"markdownlint-cli": "^0.19.0",
"rollup": "^1.30.0",
"rollup-cli": "^1.0.9",
"rollup-plugin-node-resolve": "^4.2.4",
"standard": "^11.0.1",
"standard": "^14.0.0",
"tui-jsdoc-template": "^1.2.2",
"typescript": "^3.6.2",
"y-protocols": "0.0.6"
"typescript": "^3.7.5",
"y-protocols": "^0.2.0"
}
}

View File

@@ -1,4 +1,5 @@
import nodeResolve from 'rollup-plugin-node-resolve'
import nodeResolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
const localImports = process.env.LOCALIMPORTS
@@ -37,23 +38,27 @@ const debugResolve = {
export default [{
input: './src/index.js',
output: [{
output: {
name: 'Y',
file: 'dist/yjs.js',
file: 'dist/yjs.cjs',
format: 'cjs',
sourcemap: true,
paths: path => {
if (/^lib0\//.test(path)) {
return `lib0/dist/${path.slice(5)}`
return `lib0/dist/${path.slice(5, -3)}.cjs`
}
return path
}
}, {
},
external: id => /^lib0\//.test(id)
}, {
input: './src/index.js',
output: {
name: 'Y',
file: 'dist/yjs.mjs',
format: 'es',
format: 'esm',
sourcemap: true
}],
},
external: id => /^lib0\//.test(id)
}, {
input: './tests/index.js',
@@ -68,6 +73,24 @@ export default [{
nodeResolve({
sourcemap: true,
mainFields: ['module', 'browser', 'main']
})
}),
commonjs()
]
}, {
input: './tests/index.js',
output: {
name: 'test',
file: 'dist/tests.cjs',
format: 'cjs',
sourcemap: true
},
plugins: [
debugResolve,
nodeResolve({
sourcemap: true,
mainFields: ['module', 'main']
}),
commonjs()
],
external: ['isomorphic.js']
}]

View File

@@ -53,6 +53,9 @@ export {
decodeSnapshot,
encodeSnapshot,
isDeleted,
isParentOf,
equalSnapshots,
PermanentUserData // @TODO experimental
PermanentUserData, // @TODO experimental
tryGc,
transact
} from './internals.js'

View File

@@ -24,6 +24,7 @@ export class AbstractStruct {
this.length = length
this.deleted = false
}
/**
* Merge this struct with the item to the right.
* This method is already assuming that `this.id.clock + this.length === this.id.clock`.
@@ -34,6 +35,7 @@ export class AbstractStruct {
mergeWith (right) {
return false
}
/**
* @param {encoding.Encoder} encoder The encoder to write data to.
* @param {number} offset
@@ -43,6 +45,7 @@ export class AbstractStruct {
write (encoder, offset, encodingRef) {
throw error.methodUnimplemented()
}
/**
* @param {Transaction} transaction
*/
@@ -69,6 +72,7 @@ export class AbstractStructRef {
*/
this.id = id
}
/**
* @param {Transaction} transaction
* @return {Array<ID|null>}
@@ -76,6 +80,7 @@ export class AbstractStructRef {
getMissing (transaction) {
return this._missing
}
/**
* @param {Transaction} transaction
* @param {StructStore} store

View File

@@ -18,30 +18,35 @@ export class ContentAny {
*/
this.arr = arr
}
/**
* @return {number}
*/
getLength () {
return this.arr.length
}
/**
* @return {Array<any>}
*/
getContent () {
return this.arr
}
/**
* @return {boolean}
*/
isCountable () {
return true
}
/**
* @return {ContentAny}
*/
copy () {
return new ContentAny(this.arr)
}
/**
* @param {number} offset
* @return {ContentAny}
@@ -51,6 +56,7 @@ export class ContentAny {
this.arr = this.arr.slice(0, offset)
return right
}
/**
* @param {ContentAny} right
* @return {boolean}
@@ -59,6 +65,7 @@ export class ContentAny {
this.arr = this.arr.concat(right.arr)
return true
}
/**
* @param {Transaction} transaction
* @param {Item} item
@@ -84,6 +91,7 @@ export class ContentAny {
encoding.writeAny(encoder, c)
}
}
/**
* @return {number}
*/

View File

@@ -17,30 +17,35 @@ export class ContentBinary {
constructor (content) {
this.content = content
}
/**
* @return {number}
*/
getLength () {
return 1
}
/**
* @return {Array<any>}
*/
getContent () {
return [this.content]
}
/**
* @return {boolean}
*/
isCountable () {
return true
}
/**
* @return {ContentBinary}
*/
copy () {
return new ContentBinary(this.content)
}
/**
* @param {number} offset
* @return {ContentBinary}
@@ -48,6 +53,7 @@ export class ContentBinary {
splice (offset) {
throw error.methodUnimplemented()
}
/**
* @param {ContentBinary} right
* @return {boolean}
@@ -55,6 +61,7 @@ export class ContentBinary {
mergeWith (right) {
return false
}
/**
* @param {Transaction} transaction
* @param {Item} item
@@ -75,6 +82,7 @@ export class ContentBinary {
write (encoder, offset) {
encoding.writeVarUint8Array(encoder, this.content)
}
/**
* @return {number}
*/

View File

@@ -17,30 +17,35 @@ export class ContentDeleted {
constructor (len) {
this.len = len
}
/**
* @return {number}
*/
getLength () {
return this.len
}
/**
* @return {Array<any>}
*/
getContent () {
return []
}
/**
* @return {boolean}
*/
isCountable () {
return false
}
/**
* @return {ContentDeleted}
*/
copy () {
return new ContentDeleted(this.len)
}
/**
* @param {number} offset
* @return {ContentDeleted}
@@ -50,6 +55,7 @@ export class ContentDeleted {
this.len = offset
return right
}
/**
* @param {ContentDeleted} right
* @return {boolean}
@@ -58,6 +64,7 @@ export class ContentDeleted {
this.len += right.len
return true
}
/**
* @param {Transaction} transaction
* @param {Item} item
@@ -66,6 +73,7 @@ export class ContentDeleted {
addToDeleteSet(transaction.deleteSet, item.id, this.len)
item.deleted = true
}
/**
* @param {Transaction} transaction
*/
@@ -81,6 +89,7 @@ export class ContentDeleted {
write (encoder, offset) {
encoding.writeVarUint(encoder, this.len - offset)
}
/**
* @return {number}
*/

View File

@@ -17,30 +17,35 @@ export class ContentEmbed {
constructor (embed) {
this.embed = embed
}
/**
* @return {number}
*/
getLength () {
return 1
}
/**
* @return {Array<any>}
*/
getContent () {
return [this.embed]
}
/**
* @return {boolean}
*/
isCountable () {
return true
}
/**
* @return {ContentEmbed}
*/
copy () {
return new ContentEmbed(this.embed)
}
/**
* @param {number} offset
* @return {ContentEmbed}
@@ -48,6 +53,7 @@ export class ContentEmbed {
splice (offset) {
throw error.methodUnimplemented()
}
/**
* @param {ContentEmbed} right
* @return {boolean}
@@ -55,6 +61,7 @@ export class ContentEmbed {
mergeWith (right) {
return false
}
/**
* @param {Transaction} transaction
* @param {Item} item
@@ -75,6 +82,7 @@ export class ContentEmbed {
write (encoder, offset) {
encoding.writeVarString(encoder, JSON.stringify(this.embed))
}
/**
* @return {number}
*/

View File

@@ -19,30 +19,35 @@ export class ContentFormat {
this.key = key
this.value = value
}
/**
* @return {number}
*/
getLength () {
return 1
}
/**
* @return {Array<any>}
*/
getContent () {
return []
}
/**
* @return {boolean}
*/
isCountable () {
return false
}
/**
* @return {ContentFormat}
*/
copy () {
return new ContentFormat(this.key, this.value)
}
/**
* @param {number} offset
* @return {ContentFormat}
@@ -50,6 +55,7 @@ export class ContentFormat {
splice (offset) {
throw error.methodUnimplemented()
}
/**
* @param {ContentFormat} right
* @return {boolean}
@@ -57,6 +63,7 @@ export class ContentFormat {
mergeWith (right) {
return false
}
/**
* @param {Transaction} transaction
* @param {Item} item
@@ -78,6 +85,7 @@ export class ContentFormat {
encoding.writeVarString(encoder, this.key)
encoding.writeVarString(encoder, JSON.stringify(this.value))
}
/**
* @return {number}
*/

View File

@@ -18,30 +18,35 @@ export class ContentJSON {
*/
this.arr = arr
}
/**
* @return {number}
*/
getLength () {
return this.arr.length
}
/**
* @return {Array<any>}
*/
getContent () {
return this.arr
}
/**
* @return {boolean}
*/
isCountable () {
return true
}
/**
* @return {ContentJSON}
*/
copy () {
return new ContentJSON(this.arr)
}
/**
* @param {number} offset
* @return {ContentJSON}
@@ -51,6 +56,7 @@ export class ContentJSON {
this.arr = this.arr.slice(0, offset)
return right
}
/**
* @param {ContentJSON} right
* @return {boolean}
@@ -59,6 +65,7 @@ export class ContentJSON {
this.arr = this.arr.concat(right.arr)
return true
}
/**
* @param {Transaction} transaction
* @param {Item} item
@@ -84,6 +91,7 @@ export class ContentJSON {
encoding.writeVarString(encoder, c === undefined ? 'undefined' : JSON.stringify(c))
}
}
/**
* @return {number}
*/

View File

@@ -18,30 +18,35 @@ export class ContentString {
*/
this.str = str
}
/**
* @return {number}
*/
getLength () {
return this.str.length
}
/**
* @return {Array<any>}
*/
getContent () {
return this.str.split('')
}
/**
* @return {boolean}
*/
isCountable () {
return true
}
/**
* @return {ContentString}
*/
copy () {
return new ContentString(this.str)
}
/**
* @param {number} offset
* @return {ContentString}
@@ -51,6 +56,7 @@ export class ContentString {
this.str = this.str.slice(0, offset)
return right
}
/**
* @param {ContentString} right
* @return {boolean}
@@ -59,6 +65,7 @@ export class ContentString {
this.str += right.str
return true
}
/**
* @param {Transaction} transaction
* @param {Item} item
@@ -79,6 +86,7 @@ export class ContentString {
write (encoder, offset) {
encoding.writeVarString(encoder, offset === 0 ? this.str : this.str.slice(offset))
}
/**
* @return {number}
*/

View File

@@ -49,30 +49,35 @@ export class ContentType {
*/
this.type = type
}
/**
* @return {number}
*/
getLength () {
return 1
}
/**
* @return {Array<any>}
*/
getContent () {
return [this.type]
}
/**
* @return {boolean}
*/
isCountable () {
return true
}
/**
* @return {ContentType}
*/
copy () {
return new ContentType(this.type._copy())
}
/**
* @param {number} offset
* @return {ContentType}
@@ -80,6 +85,7 @@ export class ContentType {
splice (offset) {
throw error.methodUnimplemented()
}
/**
* @param {ContentType} right
* @return {boolean}
@@ -87,6 +93,7 @@ export class ContentType {
mergeWith (right) {
return false
}
/**
* @param {Transaction} transaction
* @param {Item} item
@@ -94,6 +101,7 @@ export class ContentType {
integrate (transaction, item) {
this.type._integrate(transaction.doc, item)
}
/**
* @param {Transaction} transaction
*/
@@ -121,6 +129,7 @@ export class ContentType {
})
transaction.changed.delete(this.type)
}
/**
* @param {StructStore} store
*/
@@ -139,6 +148,7 @@ export class ContentType {
})
this.type._map = new Map()
}
/**
* @param {encoding.Encoder} encoder
* @param {number} offset
@@ -146,6 +156,7 @@ export class ContentType {
write (encoder, offset) {
this.type._write(encoder)
}
/**
* @return {number}
*/

View File

@@ -69,6 +69,7 @@ export class GCRef extends AbstractStructRef {
*/
this.length = decoding.readVarUint(decoder)
}
/**
* @param {Transaction} transaction
* @param {StructStore} store

View File

@@ -55,7 +55,7 @@ export const followRedone = (store, id) => {
item = getItem(store, nextID)
diff = nextID.clock - item.id.clock
nextID = item.redone
} while (nextID !== null)
} while (nextID !== null && item instanceof Item)
return {
item, diff
}
@@ -68,10 +68,11 @@ export const followRedone = (store, id) => {
* sending it to other peers
*
* @param {Item|null} item
* @param {boolean} keep
*/
export const keepItem = item => {
while (item !== null && !item.keep) {
item.keep = true
export const keepItem = (item, keep) => {
while (item !== null && item.keep !== keep) {
item.keep = keep
item = item.parent._item
}
}
@@ -220,7 +221,7 @@ export const redoItem = (transaction, item, redoitems) => {
item.content.copy()
)
item.redone = redoneItem.id
keepItem(redoneItem)
keepItem(redoneItem, true)
redoneItem.integrate(transaction)
return redoneItem
}
@@ -430,6 +431,7 @@ export class Item extends AbstractStruct {
get lastId () {
return createID(this.id.client, this.id.clock + this.length - 1)
}
/**
* Try to merge two items
*
@@ -578,12 +580,14 @@ export class AbstractContent {
getLength () {
throw error.methodUnimplemented()
}
/**
* @return {Array<any>}
*/
getContent () {
throw error.methodUnimplemented()
}
/**
* Should return false if this Item is some kind of meta information
* (e.g. format information).
@@ -596,12 +600,14 @@ export class AbstractContent {
isCountable () {
throw error.methodUnimplemented()
}
/**
* @return {AbstractContent}
*/
copy () {
throw error.methodUnimplemented()
}
/**
* @param {number} offset
* @return {AbstractContent}
@@ -609,6 +615,7 @@ export class AbstractContent {
splice (offset) {
throw error.methodUnimplemented()
}
/**
* @param {AbstractContent} right
* @return {boolean}
@@ -616,6 +623,7 @@ export class AbstractContent {
mergeWith (right) {
throw error.methodUnimplemented()
}
/**
* @param {Transaction} transaction
* @param {Item} item
@@ -623,18 +631,21 @@ export class AbstractContent {
integrate (transaction, item) {
throw error.methodUnimplemented()
}
/**
* @param {Transaction} transaction
*/
delete (transaction) {
throw error.methodUnimplemented()
}
/**
* @param {StructStore} store
*/
gc (store) {
throw error.methodUnimplemented()
}
/**
* @param {encoding.Encoder} encoder
* @param {number} offset
@@ -642,6 +653,7 @@ export class AbstractContent {
write (encoder, offset) {
throw error.methodUnimplemented()
}
/**
* @return {number}
*/
@@ -709,6 +721,7 @@ export class ItemRef extends AbstractStructRef {
this.content = readItemContent(decoder, info)
this.length = this.content.getLength()
}
/**
* @param {Transaction} transaction
* @param {StructStore} store

View File

@@ -171,7 +171,7 @@ export class AbstractType {
/**
* @abstract
* @return {Object | Array | number | string}
* @return {any}
*/
toJSON () {}
}
@@ -370,7 +370,7 @@ export const typeListInsertGenericsAfter = (transaction, parent, referenceItem,
let left = referenceItem
const right = referenceItem === null ? parent._start : referenceItem.right
/**
* @type {Array<Object|Array|number>}
* @type {Array<Object|Array<any>|number>}
*/
let jsonContent = []
const packJsonContent = () => {
@@ -515,7 +515,7 @@ export const typeMapSet = (transaction, parent, key, value) => {
content = new ContentAny([value])
break
case Uint8Array:
content = new ContentBinary(value)
content = new ContentBinary(/** @type {Uint8Array} */ (value))
break
default:
if (value instanceof AbstractType) {
@@ -552,7 +552,7 @@ export const typeMapGetAll = (parent) => {
/**
* @type {Object<string,any>}
*/
let res = {}
const res = {}
for (const [key, value] of parent._map) {
if (!value.deleted) {
res[key] = value.content.getContent()[value.length - 1]

View File

@@ -51,6 +51,7 @@ export class YArray extends AbstractType {
*/
this._prelimContent = []
}
/**
* Integrate this type into the Yjs instance.
*
@@ -65,7 +66,7 @@ export class YArray extends AbstractType {
*/
_integrate (y, item) {
super._integrate(y, item)
this.insert(0, /** @type {Array} */ (this._prelimContent))
this.insert(0, /** @type {Array<any>} */ (this._prelimContent))
this._prelimContent = null
}
@@ -76,6 +77,7 @@ export class YArray extends AbstractType {
get length () {
return this._prelimContent === null ? this._length : this._prelimContent.length
}
/**
* Creates YArrayEvent and calls observers.
*
@@ -110,7 +112,7 @@ export class YArray extends AbstractType {
typeListInsertGenerics(transaction, this, index, content)
})
} else {
/** @type {Array} */ (this._prelimContent).splice(index, 0, ...content)
/** @type {Array<any>} */ (this._prelimContent).splice(index, 0, ...content)
}
}
@@ -135,7 +137,7 @@ export class YArray extends AbstractType {
typeListDelete(transaction, this, index, length)
})
} else {
/** @type {Array} */ (this._prelimContent).splice(index, length)
/** @type {Array<any>} */ (this._prelimContent).splice(index, length)
}
}

View File

@@ -53,6 +53,7 @@ export class YMap extends AbstractType {
*/
this._prelimContent = new Map()
}
/**
* Integrate this type into the Yjs instance.
*
@@ -67,7 +68,7 @@ export class YMap extends AbstractType {
*/
_integrate (y, item) {
super._integrate(y, item)
for (let [key, value] of /** @type {Map<string, any>} */ (this._prelimContent)) {
for (const [key, value] of /** @type {Map<string, any>} */ (this._prelimContent)) {
this.set(key, value)
}
this._prelimContent = null
@@ -99,7 +100,7 @@ export class YMap extends AbstractType {
* @type {Object<string,T>}
*/
const map = {}
for (let [key, item] of this._map) {
for (const [key, item] of this._map) {
if (!item.deleted) {
const v = item.content.getContent()[item.length - 1]
map[key] = v instanceof AbstractType ? v.toJSON() : v
@@ -145,7 +146,7 @@ export class YMap extends AbstractType {
* @type {Object<string,T>}
*/
const map = {}
for (let [key, item] of this._map) {
for (const [key, item] of this._map) {
if (!item.deleted) {
f(item.content.getContent()[item.length - 1], key, this)
}

View File

@@ -112,10 +112,9 @@ const findNextPosition = (transaction, currentAttributes, left, right, count) =>
* @function
*/
const findPosition = (transaction, parent, index) => {
let currentAttributes = new Map()
let left = null
let right = parent._start
return findNextPosition(transaction, currentAttributes, left, right, index)
const currentAttributes = new Map()
const right = parent._start
return findNextPosition(transaction, currentAttributes, null, right, index)
}
/**
@@ -147,7 +146,7 @@ const insertNegatedAttributes = (transaction, parent, left, right, negatedAttrib
left = right
right = right.right
}
for (let [key, val] of negatedAttributes) {
for (const [key, val] of negatedAttributes) {
left = new Item(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, new ContentFormat(key, val))
left.integrate(transaction)
}
@@ -214,7 +213,7 @@ const minimizeAttributeChanges = (left, right, currentAttributes, attributes) =>
const insertAttributes = (transaction, parent, left, right, currentAttributes, attributes) => {
const negatedAttributes = new Map()
// insert format-start items
for (let key in attributes) {
for (const key in attributes) {
const val = attributes[key]
const currentVal = currentAttributes.get(key) || null
if (!equalAttrs(currentVal, val)) {
@@ -241,7 +240,7 @@ const insertAttributes = (transaction, parent, left, right, currentAttributes, a
* @function
**/
const insertText = (transaction, parent, left, right, currentAttributes, text, attributes) => {
for (let [key] of currentAttributes) {
for (const [key] of currentAttributes) {
if (attributes[key] === undefined) {
attributes[key] = null
}
@@ -281,7 +280,7 @@ const formatText = (transaction, parent, left, right, currentAttributes, length,
while (length > 0 && right !== null) {
if (!right.deleted) {
switch (right.content.constructor) {
case ContentFormat:
case ContentFormat: {
const { key, value } = /** @type {ContentFormat} */ (right.content)
const attr = attributes[key]
if (attr !== undefined) {
@@ -294,6 +293,7 @@ const formatText = (transaction, parent, left, right, currentAttributes, length,
}
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (right.content))
break
}
case ContentEmbed:
case ContentString:
if (length < right.length) {
@@ -405,6 +405,7 @@ export class YTextEvent extends YEvent {
*/
this._delta = null
}
/**
* Compute the changes in the delta format.
* A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document.
@@ -429,7 +430,10 @@ export class YTextEvent extends YEvent {
/**
* @type {Object<string,any>}
*/
let attributes = {} // counts added or removed new attributes for retain
const attributes = {} // counts added or removed new attributes for retain
/**
* @type {string|object}
*/
let insert = ''
let retain = 0
let deleteLen = 0
@@ -448,7 +452,7 @@ export class YTextEvent extends YEvent {
op = { insert }
if (currentAttributes.size > 0) {
op.attributes = {}
for (let [key, value] of currentAttributes) {
for (const [key, value] of currentAttributes) {
if (value !== null) {
op.attributes[key] = value
}
@@ -460,7 +464,7 @@ export class YTextEvent extends YEvent {
op = { retain }
if (Object.keys(attributes).length > 0) {
op.attributes = {}
for (let key in attributes) {
for (const key in attributes) {
op.attributes[key] = attributes[key]
}
}
@@ -518,7 +522,7 @@ export class YTextEvent extends YEvent {
retain += item.length
}
break
case ContentFormat:
case ContentFormat: {
const { key, value } = /** @type {ContentFormat} */ (item.content)
if (this.adds(item)) {
if (!this.deletes(item)) {
@@ -570,12 +574,13 @@ export class YTextEvent extends YEvent {
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (item.content))
}
break
}
}
item = item.right
}
addOp()
while (delta.length > 0) {
let lastOp = delta[delta.length - 1]
const lastOp = delta[delta.length - 1]
if (lastOp.retain !== undefined && lastOp.attributes === undefined) {
// retain delta's if they don't assign attributes
delta.pop()
@@ -612,6 +617,11 @@ export class YText extends AbstractType {
this._pending = string !== undefined ? [() => this.insert(0, string)] : []
}
/**
* Number of characters of this text type.
*
* @type {number}
*/
get length () {
return this._length
}
@@ -668,6 +678,16 @@ export class YText extends AbstractType {
return str
}
/**
* Returns the unformatted string representation of this YText type.
*
* @return {string}
* @public
*/
toJSON () {
return this.toString()
}
/**
* Apply a {@link Delta} on this shared YText type.
*
@@ -734,7 +754,7 @@ export class YText extends AbstractType {
*/
const attributes = {}
let addAttributes = false
for (let [key, value] of currentAttributes) {
for (const [key, value] of currentAttributes) {
addAttributes = true
attributes[key] = value
}
@@ -761,7 +781,7 @@ export class YText extends AbstractType {
while (n !== null) {
if (isVisible(n, snapshot) || (prevSnapshot !== undefined && isVisible(n, prevSnapshot))) {
switch (n.content.constructor) {
case ContentString:
case ContentString: {
const cur = currentAttributes.get('ychange')
if (snapshot !== undefined && !isVisible(n, snapshot)) {
if (cur === undefined || cur.user !== n.id.client || cur.state !== 'removed') {
@@ -779,6 +799,7 @@ export class YText extends AbstractType {
}
str += /** @type {ContentString} */ (n.content).str
break
}
case ContentEmbed:
packStr()
ops.push({
@@ -820,6 +841,7 @@ export class YText extends AbstractType {
const { left, right, currentAttributes } = findPosition(transaction, this, index)
if (!attributes) {
attributes = {}
// @ts-ignore
currentAttributes.forEach((v, k) => { attributes[k] = v })
}
insertText(transaction, this, left, right, currentAttributes, text, attributes)
@@ -891,7 +913,7 @@ export class YText extends AbstractType {
const y = this.doc
if (y !== null) {
transact(y, transaction => {
let { left, right, currentAttributes } = findPosition(transaction, this, index)
const { left, right, currentAttributes } = findPosition(transaction, this, index)
if (right === null) {
return
}

View File

@@ -74,7 +74,7 @@ export class YXmlElement extends YXmlFragment {
const attrs = this.getAttributes()
const stringBuilder = []
const keys = []
for (let key in attrs) {
for (const key in attrs) {
keys.push(key)
}
keys.sort()
@@ -140,7 +140,7 @@ export class YXmlElement extends YXmlFragment {
* Returns all attribute name/value pairs in a JSON Object.
*
* @param {Snapshot} [snapshot]
* @return {Object} A JSON Object that describes the attributes.
* @return {Object<string, any>} A JSON Object that describes the attributes.
*
* @public
*/
@@ -165,8 +165,8 @@ export class YXmlElement extends YXmlFragment {
*/
toDOM (_document = document, hooks = {}, binding) {
const dom = _document.createElement(this.nodeName)
let attrs = this.getAttributes()
for (let key in attrs) {
const attrs = this.getAttributes()
for (const key in attrs) {
dom.setAttribute(key, attrs[key])
}
typeListForEach(this, yxml => {

View File

@@ -68,6 +68,7 @@ export class YXmlTreeWalker {
[Symbol.iterator] () {
return this
}
/**
* Get the next node.
*
@@ -130,6 +131,7 @@ export class YXmlFragment extends AbstractType {
*/
this._prelimContent = []
}
/**
* Integrate this type into the Yjs instance.
*
@@ -143,7 +145,7 @@ export class YXmlFragment extends AbstractType {
*/
_integrate (y, item) {
super._integrate(y, item)
this.insert(0, /** @type {Array} */ (this._prelimContent))
this.insert(0, /** @type {Array<any>} */ (this._prelimContent))
this._prelimContent = null
}
@@ -240,6 +242,9 @@ export class YXmlFragment extends AbstractType {
return typeListMap(this, xml => xml.toString()).join('')
}
/**
* @return {string}
*/
toJSON () {
return this.toString()
}
@@ -307,6 +312,7 @@ export class YXmlFragment extends AbstractType {
this._prelimContent.splice(index, length)
}
}
/**
* Transforms this YArray to a JavaScript Array.
*
@@ -315,6 +321,7 @@ export class YXmlFragment extends AbstractType {
toArray () {
return typeListToArray(this)
}
/**
* Transform the properties of this type to binary and write it to an
* BinaryEncoder.

View File

@@ -12,6 +12,7 @@ export class YXmlText extends YText {
_copy () {
return new YXmlText()
}
/**
* Creates a Dom Element that mirrors this YXmlText.
*
@@ -39,9 +40,9 @@ export class YXmlText extends YText {
// @ts-ignore
return this.toDelta().map(delta => {
const nestedNodes = []
for (let nodeName in delta.attributes) {
for (const nodeName in delta.attributes) {
const attrs = []
for (let key in delta.attributes[nodeName]) {
for (const key in delta.attributes[nodeName]) {
attrs.push({ key, value: delta.attributes[nodeName][key] })
}
// sort attributes to get a unique order
@@ -69,6 +70,9 @@ export class YXmlText extends YText {
}).join('')
}
/**
* @return {string}
*/
toJSON () {
return this.toString()
}

View File

@@ -23,11 +23,14 @@ import * as map from 'lib0/map.js'
*/
export class Doc extends Observable {
/**
* @param {Object|undefined} conf configuration
* @param {Object} conf configuration
* @param {boolean} [conf.gc] Disable garbage collection (default: gc=true)
* @param {function(Item):boolean} [conf.gcFilter] Will be called before an Item is garbage collected. Return false to keep the Item.
*/
constructor (conf = {}) {
constructor ({ gc = true, gcFilter = () => true } = {}) {
super()
this.gc = conf.gc || true
this.gc = gc
this.gcFilter = gcFilter
this.clientID = random.uint32()
/**
* @type {Map<string, AbstractType<YEvent>>}
@@ -45,6 +48,7 @@ export class Doc extends Observable {
*/
this._transactionCleanups = []
}
/**
* Changes that happen inside of a transaction are bundled. This means that
* the observer fires _after_ the transaction is finished and that all changes
@@ -59,6 +63,7 @@ export class Doc extends Observable {
transact (f, origin = null) {
transact(this, f, origin)
}
/**
* Define a shared data type.
*
@@ -117,6 +122,7 @@ export class Doc extends Observable {
}
return type
}
/**
* @template T
* @param {string} name
@@ -128,6 +134,7 @@ export class Doc extends Observable {
// @ts-ignore
return this.get(name, YArray)
}
/**
* @param {string} name
* @return {YText}
@@ -138,6 +145,7 @@ export class Doc extends Observable {
// @ts-ignore
return this.get(name, YText)
}
/**
* @param {string} name
* @return {YMap<any>}
@@ -148,6 +156,7 @@ export class Doc extends Observable {
// @ts-ignore
return this.get(name, YMap)
}
/**
* @param {string} name
* @return {YXmlFragment}
@@ -158,6 +167,7 @@ export class Doc extends Observable {
// @ts-ignore
return this.get(name, YXmlFragment)
}
/**
* Emit `destroy` event and unregister all event handlers.
*
@@ -167,6 +177,7 @@ export class Doc extends Observable {
this.emit('destroyed', [true])
super.destroy()
}
/**
* @param {string} eventName
* @param {function} f
@@ -174,6 +185,7 @@ export class Doc extends Observable {
on (eventName, f) {
super.on(eventName, f)
}
/**
* @param {string} eventName
* @param {function} f

View File

@@ -81,7 +81,7 @@ export const readID = decoder =>
*/
export const findRootTypeKey = type => {
// @ts-ignore _y must be defined, otherwise unexpected case
for (let [key, value] of type.doc.share) {
for (const [key, value] of type.doc.share) {
if (value === type) {
return key
}

View File

@@ -15,15 +15,14 @@ import { mergeDeleteSets, isDeleted } from './DeleteSet.js'
export class PermanentUserData {
/**
* @param {Doc} doc
* @param {string} key
* @param {YMap<any>} [storeType]
*/
constructor (doc, key = 'users') {
const users = doc.getMap(key)
constructor (doc, storeType = doc.getMap('users')) {
/**
* @type {Map<string,DeleteSet>}
*/
const dss = new Map()
this.yusers = users
this.yusers = storeType
this.doc = doc
/**
* Maps from clientid to userDescription
@@ -59,20 +58,23 @@ export class PermanentUserData {
ids.forEach(addClientId)
}
// observe users
users.observe(event => {
storeType.observe(event => {
event.keysChanged.forEach(userDescription =>
initUser(users.get(userDescription), userDescription)
initUser(storeType.get(userDescription), userDescription)
)
})
// add intial data
users.forEach(initUser)
storeType.forEach(initUser)
}
/**
* @param {Doc} doc
* @param {number} clientid
* @param {string} userDescription
* @param {Object} [conf]
* @param {function(Transaction, DeleteSet):boolean} [conf.filter]
*/
setUserMapping (doc, clientid, userDescription) {
setUserMapping (doc, clientid, userDescription, { filter = () => true } = {}) {
const users = this.yusers
let user = users.get(userDescription)
if (!user) {
@@ -83,35 +85,40 @@ export class PermanentUserData {
}
user.get('ids').push([clientid])
users.observe(event => {
const userOverwrite = users.get(userDescription)
if (userOverwrite !== user) {
// user was overwritten, port all data over to the next user object
// @todo Experiment with Y.Sets here
user = userOverwrite
// @todo iterate over old type
this.clients.forEach((_userDescription, clientid) => {
if (userDescription === _userDescription) {
user.get('ids').push([clientid])
setTimeout(() => {
const userOverwrite = users.get(userDescription)
if (userOverwrite !== user) {
// user was overwritten, port all data over to the next user object
// @todo Experiment with Y.Sets here
user = userOverwrite
// @todo iterate over old type
this.clients.forEach((_userDescription, clientid) => {
if (userDescription === _userDescription) {
user.get('ids').push([clientid])
}
})
const encoder = encoding.createEncoder()
const ds = this.dss.get(userDescription)
if (ds) {
writeDeleteSet(encoder, ds)
user.get('ds').push([encoding.toUint8Array(encoder)])
}
})
const encoder = encoding.createEncoder()
const ds = this.dss.get(userDescription)
if (ds) {
writeDeleteSet(encoder, ds)
user.get('ds').push([encoding.toUint8Array(encoder)])
}
}
}, 0)
})
doc.on('afterTransaction', /** @param {Transaction} transaction */ transaction => {
const yds = user.get('ds')
const ds = transaction.deleteSet
if (transaction.local && ds.clients.size > 0) {
const encoder = encoding.createEncoder()
writeDeleteSet(encoder, ds)
yds.push([encoding.toUint8Array(encoder)])
}
setTimeout(() => {
const yds = user.get('ds')
const ds = transaction.deleteSet
if (transaction.local && ds.clients.size > 0 && filter(transaction, ds)) {
const encoder = encoding.createEncoder()
writeDeleteSet(encoder, ds)
yds.push([encoding.toUint8Array(encoder)])
}
})
})
}
/**
* @param {number} clientid
* @return {any}
@@ -119,6 +126,7 @@ export class PermanentUserData {
getUserByClientId (clientid) {
return this.clients.get(clientid) || null
}
/**
* @param {ID} id
* @return {string | null}

View File

@@ -63,7 +63,7 @@ export class RelativePosition {
}
/**
* @param {Object} json
* @param {any} json
* @return {RelativePosition}
*
* @function

View File

@@ -185,7 +185,7 @@ export const getItem = (store, id) => find(store, id)
*/
export const findIndexCleanStart = (transaction, structs, clock) => {
const index = findIndexSS(structs, clock)
let struct = structs[index]
const struct = structs[index]
if (struct.id.clock < clock && struct instanceof Item) {
structs.splice(index + 1, 0, splitItem(transaction, struct, clock - struct.id.clock))
return index + 1

View File

@@ -10,7 +10,7 @@ import {
findIndexSS,
callEventHandlerListeners,
Item,
ID, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line
StructStore, ID, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line
} from '../internals.js'
import * as encoding from 'lib0/encoding.js'
@@ -73,7 +73,7 @@ export class Transaction {
/**
* All types that were directly modified (property added or child
* inserted/deleted). New types are not included in this Set.
* Maps from type to parentSubs (`item._parentSub = null` for YArray)
* Maps from type to parentSubs (`item.parentSub = null` for YArray)
* @type {Map<AbstractType<YEvent>,Set<String|null>>}
*/
this.changed = new Map()
@@ -145,6 +145,85 @@ export const addChangedTypeToTransaction = (transaction, type, parentSub) => {
}
}
/**
* @param {Array<AbstractStruct>} structs
* @param {number} pos
*/
const tryToMergeWithLeft = (structs, pos) => {
const left = structs[pos - 1]
const right = structs[pos]
if (left.deleted === right.deleted && left.constructor === right.constructor) {
if (left.mergeWith(right)) {
structs.splice(pos, 1)
if (right instanceof Item && right.parentSub !== null && right.parent._map.get(right.parentSub) === right) {
right.parent._map.set(right.parentSub, /** @type {Item} */ (left))
}
}
}
}
/**
* @param {DeleteSet} ds
* @param {StructStore} store
* @param {function(Item):boolean} gcFilter
*/
const tryGcDeleteSet = (ds, store, gcFilter) => {
for (const [client, deleteItems] of ds.clients) {
const structs = /** @type {Array<AbstractStruct>} */ (store.clients.get(client))
for (let di = deleteItems.length - 1; di >= 0; di--) {
const deleteItem = deleteItems[di]
const endDeleteItemClock = deleteItem.clock + deleteItem.len
for (
let si = findIndexSS(structs, deleteItem.clock), struct = structs[si];
si < structs.length && struct.id.clock < endDeleteItemClock;
struct = structs[++si]
) {
const struct = structs[si]
if (deleteItem.clock + deleteItem.len <= struct.id.clock) {
break
}
if (struct instanceof Item && struct.deleted && !struct.keep && gcFilter(struct)) {
struct.gc(store, false)
}
}
}
}
}
/**
* @param {DeleteSet} ds
* @param {StructStore} store
*/
const tryMergeDeleteSet = (ds, store) => {
// try to merge deleted / gc'd items
// merge from right to left for better efficiecy and so we don't miss any merge targets
for (const [client, deleteItems] of ds.clients) {
const structs = /** @type {Array<AbstractStruct>} */ (store.clients.get(client))
for (let di = deleteItems.length - 1; di >= 0; di--) {
const deleteItem = deleteItems[di]
// start with merging the item next to the last deleted item
const mostRightIndexToCheck = math.min(structs.length - 1, 1 + findIndexSS(structs, deleteItem.clock + deleteItem.len - 1))
for (
let si = mostRightIndexToCheck, struct = structs[si];
si > 0 && struct.id.clock >= deleteItem.clock;
struct = structs[--si]
) {
tryToMergeWithLeft(structs, si)
}
}
}
}
/**
* @param {DeleteSet} ds
* @param {StructStore} store
* @param {function(Item):boolean} gcFilter
*/
export const tryGc = (ds, store, gcFilter) => {
tryGcDeleteSet(ds, store, gcFilter)
tryMergeDeleteSet(ds, store)
}
/**
* @param {Array<Transaction>} transactionCleanups
* @param {number} i
@@ -201,63 +280,12 @@ const cleanupTransactions = (transactionCleanups, i) => {
})
callAll(fs, [])
} finally {
/**
* @param {Array<AbstractStruct>} structs
* @param {number} pos
*/
const tryToMergeWithLeft = (structs, pos) => {
const left = structs[pos - 1]
const right = structs[pos]
if (left.deleted === right.deleted && left.constructor === right.constructor) {
if (left.mergeWith(right)) {
structs.splice(pos, 1)
if (right instanceof Item && right.parentSub !== null && right.parent._map.get(right.parentSub) === right) {
right.parent._map.set(right.parentSub, /** @type {Item} */ (left))
}
}
}
}
// Replace deleted items with ItemDeleted / GC.
// This is where content is actually remove from the Yjs Doc.
if (doc.gc) {
for (const [client, deleteItems] of ds.clients) {
const structs = /** @type {Array<AbstractStruct>} */ (store.clients.get(client))
for (let di = deleteItems.length - 1; di >= 0; di--) {
const deleteItem = deleteItems[di]
const endDeleteItemClock = deleteItem.clock + deleteItem.len
for (
let si = findIndexSS(structs, deleteItem.clock), struct = structs[si];
si < structs.length && struct.id.clock < endDeleteItemClock;
struct = structs[++si]
) {
const struct = structs[si]
if (deleteItem.clock + deleteItem.len <= struct.id.clock) {
break
}
if (struct instanceof Item && struct.deleted && !struct.keep) {
struct.gc(store, false)
}
}
}
}
}
// try to merge deleted / gc'd items
// merge from right to left for better efficiecy and so we don't miss any merge targets
for (const [client, deleteItems] of ds.clients) {
const structs = /** @type {Array<AbstractStruct>} */ (store.clients.get(client))
for (let di = deleteItems.length - 1; di >= 0; di--) {
const deleteItem = deleteItems[di]
// start with merging the item next to the last deleted item
const mostRightIndexToCheck = math.min(structs.length - 1, 1 + findIndexSS(structs, deleteItem.clock + deleteItem.len - 1))
for (
let si = mostRightIndexToCheck, struct = structs[si];
si > 0 && struct.id.clock >= deleteItem.clock;
struct = structs[--si]
) {
tryToMergeWithLeft(structs, si)
}
}
tryGcDeleteSet(ds, store, doc.gcFilter)
}
tryMergeDeleteSet(ds, store)
// on all affected store.clients props, try to merge
for (const [client, clock] of transaction.afterState) {
@@ -310,7 +338,6 @@ const cleanupTransactions = (transactionCleanups, i) => {
* @param {function(Transaction):void} f
* @param {any} [origin=true]
*
* @private
* @function
*/
export const transact = (doc, f, origin = null, local = true) => {

View File

@@ -199,13 +199,32 @@ export class UndoManager extends Observable {
// 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))) {
keepItem(item)
keepItem(item, true)
}
})
this.emit('stack-item-added', [{ stackItem: stack[stack.length - 1], origin: transaction.origin, type: undoing ? 'redo' : 'undo' }, this])
})
}
clear () {
this.doc.transact(transaction => {
/**
* @param {StackItem} stackItem
*/
const clearItem = stackItem => {
iterateDeletedStructs(transaction, stackItem.ds, item => {
if (item instanceof Item && this.scope.some(type => isParentOf(type, item))) {
keepItem(item, false)
}
})
}
this.undoStack.forEach(clearItem)
this.redoStack.forEach(clearItem)
})
this.undoStack = []
this.redoStack = []
}
/**
* UndoManager merges Undo-StackItem if they are created within time-gap
* smaller than `options.captureTimeout`. Call `um.stopCapturing()` so that the next

View File

@@ -181,7 +181,7 @@ export class YEvent {
})
this._changes = changes
}
return changes
return /** @type {any} */ (changes)
}
}

View File

@@ -12,6 +12,7 @@ import * as prng from 'lib0/prng.js'
import * as encoding from 'lib0/encoding.js'
import * as decoding from 'lib0/decoding.js'
import * as syncProtocol from 'y-protocols/sync.js'
import * as object from 'lib0/object.js'
export * from '../src/internals.js'
/**
@@ -55,6 +56,7 @@ export class TestYInstance extends Doc {
})
this.connect()
}
/**
* Disconnect from TestConnector.
*/
@@ -62,6 +64,7 @@ export class TestYInstance extends Doc {
this.receiving = new Map()
this.tc.onlineConns.delete(this)
}
/**
* Append yourself to the list of known Y instances in testconnector.
* Also initiate sync with all clients.
@@ -83,6 +86,7 @@ export class TestYInstance extends Doc {
})
}
}
/**
* Receive a message from another client. This message is only appended to the list of receiving messages.
* TestConnector decides when this client actually reads this message.
@@ -124,6 +128,7 @@ export class TestConnector {
*/
this.prng = gen
}
/**
* Create a new Y instance and add it to the list of connections
* @param {number} clientID
@@ -131,6 +136,7 @@ export class TestConnector {
createY (clientID) {
return new TestYInstance(this, clientID)
}
/**
* Choose random connection and flush a random message from a random sender.
*
@@ -162,6 +168,7 @@ export class TestConnector {
}
return false
}
/**
* @return {boolean} True iff this function actually flushed something
*/
@@ -172,16 +179,20 @@ export class TestConnector {
}
return didSomething
}
reconnectAll () {
this.allConns.forEach(conn => conn.connect())
}
disconnectAll () {
this.allConns.forEach(conn => conn.disconnect())
}
syncAll () {
this.reconnectAll()
this.flushAllMessages()
}
/**
* @return {boolean} Whether it was possible to disconnect a randon connection.
*/
@@ -192,6 +203,7 @@ export class TestConnector {
prng.oneOf(this.prng, Array.from(this.onlineConns)).disconnect()
return true
}
/**
* @return {boolean} Whether it was possible to reconnect a random connection.
*/
@@ -270,12 +282,12 @@ export const compare = users => {
// Test Map iterator
const ymapkeys = Array.from(users[0].getMap('map').keys())
t.assert(ymapkeys.length === Object.keys(userMapValues[0]).length)
ymapkeys.forEach(key => t.assert(userMapValues[0].hasOwnProperty(key)))
ymapkeys.forEach(key => t.assert(object.hasProperty(userMapValues[0], key)))
/**
* @type {Object<string,any>}
*/
const mapRes = {}
for (let [k, v] of users[0].getMap('map')) {
for (const [k, v] of users[0].getMap('map')) {
mapRes[k] = v instanceof Y.AbstractType ? v.toJSON() : v
}
t.compare(userMapValues[0], mapRes)

View File

@@ -69,11 +69,11 @@ export const testUndoMap = tc => {
const subType = new Y.Map()
map0.set('a', subType)
subType.set('x', 42)
t.compare(map0.toJSON(), /** @type {any} */ ({ 'a': { x: 42 } }))
t.compare(map0.toJSON(), /** @type {any} */ ({ a: { x: 42 } }))
undoManager.undo()
t.assert(map0.get('a') === 1)
undoManager.redo()
t.compare(map0.toJSON(), /** @type {any} */ ({ 'a': { x: 42 } }))
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)

View File

@@ -227,7 +227,7 @@ export const testInsertAndDeleteEventsForTypes2 = tc => {
/**
* @type {Array<Object<string,any>>}
*/
let events = []
const events = []
array0.observe(e => {
events.push(e)
})
@@ -318,7 +318,7 @@ export const testIteratingArrayContainingTypes = tc => {
arr.push([map])
}
let cnt = 0
for (let item of arr) {
for (const item of arr) {
t.assert(item.get('value') === cnt++, 'value is correct')
}
y.destroy()

View File

@@ -66,7 +66,7 @@ export const testGetAndSetOfMapProperty = tc => {
testConnector.flushAllMessages()
for (let user of users) {
for (const user of users) {
const u = user.getMap('map')
t.compare(u.get('stuff'), 'stuffy')
t.assert(u.get('undefined') === undefined, 'undefined')
@@ -108,7 +108,7 @@ export const testGetAndSetOfMapPropertySyncs = tc => {
map0.set('stuff', 'stuffy')
t.compare(map0.get('stuff'), 'stuffy')
testConnector.flushAllMessages()
for (let user of users) {
for (const user of users) {
var u = user.getMap('map')
t.compare(u.get('stuff'), 'stuffy')
}
@@ -123,7 +123,7 @@ export const testGetAndSetOfMapPropertyWithConflict = tc => {
map0.set('stuff', 'c0')
map1.set('stuff', 'c1')
testConnector.flushAllMessages()
for (let user of users) {
for (const user of users) {
var u = user.getMap('map')
t.compare(u.get('stuff'), 'c1')
}
@@ -139,7 +139,7 @@ export const testGetAndSetAndDeleteOfMapProperty = tc => {
map1.set('stuff', 'c1')
map1.delete('stuff')
testConnector.flushAllMessages()
for (let user of users) {
for (const user of users) {
var u = user.getMap('map')
t.assert(u.get('stuff') === undefined)
}
@@ -156,7 +156,7 @@ export const testGetAndSetOfMapPropertyWithThreeConflicts = tc => {
map1.set('stuff', 'c2')
map2.set('stuff', 'c3')
testConnector.flushAllMessages()
for (let user of users) {
for (const user of users) {
var u = user.getMap('map')
t.compare(u.get('stuff'), 'c3')
}
@@ -179,7 +179,7 @@ export const testGetAndSetAndDeleteOfMapPropertyWithThreeConflicts = tc => {
map3.set('stuff', 'c3')
map3.delete('stuff')
testConnector.flushAllMessages()
for (let user of users) {
for (const user of users) {
var u = user.getMap('map')
t.assert(u.get('stuff') === undefined)
}
@@ -430,12 +430,12 @@ export const testYmapEventHasCorrectValueWhenSettingAPrimitiveFromOtherUser = tc
*/
const mapTransactions = [
function set (user, gen) {
let key = prng.oneOf(gen, ['one', 'two'])
const key = prng.oneOf(gen, ['one', 'two'])
var value = prng.utf16String(gen)
user.getMap('map').set(key, value)
},
function setType (user, gen) {
let key = prng.oneOf(gen, ['one', 'two'])
const key = prng.oneOf(gen, ['one', 'two'])
var type = prng.oneOf(gen, [new Y.Array(), new Y.Map()])
user.getMap('map').set(key, type)
if (type instanceof Y.Array) {
@@ -445,7 +445,7 @@ const mapTransactions = [
}
},
function _delete (user, gen) {
let key = prng.oneOf(gen, ['one', 'two'])
const key = prng.oneOf(gen, ['one', 'two'])
user.getMap('map').delete(key)
}
]

View File

@@ -149,3 +149,12 @@ export const testSnapshotDeleteAfter = tc => {
const state1 = text0.toDelta(snapshot1)
t.compare(state1, [{ insert: 'abcd' }])
}
/**
* @param {t.TestCase} tc
*/
export const testToJson = tc => {
const { text0 } = init(tc, { users: 1 })
text0.insert(0, 'abc', { bold: true })
t.assert(text0.toJSON() === 'abc', 'toJSON returns the unformatted text')
}

View File

@@ -60,13 +60,13 @@ export const testEvents = tc => {
*/
export const testTreewalker = tc => {
const { users, xml0 } = init(tc, { users: 3 })
let paragraph1 = new Y.XmlElement('p')
let paragraph2 = new Y.XmlElement('p')
let text1 = new Y.XmlText('init')
let text2 = new Y.XmlText('text')
const paragraph1 = new Y.XmlElement('p')
const paragraph2 = new Y.XmlElement('p')
const text1 = new Y.XmlText('init')
const text2 = new Y.XmlText('text')
paragraph1.insert(0, [text1, text2])
xml0.insert(0, [paragraph1, paragraph2, new Y.XmlElement('img')])
let allParagraphs = xml0.querySelectorAll('p')
const allParagraphs = xml0.querySelectorAll('p')
t.assert(allParagraphs.length === 2, 'found exactly two paragraphs')
t.assert(allParagraphs[0] === paragraph1, 'querySelectorAll found paragraph1')
t.assert(allParagraphs[1] === paragraph2, 'querySelectorAll found paragraph2')

View File

@@ -6,15 +6,15 @@
"allowJs": true, /* Allow javascript files to be compiled. */
"checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
"declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./build", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "outFile": "./dist/yjs.js", /* Concatenate and emit output to single file. */
"outDir": "./dist", /* Redirect output structure to the directory. */
"rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true, /* Do not emit outputs. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
@@ -22,6 +22,7 @@
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"emitDeclarationOnly": true,
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
@@ -62,6 +63,6 @@
"maxNodeModuleJsDepth": 5,
// "types": ["./src/utils/typedefs.js"]
},
"include": ["./src/**/*", "./tests/**/*"],
"include": ["./src/**/*.js", "./tests/**/*.js"],
"exclude": ["../lib0/**/*", "node_modules/**/*", "dist", "dist/**/*.js"]
}